⚗ writing telemetry

This commit is contained in:
2026-06-08 20:33:43 +10:00
parent 9a39fe6cb5
commit 27abb8b843
9 changed files with 332 additions and 5 deletions

View File

@@ -47,6 +47,18 @@ export interface RevisionMeta {
wordCount: number
}
export interface TelemetrySession {
id: string
storyId: string
date: string // YYYY-MM-DD local date
startedAt: number // ms timestamp
endedAt: number // ms timestamp
wordsStart: number
wordsEnd: number
activeMs: number // typing time, excluding idle gaps
wpm: number
}
// ─── Path helpers ─────────────────────────────────────────────────────────────
const borgesDir = (): string => join(getCollectionRoot(), '.borges')
@@ -56,6 +68,7 @@ const sessionFile = (): string => join(borgesDir(), 'session.json')
const marketsFile = (): string => join(borgesDir(), 'markets.json')
const submissionsFile = (): string => join(borgesDir(), 'submissions.json')
const revisionsDir = (): string => join(borgesDir(), 'revisions')
const telemetryFile = (): string => join(borgesDir(), 'telemetry.json')
const MAX_REVISIONS = 50
@@ -320,3 +333,23 @@ export async function loadRevision(filePath: string, revisionId: string): Promis
const raw = JSON.parse(await readFile(revPath, 'utf-8'))
return raw.content
}
// ─── Telemetry ────────────────────────────────────────────────────────────────
export async function appendTelemetrySession(session: TelemetrySession): Promise<void> {
await mkdir(borgesDir(), { recursive: true })
let sessions: TelemetrySession[] = []
try {
sessions = JSON.parse(await readFile(telemetryFile(), 'utf-8'))
} catch { /* first write */ }
sessions.push(session)
await writeFile(telemetryFile(), JSON.stringify(sessions, null, 2), 'utf-8')
}
export async function readTelemetry(): Promise<TelemetrySession[]> {
try {
return JSON.parse(await readFile(telemetryFile(), 'utf-8'))
} catch {
return []
}
}

View File

@@ -5,9 +5,10 @@ import {
saveOrderList, readSession, writeSession,
listMarkets, upsertMarket, deleteMarket,
listSubmissions, addSubmission, updateSubmission,
saveRevision, listRevisions, loadRevision
saveRevision, listRevisions, loadRevision,
appendTelemetrySession, readTelemetry
} from './fileSystem'
import type { Market, Submission, StoryMeta } from './fileSystem'
import type { Market, Submission, StoryMeta, TelemetrySession } from './fileSystem'
import { streamMessage, streamPrompt, resetClient } from './aiService'
import type { AIPayload } from './aiService'
import { readGlobalConfig, writeGlobalConfig } from './globalConfig'
@@ -48,6 +49,10 @@ export function registerIpcHandlers(): void {
ipcMain.handle('revisions:list', async (_e, path: string) => listRevisions(path))
ipcMain.handle('revisions:load', async (_e, path: string, id: string) => loadRevision(path, id))
// ── Telemetry ─────────────────────────────────────────────────────────────────
ipcMain.handle('telemetry:append', async (_e, session: TelemetrySession) => appendTelemetrySession(session))
ipcMain.handle('telemetry:read', async () => readTelemetry())
// ── Config ────────────────────────────────────────────────────────────────────
ipcMain.handle('config:read', async () => readGlobalConfig())
ipcMain.handle('config:write', async (_e, updates: Partial<GlobalConfig>) => {