@runtimescope/collector 0.7.0 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/ring-buffer.ts","../src/store.ts","../src/sqlite-store.ts","../src/rate-limiter.ts","../src/tls.ts","../src/server.ts","../src/project-manager.ts","../src/auth.ts","../src/redactor.ts","../src/session-manager.ts","../src/http-server.ts","../src/pm/pm-routes.ts","../src/pm/pm-store.ts","../src/pm/session-parser.ts","../src/pm/project-discovery.ts"],"sourcesContent":["/**\n * Fixed-size circular buffer. When full, new items overwrite the oldest.\n * Optimized for append-heavy workloads with periodic filtered scans.\n */\nexport class RingBuffer<T> {\n private buffer: (T | undefined)[];\n private head: number = 0;\n private _count: number = 0;\n\n constructor(private capacity: number) {\n this.buffer = new Array(capacity);\n }\n\n get count(): number {\n return this._count;\n }\n\n push(item: T): void {\n this.buffer[this.head] = item;\n this.head = (this.head + 1) % this.capacity;\n if (this._count < this.capacity) this._count++;\n }\n\n /** Returns all items from oldest to newest. */\n toArray(): T[] {\n if (this._count === 0) return [];\n const result: T[] = [];\n const start = this._count < this.capacity ? 0 : this.head;\n for (let i = 0; i < this._count; i++) {\n const idx = (start + i) % this.capacity;\n result.push(this.buffer[idx] as T);\n }\n return result;\n }\n\n /** Returns matching items from newest to oldest (most recent first). */\n query(predicate: (item: T) => boolean): T[] {\n if (this._count === 0) return [];\n const result: T[] = [];\n const start = this._count < this.capacity ? 0 : this.head;\n for (let i = this._count - 1; i >= 0; i--) {\n const idx = (start + i) % this.capacity;\n const item = this.buffer[idx] as T;\n if (predicate(item)) result.push(item);\n }\n return result;\n }\n\n clear(): void {\n this.buffer = new Array(this.capacity);\n this.head = 0;\n this._count = 0;\n }\n}\n","import { RingBuffer } from './ring-buffer.js';\nimport type { SqliteStore } from './sqlite-store.js';\nimport type { Redactor } from './redactor.js';\nimport type {\n RuntimeEvent,\n NetworkEvent,\n ConsoleEvent,\n SessionEvent,\n StateEvent,\n RenderEvent,\n PerformanceEvent,\n DatabaseEvent,\n CustomEvent,\n CustomEventFilter,\n ReconMetadataEvent,\n ReconDesignTokensEvent,\n ReconFontsEvent,\n ReconLayoutTreeEvent,\n ReconAccessibilityEvent,\n ReconComputedStylesEvent,\n ReconElementSnapshotEvent,\n ReconAssetInventoryEvent,\n NetworkFilter,\n ConsoleFilter,\n StateFilter,\n RenderFilter,\n PerformanceFilter,\n DatabaseFilter,\n ReconFilter,\n ReconEventType,\n SessionInfo,\n TimelineFilter,\n EventType,\n} from './types.js';\n\nexport class EventStore {\n private buffer: RingBuffer<RuntimeEvent>;\n private sessions: Map<string, SessionInfo> = new Map();\n private sqliteStore: SqliteStore | null = null;\n private currentProject: string | null = null;\n private onEventCallbacks: ((event: RuntimeEvent) => void)[] = [];\n private redactor: Redactor | null = null;\n\n constructor(capacity = 10_000) {\n this.buffer = new RingBuffer<RuntimeEvent>(capacity);\n }\n\n setRedactor(redactor: Redactor): void {\n this.redactor = redactor;\n }\n\n get eventCount(): number {\n return this.buffer.count;\n }\n\n setSqliteStore(store: SqliteStore, project: string): void {\n this.sqliteStore = store;\n this.currentProject = project;\n }\n\n onEvent(callback: (event: RuntimeEvent) => void): void {\n this.onEventCallbacks.push(callback);\n }\n\n removeEventListener(callback: (event: RuntimeEvent) => void): void {\n const idx = this.onEventCallbacks.indexOf(callback);\n if (idx !== -1) this.onEventCallbacks.splice(idx, 1);\n }\n\n addEvent(event: RuntimeEvent): void {\n // Apply redaction before storing (defense in depth)\n if (this.redactor?.isEnabled()) {\n event = this.redactor.redactEvent(event);\n }\n\n this.buffer.push(event);\n\n if (event.eventType === 'session') {\n const se = event as SessionEvent;\n this.sessions.set(se.sessionId, {\n sessionId: se.sessionId,\n appName: se.appName,\n connectedAt: se.connectedAt,\n sdkVersion: se.sdkVersion,\n eventCount: 0,\n isConnected: true,\n });\n }\n\n const session = this.sessions.get(event.sessionId);\n if (session) session.eventCount++;\n\n // SQLite dual-write\n if (this.sqliteStore && this.currentProject) {\n this.sqliteStore.addEvent(event, this.currentProject);\n }\n\n // Notify listeners\n for (const cb of this.onEventCallbacks) {\n try {\n cb(event);\n } catch {\n // Don't let listener errors break event ingestion\n }\n }\n }\n\n getNetworkRequests(filter: NetworkFilter = {}): NetworkEvent[] {\n const since = filter.sinceSeconds\n ? Date.now() - filter.sinceSeconds * 1000\n : 0;\n\n return this.buffer.query((e) => {\n if (e.eventType !== 'network') return false;\n if (filter.sessionId && e.sessionId !== filter.sessionId) return false;\n const ne = e as NetworkEvent;\n if (ne.timestamp < since) return false;\n if (filter.urlPattern && !ne.url.includes(filter.urlPattern)) return false;\n if (filter.status !== undefined && ne.status !== filter.status) return false;\n if (filter.method && ne.method.toUpperCase() !== filter.method.toUpperCase())\n return false;\n return true;\n }) as NetworkEvent[];\n }\n\n getConsoleMessages(filter: ConsoleFilter = {}): ConsoleEvent[] {\n const since = filter.sinceSeconds\n ? Date.now() - filter.sinceSeconds * 1000\n : 0;\n\n return this.buffer.query((e) => {\n if (e.eventType !== 'console') return false;\n if (filter.sessionId && e.sessionId !== filter.sessionId) return false;\n const ce = e as ConsoleEvent;\n if (ce.timestamp < since) return false;\n if (filter.level && ce.level !== filter.level) return false;\n if (filter.search && !ce.message.toLowerCase().includes(filter.search.toLowerCase()))\n return false;\n return true;\n }) as ConsoleEvent[];\n }\n\n getSessionInfo(): SessionInfo[] {\n return Array.from(this.sessions.values());\n }\n\n markDisconnected(sessionId: string): void {\n const s = this.sessions.get(sessionId);\n if (s) s.isConnected = false;\n }\n\n getEventTimeline(filter: TimelineFilter = {}): RuntimeEvent[] {\n const since = filter.sinceSeconds\n ? Date.now() - filter.sinceSeconds * 1000\n : 0;\n const typeSet = filter.eventTypes\n ? new Set<EventType>(filter.eventTypes)\n : null;\n\n // toArray returns oldest→newest (chronological order)\n return this.buffer.toArray().filter((e) => {\n if (e.timestamp < since) return false;\n if (filter.sessionId && e.sessionId !== filter.sessionId) return false;\n if (typeSet && !typeSet.has(e.eventType)) return false;\n return true;\n });\n }\n\n getAllEvents(sinceSeconds?: number, sessionId?: string): RuntimeEvent[] {\n const since = sinceSeconds ? Date.now() - sinceSeconds * 1000 : 0;\n return this.buffer.toArray().filter((e) => {\n if (e.timestamp < since) return false;\n if (sessionId && e.sessionId !== sessionId) return false;\n return true;\n });\n }\n\n getSessionIdsForProject(appName: string): string[] {\n return Array.from(this.sessions.values())\n .filter((s) => s.appName === appName)\n .map((s) => s.sessionId);\n }\n\n getStateEvents(filter: StateFilter = {}): StateEvent[] {\n const since = filter.sinceSeconds\n ? Date.now() - filter.sinceSeconds * 1000\n : 0;\n\n return this.buffer.query((e) => {\n if (e.eventType !== 'state') return false;\n if (filter.sessionId && e.sessionId !== filter.sessionId) return false;\n const se = e as StateEvent;\n if (se.timestamp < since) return false;\n if (filter.storeId && se.storeId !== filter.storeId) return false;\n return true;\n }) as StateEvent[];\n }\n\n getRenderEvents(filter: RenderFilter = {}): RenderEvent[] {\n const since = filter.sinceSeconds\n ? Date.now() - filter.sinceSeconds * 1000\n : 0;\n\n return this.buffer.query((e) => {\n if (e.eventType !== 'render') return false;\n if (filter.sessionId && e.sessionId !== filter.sessionId) return false;\n const re = e as RenderEvent;\n if (re.timestamp < since) return false;\n if (filter.componentName) {\n const hasMatch = re.profiles.some((p) =>\n p.componentName.toLowerCase().includes(filter.componentName!.toLowerCase())\n );\n if (!hasMatch) return false;\n }\n return true;\n }) as RenderEvent[];\n }\n\n getPerformanceMetrics(filter: PerformanceFilter = {}): PerformanceEvent[] {\n const since = filter.sinceSeconds\n ? Date.now() - filter.sinceSeconds * 1000\n : 0;\n\n return this.buffer.query((e) => {\n if (e.eventType !== 'performance') return false;\n if (filter.sessionId && e.sessionId !== filter.sessionId) return false;\n const pe = e as PerformanceEvent;\n if (pe.timestamp < since) return false;\n if (filter.metricName && pe.metricName !== filter.metricName) return false;\n return true;\n }) as PerformanceEvent[];\n }\n\n getDatabaseEvents(filter: DatabaseFilter = {}): DatabaseEvent[] {\n const since = filter.sinceSeconds\n ? Date.now() - filter.sinceSeconds * 1000\n : 0;\n\n return this.buffer.query((e) => {\n if (e.eventType !== 'database') return false;\n if (filter.sessionId && e.sessionId !== filter.sessionId) return false;\n const de = e as DatabaseEvent;\n if (de.timestamp < since) return false;\n if (filter.table) {\n const hasTable = de.tablesAccessed.some(\n (t) => t.toLowerCase() === filter.table!.toLowerCase()\n );\n if (!hasTable) return false;\n }\n if (filter.minDurationMs !== undefined && de.duration < filter.minDurationMs) return false;\n if (filter.search && !de.query.toLowerCase().includes(filter.search.toLowerCase()))\n return false;\n if (filter.operation && de.operation !== filter.operation) return false;\n if (filter.source && de.source !== filter.source) return false;\n return true;\n }) as DatabaseEvent[];\n }\n\n getCustomEvents(filter: CustomEventFilter = {}): CustomEvent[] {\n const since = filter.sinceSeconds\n ? Date.now() - filter.sinceSeconds * 1000\n : 0;\n\n return this.buffer.query((e) => {\n if (e.eventType !== 'custom') return false;\n if (filter.sessionId && e.sessionId !== filter.sessionId) return false;\n const ce = e as CustomEvent;\n if (ce.timestamp < since) return false;\n if (filter.name && ce.name !== filter.name) return false;\n return true;\n }) as CustomEvent[];\n }\n\n // ============================================================\n // Recon event queries — returns the most recent event of each type\n // ============================================================\n\n private getLatestReconEvent<T extends RuntimeEvent>(\n eventType: ReconEventType,\n filter: ReconFilter = {},\n ): T | null {\n const since = filter.sinceSeconds\n ? Date.now() - filter.sinceSeconds * 1000\n : 0;\n\n // query() returns newest-first, so the first match is the most recent\n const results = this.buffer.query((e) => {\n if (e.eventType !== eventType) return false;\n if (filter.sessionId && e.sessionId !== filter.sessionId) return false;\n if (e.timestamp < since) return false;\n if (filter.url) {\n const re = e as unknown as { url?: string };\n if (re.url && !re.url.includes(filter.url)) return false;\n }\n return true;\n });\n\n return (results[0] as T) ?? null;\n }\n\n private getReconEvents<T extends RuntimeEvent>(\n eventType: ReconEventType,\n filter: ReconFilter = {},\n ): T[] {\n const since = filter.sinceSeconds\n ? Date.now() - filter.sinceSeconds * 1000\n : 0;\n\n return this.buffer.query((e) => {\n if (e.eventType !== eventType) return false;\n if (filter.sessionId && e.sessionId !== filter.sessionId) return false;\n if (e.timestamp < since) return false;\n if (filter.url) {\n const re = e as unknown as { url?: string };\n if (re.url && !re.url.includes(filter.url)) return false;\n }\n return true;\n }) as T[];\n }\n\n getReconMetadata(filter: ReconFilter = {}): ReconMetadataEvent | null {\n return this.getLatestReconEvent<ReconMetadataEvent>('recon_metadata', filter);\n }\n\n getReconDesignTokens(filter: ReconFilter = {}): ReconDesignTokensEvent | null {\n return this.getLatestReconEvent<ReconDesignTokensEvent>('recon_design_tokens', filter);\n }\n\n getReconFonts(filter: ReconFilter = {}): ReconFontsEvent | null {\n return this.getLatestReconEvent<ReconFontsEvent>('recon_fonts', filter);\n }\n\n getReconLayoutTree(filter: ReconFilter = {}): ReconLayoutTreeEvent | null {\n return this.getLatestReconEvent<ReconLayoutTreeEvent>('recon_layout_tree', filter);\n }\n\n getReconAccessibility(filter: ReconFilter = {}): ReconAccessibilityEvent | null {\n return this.getLatestReconEvent<ReconAccessibilityEvent>('recon_accessibility', filter);\n }\n\n getReconComputedStyles(filter: ReconFilter = {}): ReconComputedStylesEvent[] {\n return this.getReconEvents<ReconComputedStylesEvent>('recon_computed_styles', filter);\n }\n\n getReconElementSnapshots(filter: ReconFilter = {}): ReconElementSnapshotEvent[] {\n return this.getReconEvents<ReconElementSnapshotEvent>('recon_element_snapshot', filter);\n }\n\n getReconAssetInventory(filter: ReconFilter = {}): ReconAssetInventoryEvent | null {\n return this.getLatestReconEvent<ReconAssetInventoryEvent>('recon_asset_inventory', filter);\n }\n\n clear(): { clearedCount: number } {\n const count = this.buffer.count;\n this.buffer.clear();\n this.sessions.clear();\n return { clearedCount: count };\n }\n}\n","import Database from 'better-sqlite3';\nimport type {\n RuntimeEvent,\n HistoricalFilter,\n SessionInfoExtended,\n SessionMetrics,\n SessionSnapshot,\n EventType,\n} from './types.js';\n\n// ============================================================\n// SQLite Persistence Layer\n// Uses write buffering for high-throughput event ingestion\n// ============================================================\n\nexport interface SqliteStoreOptions {\n dbPath: string;\n walMode?: boolean;\n flushIntervalMs?: number;\n batchSize?: number;\n}\n\nexport class SqliteStore {\n private db: InstanceType<typeof Database>;\n private writeBuffer: { event: RuntimeEvent; project: string }[] = [];\n private flushTimer: ReturnType<typeof setInterval> | null = null;\n private readonly batchSize: number;\n\n private insertEventStmt: Database.Statement;\n private insertSessionStmt: Database.Statement;\n private updateSessionDisconnectedStmt: Database.Statement;\n\n constructor(options: SqliteStoreOptions) {\n this.db = new Database(options.dbPath);\n this.batchSize = options.batchSize ?? 50;\n\n if (options.walMode !== false) {\n this.db.pragma('journal_mode = WAL');\n }\n this.db.pragma('synchronous = NORMAL');\n\n this.createSchema();\n\n // Prepare statements\n this.insertEventStmt = this.db.prepare(`\n INSERT INTO events (event_id, session_id, project, event_type, timestamp, data)\n VALUES (?, ?, ?, ?, ?, ?)\n `);\n\n this.insertSessionStmt = this.db.prepare(`\n INSERT OR REPLACE INTO sessions (\n session_id, project, app_name, connected_at, sdk_version,\n event_count, is_connected, build_meta\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n `);\n\n this.updateSessionDisconnectedStmt = this.db.prepare(`\n UPDATE sessions SET is_connected = 0, disconnected_at = ? WHERE session_id = ?\n `);\n\n // Start flush timer\n const flushInterval = options.flushIntervalMs ?? 100;\n this.flushTimer = setInterval(() => this.flush(), flushInterval);\n }\n\n private createSchema(): void {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n event_id TEXT NOT NULL UNIQUE,\n session_id TEXT NOT NULL,\n project TEXT NOT NULL,\n event_type TEXT NOT NULL,\n timestamp INTEGER NOT NULL,\n data TEXT NOT NULL\n );\n\n CREATE INDEX IF NOT EXISTS idx_events_session ON events(session_id);\n CREATE INDEX IF NOT EXISTS idx_events_type ON events(event_type);\n CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp);\n CREATE INDEX IF NOT EXISTS idx_events_type_timestamp ON events(event_type, timestamp);\n CREATE INDEX IF NOT EXISTS idx_events_project ON events(project);\n\n CREATE TABLE IF NOT EXISTS sessions (\n session_id TEXT PRIMARY KEY,\n project TEXT NOT NULL,\n app_name TEXT NOT NULL,\n connected_at INTEGER NOT NULL,\n disconnected_at INTEGER,\n sdk_version TEXT NOT NULL,\n event_count INTEGER DEFAULT 0,\n is_connected INTEGER DEFAULT 1,\n build_meta TEXT\n );\n\n CREATE INDEX IF NOT EXISTS idx_sessions_project ON sessions(project);\n\n CREATE TABLE IF NOT EXISTS session_snapshots (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n session_id TEXT NOT NULL,\n project TEXT NOT NULL,\n label TEXT,\n metrics TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n FOREIGN KEY (session_id) REFERENCES sessions(session_id)\n );\n\n CREATE INDEX IF NOT EXISTS idx_snapshots_session ON session_snapshots(session_id);\n CREATE INDEX IF NOT EXISTS idx_snapshots_project ON session_snapshots(project, created_at);\n `);\n\n // Migrate from old session_metrics table if it exists\n this.migrateSessionMetrics();\n }\n\n // --- Write Operations ---\n\n addEvent(event: RuntimeEvent, project: string): void {\n this.writeBuffer.push({ event, project });\n if (this.writeBuffer.length >= this.batchSize) {\n this.flush();\n }\n }\n\n flush(): void {\n if (this.writeBuffer.length === 0) return;\n\n const batch = this.writeBuffer.splice(0);\n const insertMany = this.db.transaction(() => {\n for (const { event, project } of batch) {\n try {\n this.insertEventStmt.run(\n event.eventId,\n event.sessionId,\n project,\n event.eventType,\n event.timestamp,\n JSON.stringify(event)\n );\n } catch {\n // Ignore duplicate event_id (UNIQUE constraint)\n }\n }\n });\n\n try {\n insertMany();\n } catch (err) {\n console.error('[RuntimeScope] SQLite flush error:', (err as Error).message);\n }\n }\n\n saveSession(info: SessionInfoExtended): void {\n this.insertSessionStmt.run(\n info.sessionId,\n info.project,\n info.appName,\n info.connectedAt,\n info.sdkVersion,\n info.eventCount,\n info.isConnected ? 1 : 0,\n info.buildMeta ? JSON.stringify(info.buildMeta) : null\n );\n }\n\n updateSessionDisconnected(sessionId: string, disconnectedAt: number): void {\n this.updateSessionDisconnectedStmt.run(disconnectedAt, sessionId);\n }\n\n saveSessionMetrics(sessionId: string, project: string, metrics: unknown, label?: string): void {\n this.db.prepare(`\n INSERT INTO session_snapshots (session_id, project, label, metrics, created_at)\n VALUES (?, ?, ?, ?, ?)\n `).run(sessionId, project, label ?? null, JSON.stringify(metrics), Date.now());\n }\n\n // --- Read Operations ---\n\n getEvents(filter: HistoricalFilter): RuntimeEvent[] {\n const conditions: string[] = [];\n const params: unknown[] = [];\n\n if (filter.project) {\n conditions.push('project = ?');\n params.push(filter.project);\n }\n if (filter.sessionId) {\n conditions.push('session_id = ?');\n params.push(filter.sessionId);\n }\n if (filter.eventTypes && filter.eventTypes.length > 0) {\n const placeholders = filter.eventTypes.map(() => '?').join(', ');\n conditions.push(`event_type IN (${placeholders})`);\n params.push(...filter.eventTypes);\n }\n if (filter.since) {\n conditions.push('timestamp >= ?');\n params.push(filter.since);\n }\n if (filter.until) {\n conditions.push('timestamp <= ?');\n params.push(filter.until);\n }\n\n const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';\n params.push(filter.limit ?? 1000);\n params.push(filter.offset ?? 0);\n\n const rows = this.db\n .prepare(`SELECT data FROM events ${where} ORDER BY timestamp ASC LIMIT ? OFFSET ?`)\n .all(...params) as { data: string }[];\n\n return rows.map((row) => JSON.parse(row.data) as RuntimeEvent);\n }\n\n getEventCount(filter: HistoricalFilter): number {\n const conditions: string[] = [];\n const params: unknown[] = [];\n\n if (filter.project) {\n conditions.push('project = ?');\n params.push(filter.project);\n }\n if (filter.sessionId) {\n conditions.push('session_id = ?');\n params.push(filter.sessionId);\n }\n if (filter.eventTypes && filter.eventTypes.length > 0) {\n const placeholders = filter.eventTypes.map(() => '?').join(', ');\n conditions.push(`event_type IN (${placeholders})`);\n params.push(...filter.eventTypes);\n }\n if (filter.since) {\n conditions.push('timestamp >= ?');\n params.push(filter.since);\n }\n if (filter.until) {\n conditions.push('timestamp <= ?');\n params.push(filter.until);\n }\n\n const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';\n const row = this.db\n .prepare(`SELECT COUNT(*) as count FROM events ${where}`)\n .get(...params) as { count: number };\n\n return row.count;\n }\n\n getSessions(project: string, limit = 50): SessionInfoExtended[] {\n const rows = this.db\n .prepare(`\n SELECT session_id, project, app_name, connected_at, disconnected_at,\n sdk_version, event_count, is_connected, build_meta\n FROM sessions\n WHERE project = ?\n ORDER BY connected_at DESC\n LIMIT ?\n `)\n .all(project, limit) as {\n session_id: string;\n project: string;\n app_name: string;\n connected_at: number;\n disconnected_at: number | null;\n sdk_version: string;\n event_count: number;\n is_connected: number;\n build_meta: string | null;\n }[];\n\n return rows.map((row) => ({\n sessionId: row.session_id,\n project: row.project,\n appName: row.app_name,\n connectedAt: row.connected_at,\n disconnectedAt: row.disconnected_at ?? undefined,\n sdkVersion: row.sdk_version,\n eventCount: row.event_count,\n isConnected: row.is_connected === 1,\n buildMeta: row.build_meta ? JSON.parse(row.build_meta) : undefined,\n }));\n }\n\n getSessionMetrics(sessionId: string): SessionMetrics | null {\n const row = this.db\n .prepare('SELECT metrics FROM session_snapshots WHERE session_id = ? ORDER BY created_at DESC LIMIT 1')\n .get(sessionId) as { metrics: string } | undefined;\n\n return row ? JSON.parse(row.metrics) as SessionMetrics : null;\n }\n\n getSessionSnapshots(sessionId: string): SessionSnapshot[] {\n const rows = this.db\n .prepare(`\n SELECT id, session_id, project, label, metrics, created_at\n FROM session_snapshots\n WHERE session_id = ?\n ORDER BY created_at ASC\n `)\n .all(sessionId) as {\n id: number;\n session_id: string;\n project: string;\n label: string | null;\n metrics: string;\n created_at: number;\n }[];\n\n return rows.map((row) => ({\n id: row.id,\n sessionId: row.session_id,\n project: row.project,\n label: row.label ?? undefined,\n metrics: JSON.parse(row.metrics) as SessionMetrics,\n createdAt: row.created_at,\n }));\n }\n\n getSnapshotById(snapshotId: number): SessionSnapshot | null {\n const row = this.db\n .prepare('SELECT id, session_id, project, label, metrics, created_at FROM session_snapshots WHERE id = ?')\n .get(snapshotId) as {\n id: number;\n session_id: string;\n project: string;\n label: string | null;\n metrics: string;\n created_at: number;\n } | undefined;\n\n if (!row) return null;\n return {\n id: row.id,\n sessionId: row.session_id,\n project: row.project,\n label: row.label ?? undefined,\n metrics: JSON.parse(row.metrics) as SessionMetrics,\n createdAt: row.created_at,\n };\n }\n\n getEventsByType(project: string, eventType: EventType, sinceMs?: number): RuntimeEvent[] {\n const conditions = ['project = ?', 'event_type = ?'];\n const params: unknown[] = [project, eventType];\n\n if (sinceMs) {\n conditions.push('timestamp >= ?');\n params.push(sinceMs);\n }\n\n const where = conditions.join(' AND ');\n const rows = this.db\n .prepare(`SELECT data FROM events WHERE ${where} ORDER BY timestamp ASC LIMIT 1000`)\n .all(...params) as { data: string }[];\n\n return rows.map((row) => JSON.parse(row.data) as RuntimeEvent);\n }\n\n // --- Migration ---\n\n private migrateSessionMetrics(): void {\n const hasOldTable = this.db.prepare(\n \"SELECT name FROM sqlite_master WHERE type='table' AND name='session_metrics'\"\n ).get();\n\n if (hasOldTable) {\n this.db.exec(`\n INSERT OR IGNORE INTO session_snapshots (session_id, project, label, metrics, created_at)\n SELECT session_id, project, 'auto-disconnect', metrics, created_at\n FROM session_metrics\n `);\n this.db.exec('DROP TABLE session_metrics');\n }\n }\n\n // --- Maintenance ---\n\n deleteOldEvents(beforeTimestamp: number): number {\n const result = this.db\n .prepare('DELETE FROM events WHERE timestamp < ?')\n .run(beforeTimestamp);\n return result.changes;\n }\n\n vacuum(): void {\n this.db.exec('VACUUM');\n }\n\n close(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n this.flush(); // Final flush\n this.db.close();\n }\n}\n","// ============================================================\n// Per-Session Rate Limiter\n// Sliding-window counters to prevent any single SDK from\n// flooding the collector with events\n// ============================================================\n\nexport interface RateLimitConfig {\n maxEventsPerSecond?: number;\n maxEventsPerMinute?: number;\n}\n\ninterface SessionWindow {\n secondCount: number;\n secondStart: number;\n minuteCount: number;\n minuteStart: number;\n lastWarning: number;\n}\n\nexport class SessionRateLimiter {\n private windows: Map<string, SessionWindow> = new Map();\n private maxPerSecond: number;\n private maxPerMinute: number;\n private _droppedTotal = 0;\n\n constructor(config: RateLimitConfig = {}) {\n this.maxPerSecond = config.maxEventsPerSecond ?? Infinity;\n this.maxPerMinute = config.maxEventsPerMinute ?? Infinity;\n }\n\n get droppedTotal(): number {\n return this._droppedTotal;\n }\n\n isEnabled(): boolean {\n return this.maxPerSecond !== Infinity || this.maxPerMinute !== Infinity;\n }\n\n /** Returns true if the event should be accepted, false if rate-limited. */\n allow(sessionId: string): boolean {\n if (!this.isEnabled()) return true;\n\n // Global cap: prevent memory exhaustion from unlimited unique session IDs\n if (this.windows.size > 10_000 && !this.windows.has(sessionId)) {\n this.prune(60_000);\n if (this.windows.size > 10_000) {\n this._droppedTotal++;\n return false;\n }\n }\n\n const now = Date.now();\n let w = this.windows.get(sessionId);\n\n if (!w) {\n w = {\n secondCount: 0,\n secondStart: now,\n minuteCount: 0,\n minuteStart: now,\n lastWarning: 0,\n };\n this.windows.set(sessionId, w);\n }\n\n // Reset second window\n if (now - w.secondStart >= 1000) {\n w.secondCount = 0;\n w.secondStart = now;\n }\n\n // Reset minute window\n if (now - w.minuteStart >= 60_000) {\n w.minuteCount = 0;\n w.minuteStart = now;\n }\n\n // Check per-second limit\n if (w.secondCount >= this.maxPerSecond) {\n this._droppedTotal++;\n this.maybeWarn(sessionId, w, now);\n return false;\n }\n\n // Check per-minute limit\n if (w.minuteCount >= this.maxPerMinute) {\n this._droppedTotal++;\n this.maybeWarn(sessionId, w, now);\n return false;\n }\n\n w.secondCount++;\n w.minuteCount++;\n return true;\n }\n\n /** Allow a batch of N events. Returns the number accepted. */\n allowBatch(sessionId: string, count: number): number {\n let accepted = 0;\n for (let i = 0; i < count; i++) {\n if (this.allow(sessionId)) accepted++;\n else break; // once limited, remaining events in batch are also limited\n }\n return accepted;\n }\n\n /** Remove tracking for sessions that haven't been seen in maxAgeMs. */\n prune(maxAgeMs = 300_000): void {\n const cutoff = Date.now() - maxAgeMs;\n for (const [id, w] of this.windows) {\n if (w.minuteStart < cutoff) {\n this.windows.delete(id);\n }\n }\n }\n\n private maybeWarn(sessionId: string, w: SessionWindow, now: number): void {\n // Log at most once per minute per session\n if (now - w.lastWarning >= 60_000) {\n w.lastWarning = now;\n console.error(\n `[RuntimeScope] Rate limiting session ${sessionId.slice(0, 8)}... (dropped ${this._droppedTotal} total)`\n );\n }\n }\n}\n","import { readFileSync } from 'node:fs';\nimport type { SecureContextOptions } from 'node:tls';\n\n// ============================================================\n// TLS Support\n// Loads certificate files for WSS + HTTPS\n// ============================================================\n\nexport interface TlsConfig {\n certPath: string;\n keyPath: string;\n caPath?: string;\n}\n\n/**\n * Load TLS certificate files and return options suitable for\n * https.createServer() or tls.createSecureContext().\n */\nexport function loadTlsOptions(config: TlsConfig): SecureContextOptions {\n return {\n cert: readFileSync(config.certPath, 'utf-8'),\n key: readFileSync(config.keyPath, 'utf-8'),\n ...(config.caPath ? { ca: readFileSync(config.caPath, 'utf-8') } : {}),\n };\n}\n\n/**\n * Resolve TLS config from environment variables.\n * Returns null if TLS is not configured.\n */\nexport function resolveTlsConfig(): TlsConfig | null {\n const certPath = process.env.RUNTIMESCOPE_TLS_CERT;\n const keyPath = process.env.RUNTIMESCOPE_TLS_KEY;\n\n if (!certPath || !keyPath) return null;\n\n return {\n certPath,\n keyPath,\n caPath: process.env.RUNTIMESCOPE_TLS_CA,\n };\n}\n","import { createServer as createHttpsServer } from 'node:https';\nimport { WebSocketServer, type WebSocket } from 'ws';\nimport { EventStore } from './store.js';\nimport { ProjectManager } from './project-manager.js';\nimport { SqliteStore } from './sqlite-store.js';\nimport { AuthManager } from './auth.js';\nimport { SessionRateLimiter, type RateLimitConfig } from './rate-limiter.js';\nimport { loadTlsOptions, type TlsConfig } from './tls.js';\nimport type {\n WSMessage,\n HandshakePayload,\n EventBatchPayload,\n CommandResponse,\n SessionInfoExtended,\n} from './types.js';\n\nexport interface CollectorServerOptions {\n port?: number;\n host?: string;\n bufferSize?: number;\n maxRetries?: number;\n retryDelayMs?: number;\n projectManager?: ProjectManager;\n authManager?: AuthManager;\n rateLimits?: RateLimitConfig;\n tls?: TlsConfig;\n}\n\ninterface ClientInfo {\n sessionId: string;\n projectName: string;\n}\n\ninterface PendingCommand {\n resolve: (value: unknown) => void;\n reject: (reason: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n}\n\nexport class CollectorServer {\n private wss: WebSocketServer | null = null;\n private store: EventStore;\n private projectManager: ProjectManager | null;\n private authManager: AuthManager | null = null;\n private rateLimiter: SessionRateLimiter;\n private clients: Map<WebSocket, ClientInfo> = new Map();\n private pendingHandshakes: Set<WebSocket> = new Set();\n private pendingCommands: Map<string, PendingCommand> = new Map();\n private sqliteStores: Map<string, SqliteStore> = new Map();\n private connectCallbacks: ((sessionId: string, projectName: string) => void)[] = [];\n private disconnectCallbacks: ((sessionId: string, projectName: string) => void)[] = [];\n private pruneTimer: ReturnType<typeof setInterval> | null = null;\n private tlsConfig: TlsConfig | null = null;\n\n constructor(options: CollectorServerOptions = {}) {\n this.store = new EventStore(options.bufferSize ?? 10_000);\n this.projectManager = options.projectManager ?? null;\n this.authManager = options.authManager ?? null;\n this.rateLimiter = new SessionRateLimiter(options.rateLimits ?? {});\n this.tlsConfig = options.tls ?? null;\n\n if (this.projectManager) {\n this.projectManager.ensureGlobalDir();\n }\n\n // Periodically prune stale rate limiter entries\n if (this.rateLimiter.isEnabled()) {\n this.pruneTimer = setInterval(() => this.rateLimiter.prune(), 60_000);\n }\n }\n\n getStore(): EventStore {\n return this.store;\n }\n\n getPort(): number | null {\n const addr = this.wss?.address();\n return addr && typeof addr === 'object' ? addr.port : null;\n }\n\n getClientCount(): number {\n return this.clients.size;\n }\n\n getProjectManager(): ProjectManager | null {\n return this.projectManager;\n }\n\n getSqliteStore(projectName: string): SqliteStore | undefined {\n return this.sqliteStores.get(projectName);\n }\n\n getSqliteStores(): Map<string, SqliteStore> {\n return this.sqliteStores;\n }\n\n getRateLimiter(): SessionRateLimiter {\n return this.rateLimiter;\n }\n\n onConnect(cb: (sessionId: string, projectName: string) => void): void {\n this.connectCallbacks.push(cb);\n }\n\n onDisconnect(cb: (sessionId: string, projectName: string) => void): void {\n this.disconnectCallbacks.push(cb);\n }\n\n start(options: CollectorServerOptions = {}): Promise<void> {\n const port = options.port ?? 9090;\n const host = options.host ?? '127.0.0.1';\n const maxRetries = options.maxRetries ?? 5;\n const retryDelayMs = options.retryDelayMs ?? 1000;\n const tls = options.tls ?? this.tlsConfig;\n\n return this.tryStart(port, host, maxRetries, retryDelayMs, tls);\n }\n\n private tryStart(\n port: number,\n host: string,\n retriesLeft: number,\n retryDelayMs: number,\n tls?: TlsConfig | null\n ): Promise<void> {\n return new Promise((resolve, reject) => {\n let wss: WebSocketServer;\n\n if (tls) {\n // TLS mode: create HTTPS server, then attach WebSocket to it\n const httpsServer = createHttpsServer(loadTlsOptions(tls));\n wss = new WebSocketServer({ server: httpsServer });\n\n httpsServer.on('listening', () => {\n this.wss = wss;\n this.setupConnectionHandler(wss);\n console.error(`[RuntimeScope] Collector listening on wss://${host}:${port}`);\n resolve();\n });\n\n httpsServer.on('error', (err: NodeJS.ErrnoException) => {\n httpsServer.close();\n this.handleStartError(err, port, host, retriesLeft, retryDelayMs, tls, resolve, reject);\n });\n\n httpsServer.listen(port, host);\n } else {\n // Plain WS mode (default — backward compatible)\n wss = new WebSocketServer({ port, host });\n\n wss.on('listening', () => {\n this.wss = wss;\n this.setupConnectionHandler(wss);\n console.error(`[RuntimeScope] Collector listening on ws://${host}:${port}`);\n resolve();\n });\n\n wss.on('error', (err: NodeJS.ErrnoException) => {\n wss.close();\n this.handleStartError(err, port, host, retriesLeft, retryDelayMs, tls, resolve, reject);\n });\n }\n });\n }\n\n private handleStartError(\n err: NodeJS.ErrnoException,\n port: number,\n host: string,\n retriesLeft: number,\n retryDelayMs: number,\n tls: TlsConfig | null | undefined,\n resolve: () => void,\n reject: (err: Error) => void\n ): void {\n if (err.code === 'EADDRINUSE' && retriesLeft > 0) {\n console.error(\n `[RuntimeScope] Port ${port} in use, retrying in ${retryDelayMs}ms (${retriesLeft} attempts left)...`\n );\n setTimeout(() => {\n this.tryStart(port, host, retriesLeft - 1, retryDelayMs, tls)\n .then(resolve)\n .catch(reject);\n }, retryDelayMs);\n } else {\n console.error('[RuntimeScope] WebSocket server error:', err.message);\n reject(err);\n }\n }\n\n private ensureSqliteStore(projectName: string): SqliteStore | null {\n if (!this.projectManager) return null;\n\n let sqliteStore = this.sqliteStores.get(projectName);\n if (!sqliteStore) {\n try {\n this.projectManager.ensureProjectDir(projectName);\n const dbPath = this.projectManager.getProjectDbPath(projectName);\n sqliteStore = new SqliteStore({ dbPath });\n this.sqliteStores.set(projectName, sqliteStore);\n this.store.setSqliteStore(sqliteStore, projectName);\n console.error(`[RuntimeScope] SQLite store opened for project \"${projectName}\"`);\n } catch (err) {\n console.error(\n `[RuntimeScope] Failed to open SQLite for \"${projectName}\":`,\n (err as Error).message\n );\n return null;\n }\n }\n return sqliteStore;\n }\n\n private setupConnectionHandler(wss: WebSocketServer): void {\n wss.on('connection', (ws) => {\n // If auth is enabled, the connection starts in a pending state.\n // The first message must be a valid handshake with an authToken.\n if (this.authManager?.isEnabled()) {\n this.pendingHandshakes.add(ws);\n\n // Auto-close if no valid handshake within 5 seconds\n const authTimeout = setTimeout(() => {\n if (this.pendingHandshakes.has(ws)) {\n this.pendingHandshakes.delete(ws);\n try {\n ws.send(JSON.stringify({\n type: 'error',\n payload: { code: 'AUTH_TIMEOUT', message: 'Handshake timeout' },\n timestamp: Date.now(),\n }));\n } catch { /* ignore */ }\n ws.close(4001, 'Authentication timeout');\n }\n }, 5000);\n\n ws.on('close', () => {\n clearTimeout(authTimeout);\n this.pendingHandshakes.delete(ws);\n });\n }\n\n ws.on('message', (data) => {\n try {\n const msg: WSMessage = JSON.parse(data.toString());\n this.handleMessage(ws, msg);\n } catch {\n console.error('[RuntimeScope] Malformed WebSocket message, ignoring');\n }\n });\n\n ws.on('close', () => {\n const clientInfo = this.clients.get(ws);\n if (clientInfo) {\n this.store.markDisconnected(clientInfo.sessionId);\n\n // Update SQLite session record\n const sqliteStore = this.sqliteStores.get(clientInfo.projectName);\n if (sqliteStore) {\n sqliteStore.updateSessionDisconnected(clientInfo.sessionId, Date.now());\n }\n\n console.error(`[RuntimeScope] Session ${clientInfo.sessionId} disconnected`);\n\n // Notify disconnect listeners (for session snapshotting)\n for (const cb of this.disconnectCallbacks) {\n try {\n cb(clientInfo.sessionId, clientInfo.projectName);\n } catch {\n // Don't let listener errors break disconnect handling\n }\n }\n }\n this.clients.delete(ws);\n });\n\n ws.on('error', (err) => {\n console.error('[RuntimeScope] WebSocket client error:', err.message);\n });\n });\n }\n\n private handleMessage(ws: WebSocket, msg: WSMessage): void {\n switch (msg.type) {\n case 'handshake': {\n const payload = msg.payload as HandshakePayload;\n\n // Authenticate if auth is enabled\n if (this.authManager?.isEnabled()) {\n if (!this.authManager.isAuthorized(payload.authToken)) {\n try {\n ws.send(JSON.stringify({\n type: 'error',\n payload: { code: 'AUTH_FAILED', message: 'Invalid or missing API key' },\n timestamp: Date.now(),\n }));\n } catch { /* ignore */ }\n ws.close(4001, 'Authentication failed');\n return;\n }\n this.pendingHandshakes.delete(ws);\n }\n\n const projectName = payload.appName;\n\n this.clients.set(ws, {\n sessionId: payload.sessionId,\n projectName,\n });\n\n // Initialize SQLite for this project\n const sqliteStore = this.ensureSqliteStore(projectName);\n\n // Save session info to SQLite\n if (sqliteStore) {\n const sessionInfo: SessionInfoExtended = {\n sessionId: payload.sessionId,\n project: projectName,\n appName: payload.appName,\n connectedAt: msg.timestamp,\n sdkVersion: payload.sdkVersion,\n eventCount: 0,\n isConnected: true,\n };\n sqliteStore.saveSession(sessionInfo);\n }\n\n console.error(\n `[RuntimeScope] Session ${payload.sessionId} connected (${payload.appName} v${payload.sdkVersion})`\n );\n\n // Notify connect listeners\n for (const cb of this.connectCallbacks) {\n try { cb(payload.sessionId, projectName); } catch { /* non-fatal */ }\n }\n break;\n }\n case 'event': {\n // Reject events from unauthenticated connections\n if (this.pendingHandshakes.has(ws)) return;\n\n const clientInfo = this.clients.get(ws);\n const payload = msg.payload as EventBatchPayload;\n if (Array.isArray(payload.events)) {\n for (const event of payload.events) {\n // Rate limit per session\n if (clientInfo && !this.rateLimiter.allow(clientInfo.sessionId)) {\n break; // drop remaining events in this batch\n }\n this.store.addEvent(event);\n }\n }\n break;\n }\n case 'command_response': {\n const resp = msg as unknown as CommandResponse;\n const pending = this.pendingCommands.get(resp.requestId);\n if (pending) {\n clearTimeout(pending.timer);\n this.pendingCommands.delete(resp.requestId);\n pending.resolve(resp.payload);\n }\n break;\n }\n case 'heartbeat':\n break;\n }\n }\n\n /** Find the WebSocket for a given sessionId */\n private findWsBySessionId(sessionId: string): WebSocket | undefined {\n for (const [ws, info] of this.clients) {\n if (info.sessionId === sessionId) return ws;\n }\n return undefined;\n }\n\n /** Get the first connected session ID (for single-app use) */\n getFirstSessionId(): string | undefined {\n for (const [, info] of this.clients) {\n return info.sessionId;\n }\n return undefined;\n }\n\n /** Get the project name for a session */\n getProjectForSession(sessionId: string): string | undefined {\n for (const [, info] of this.clients) {\n if (info.sessionId === sessionId) return info.projectName;\n }\n return undefined;\n }\n\n /** Get all connected session IDs with their project names */\n getConnectedSessions(): { sessionId: string; projectName: string }[] {\n const sessions: { sessionId: string; projectName: string }[] = [];\n for (const [, info] of this.clients) {\n sessions.push({ sessionId: info.sessionId, projectName: info.projectName });\n }\n return sessions;\n }\n\n /** Send a command to the SDK and await the response */\n sendCommand(\n sessionId: string,\n command: { command: string; requestId: string; params?: Record<string, unknown> },\n timeoutMs = 10_000\n ): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const ws = this.findWsBySessionId(sessionId);\n if (!ws || ws.readyState !== 1 /* OPEN */) {\n reject(new Error(`No active WebSocket for session ${sessionId}`));\n return;\n }\n\n const timer = setTimeout(() => {\n this.pendingCommands.delete(command.requestId);\n reject(new Error(`Command ${command.command} timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n\n this.pendingCommands.set(command.requestId, { resolve, reject, timer });\n\n try {\n ws.send(JSON.stringify({\n type: 'command',\n payload: command,\n timestamp: Date.now(),\n sessionId,\n }));\n } catch (err) {\n clearTimeout(timer);\n this.pendingCommands.delete(command.requestId);\n reject(err);\n }\n });\n }\n\n stop(): void {\n // Stop rate limiter pruning\n if (this.pruneTimer) {\n clearInterval(this.pruneTimer);\n this.pruneTimer = null;\n }\n\n // Close all SQLite stores\n for (const [name, sqliteStore] of this.sqliteStores) {\n try {\n sqliteStore.close();\n console.error(`[RuntimeScope] SQLite store closed for \"${name}\"`);\n } catch {\n // Ignore close errors during shutdown\n }\n }\n this.sqliteStores.clear();\n\n if (this.wss) {\n this.wss.close();\n this.wss = null;\n console.error('[RuntimeScope] Collector stopped');\n }\n }\n}\n","import { mkdirSync, readFileSync, writeFileSync, existsSync, readdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport type { ApiKeyEntry } from './auth.js';\n\n// ============================================================\n// Project Manager — manages ~/.runtimescope/ directory structure\n// ============================================================\n\nexport interface GlobalConfig {\n defaultPort: number;\n bufferSize: number;\n httpPort: number;\n /** Authentication configuration */\n auth?: {\n enabled: boolean;\n apiKeys: ApiKeyEntry[];\n };\n /** TLS certificate paths */\n tls?: {\n certPath: string;\n keyPath: string;\n caPath?: string;\n };\n /** CORS allowed origins (defaults to '*' when not set) */\n corsOrigins?: string[];\n /** Rate limiting configuration */\n rateLimits?: {\n maxEventsPerSecond?: number;\n maxEventsPerMinute?: number;\n };\n /** Payload redaction configuration */\n redaction?: {\n enabled: boolean;\n rules?: { name: string; pattern: string; replacement: string }[];\n };\n}\n\nexport interface ProjectConfig {\n name: string;\n createdAt: string;\n sdkVersion?: string;\n settings: {\n bufferSize?: number;\n retentionDays?: number;\n };\n}\n\nexport interface InfrastructureConfig {\n project?: string;\n databases?: Record<string, {\n type: string;\n connection_string?: string;\n project_ref?: string;\n service_key?: string;\n label?: string;\n }>;\n deployments?: Record<string, {\n platform: string;\n project_id?: string;\n team_id?: string;\n worker_name?: string;\n account_id?: string;\n }>;\n services?: Record<string, string>;\n}\n\nconst DEFAULT_GLOBAL_CONFIG: GlobalConfig = {\n defaultPort: 9090,\n bufferSize: 10_000,\n httpPort: 9091,\n};\n\nexport class ProjectManager {\n private readonly baseDir: string;\n\n constructor(baseDir?: string) {\n this.baseDir = baseDir ?? join(homedir(), '.runtimescope');\n }\n\n get rootDir(): string {\n return this.baseDir;\n }\n\n // --- Directory helpers ---\n\n getProjectDir(projectName: string): string {\n // Sanitize to prevent path traversal (e.g., ../../etc)\n const safe = projectName.replace(/[^a-zA-Z0-9_.-]/g, '_');\n if (!safe || safe === '.' || safe === '..') {\n return join(this.baseDir, 'projects', '_invalid');\n }\n return join(this.baseDir, 'projects', safe);\n }\n\n getProjectDbPath(projectName: string): string {\n return join(this.getProjectDir(projectName), 'events.db');\n }\n\n // --- Lifecycle (idempotent) ---\n\n ensureGlobalDir(): void {\n this.mkdirp(this.baseDir);\n this.mkdirp(join(this.baseDir, 'projects'));\n\n // Create default global config if it doesn't exist\n const configPath = join(this.baseDir, 'config.json');\n if (!existsSync(configPath)) {\n this.writeJson(configPath, DEFAULT_GLOBAL_CONFIG);\n }\n }\n\n ensureProjectDir(projectName: string): void {\n const projectDir = this.getProjectDir(projectName);\n this.mkdirp(projectDir);\n // Create default project config if it doesn't exist\n const configPath = join(projectDir, 'config.json');\n if (!existsSync(configPath)) {\n const config: ProjectConfig = {\n name: projectName,\n createdAt: new Date().toISOString(),\n settings: {\n retentionDays: 30,\n },\n };\n this.writeJson(configPath, config);\n }\n }\n\n // --- Config ---\n\n getGlobalConfig(): GlobalConfig {\n const configPath = join(this.baseDir, 'config.json');\n if (!existsSync(configPath)) return { ...DEFAULT_GLOBAL_CONFIG };\n return { ...DEFAULT_GLOBAL_CONFIG, ...(this.readJson(configPath) as Partial<GlobalConfig>) };\n }\n\n saveGlobalConfig(config: GlobalConfig): void {\n this.writeJson(join(this.baseDir, 'config.json'), config);\n }\n\n getProjectConfig(projectName: string): ProjectConfig | null {\n const configPath = join(this.getProjectDir(projectName), 'config.json');\n if (!existsSync(configPath)) return null;\n return this.readJson(configPath) as ProjectConfig;\n }\n\n saveProjectConfig(projectName: string, config: ProjectConfig): void {\n this.writeJson(join(this.getProjectDir(projectName), 'config.json'), config);\n }\n\n getInfrastructureConfig(projectName: string): InfrastructureConfig | null {\n // Try JSON first, then YAML (if js-yaml is available)\n const jsonPath = join(this.getProjectDir(projectName), 'infrastructure.json');\n if (existsSync(jsonPath)) {\n const config = this.readJson(jsonPath) as InfrastructureConfig;\n return this.resolveConfigEnvVars(config);\n }\n\n // Try YAML (loaded lazily to avoid hard dependency)\n const yamlPath = join(this.getProjectDir(projectName), 'infrastructure.yaml');\n if (existsSync(yamlPath)) {\n try {\n // Dynamic import for optional js-yaml dependency\n const content = readFileSync(yamlPath, 'utf-8');\n // Simple YAML parsing for key: value pairs — full YAML support via js-yaml\n return this.resolveConfigEnvVars(this.parseSimpleYaml(content));\n } catch {\n return null;\n }\n }\n\n return null;\n }\n\n getClaudeInstructions(projectName: string): string | null {\n const filePath = join(this.getProjectDir(projectName), 'claude-instructions.md');\n if (!existsSync(filePath)) return null;\n return readFileSync(filePath, 'utf-8');\n }\n\n // --- Discovery ---\n\n listProjects(): string[] {\n const projectsDir = join(this.baseDir, 'projects');\n if (!existsSync(projectsDir)) return [];\n return readdirSync(projectsDir, { withFileTypes: true })\n .filter((d) => d.isDirectory())\n .map((d) => d.name);\n }\n\n projectExists(projectName: string): boolean {\n return existsSync(this.getProjectDir(projectName));\n }\n\n // --- Environment variable resolution ---\n\n resolveEnvVars(value: string): string {\n return value.replace(/\\$\\{([^}]+)\\}/g, (_match, varName: string) => {\n return process.env[varName] ?? '';\n });\n }\n\n // --- Private helpers ---\n\n private mkdirp(dir: string): void {\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n }\n\n private readJson(path: string): unknown {\n const content = readFileSync(path, 'utf-8');\n return JSON.parse(content);\n }\n\n private writeJson(path: string, data: unknown): void {\n writeFileSync(path, JSON.stringify(data, null, 2) + '\\n', 'utf-8');\n }\n\n private resolveConfigEnvVars(config: unknown): InfrastructureConfig {\n // Deep-resolve ${VAR} patterns in all string values\n const resolve = (obj: unknown): unknown => {\n if (typeof obj === 'string') return this.resolveEnvVars(obj);\n if (Array.isArray(obj)) return obj.map(resolve);\n if (obj && typeof obj === 'object') {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n result[key] = resolve(value);\n }\n return result;\n }\n return obj;\n };\n return resolve(config) as InfrastructureConfig;\n }\n\n /**\n * Minimal YAML parser for simple infrastructure config files.\n * Handles flat key-value pairs and one level of nesting.\n * For full YAML support, install js-yaml.\n */\n private parseSimpleYaml(content: string): InfrastructureConfig {\n try {\n // Try to use js-yaml if available\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const yaml = require('js-yaml');\n return yaml.load(content) as InfrastructureConfig;\n } catch {\n // Fallback: try JSON.parse in case it's JSON with .yaml extension\n try {\n return JSON.parse(content) as InfrastructureConfig;\n } catch {\n return {};\n }\n }\n }\n}\n","import { randomBytes, timingSafeEqual } from 'node:crypto';\n\n// ============================================================\n// API Key Authentication\n// Validates SDK and HTTP API connections via bearer tokens\n// ============================================================\n\nexport interface ApiKeyEntry {\n key: string;\n label: string;\n project?: string;\n createdAt: number;\n}\n\nexport interface AuthConfig {\n enabled: boolean;\n apiKeys: ApiKeyEntry[];\n}\n\nexport class AuthManager {\n private keys: Map<string, ApiKeyEntry> = new Map();\n private enabled: boolean;\n\n constructor(config: Partial<AuthConfig> = {}) {\n this.enabled = config.enabled ?? false;\n for (const entry of config.apiKeys ?? []) {\n this.keys.set(entry.key, entry);\n }\n }\n\n isEnabled(): boolean {\n return this.enabled;\n }\n\n /** Validate an API key. Returns the entry if valid, null if invalid. */\n validate(key: string | undefined): ApiKeyEntry | null {\n if (!this.enabled) return null; // auth disabled — everything passes\n if (!key) return null;\n\n for (const [storedKey, entry] of this.keys) {\n if (this.safeCompare(key, storedKey)) {\n return entry;\n }\n }\n return null;\n }\n\n /** Check if request is authorized. Returns true if auth is disabled or key is valid. */\n isAuthorized(key: string | undefined): boolean {\n if (!this.enabled) return true;\n return this.validate(key) !== null;\n }\n\n /** Extract bearer token from Authorization header value. */\n static extractBearer(header: string | undefined): string | undefined {\n if (!header) return undefined;\n const match = header.match(/^Bearer\\s+(\\S+)$/i);\n return match?.[1];\n }\n\n /** Constant-time string comparison to prevent timing attacks. */\n private safeCompare(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n try {\n return timingSafeEqual(Buffer.from(a, 'utf-8'), Buffer.from(b, 'utf-8'));\n } catch {\n return false;\n }\n }\n}\n\n/** Generate a cryptographically secure API key (64 hex chars = 32 bytes). */\nexport function generateApiKey(label: string, project?: string): ApiKeyEntry {\n return {\n key: randomBytes(32).toString('hex'),\n label,\n project,\n createdAt: Date.now(),\n };\n}\n","import type { RuntimeEvent } from './types.js';\n\n// ============================================================\n// Payload Redaction Engine\n// Defense-in-depth: redacts PII/secrets from events at the\n// collector level (supplements SDK-side beforeSend)\n// ============================================================\n\nexport interface RedactionRule {\n name: string;\n pattern: RegExp;\n replacement: string;\n}\n\nexport interface RedactorConfig {\n enabled?: boolean;\n useBuiltIn?: boolean;\n rules?: RedactionRule[];\n /** Redact JSON object keys matching these patterns (case-insensitive). */\n sensitiveKeys?: string[];\n}\n\n/** Built-in patterns for common secret/PII formats. */\nexport const BUILT_IN_RULES: RedactionRule[] = [\n {\n name: 'jwt',\n pattern: /eyJ[A-Za-z0-9_-]{10,}\\.eyJ[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]+/g,\n replacement: '[REDACTED:jwt]',\n },\n {\n name: 'credit_card',\n pattern: /\\b\\d{4}[-\\s]?\\d{4}[-\\s]?\\d{4}[-\\s]?\\d{4}\\b/g,\n replacement: '[REDACTED:cc]',\n },\n {\n name: 'ssn',\n pattern: /\\b\\d{3}-\\d{2}-\\d{4}\\b/g,\n replacement: '[REDACTED:ssn]',\n },\n {\n name: 'email',\n pattern: /\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z]{2,}\\b/gi,\n replacement: '[REDACTED:email]',\n },\n {\n name: 'bearer_token',\n pattern: /Bearer\\s+[A-Za-z0-9._~+/=-]+/gi,\n replacement: 'Bearer [REDACTED]',\n },\n {\n name: 'api_key_param',\n pattern: /(?:api[_-]?key|apikey|secret|token|password|passwd|authorization)=[^&\\s\"']+/gi,\n replacement: '[REDACTED:param]',\n },\n];\n\n/** Default sensitive JSON keys — values for these keys get redacted. */\nconst DEFAULT_SENSITIVE_KEYS = [\n 'password', 'passwd', 'secret', 'token', 'accessToken', 'refreshToken',\n 'apiKey', 'api_key', 'authorization', 'credit_card', 'creditCard',\n 'ssn', 'socialSecurity',\n];\n\nexport class Redactor {\n private rules: RedactionRule[];\n private sensitiveKeyPattern: RegExp | null;\n private enabled: boolean;\n\n constructor(config: RedactorConfig = {}) {\n this.enabled = config.enabled ?? true;\n this.rules = [];\n\n if (config.useBuiltIn !== false) {\n this.rules.push(...BUILT_IN_RULES);\n }\n if (config.rules) {\n this.rules.push(...config.rules);\n }\n\n const keys = config.sensitiveKeys ?? DEFAULT_SENSITIVE_KEYS;\n this.sensitiveKeyPattern = keys.length > 0\n ? new RegExp(`^(${keys.join('|')})$`, 'i')\n : null;\n }\n\n isEnabled(): boolean {\n return this.enabled;\n }\n\n /** Apply all redaction rules to a string value. */\n redactString(value: string): string {\n let result = value;\n for (const rule of this.rules) {\n // Reset lastIndex for global regexes\n rule.pattern.lastIndex = 0;\n result = result.replace(rule.pattern, rule.replacement);\n }\n return result;\n }\n\n /**\n * Deep-walk an event and redact all string fields.\n * Returns a new event object (does not mutate the original).\n */\n redactEvent(event: RuntimeEvent): RuntimeEvent {\n if (!this.enabled) return event;\n return this.deepRedact(event) as RuntimeEvent;\n }\n\n private deepRedact(value: unknown, key?: string): unknown {\n if (value === null || value === undefined) return value;\n\n // If this key is a known sensitive field, redact the entire value\n if (key && this.sensitiveKeyPattern?.test(key)) {\n return '[REDACTED]';\n }\n\n if (typeof value === 'string') {\n return this.redactString(value);\n }\n\n if (Array.isArray(value)) {\n return value.map((item) => this.deepRedact(item));\n }\n\n if (typeof value === 'object') {\n const result: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value)) {\n result[k] = this.deepRedact(v, k);\n }\n return result;\n }\n\n // numbers, booleans — pass through\n return value;\n }\n}\n","import type { ProjectManager } from './project-manager.js';\nimport type { SqliteStore } from './sqlite-store.js';\nimport type { EventStore } from './store.js';\nimport type {\n RuntimeEvent,\n NetworkEvent,\n RenderEvent,\n StateEvent,\n PerformanceEvent,\n DatabaseEvent,\n ConsoleEvent,\n SessionMetrics,\n SessionSnapshot,\n WebVitalRating,\n} from './types.js';\n\n// ============================================================\n// Session Manager\n// Computes session metrics and manages session snapshots\n// ============================================================\n\nexport class SessionManager {\n private projectManager: ProjectManager;\n private sqliteStores: Map<string, SqliteStore>;\n private store: EventStore;\n\n constructor(\n projectManager: ProjectManager,\n sqliteStores: Map<string, SqliteStore>,\n store: EventStore\n ) {\n this.projectManager = projectManager;\n this.sqliteStores = sqliteStores;\n this.store = store;\n }\n\n computeMetrics(sessionId: string, project: string, events: RuntimeEvent[]): SessionMetrics {\n const sessionEvents = events.filter((e) => e.sessionId === sessionId);\n\n const networkEvents = sessionEvents.filter((e) => e.eventType === 'network') as NetworkEvent[];\n const renderEvents = sessionEvents.filter((e) => e.eventType === 'render') as RenderEvent[];\n const stateEvents = sessionEvents.filter((e) => e.eventType === 'state') as StateEvent[];\n const performanceEvents = sessionEvents.filter((e) => e.eventType === 'performance') as PerformanceEvent[];\n const databaseEvents = sessionEvents.filter((e) => e.eventType === 'database') as DatabaseEvent[];\n const consoleEvents = sessionEvents.filter((e) => e.eventType === 'console') as ConsoleEvent[];\n\n // Endpoint metrics\n const endpoints: Record<string, { avgLatency: number; errorRate: number; callCount: number }> = {};\n const endpointGroups = new Map<string, NetworkEvent[]>();\n for (const e of networkEvents) {\n const key = `${e.method} ${e.url}`;\n const group = endpointGroups.get(key) ?? [];\n group.push(e);\n endpointGroups.set(key, group);\n }\n for (const [key, group] of endpointGroups) {\n const avgLatency = group.reduce((s, e) => s + e.duration, 0) / group.length;\n const errorCount = group.filter((e) => e.status >= 400).length;\n endpoints[key] = { avgLatency, errorRate: errorCount / group.length, callCount: group.length };\n }\n\n // Component metrics\n const components: Record<string, { renderCount: number; avgDuration: number }> = {};\n for (const re of renderEvents) {\n for (const p of re.profiles) {\n const existing = components[p.componentName];\n if (existing) {\n existing.renderCount += p.renderCount;\n existing.avgDuration = (existing.avgDuration + p.avgDuration) / 2;\n } else {\n components[p.componentName] = { renderCount: p.renderCount, avgDuration: p.avgDuration };\n }\n }\n }\n\n // Store metrics\n const stores: Record<string, { updateCount: number }> = {};\n for (const se of stateEvents) {\n const existing = stores[se.storeId];\n if (existing) {\n existing.updateCount++;\n } else {\n stores[se.storeId] = { updateCount: 1 };\n }\n }\n\n // Web Vitals (only include events with a rating — i.e., browser Web Vitals, not server metrics)\n const webVitals: Record<string, { value: number; rating: WebVitalRating }> = {};\n for (const pe of performanceEvents) {\n if (pe.rating) {\n webVitals[pe.metricName] = { value: pe.value, rating: pe.rating };\n }\n }\n\n // Query metrics\n const queries: Record<string, { avgDuration: number; callCount: number }> = {};\n const queryGroups = new Map<string, DatabaseEvent[]>();\n for (const de of databaseEvents) {\n const group = queryGroups.get(de.normalizedQuery) ?? [];\n group.push(de);\n queryGroups.set(de.normalizedQuery, group);\n }\n for (const [key, group] of queryGroups) {\n const avgDuration = group.reduce((s, e) => s + e.duration, 0) / group.length;\n queries[key] = { avgDuration, callCount: group.length };\n }\n\n const timestamps = sessionEvents.map((e) => e.timestamp);\n const errorCount = consoleEvents.filter((e) => e.level === 'error').length +\n networkEvents.filter((e) => e.status >= 400).length;\n\n return {\n sessionId,\n project,\n connectedAt: timestamps.length > 0 ? Math.min(...timestamps) : Date.now(),\n disconnectedAt: timestamps.length > 0 ? Math.max(...timestamps) : Date.now(),\n totalEvents: sessionEvents.length,\n errorCount,\n endpoints,\n components,\n stores,\n webVitals,\n queries,\n };\n }\n\n createSnapshot(sessionId: string, project: string, label?: string): SessionSnapshot {\n const events = this.store.getAllEvents();\n const metrics = this.computeMetrics(sessionId, project, events);\n\n // Save to SQLite\n const sqliteStore = this.sqliteStores.get(project);\n if (sqliteStore) {\n sqliteStore.saveSessionMetrics(sessionId, project, metrics, label);\n }\n\n return {\n sessionId,\n project,\n label,\n metrics,\n createdAt: Date.now(),\n };\n }\n\n getSessionSnapshots(project: string, sessionId: string): SessionSnapshot[] {\n const sqliteStore = this.sqliteStores.get(project);\n if (!sqliteStore) return [];\n return sqliteStore.getSessionSnapshots(sessionId);\n }\n\n getSnapshotById(project: string, snapshotId: number): SessionSnapshot | null {\n const sqliteStore = this.sqliteStores.get(project);\n if (!sqliteStore) return null;\n return sqliteStore.getSnapshotById(snapshotId);\n }\n\n getSessionHistory(project: string, limit = 20): SessionSnapshot[] {\n const sqliteStore = this.sqliteStores.get(project);\n if (!sqliteStore) return [];\n\n const sessions = sqliteStore.getSessions(project, limit);\n const snapshots: SessionSnapshot[] = [];\n\n for (const session of sessions) {\n const metricsData = sqliteStore.getSessionMetrics(session.sessionId);\n if (metricsData) {\n snapshots.push({\n sessionId: session.sessionId,\n project,\n metrics: metricsData,\n buildMeta: session.buildMeta,\n createdAt: session.disconnectedAt ?? session.connectedAt,\n });\n }\n }\n\n return snapshots;\n }\n}\n","import { createServer, type IncomingMessage, type ServerResponse, type Server } from 'node:http';\nimport { createServer as createHttpsServer } from 'node:https';\nimport { readFileSync, existsSync } from 'node:fs';\nimport { resolve, dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { WebSocketServer, type WebSocket } from 'ws';\nimport type { EventStore } from './store.js';\nimport type { ProcessMonitor } from './engines/process-monitor.js';\nimport type { RuntimeEvent, DevProcessType } from './types.js';\nimport { AuthManager } from './auth.js';\nimport type { SessionRateLimiter } from './rate-limiter.js';\nimport { loadTlsOptions, type TlsConfig } from './tls.js';\nimport type { PmStore } from './pm/pm-store.js';\nimport type { ProjectDiscovery } from './pm/project-discovery.js';\nimport { createPmRouter } from './pm/pm-routes.js';\n\n// ============================================================\n// HTTP API Server for Dashboard\n// Lightweight REST API + WebSocket real-time event streaming\n// Uses Node.js built-in http module (no framework deps)\n// ============================================================\n\nexport interface HttpServerOptions {\n port?: number;\n host?: string;\n authManager?: AuthManager;\n allowedOrigins?: string[];\n tls?: TlsConfig;\n}\n\ninterface RouteHandler {\n (req: IncomingMessage, res: ServerResponse, params: URLSearchParams): void | Promise<void>;\n}\n\nexport class HttpServer {\n private server: Server | null = null;\n private wss: WebSocketServer | null = null;\n private store: EventStore;\n private processMonitor: ProcessMonitor | null;\n private authManager: AuthManager | null;\n private allowedOrigins: string[] | null;\n private rateLimiter: SessionRateLimiter | null;\n private dashboardClients: Set<WebSocket> = new Set();\n private eventListener: ((event: RuntimeEvent) => void) | null = null;\n private routes: Map<string, RouteHandler> = new Map();\n private pmRouter: ReturnType<typeof createPmRouter> | null = null;\n private sdkBundlePath: string | null = null;\n private activePort = 9091;\n private startedAt = Date.now();\n\n constructor(\n store: EventStore,\n processMonitor?: ProcessMonitor,\n options?: {\n authManager?: AuthManager;\n allowedOrigins?: string[];\n rateLimiter?: SessionRateLimiter;\n pmStore?: PmStore;\n discovery?: ProjectDiscovery;\n }\n ) {\n this.store = store;\n this.processMonitor = processMonitor ?? null;\n this.authManager = options?.authManager ?? null;\n this.allowedOrigins = options?.allowedOrigins ?? null;\n this.rateLimiter = options?.rateLimiter ?? null;\n this.registerRoutes();\n\n // Register PM routes if PM store is available\n if (options?.pmStore && options?.discovery) {\n this.pmRouter = createPmRouter(options.pmStore, options.discovery, {\n json: (res, data, status) => this.json(res, data, status),\n readBody: (req, maxBytes) => this.readBody(req, maxBytes),\n }, (msg) => this.broadcastDevServer(msg));\n }\n }\n\n private registerRoutes(): void {\n // Health check — always unauthenticated for load balancer probes\n this.routes.set('GET /api/health', (_req, res) => {\n this.json(res, {\n status: 'ok',\n timestamp: Date.now(),\n uptime: Math.floor((Date.now() - this.startedAt) / 1000),\n sessions: this.store.getSessionInfo().filter(s => s.isConnected).length,\n authEnabled: this.authManager?.isEnabled() ?? false,\n });\n });\n\n // Sessions\n this.routes.set('GET /api/sessions', (_req, res) => {\n const sessions = this.store.getSessionInfo();\n this.json(res, { data: sessions, count: sessions.length });\n });\n\n // Projects (sessions grouped by appName)\n this.routes.set('GET /api/projects', (_req, res) => {\n const sessions = this.store.getSessionInfo();\n const projectMap = new Map<string, { appName: string; sessions: string[]; isConnected: boolean; eventCount: number }>();\n\n for (const s of sessions) {\n const existing = projectMap.get(s.appName);\n if (existing) {\n existing.sessions.push(s.sessionId);\n existing.eventCount += s.eventCount;\n if (s.isConnected) existing.isConnected = true;\n } else {\n projectMap.set(s.appName, {\n appName: s.appName,\n sessions: [s.sessionId],\n isConnected: s.isConnected,\n eventCount: s.eventCount,\n });\n }\n }\n\n const projects = Array.from(projectMap.values());\n this.json(res, { data: projects, count: projects.length });\n });\n\n // Processes (served from background scan cache — no blocking lsof calls)\n this.routes.set('GET /api/processes', (_req, res, params) => {\n if (!this.processMonitor) {\n this.json(res, { data: [], count: 0 });\n return;\n }\n const type = params.get('type') as DevProcessType | undefined ?? undefined;\n const project = params.get('project') ?? undefined;\n const processes = this.processMonitor.getProcesses({ type, project });\n this.json(res, { data: processes, count: processes.length });\n });\n\n // Kill a process by PID\n this.routes.set('DELETE /api/processes', async (req, res, params) => {\n if (!this.processMonitor) {\n this.json(res, { error: 'Process monitor not available' }, 500);\n return;\n }\n const pid = numParam(params, 'pid');\n if (!pid) {\n // Try reading from body\n const body = await this.readBody(req, 1024);\n const parsed = body ? JSON.parse(body) : {};\n if (!parsed.pid) {\n this.json(res, { error: 'pid is required' }, 400);\n return;\n }\n const result = this.processMonitor.killProcess(parsed.pid, parsed.signal ?? 'SIGTERM');\n this.json(res, { data: result });\n return;\n }\n const signal = (params.get('signal') as 'SIGTERM' | 'SIGKILL') ?? 'SIGTERM';\n const result = this.processMonitor.killProcess(pid, signal);\n this.json(res, { data: result });\n });\n\n // Port usage (served from background scan cache)\n this.routes.set('GET /api/ports', (_req, res, params) => {\n if (!this.processMonitor) {\n this.json(res, { data: [], count: 0 });\n return;\n }\n const port = numParam(params, 'port');\n const ports = this.processMonitor.getPortUsage(port);\n this.json(res, { data: ports, count: ports.length });\n });\n\n // Network events\n this.routes.set('GET /api/events/network', (_req, res, params) => {\n const events = this.store.getNetworkRequests({\n sinceSeconds: numParam(params, 'since_seconds'),\n urlPattern: params.get('url_pattern') ?? undefined,\n method: params.get('method') ?? undefined,\n sessionId: params.get('session_id') ?? undefined,\n });\n this.json(res, { data: events, count: events.length });\n });\n\n // Console events\n this.routes.set('GET /api/events/console', (_req, res, params) => {\n const events = this.store.getConsoleMessages({\n sinceSeconds: numParam(params, 'since_seconds'),\n level: params.get('level') ?? undefined,\n search: params.get('search') ?? undefined,\n sessionId: params.get('session_id') ?? undefined,\n });\n this.json(res, { data: events, count: events.length });\n });\n\n // State events\n this.routes.set('GET /api/events/state', (_req, res, params) => {\n const events = this.store.getStateEvents({\n sinceSeconds: numParam(params, 'since_seconds'),\n storeId: params.get('store_id') ?? undefined,\n sessionId: params.get('session_id') ?? undefined,\n });\n this.json(res, { data: events, count: events.length });\n });\n\n // Render events\n this.routes.set('GET /api/events/renders', (_req, res, params) => {\n const events = this.store.getRenderEvents({\n sinceSeconds: numParam(params, 'since_seconds'),\n componentName: params.get('component') ?? undefined,\n sessionId: params.get('session_id') ?? undefined,\n });\n this.json(res, { data: events, count: events.length });\n });\n\n // Performance events\n this.routes.set('GET /api/events/performance', (_req, res, params) => {\n const events = this.store.getPerformanceMetrics({\n sinceSeconds: numParam(params, 'since_seconds'),\n metricName: params.get('metric') ?? undefined,\n sessionId: params.get('session_id') ?? undefined,\n });\n this.json(res, { data: events, count: events.length });\n });\n\n // Database events\n this.routes.set('GET /api/events/database', (_req, res, params) => {\n const events = this.store.getDatabaseEvents({\n sinceSeconds: numParam(params, 'since_seconds'),\n table: params.get('table') ?? undefined,\n minDurationMs: numParam(params, 'min_duration_ms'),\n search: params.get('search') ?? undefined,\n sessionId: params.get('session_id') ?? undefined,\n });\n this.json(res, { data: events, count: events.length });\n });\n\n // Timeline\n this.routes.set('GET /api/events/timeline', (_req, res, params) => {\n const eventTypes = params.get('event_types')?.split(',') ?? undefined;\n const events = this.store.getEventTimeline({\n sinceSeconds: numParam(params, 'since_seconds'),\n eventTypes: eventTypes as RuntimeEvent['eventType'][] | undefined,\n sessionId: params.get('session_id') ?? undefined,\n });\n this.json(res, { data: events, count: events.length });\n });\n\n this.routes.set('GET /api/events/custom', (_req, res, params) => {\n const events = this.store.getCustomEvents({\n name: params.get('name') ?? undefined,\n sinceSeconds: numParam(params, 'since_seconds'),\n sessionId: params.get('session_id') ?? undefined,\n });\n this.json(res, { data: events, count: events.length });\n });\n\n // Clear events\n this.routes.set('DELETE /api/events', (_req, res) => {\n const result = this.store.clear();\n this.json(res, result);\n });\n\n // POST event ingestion — HTTP alternative to WebSocket for serverless environments\n this.routes.set('POST /api/events', async (req, res) => {\n const body = await this.readBody(req, 1_048_576); // 1MB limit\n if (!body) {\n this.json(res, { error: 'Request body required', code: 'EMPTY_BODY' }, 400);\n return;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(body);\n } catch {\n this.json(res, { error: 'Invalid JSON', code: 'PARSE_ERROR' }, 400);\n return;\n }\n\n const payload = parsed as {\n sessionId?: string;\n appName?: string;\n sdkVersion?: string;\n events?: unknown[];\n };\n\n if (!payload.sessionId || !Array.isArray(payload.events) || payload.events.length === 0) {\n this.json(res, {\n error: 'Required: sessionId (string), events (non-empty array)',\n code: 'INVALID_PAYLOAD',\n }, 400);\n return;\n }\n\n // Auto-register session on first event from this sessionId\n const sessions = this.store.getSessionInfo();\n const knownSession = sessions.find(s => s.sessionId === payload.sessionId);\n if (!knownSession && payload.appName) {\n this.store.addEvent({\n eventId: `session-${payload.sessionId}`,\n sessionId: payload.sessionId,\n timestamp: Date.now(),\n eventType: 'session',\n appName: payload.appName,\n connectedAt: Date.now(),\n sdkVersion: payload.sdkVersion ?? 'http',\n } as RuntimeEvent);\n }\n\n const VALID_EVENT_TYPES = new Set([\n 'network', 'console', 'session', 'state', 'render',\n 'dom_snapshot', 'performance', 'database',\n 'recon_metadata', 'recon_design_tokens', 'recon_fonts',\n 'recon_layout_tree', 'recon_accessibility', 'recon_computed_styles',\n 'recon_element_snapshot', 'recon_asset_inventory',\n ]);\n\n let accepted = 0;\n let dropped = 0;\n let rejected = 0;\n\n for (const raw of payload.events) {\n const event = raw as RuntimeEvent;\n\n // Validate event shape\n if (!event.eventType || !VALID_EVENT_TYPES.has(event.eventType)) {\n rejected++;\n continue;\n }\n\n if (!event.eventId) event.eventId = `http-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n if (!event.sessionId) event.sessionId = payload.sessionId!;\n if (!event.timestamp) event.timestamp = Date.now();\n\n if (this.rateLimiter && !this.rateLimiter.allow(payload.sessionId!)) {\n dropped++;\n continue;\n }\n\n this.store.addEvent(event);\n accepted++;\n }\n\n this.json(res, { accepted, dropped, rejected, sessionId: payload.sessionId }, accepted > 0 ? 200 : 429);\n });\n }\n\n /**\n * Resolve the SDK IIFE bundle path.\n * Tries multiple locations for monorepo and installed-package scenarios.\n */\n private resolveSdkPath(): string | null {\n if (this.sdkBundlePath) return this.sdkBundlePath;\n\n const __dir = dirname(fileURLToPath(import.meta.url));\n const candidates = [\n resolve(__dir, '../../sdk/dist/index.global.js'), // monorepo: packages/collector/dist -> packages/sdk/dist\n resolve(__dir, '../../../node_modules/@runtimescope/sdk/dist/index.global.js'), // npm installed\n ];\n\n for (const p of candidates) {\n if (existsSync(p)) {\n this.sdkBundlePath = p;\n return p;\n }\n }\n return null;\n }\n\n async start(options: HttpServerOptions = {}): Promise<void> {\n const basePort = options.port ?? parseInt(process.env.RUNTIMESCOPE_HTTP_PORT ?? '9091', 10);\n const host = options.host ?? '127.0.0.1';\n const tls = options.tls;\n const maxRetries = 5;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const port = basePort + attempt;\n try {\n await this.tryStart(port, host, tls);\n return;\n } catch (err) {\n const isAddrInUse = (err as NodeJS.ErrnoException).code === 'EADDRINUSE';\n if (isAddrInUse && attempt < maxRetries) {\n console.error(`[RuntimeScope] HTTP port ${port} in use, trying ${port + 1}...`);\n continue;\n }\n throw err;\n }\n }\n }\n\n private tryStart(port: number, host: string, tls?: TlsConfig): Promise<void> {\n return new Promise((resolve, reject) => {\n const handler = (req: IncomingMessage, res: ServerResponse) => this.handleRequest(req, res);\n const server = tls\n ? createHttpsServer(loadTlsOptions(tls), handler)\n : createServer(handler);\n\n // Set up WebSocket server for real-time event streaming\n this.wss = new WebSocketServer({ server, path: '/api/ws/events' });\n this.wss.on('connection', (ws, req) => {\n // Authenticate WebSocket upgrade requests when auth is enabled\n if (this.authManager?.isEnabled()) {\n const wsUrl = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);\n const token = wsUrl.searchParams.get('token')\n ?? AuthManager.extractBearer(req.headers.authorization);\n if (!this.authManager.isAuthorized(token)) {\n ws.close(4001, 'Authentication required');\n return;\n }\n }\n this.dashboardClients.add(ws);\n ws.on('close', () => this.dashboardClients.delete(ws));\n ws.on('error', () => this.dashboardClients.delete(ws));\n });\n\n // Subscribe to EventStore for real-time broadcasting\n this.eventListener = (event: RuntimeEvent) => this.broadcastEvent(event);\n this.store.onEvent(this.eventListener);\n\n server.on('listening', () => {\n this.server = server;\n this.activePort = port;\n this.startedAt = Date.now();\n const proto = tls ? 'https' : 'http';\n console.error(`[RuntimeScope] HTTP API listening on ${proto}://${host}:${port}`);\n resolve();\n });\n\n server.on('error', (err) => {\n // Clean up the WebSocket server on failure\n this.wss?.close();\n this.wss = null;\n if (this.eventListener) {\n this.store.removeEventListener(this.eventListener);\n this.eventListener = null;\n }\n reject(err);\n });\n\n server.listen(port, host);\n });\n }\n\n async stop(): Promise<void> {\n if (this.eventListener) {\n this.store.removeEventListener(this.eventListener);\n this.eventListener = null;\n }\n\n for (const ws of this.dashboardClients) {\n ws.close();\n }\n this.dashboardClients.clear();\n\n if (this.wss) {\n this.wss.close();\n this.wss = null;\n }\n\n if (this.server) {\n return new Promise((resolve) => {\n this.server!.close(() => {\n this.server = null;\n console.error('[RuntimeScope] HTTP API stopped');\n resolve();\n });\n });\n }\n }\n\n broadcastEvent(event: RuntimeEvent): void {\n if (this.dashboardClients.size === 0) return;\n\n const message = JSON.stringify({ type: 'event', data: event });\n for (const ws of this.dashboardClients) {\n if (ws.readyState === 1 /* OPEN */) {\n try {\n ws.send(message);\n } catch {\n this.dashboardClients.delete(ws);\n }\n }\n }\n }\n\n broadcastSessionChange(type: 'session_connected' | 'session_disconnected', sessionId: string, appName: string): void {\n if (this.dashboardClients.size === 0) return;\n const message = JSON.stringify({ type, sessionId, appName });\n for (const ws of this.dashboardClients) {\n if (ws.readyState === 1) {\n try { ws.send(message); } catch { this.dashboardClients.delete(ws); }\n }\n }\n }\n\n private broadcastDevServer(msg: unknown): void {\n if (this.dashboardClients.size === 0) return;\n\n const message = JSON.stringify(msg);\n for (const ws of this.dashboardClients) {\n if (ws.readyState === 1 /* OPEN */) {\n try {\n ws.send(message);\n } catch {\n this.dashboardClients.delete(ws);\n }\n }\n }\n }\n\n private handleRequest(req: IncomingMessage, res: ServerResponse): void {\n const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);\n\n // CORS headers — use specific origin when configured, wildcard for dev\n this.setCorsHeaders(req, res);\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n // Auth check — skip for health endpoint and static assets\n const isPublic = url.pathname === '/api/health'\n || url.pathname === '/runtimescope.js'\n || url.pathname === '/snippet';\n\n if (!isPublic && this.authManager?.isEnabled()) {\n const token = AuthManager.extractBearer(req.headers.authorization);\n if (!this.authManager.isAuthorized(token)) {\n this.json(res, { error: 'Unauthorized', code: 'AUTH_FAILED' }, 401);\n return;\n }\n }\n\n // Serve SDK IIFE bundle — works in any HTML page via <script> tag\n if (req.method === 'GET' && url.pathname === '/runtimescope.js') {\n const sdkPath = this.resolveSdkPath();\n if (sdkPath) {\n const bundle = readFileSync(sdkPath, 'utf-8');\n res.writeHead(200, {\n 'Content-Type': 'application/javascript',\n 'Cache-Control': 'no-cache',\n });\n res.end(bundle);\n } else {\n res.writeHead(404, { 'Content-Type': 'text/plain' });\n res.end('SDK bundle not found. Run: npm run build -w packages/sdk');\n }\n return;\n }\n\n // Serve a ready-to-paste snippet for any tech stack\n if (req.method === 'GET' && url.pathname === '/snippet') {\n const appName = (url.searchParams.get('app') || 'my-app').replace(/[^a-zA-Z0-9_-]/g, '');\n const wsPort = process.env.RUNTIMESCOPE_PORT ?? '9090';\n const snippet = `<!-- RuntimeScope SDK — paste before </body> -->\n<script src=\"http://localhost:${this.activePort}/runtimescope.js\"></script>\n<script>\n RuntimeScope.init({\n appName: '${appName}',\n endpoint: 'ws://localhost:${wsPort}',\n });\n</script>`;\n res.writeHead(200, {\n 'Content-Type': 'text/plain',\n });\n res.end(snippet);\n return;\n }\n\n const routeKey = `${req.method} ${url.pathname}`;\n const handler = this.routes.get(routeKey);\n\n if (handler) {\n try {\n const result = handler(req, res, url.searchParams);\n if (result instanceof Promise) {\n result.catch((err) => {\n this.json(res, { error: (err as Error).message }, 500);\n });\n }\n } catch (err) {\n this.json(res, { error: (err as Error).message }, 500);\n }\n return;\n }\n\n // Try PM pattern routes (e.g. /api/pm/projects/:id)\n if (this.pmRouter && url.pathname.startsWith('/api/pm/')) {\n const match = this.pmRouter.match(req.method!, url.pathname);\n if (match) {\n // Merge path params into search params\n for (const [k, v] of Object.entries(match.pathParams)) {\n url.searchParams.set(k, v);\n }\n try {\n const result = match.handler(req, res, url.searchParams);\n if (result instanceof Promise) {\n result.catch((err) => {\n this.json(res, { error: (err as Error).message }, 500);\n });\n }\n } catch (err) {\n this.json(res, { error: (err as Error).message }, 500);\n }\n return;\n }\n }\n\n this.json(res, { error: 'Not found', path: url.pathname }, 404);\n }\n\n private setCorsHeaders(req: IncomingMessage, res: ServerResponse): void {\n const origin = req.headers.origin;\n\n if (this.allowedOrigins && this.allowedOrigins.length > 0) {\n // Whitelist mode: only allow configured origins\n if (origin && this.allowedOrigins.includes(origin)) {\n res.setHeader('Access-Control-Allow-Origin', origin);\n res.setHeader('Vary', 'Origin');\n }\n // If origin not in whitelist, omit the header (browser blocks the request)\n } else {\n // Default: wildcard for backward compat in local dev\n res.setHeader('Access-Control-Allow-Origin', '*');\n }\n\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');\n }\n\n private readBody(req: IncomingMessage, maxBytes: number): Promise<string | null> {\n return new Promise((resolve) => {\n const chunks: Buffer[] = [];\n let size = 0;\n let resolved = false;\n\n const done = (result: string | null) => {\n if (resolved) return;\n resolved = true;\n clearTimeout(timer);\n resolve(result);\n };\n\n // 30s timeout to prevent slow-read DoS\n const timer = setTimeout(() => {\n req.destroy();\n done(null);\n }, 30_000);\n\n req.on('data', (chunk: Buffer) => {\n size += chunk.length;\n if (size > maxBytes) {\n req.destroy();\n done(null);\n return;\n }\n chunks.push(chunk);\n });\n\n req.on('end', () => {\n if (size === 0) { done(null); return; }\n done(Buffer.concat(chunks).toString('utf-8'));\n });\n\n req.on('error', () => done(null));\n });\n }\n\n private json(res: ServerResponse, data: unknown, status = 200): void {\n res.writeHead(status, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(data));\n }\n}\n\nfunction numParam(params: URLSearchParams, key: string): number | undefined {\n const val = params.get(key);\n if (!val) return undefined;\n const num = parseInt(val, 10);\n return isNaN(num) ? undefined : num;\n}\n","import { readdir, readFile, writeFile, unlink, mkdir, stat } from 'node:fs/promises';\nimport { existsSync } from 'node:fs';\nimport { join, basename } from 'node:path';\nimport { homedir } from 'node:os';\nimport { spawn, execSync, execFileSync } from 'node:child_process';\nimport type { ChildProcess } from 'node:child_process';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport type { PmStore } from './pm-store.js';\nimport type { ProjectDiscovery } from './project-discovery.js';\nimport type { TaskStatus, PmTask, PmNote, GitFileStatus, GitFileChange, GitStatus, GitCommit } from './pm-types.js';\n\n// ============================================================\n// Managed Dev Server Processes\n// ============================================================\n\ntype DevServerStatus = 'starting' | 'running' | 'stopped' | 'crashed';\n\ninterface ManagedProcess {\n pid: number;\n command: string;\n projectId: string;\n startedAt: number;\n status: DevServerStatus;\n child: ChildProcess;\n logs: string[];\n exitCode: number | null;\n}\n\nconst LOG_RING_SIZE = 500;\nconst managedProcesses = new Map<string, ManagedProcess>();\n\nfunction pushLog(mp: ManagedProcess, stream: 'stdout' | 'stderr', line: string): void {\n const entry = `[${stream}] ${line}`;\n if (mp.logs.length >= LOG_RING_SIZE) mp.logs.shift();\n mp.logs.push(entry);\n}\n\nexport type DevServerBroadcast = (msg: unknown) => void;\n\n// ============================================================\n// Project Management HTTP Routes\n// All routes under /api/pm/* — registered as pattern routes\n// ============================================================\n\ntype RouteHandler = (\n req: IncomingMessage,\n res: ServerResponse,\n params: URLSearchParams,\n) => void | Promise<void>;\n\ninterface RouteHelpers {\n json: (res: ServerResponse, data: unknown, status?: number) => void;\n readBody: (req: IncomingMessage, maxBytes: number) => Promise<string | null>;\n}\n\ninterface PatternRoute {\n method: string;\n pattern: string; // e.g. '/api/pm/projects/:id'\n segments: string[]; // split pattern for matching\n handler: RouteHandler;\n}\n\nexport function createPmRouter(\n pmStore: PmStore,\n discovery: ProjectDiscovery,\n helpers: RouteHelpers,\n broadcastDevServer?: DevServerBroadcast,\n): { match: (method: string, pathname: string) => { handler: RouteHandler; pathParams: Record<string, string> } | null } {\n const routes: PatternRoute[] = [];\n\n function route(method: string, pattern: string, handler: RouteHandler): void {\n routes.push({ method, pattern, segments: pattern.split('/'), handler });\n }\n\n // ============================================================\n // Discovery\n // ============================================================\n\n route('POST', '/api/pm/discover', async (_req, res) => {\n try {\n const result = await discovery.discoverAll();\n helpers.json(res, result);\n } catch (err) {\n helpers.json(res, { error: (err as Error).message }, 500);\n }\n });\n\n // ============================================================\n // Projects\n // ============================================================\n\n route('GET', '/api/pm/categories', (_req, res) => {\n const categories = pmStore.listCategories();\n helpers.json(res, { data: categories });\n });\n\n route('GET', '/api/pm/projects', (_req, res, params) => {\n const id = params.get('id');\n if (id) {\n const project = pmStore.getProject(id);\n if (!project) { helpers.json(res, { error: 'Project not found' }, 404); return; }\n const stats = pmStore.getSessionStats(id);\n helpers.json(res, { ...project, stats });\n return;\n }\n const projects = pmStore.listProjects();\n helpers.json(res, { data: projects, count: projects.length });\n });\n\n route('GET', '/api/pm/projects/export-csv', (_req, res, params) => {\n const startDate = params.get('start_date') ?? undefined;\n const endDate = params.get('end_date') ?? undefined;\n const hideEmpty = params.get('hide_empty') === '1' || params.get('hide_empty') === 'true';\n const projectIdsRaw = params.get('project_ids') ?? '';\n const projectIds = projectIdsRaw ? projectIdsRaw.split(',').filter(Boolean) : undefined;\n\n // Get summaries\n const allSummaries = pmStore.getProjectSummaries({ startDate, endDate, hideEmpty });\n const summaries = projectIds\n ? allSummaries.filter((s) => projectIds.includes(s.id))\n : allSummaries;\n\n // Get sessions for these projects\n const projectIdSet = new Set(summaries.map((s) => s.id));\n const allSessions: ReturnType<typeof pmStore.listSessions> = [];\n for (const pid of projectIdSet) {\n const sessions = pmStore.listSessions(pid, { limit: 10000, offset: 0, startDate, endDate, hideEmpty });\n allSessions.push(...sessions);\n }\n // Sort sessions newest first\n allSessions.sort((a, b) => b.startedAt - a.startedAt);\n\n // Build CSV\n const csvEscape = (val: string | number | null | undefined): string => {\n if (val === null || val === undefined) return '';\n const s = String(val);\n if (s.includes(',') || s.includes('\"') || s.includes('\\n')) {\n return `\"${s.replace(/\"/g, '\"\"')}\"`;\n }\n return s;\n };\n\n const lines: string[] = [];\n\n // Projects section\n lines.push('=== PROJECTS ===');\n lines.push('Project,Category,Sessions,Messages,Cost ($),Active Time (min),Last Session');\n for (const p of summaries) {\n lines.push([\n csvEscape(p.name),\n csvEscape(p.category),\n p.session_count,\n p.total_messages,\n (p.total_cost / 1_000_000).toFixed(2),\n Math.round(p.total_active_minutes),\n p.last_session_at ? new Date(p.last_session_at).toISOString().split('T')[0] : '',\n ].join(','));\n }\n\n lines.push('');\n\n // Sessions section\n lines.push('=== SESSIONS ===');\n lines.push('Project,Session ID,Slug,Model,Date,Messages,Tokens In,Tokens Out,Cost ($),Active Time (min),Branch');\n for (const s of allSessions) {\n const proj = summaries.find((p) => p.id === s.projectId);\n lines.push([\n csvEscape(proj?.name ?? s.projectId),\n csvEscape(s.id),\n csvEscape(s.slug),\n csvEscape(s.model),\n new Date(s.startedAt).toISOString().split('T')[0],\n s.messageCount,\n s.totalInputTokens,\n s.totalOutputTokens,\n (s.costMicrodollars / 1_000_000).toFixed(2),\n Math.round(s.activeMinutes),\n csvEscape(s.gitBranch),\n ].join(','));\n }\n\n const csv = lines.join('\\n');\n res.writeHead(200, {\n 'Content-Type': 'text/csv',\n 'Content-Disposition': `attachment; filename=\"runtimescope-export-${new Date().toISOString().split('T')[0]}.csv\"`,\n });\n res.end(csv);\n });\n\n route('GET', '/api/pm/projects/summaries', (_req, res, params) => {\n const startDate = params.get('start_date') ?? undefined;\n const endDate = params.get('end_date') ?? undefined;\n const hideEmpty = params.get('hide_empty') === '1' || params.get('hide_empty') === 'true';\n const summaries = pmStore.getProjectSummaries({ startDate, endDate, hideEmpty });\n helpers.json(res, { data: summaries, count: summaries.length });\n });\n\n route('GET', '/api/pm/projects/:id', (_req, res, params) => {\n const id = params.get('id')!;\n const project = pmStore.getProject(id);\n if (!project) { helpers.json(res, { error: 'Project not found' }, 404); return; }\n const stats = pmStore.getSessionStats(id);\n helpers.json(res, { ...project, stats });\n });\n\n route('PUT', '/api/pm/projects/:id', async (req, res, params) => {\n const id = params.get('id')!;\n const body = await helpers.readBody(req, 65536);\n if (!body) { helpers.json(res, { error: 'Body required' }, 400); return; }\n try {\n const updates = JSON.parse(body);\n pmStore.updateProject(id, updates);\n helpers.json(res, { ok: true });\n } catch (err) {\n helpers.json(res, { error: (err as Error).message }, 400);\n }\n });\n\n // ============================================================\n // Tasks\n // ============================================================\n\n route('GET', '/api/pm/tasks', (_req, res, params) => {\n const projectId = params.get('project_id') ?? undefined;\n const status = (params.get('status') ?? undefined) as TaskStatus | undefined;\n const tasks = pmStore.listTasks(projectId, status);\n helpers.json(res, { data: tasks, count: tasks.length });\n });\n\n route('POST', '/api/pm/tasks', async (req, res) => {\n const body = await helpers.readBody(req, 65536);\n if (!body) { helpers.json(res, { error: 'Body required' }, 400); return; }\n try {\n const data = JSON.parse(body);\n const now = Date.now();\n const task: PmTask = {\n id: crypto.randomUUID(),\n projectId: data.projectId ?? undefined,\n title: data.title,\n description: data.description ?? undefined,\n status: data.status ?? 'todo',\n priority: data.priority ?? 'medium',\n labels: data.labels ?? [],\n source: data.source ?? 'manual',\n sourceRef: data.sourceRef ?? undefined,\n sortOrder: data.sortOrder ?? now,\n assignedTo: data.assignedTo ?? undefined,\n dueDate: data.dueDate ?? undefined,\n createdAt: now,\n updatedAt: now,\n completedAt: undefined,\n };\n pmStore.createTask(task);\n helpers.json(res, task, 201);\n } catch (err) {\n helpers.json(res, { error: (err as Error).message }, 400);\n }\n });\n\n route('PUT', '/api/pm/tasks/:id', async (req, res, params) => {\n const id = params.get('id')!;\n const body = await helpers.readBody(req, 65536);\n if (!body) { helpers.json(res, { error: 'Body required' }, 400); return; }\n try {\n const updates = JSON.parse(body);\n pmStore.updateTask(id, updates);\n helpers.json(res, { ok: true });\n } catch (err) {\n helpers.json(res, { error: (err as Error).message }, 400);\n }\n });\n\n route('DELETE', '/api/pm/tasks/:id', (_req, res, params) => {\n const id = params.get('id')!;\n pmStore.deleteTask(id);\n helpers.json(res, { ok: true });\n });\n\n route('PUT', '/api/pm/tasks/:id/reorder', async (req, res, params) => {\n const id = params.get('id')!;\n const body = await helpers.readBody(req, 4096);\n if (!body) { helpers.json(res, { error: 'Body required' }, 400); return; }\n try {\n const { status, sortOrder } = JSON.parse(body);\n pmStore.reorderTask(id, status, sortOrder);\n helpers.json(res, { ok: true });\n } catch (err) {\n helpers.json(res, { error: (err as Error).message }, 400);\n }\n });\n\n // ============================================================\n // Sessions\n // ============================================================\n\n route('GET', '/api/pm/sessions', (_req, res, params) => {\n const projectId = params.get('project_id') ?? undefined;\n const limit = parseInt(params.get('limit') ?? '100', 10);\n const offset = parseInt(params.get('offset') ?? '0', 10);\n const startDate = params.get('start_date') ?? undefined;\n const endDate = params.get('end_date') ?? undefined;\n const hideEmpty = params.get('hide_empty') === '1' || params.get('hide_empty') === 'true';\n const sessions = pmStore.listSessions(projectId, { limit, offset, startDate, endDate, hideEmpty });\n const stats = pmStore.getSessionStats(projectId, { startDate, endDate, hideEmpty });\n helpers.json(res, { data: sessions, count: sessions.length, total: stats.totalSessions });\n });\n\n route('GET', '/api/pm/sessions/stats', (_req, res, params) => {\n const projectId = params.get('project_id') || undefined;\n const startDate = params.get('start_date') ?? undefined;\n const endDate = params.get('end_date') ?? undefined;\n const hideEmpty = params.get('hide_empty') === '1' || params.get('hide_empty') === 'true';\n const stats = pmStore.getSessionStats(projectId, { startDate, endDate, hideEmpty });\n helpers.json(res, stats);\n });\n\n route('GET', '/api/pm/sessions/:id', (_req, res, params) => {\n const id = params.get('id')!;\n const session = pmStore.getSession(id);\n if (!session) { helpers.json(res, { error: 'Session not found' }, 404); return; }\n helpers.json(res, session);\n });\n\n route('POST', '/api/pm/sessions/:id/refresh', async (_req, res, params) => {\n const id = params.get('id')!;\n const session = pmStore.getSession(id);\n if (!session) { helpers.json(res, { error: 'Session not found' }, 404); return; }\n try {\n await discovery.indexSession(id, session.projectId, session.jsonlPath);\n const updated = pmStore.getSession(id);\n helpers.json(res, updated);\n } catch (err) {\n helpers.json(res, { error: (err as Error).message }, 500);\n }\n });\n\n // ============================================================\n // Notes\n // ============================================================\n\n route('GET', '/api/pm/notes', (_req, res, params) => {\n const projectId = params.get('project_id') ?? undefined;\n const pinned = params.get('pinned') === '1' ? true : undefined;\n const notes = pmStore.listNotes({ projectId, pinned });\n helpers.json(res, { data: notes, count: notes.length });\n });\n\n route('POST', '/api/pm/notes', async (req, res) => {\n const body = await helpers.readBody(req, 1_048_576); // 1MB for note content\n if (!body) { helpers.json(res, { error: 'Body required' }, 400); return; }\n try {\n const data = JSON.parse(body);\n const now = Date.now();\n const note: PmNote = {\n id: crypto.randomUUID(),\n projectId: data.projectId ?? undefined,\n sessionId: data.sessionId ?? undefined,\n title: data.title ?? 'Untitled',\n content: data.content ?? '',\n pinned: data.pinned ?? false,\n tags: data.tags ?? [],\n createdAt: now,\n updatedAt: now,\n };\n pmStore.createNote(note);\n helpers.json(res, note, 201);\n } catch (err) {\n helpers.json(res, { error: (err as Error).message }, 400);\n }\n });\n\n route('PUT', '/api/pm/notes/:id', async (req, res, params) => {\n const id = params.get('id')!;\n const body = await helpers.readBody(req, 1_048_576);\n if (!body) { helpers.json(res, { error: 'Body required' }, 400); return; }\n try {\n const updates = JSON.parse(body);\n pmStore.updateNote(id, updates);\n helpers.json(res, { ok: true });\n } catch (err) {\n helpers.json(res, { error: (err as Error).message }, 400);\n }\n });\n\n route('DELETE', '/api/pm/notes/:id', (_req, res, params) => {\n const id = params.get('id')!;\n pmStore.deleteNote(id);\n helpers.json(res, { ok: true });\n });\n\n // ============================================================\n // Memory Files\n // ============================================================\n\n route('GET', '/api/pm/memory/:projectId', async (_req, res, params) => {\n const projectId = params.get('projectId')!;\n const project = pmStore.getProject(projectId);\n if (!project?.claudeProjectKey) {\n helpers.json(res, { data: [], count: 0 });\n return;\n }\n\n const memoryDir = join(homedir(), '.claude', 'projects', project.claudeProjectKey, 'memory');\n try {\n const files = await readdir(memoryDir);\n const mdFiles = files.filter(f => f.endsWith('.md'));\n const result = await Promise.all(\n mdFiles.map(async (filename) => {\n const content = await readFile(join(memoryDir, filename), 'utf-8');\n return { filename, content, sizeBytes: Buffer.byteLength(content) };\n }),\n );\n helpers.json(res, { data: result, count: result.length });\n } catch {\n helpers.json(res, { data: [], count: 0 });\n }\n });\n\n route('GET', '/api/pm/memory/:projectId/:filename', async (_req, res, params) => {\n const projectId = params.get('projectId')!;\n const filename = sanitizeFilename(params.get('filename')!);\n const project = pmStore.getProject(projectId);\n if (!project?.claudeProjectKey) { helpers.json(res, { error: 'Project not found' }, 404); return; }\n\n const filePath = join(homedir(), '.claude', 'projects', project.claudeProjectKey, 'memory', filename);\n try {\n const content = await readFile(filePath, 'utf-8');\n helpers.json(res, { filename, content, sizeBytes: Buffer.byteLength(content) });\n } catch {\n helpers.json(res, { error: 'File not found' }, 404);\n }\n });\n\n route('PUT', '/api/pm/memory/:projectId/:filename', async (req, res, params) => {\n const projectId = params.get('projectId')!;\n const filename = sanitizeFilename(params.get('filename')!);\n const project = pmStore.getProject(projectId);\n if (!project?.claudeProjectKey) { helpers.json(res, { error: 'Project not found' }, 404); return; }\n\n const body = await helpers.readBody(req, 1_048_576); // 1MB limit\n if (!body) { helpers.json(res, { error: 'Body required' }, 400); return; }\n\n try {\n const { content } = JSON.parse(body);\n const memoryDir = join(homedir(), '.claude', 'projects', project.claudeProjectKey, 'memory');\n await mkdir(memoryDir, { recursive: true });\n await writeFile(join(memoryDir, filename), content, 'utf-8');\n helpers.json(res, { ok: true });\n } catch (err) {\n helpers.json(res, { error: (err as Error).message }, 500);\n }\n });\n\n route('DELETE', '/api/pm/memory/:projectId/:filename', async (_req, res, params) => {\n const projectId = params.get('projectId')!;\n const filename = sanitizeFilename(params.get('filename')!);\n const project = pmStore.getProject(projectId);\n if (!project?.claudeProjectKey) { helpers.json(res, { error: 'Project not found' }, 404); return; }\n\n const filePath = join(homedir(), '.claude', 'projects', project.claudeProjectKey, 'memory', filename);\n try {\n await unlink(filePath);\n helpers.json(res, { ok: true });\n } catch {\n helpers.json(res, { error: 'File not found' }, 404);\n }\n });\n\n // ============================================================\n // Rules (CLAUDE.md at 3 scopes)\n // ============================================================\n\n route('GET', '/api/pm/rules/:projectId', async (_req, res, params) => {\n const projectId = params.get('projectId')!;\n const project = pmStore.getProject(projectId);\n if (!project) { helpers.json(res, { error: 'Project not found' }, 404); return; }\n\n const paths = getRulesPaths(project.claudeProjectKey, project.path);\n const result = {\n global: await readRuleFile(paths.global),\n project: await readRuleFile(paths.project),\n local: await readRuleFile(paths.local),\n };\n helpers.json(res, result);\n });\n\n route('GET', '/api/pm/rules/:projectId/:scope', async (_req, res, params) => {\n const projectId = params.get('projectId')!;\n const scope = params.get('scope')!;\n if (!['global', 'project', 'local'].includes(scope)) {\n helpers.json(res, { error: 'Invalid scope. Must be: global, project, or local' }, 400);\n return;\n }\n const project = pmStore.getProject(projectId);\n if (!project) { helpers.json(res, { error: 'Project not found' }, 404); return; }\n\n const paths = getRulesPaths(project.claudeProjectKey, project.path);\n const filePath = paths[scope as keyof typeof paths];\n helpers.json(res, await readRuleFile(filePath));\n });\n\n route('PUT', '/api/pm/rules/:projectId/:scope', async (req, res, params) => {\n const projectId = params.get('projectId')!;\n const scope = params.get('scope')!;\n if (!['global', 'project', 'local'].includes(scope)) {\n helpers.json(res, { error: 'Invalid scope' }, 400);\n return;\n }\n const project = pmStore.getProject(projectId);\n if (!project) { helpers.json(res, { error: 'Project not found' }, 404); return; }\n\n const body = await helpers.readBody(req, 1_048_576);\n if (!body) { helpers.json(res, { error: 'Body required' }, 400); return; }\n\n try {\n const { content } = JSON.parse(body);\n const paths = getRulesPaths(project.claudeProjectKey, project.path);\n const filePath = paths[scope as keyof typeof paths];\n\n // Ensure parent directory exists\n const dir = join(filePath, '..');\n await mkdir(dir, { recursive: true });\n await writeFile(filePath, content, 'utf-8');\n helpers.json(res, { ok: true });\n } catch (err) {\n helpers.json(res, { error: (err as Error).message }, 500);\n }\n });\n\n // ============================================================\n // Dev Server Management\n // ============================================================\n\n route('GET', '/api/pm/projects/:id/scripts', async (_req, res, params) => {\n const id = params.get('id')!;\n const project = pmStore.getProject(id);\n if (!project) { helpers.json(res, { error: 'Project not found' }, 404); return; }\n if (!project.path) { helpers.json(res, { data: { scripts: {}, recommended: null } }); return; }\n\n try {\n const pkgPath = join(project.path, 'package.json');\n const pkg = JSON.parse(await readFile(pkgPath, 'utf-8'));\n const scripts: Record<string, string> = pkg.scripts ?? {};\n const recommended = ['dev', 'start', 'serve'].find((s) => s in scripts) ?? null;\n helpers.json(res, { data: { scripts, recommended } });\n } catch {\n helpers.json(res, { data: { scripts: {}, recommended: null } });\n }\n });\n\n route('GET', '/api/pm/projects/:id/dev-server', (_req, res, params) => {\n const id = params.get('id')!;\n const mp = managedProcesses.get(id);\n if (!mp) { helpers.json(res, { data: { status: 'stopped' } }); return; }\n // Verify PID still alive\n try { process.kill(mp.pid, 0); } catch {\n managedProcesses.delete(id);\n helpers.json(res, { data: { status: 'stopped' } });\n return;\n }\n helpers.json(res, {\n data: {\n status: mp.status,\n pid: mp.pid,\n command: mp.command,\n startedAt: mp.startedAt,\n exitCode: mp.exitCode,\n logs: mp.logs.slice(-100),\n },\n });\n });\n\n route('POST', '/api/pm/projects/:id/dev-server', async (req, res, params) => {\n const id = params.get('id')!;\n const project = pmStore.getProject(id);\n if (!project) { helpers.json(res, { error: 'Project not found' }, 404); return; }\n if (!project.path) { helpers.json(res, { error: 'Project has no filesystem path' }, 400); return; }\n\n // Check if already running\n const existing = managedProcesses.get(id);\n if (existing) {\n try { process.kill(existing.pid, 0); helpers.json(res, { error: 'Dev server already running', data: { pid: existing.pid, status: existing.status } }, 409); return; } catch { managedProcesses.delete(id); }\n }\n\n const body = await helpers.readBody(req, 4096);\n let script: string | undefined;\n let command: string | undefined;\n if (body) {\n try {\n const data = JSON.parse(body);\n script = data.script;\n command = data.command;\n } catch { /* use defaults */ }\n }\n\n const finalCommand = command ?? (script ? `npm run ${script}` : 'npm run dev');\n const broadcast = broadcastDevServer ?? (() => {});\n\n try {\n const child = spawn(finalCommand, {\n cwd: project.path,\n shell: true,\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n const pid = child.pid;\n if (!pid) { helpers.json(res, { error: 'Failed to spawn process' }, 500); return; }\n\n const managed: ManagedProcess = {\n pid,\n command: finalCommand,\n projectId: id,\n startedAt: Date.now(),\n status: 'starting',\n child,\n logs: [],\n exitCode: null,\n };\n managedProcesses.set(id, managed);\n\n // Broadcast starting status\n broadcast({ type: 'dev_server_status', projectId: id, status: 'starting', pid });\n\n // Flip to 'running' on first output or after 500ms\n let detectedPort: number | null = null;\n const flipTimer = setTimeout(() => {\n if (managed.status === 'starting') {\n managed.status = 'running';\n broadcast({ type: 'dev_server_status', projectId: id, status: 'running', pid, port: detectedPort });\n }\n }, 500);\n\n // Detect port from log output (e.g. \"localhost:3000\", \"http://localhost:5173\")\n const PORT_RE = /(?:localhost|127\\.0\\.0\\.1|0\\.0\\.0\\.0):(\\d{4,5})/;\n\n // Capture stdout/stderr and broadcast log lines\n for (const [stream, src] of [['stdout', child.stdout!], ['stderr', child.stderr!]] as const) {\n let buf = '';\n src.on('data', (chunk: Buffer) => {\n if (managed.status === 'starting') {\n managed.status = 'running';\n clearTimeout(flipTimer);\n broadcast({ type: 'dev_server_status', projectId: id, status: 'running', pid, port: detectedPort });\n }\n buf += chunk.toString('utf-8');\n const lines = buf.split('\\n');\n buf = lines.pop() ?? '';\n for (const line of lines) {\n if (!line) continue;\n // Detect port from output\n if (!detectedPort) {\n const portMatch = line.match(PORT_RE);\n if (portMatch) {\n detectedPort = parseInt(portMatch[1], 10);\n broadcast({ type: 'dev_server_status', projectId: id, status: 'running', pid, port: detectedPort });\n }\n }\n pushLog(managed, stream, line);\n broadcast({ type: 'dev_server_log', projectId: id, stream, line, ts: Date.now() });\n }\n });\n }\n\n child.on('exit', (code) => {\n clearTimeout(flipTimer);\n managed.status = code === 0 ? 'stopped' : 'crashed';\n managed.exitCode = code;\n broadcast({ type: 'dev_server_status', projectId: id, status: managed.status, pid, exitCode: code });\n // Keep in map briefly so status can be queried, then clean up\n setTimeout(() => managedProcesses.delete(id), 5000);\n });\n\n child.on('error', (err) => {\n clearTimeout(flipTimer);\n managed.status = 'crashed';\n pushLog(managed, 'stderr', `[error] ${err.message}`);\n broadcast({ type: 'dev_server_status', projectId: id, status: 'crashed', pid, error: err.message });\n setTimeout(() => managedProcesses.delete(id), 5000);\n });\n\n helpers.json(res, { data: { pid, command: finalCommand, cwd: project.path, status: 'starting' } });\n } catch (err) {\n helpers.json(res, { error: (err as Error).message }, 500);\n }\n });\n\n route('DELETE', '/api/pm/projects/:id/dev-server', async (req, res, params) => {\n const id = params.get('id')!;\n const project = pmStore.getProject(id);\n if (!project) { helpers.json(res, { error: 'Project not found' }, 404); return; }\n\n // Read optional signal from body\n let signal: NodeJS.Signals = 'SIGTERM';\n const body = await helpers.readBody(req, 1024);\n if (body) {\n try {\n const data = JSON.parse(body);\n if (data.signal === 'SIGKILL') signal = 'SIGKILL';\n } catch { /* use default */ }\n }\n\n // Find the process — check managed map first, then scan by cwd\n let pid: number | null = null;\n\n const managed = managedProcesses.get(id);\n if (managed) {\n pid = managed.pid;\n // Kill the child process tree if we have the reference\n try { managed.child.kill(signal); } catch { /* fallthrough to process.kill */ }\n managedProcesses.delete(id);\n } else if (project.path) {\n // Try to find a dev server process in the project's directory\n try {\n const output = execSync(\n `lsof -t +D \"${project.path}\" 2>/dev/null | head -5`,\n { encoding: 'utf-8', timeout: 5000 },\n ).trim();\n const pids = output.split('\\n').filter(Boolean).map(Number).filter((n) => n > 1 && n !== process.pid);\n if (pids.length > 0) pid = pids[0];\n } catch { /* no process found */ }\n }\n\n if (!pid) { helpers.json(res, { error: 'No running dev server found for this project' }, 404); return; }\n\n try {\n process.kill(pid, signal);\n managedProcesses.delete(id);\n helpers.json(res, { data: { killed: true, pid, signal } });\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n managedProcesses.delete(id);\n if (code === 'ESRCH') {\n // Process already exited — treat as success\n helpers.json(res, { data: { killed: true, pid, signal, note: 'Process already exited' } });\n } else {\n helpers.json(res, { error: `Failed to kill PID ${pid}: ${(err as Error).message}` }, 500);\n }\n }\n });\n\n // ============================================================\n // Git\n // ============================================================\n\n route('GET', '/api/pm/projects/:id/git/status', (_req, res, params) => {\n const id = params.get('id')!;\n const project = pmStore.getProject(id);\n if (!project) { helpers.json(res, { error: 'Project not found' }, 404); return; }\n if (!project.path) { helpers.json(res, { data: { isGitRepo: false, branch: '', staged: [], unstaged: [], untracked: [] } }); return; }\n\n if (!isGitRepo(project.path)) {\n helpers.json(res, { data: { isGitRepo: false, branch: '', staged: [], unstaged: [], untracked: [] } });\n return;\n }\n\n try {\n const branch = execGit(['rev-parse', '--abbrev-ref', 'HEAD'], project.path).trim();\n const porcelain = execGit(['status', '--porcelain'], project.path);\n const { staged, unstaged, untracked } = parseGitStatus(porcelain);\n helpers.json(res, { data: { isGitRepo: true, branch, staged, unstaged, untracked } });\n } catch (err) {\n helpers.json(res, { error: (err as Error).message }, 500);\n }\n });\n\n route('GET', '/api/pm/projects/:id/git/log', (_req, res, params) => {\n const id = params.get('id')!;\n const project = pmStore.getProject(id);\n if (!project) { helpers.json(res, { error: 'Project not found' }, 404); return; }\n if (!project.path || !isGitRepo(project.path)) { helpers.json(res, { data: [] }); return; }\n\n try {\n const raw = execGit(['log', '-30', '--format=%H%x00%h%x00%B%x00%an%x00%cr%x00%D%x01'], project.path);\n const commits: GitCommit[] = raw.trim().split('\\x01').filter(Boolean).map((entry) => {\n const [hash, shortHash, message, author, relativeDate, refs] = entry.trim().split('\\0');\n const fullMsg = (message ?? '').trim();\n const subject = fullMsg.split('\\n')[0];\n return { hash, shortHash, subject, message: fullMsg, author, relativeDate, refs: refs?.trim() || '' };\n });\n helpers.json(res, { data: commits });\n } catch (err) {\n helpers.json(res, { error: (err as Error).message }, 500);\n }\n });\n\n route('POST', '/api/pm/projects/:id/git/stage', async (req, res, params) => {\n const id = params.get('id')!;\n const project = pmStore.getProject(id);\n if (!project) { helpers.json(res, { error: 'Project not found' }, 404); return; }\n if (!project.path || !isGitRepo(project.path)) { helpers.json(res, { error: 'Not a git repo' }, 400); return; }\n\n const body = await helpers.readBody(req, 65536);\n let files: string[] | undefined;\n if (body) {\n try { files = JSON.parse(body).files; } catch { /* stage all */ }\n }\n\n try {\n if (files && files.length > 0) {\n execGit(['add', '--', ...files], project.path);\n } else {\n execGit(['add', '-A'], project.path);\n }\n helpers.json(res, { ok: true });\n } catch (err) {\n helpers.json(res, { error: (err as Error).message }, 500);\n }\n });\n\n route('POST', '/api/pm/projects/:id/git/unstage', async (req, res, params) => {\n const id = params.get('id')!;\n const project = pmStore.getProject(id);\n if (!project) { helpers.json(res, { error: 'Project not found' }, 404); return; }\n if (!project.path || !isGitRepo(project.path)) { helpers.json(res, { error: 'Not a git repo' }, 400); return; }\n\n const body = await helpers.readBody(req, 65536);\n let files: string[] | undefined;\n if (body) {\n try { files = JSON.parse(body).files; } catch { /* unstage all */ }\n }\n\n try {\n if (files && files.length > 0) {\n execGit(['restore', '--staged', '--', ...files], project.path);\n } else {\n execGit(['reset', 'HEAD'], project.path);\n }\n helpers.json(res, { ok: true });\n } catch (err) {\n helpers.json(res, { error: (err as Error).message }, 500);\n }\n });\n\n route('POST', '/api/pm/projects/:id/git/commit', async (req, res, params) => {\n const id = params.get('id')!;\n const project = pmStore.getProject(id);\n if (!project) { helpers.json(res, { error: 'Project not found' }, 404); return; }\n if (!project.path || !isGitRepo(project.path)) { helpers.json(res, { error: 'Not a git repo' }, 400); return; }\n\n const body = await helpers.readBody(req, 65536);\n if (!body) { helpers.json(res, { error: 'Body required' }, 400); return; }\n\n try {\n const { message } = JSON.parse(body);\n if (!message || !message.trim()) { helpers.json(res, { error: 'Commit message required' }, 400); return; }\n const output = execGit(['commit', '-m', message], project.path);\n // Extract hash from output\n const hashMatch = output.match(/\\[[\\w/.-]+ ([a-f0-9]+)\\]/);\n helpers.json(res, { ok: true, hash: hashMatch?.[1] ?? '' });\n } catch (err) {\n helpers.json(res, { error: (err as Error).message }, 500);\n }\n });\n\n route('GET', '/api/pm/projects/:id/git/diff', (_req, res, params) => {\n const id = params.get('id')!;\n const project = pmStore.getProject(id);\n if (!project) { helpers.json(res, { error: 'Project not found' }, 404); return; }\n if (!project.path || !isGitRepo(project.path)) { helpers.json(res, { data: { diff: '' } }); return; }\n\n const staged = params.get('staged') === '1' || params.get('staged') === 'true';\n const file = params.get('file') ?? undefined;\n\n try {\n const args = ['diff'];\n if (staged) args.push('--staged');\n if (file) args.push('--', file);\n const diff = execGit(args, project.path);\n helpers.json(res, { data: { diff } });\n } catch (err) {\n helpers.json(res, { error: (err as Error).message }, 500);\n }\n });\n\n // ============================================================\n // CapEx\n // ============================================================\n\n route('GET', '/api/pm/capex/:projectId', (_req, res, params) => {\n const projectId = params.get('projectId')!;\n const month = params.get('month') ?? undefined;\n const confirmed = params.get('confirmed') === '1' ? true : params.get('confirmed') === '0' ? false : undefined;\n const entries = pmStore.listCapexEntries(projectId, { month, confirmed });\n helpers.json(res, { data: entries, count: entries.length });\n });\n\n route('GET', '/api/pm/capex/:projectId/summary', (_req, res, params) => {\n const projectId = params.get('projectId')!;\n const startDate = params.get('start_date') ?? undefined;\n const endDate = params.get('end_date') ?? undefined;\n const summary = pmStore.getCapexSummary(projectId, { startDate, endDate });\n helpers.json(res, summary);\n });\n\n route('PUT', '/api/pm/capex/:projectId/:entryId', async (req, res, params) => {\n const entryId = params.get('entryId')!;\n const body = await helpers.readBody(req, 65536);\n if (!body) { helpers.json(res, { error: 'Body required' }, 400); return; }\n try {\n const updates = JSON.parse(body);\n pmStore.updateCapexEntry(entryId, updates);\n helpers.json(res, { ok: true });\n } catch (err) {\n helpers.json(res, { error: (err as Error).message }, 400);\n }\n });\n\n route('POST', '/api/pm/capex/:projectId/:entryId/confirm', (_req, res, params) => {\n const entryId = params.get('entryId')!;\n pmStore.confirmCapexEntry(entryId);\n helpers.json(res, { ok: true });\n });\n\n route('GET', '/api/pm/capex/:projectId/export', (_req, res, params) => {\n const projectId = params.get('projectId')!;\n const startDate = params.get('start_date') ?? undefined;\n const endDate = params.get('end_date') ?? undefined;\n const csv = pmStore.exportCapexCsv(projectId, { startDate, endDate });\n res.writeHead(200, {\n 'Content-Type': 'text/csv',\n 'Content-Disposition': `attachment; filename=\"capex-${projectId}.csv\"`,\n });\n res.end(csv);\n });\n\n // ============================================================\n // Pattern matcher\n // ============================================================\n\n return {\n match(method: string, pathname: string) {\n const pathSegments = pathname.split('/');\n\n for (const r of routes) {\n if (r.method !== method) continue;\n if (r.segments.length !== pathSegments.length) continue;\n\n const pathParams: Record<string, string> = {};\n let matched = true;\n\n for (let i = 0; i < r.segments.length; i++) {\n if (r.segments[i].startsWith(':')) {\n pathParams[r.segments[i].slice(1)] = decodeURIComponent(pathSegments[i]);\n } else if (r.segments[i] !== pathSegments[i]) {\n matched = false;\n break;\n }\n }\n\n if (matched) {\n return { handler: r.handler, pathParams };\n }\n }\n\n return null;\n },\n };\n}\n\n// ============================================================\n// Helpers\n// ============================================================\n\nfunction sanitizeFilename(name: string): string {\n // Strip path separators and double dots to prevent traversal\n return name.replace(/[/\\\\]/g, '').replace(/\\.\\./g, '');\n}\n\nfunction getRulesPaths(claudeProjectKey?: string, projectPath?: string) {\n const home = homedir();\n return {\n global: join(home, '.claude', 'CLAUDE.md'),\n project: claudeProjectKey\n ? join(home, '.claude', 'projects', claudeProjectKey, 'CLAUDE.md')\n : join(projectPath ?? '', '.claude', 'CLAUDE.md'),\n local: projectPath\n ? join(projectPath, 'CLAUDE.md')\n : join(home, 'CLAUDE.md'),\n };\n}\n\nfunction execGit(args: string[], cwd: string): string {\n return execFileSync('git', args, { cwd, encoding: 'utf-8', timeout: 10000, maxBuffer: 10 * 1024 * 1024 });\n}\n\nfunction isGitRepo(path: string): boolean {\n try {\n execFileSync('git', ['rev-parse', '--git-dir'], { cwd: path, encoding: 'utf-8', timeout: 3000 });\n return true;\n } catch {\n return false;\n }\n}\n\nfunction parseGitStatus(porcelain: string): { staged: GitFileChange[]; unstaged: GitFileChange[]; untracked: GitFileChange[] } {\n const staged: GitFileChange[] = [];\n const unstaged: GitFileChange[] = [];\n const untracked: GitFileChange[] = [];\n\n for (const line of porcelain.split('\\n')) {\n if (!line || line.length < 4) continue;\n const x = line[0]; // index (staging area) status\n const y = line[1]; // worktree status\n const filepath = line.slice(3);\n\n // Handle renames: \"R old -> new\"\n let path = filepath;\n let oldPath: string | undefined;\n if (filepath.includes(' -> ')) {\n const parts = filepath.split(' -> ');\n oldPath = parts[0];\n path = parts[1];\n }\n\n // Untracked\n if (x === '?' && y === '?') {\n untracked.push({ path, status: '?' });\n continue;\n }\n\n // Staged changes (index column)\n if (x !== ' ' && x !== '?') {\n staged.push({ path, status: x as GitFileStatus, oldPath });\n }\n\n // Unstaged changes (worktree column)\n if (y !== ' ' && y !== '?') {\n unstaged.push({ path, status: y as GitFileStatus });\n }\n }\n\n return { staged, unstaged, untracked };\n}\n\nasync function readRuleFile(filePath: string): Promise<{ path: string; content: string; exists: boolean }> {\n try {\n if (existsSync(filePath)) {\n const content = await readFile(filePath, 'utf-8');\n return { path: filePath, content, exists: true };\n }\n } catch { /* ignore */ }\n return { path: filePath, content: '', exists: false };\n}\n","import Database from 'better-sqlite3';\nimport type {\n PmProject,\n PmTask,\n PmSession,\n PmNote,\n PmCapexEntry,\n CapexSummary,\n SessionStats,\n TaskStatus,\n ProjectPhase,\n ProjectStatus,\n} from './pm-types.js';\n\n// ============================================================\n// Project Management SQLite Store\n// Global database at ~/.runtimescope/pm.db\n// All tables prefixed with pm_ to avoid collision\n// ============================================================\n\nexport interface PmStoreOptions {\n dbPath: string;\n walMode?: boolean;\n}\n\nexport class PmStore {\n private db: InstanceType<typeof Database>;\n\n constructor(options: PmStoreOptions) {\n this.db = new Database(options.dbPath);\n\n if (options.walMode !== false) {\n this.db.pragma('journal_mode = WAL');\n }\n this.db.pragma('synchronous = NORMAL');\n\n this.createSchema();\n this.runMigrations();\n }\n\n private createSchema(): void {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS pm_projects (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n path TEXT,\n claude_project_key TEXT,\n runtimescope_project TEXT,\n phase TEXT NOT NULL DEFAULT 'preliminary',\n management_authorized INTEGER NOT NULL DEFAULT 0,\n probable_to_complete INTEGER NOT NULL DEFAULT 0,\n project_status TEXT NOT NULL DEFAULT 'active',\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n metadata TEXT\n );\n\n CREATE TABLE IF NOT EXISTS pm_tasks (\n id TEXT PRIMARY KEY,\n project_id TEXT,\n title TEXT NOT NULL,\n description TEXT,\n status TEXT NOT NULL DEFAULT 'todo',\n priority TEXT NOT NULL DEFAULT 'medium',\n labels TEXT,\n source TEXT DEFAULT 'manual',\n source_ref TEXT,\n sort_order REAL NOT NULL DEFAULT 0,\n assigned_to TEXT,\n due_date TEXT,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n completed_at INTEGER,\n FOREIGN KEY (project_id) REFERENCES pm_projects(id)\n );\n\n CREATE INDEX IF NOT EXISTS idx_pm_tasks_project ON pm_tasks(project_id);\n CREATE INDEX IF NOT EXISTS idx_pm_tasks_status ON pm_tasks(status);\n CREATE INDEX IF NOT EXISTS idx_pm_tasks_sort ON pm_tasks(status, sort_order);\n\n CREATE TABLE IF NOT EXISTS pm_sessions (\n id TEXT PRIMARY KEY,\n project_id TEXT NOT NULL,\n jsonl_path TEXT NOT NULL,\n jsonl_size INTEGER,\n first_prompt TEXT,\n summary TEXT,\n slug TEXT,\n model TEXT,\n version TEXT,\n git_branch TEXT,\n message_count INTEGER NOT NULL DEFAULT 0,\n user_message_count INTEGER NOT NULL DEFAULT 0,\n assistant_message_count INTEGER NOT NULL DEFAULT 0,\n total_input_tokens INTEGER NOT NULL DEFAULT 0,\n total_output_tokens INTEGER NOT NULL DEFAULT 0,\n total_cache_creation_tokens INTEGER NOT NULL DEFAULT 0,\n total_cache_read_tokens INTEGER NOT NULL DEFAULT 0,\n cost_microdollars INTEGER NOT NULL DEFAULT 0,\n started_at INTEGER NOT NULL,\n ended_at INTEGER,\n active_minutes REAL NOT NULL DEFAULT 0,\n compaction_count INTEGER NOT NULL DEFAULT 0,\n pre_compaction_tokens INTEGER,\n permission_mode TEXT,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n FOREIGN KEY (project_id) REFERENCES pm_projects(id)\n );\n\n CREATE INDEX IF NOT EXISTS idx_pm_sessions_project ON pm_sessions(project_id);\n CREATE INDEX IF NOT EXISTS idx_pm_sessions_started ON pm_sessions(started_at DESC);\n\n CREATE TABLE IF NOT EXISTS pm_notes (\n id TEXT PRIMARY KEY,\n project_id TEXT,\n session_id TEXT,\n title TEXT NOT NULL,\n content TEXT NOT NULL DEFAULT '',\n pinned INTEGER NOT NULL DEFAULT 0,\n tags TEXT,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n FOREIGN KEY (project_id) REFERENCES pm_projects(id),\n FOREIGN KEY (session_id) REFERENCES pm_sessions(id)\n );\n\n CREATE INDEX IF NOT EXISTS idx_pm_notes_project ON pm_notes(project_id);\n CREATE INDEX IF NOT EXISTS idx_pm_notes_pinned ON pm_notes(pinned DESC, updated_at DESC);\n\n CREATE TABLE IF NOT EXISTS pm_capex_entries (\n id TEXT PRIMARY KEY,\n project_id TEXT NOT NULL,\n session_id TEXT NOT NULL,\n classification TEXT NOT NULL DEFAULT 'expensed',\n work_type TEXT,\n active_minutes REAL NOT NULL DEFAULT 0,\n cost_microdollars INTEGER NOT NULL DEFAULT 0,\n adjustment_factor REAL NOT NULL DEFAULT 1.0,\n adjusted_cost_microdollars INTEGER NOT NULL DEFAULT 0,\n confirmed INTEGER NOT NULL DEFAULT 0,\n confirmed_at INTEGER,\n confirmed_by TEXT,\n notes TEXT,\n period TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n FOREIGN KEY (project_id) REFERENCES pm_projects(id),\n FOREIGN KEY (session_id) REFERENCES pm_sessions(id)\n );\n\n CREATE INDEX IF NOT EXISTS idx_pm_capex_project ON pm_capex_entries(project_id);\n CREATE INDEX IF NOT EXISTS idx_pm_capex_period ON pm_capex_entries(period);\n CREATE INDEX IF NOT EXISTS idx_pm_capex_confirmed ON pm_capex_entries(confirmed);\n `);\n }\n\n private runMigrations(): void {\n // Add category and sdk_installed columns (added in v2)\n try { this.db.exec('ALTER TABLE pm_projects ADD COLUMN category TEXT DEFAULT NULL'); } catch { /* already exists */ }\n try { this.db.exec('ALTER TABLE pm_projects ADD COLUMN sdk_installed INTEGER DEFAULT 0'); } catch { /* already exists */ }\n }\n\n // ============================================================\n // Projects\n // ============================================================\n\n upsertProject(project: PmProject): void {\n this.db.prepare(`\n INSERT INTO pm_projects (id, name, path, claude_project_key, runtimescope_project,\n phase, management_authorized, probable_to_complete, project_status,\n category, sdk_installed,\n created_at, updated_at, metadata)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(id) DO UPDATE SET\n name = excluded.name,\n path = COALESCE(excluded.path, pm_projects.path),\n claude_project_key = COALESCE(excluded.claude_project_key, pm_projects.claude_project_key),\n runtimescope_project = COALESCE(excluded.runtimescope_project, pm_projects.runtimescope_project),\n sdk_installed = CASE WHEN excluded.sdk_installed = 1 THEN 1 ELSE pm_projects.sdk_installed END,\n updated_at = excluded.updated_at,\n metadata = COALESCE(excluded.metadata, pm_projects.metadata)\n `).run(\n project.id,\n project.name,\n project.path ?? null,\n project.claudeProjectKey ?? null,\n project.runtimescopeProject ?? null,\n project.phase,\n project.managementAuthorized ? 1 : 0,\n project.probableToComplete ? 1 : 0,\n project.projectStatus,\n project.category ?? null,\n project.sdkInstalled ? 1 : 0,\n project.createdAt,\n project.updatedAt,\n project.metadata ? JSON.stringify(project.metadata) : null,\n );\n }\n\n getProject(id: string): PmProject | null {\n const row = this.db\n .prepare('SELECT * FROM pm_projects WHERE id = ?')\n .get(id) as PmProjectRow | undefined;\n return row ? this.mapProjectRow(row) : null;\n }\n\n listProjects(): PmProject[] {\n const rows = this.db\n .prepare('SELECT * FROM pm_projects ORDER BY name ASC')\n .all() as PmProjectRow[];\n return rows.map(r => this.mapProjectRow(r));\n }\n\n updateProject(id: string, updates: Partial<PmProject>): void {\n const sets: string[] = [];\n const params: unknown[] = [];\n\n if (updates.name !== undefined) { sets.push('name = ?'); params.push(updates.name); }\n if (updates.phase !== undefined) { sets.push('phase = ?'); params.push(updates.phase); }\n if (updates.managementAuthorized !== undefined) { sets.push('management_authorized = ?'); params.push(updates.managementAuthorized ? 1 : 0); }\n if (updates.probableToComplete !== undefined) { sets.push('probable_to_complete = ?'); params.push(updates.probableToComplete ? 1 : 0); }\n if (updates.projectStatus !== undefined) { sets.push('project_status = ?'); params.push(updates.projectStatus); }\n if (updates.category !== undefined) { sets.push('category = ?'); params.push(updates.category); }\n if (updates.sdkInstalled !== undefined) { sets.push('sdk_installed = ?'); params.push(updates.sdkInstalled ? 1 : 0); }\n if (updates.metadata !== undefined) { sets.push('metadata = ?'); params.push(JSON.stringify(updates.metadata)); }\n\n if (sets.length === 0) return;\n\n sets.push('updated_at = ?');\n params.push(Date.now());\n params.push(id);\n\n this.db.prepare(`UPDATE pm_projects SET ${sets.join(', ')} WHERE id = ?`).run(...params);\n }\n\n listCategories(): string[] {\n const rows = this.db\n .prepare('SELECT DISTINCT category FROM pm_projects WHERE category IS NOT NULL ORDER BY category ASC')\n .all() as { category: string }[];\n return rows.map(r => r.category);\n }\n\n private mapProjectRow(row: PmProjectRow): PmProject {\n return {\n id: row.id,\n name: row.name,\n path: row.path ?? undefined,\n claudeProjectKey: row.claude_project_key ?? undefined,\n runtimescopeProject: row.runtimescope_project ?? undefined,\n phase: row.phase as ProjectPhase,\n managementAuthorized: row.management_authorized === 1,\n probableToComplete: row.probable_to_complete === 1,\n projectStatus: row.project_status as ProjectStatus,\n category: row.category ?? undefined,\n sdkInstalled: row.sdk_installed === 1,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n metadata: row.metadata ? JSON.parse(row.metadata) : undefined,\n };\n }\n\n // ============================================================\n // Tasks\n // ============================================================\n\n createTask(task: PmTask): PmTask {\n this.db.prepare(`\n INSERT INTO pm_tasks (id, project_id, title, description, status, priority,\n labels, source, source_ref, sort_order, assigned_to, due_date,\n created_at, updated_at, completed_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `).run(\n task.id,\n task.projectId ?? null,\n task.title,\n task.description ?? null,\n task.status,\n task.priority,\n JSON.stringify(task.labels),\n task.source,\n task.sourceRef ?? null,\n task.sortOrder,\n task.assignedTo ?? null,\n task.dueDate ?? null,\n task.createdAt,\n task.updatedAt,\n task.completedAt ?? null,\n );\n return task;\n }\n\n updateTask(id: string, updates: Partial<PmTask>): void {\n const sets: string[] = [];\n const params: unknown[] = [];\n\n if (updates.title !== undefined) { sets.push('title = ?'); params.push(updates.title); }\n if (updates.description !== undefined) { sets.push('description = ?'); params.push(updates.description); }\n if (updates.status !== undefined) { sets.push('status = ?'); params.push(updates.status); }\n if (updates.priority !== undefined) { sets.push('priority = ?'); params.push(updates.priority); }\n if (updates.labels !== undefined) { sets.push('labels = ?'); params.push(JSON.stringify(updates.labels)); }\n if (updates.sortOrder !== undefined) { sets.push('sort_order = ?'); params.push(updates.sortOrder); }\n if (updates.assignedTo !== undefined) { sets.push('assigned_to = ?'); params.push(updates.assignedTo); }\n if (updates.dueDate !== undefined) { sets.push('due_date = ?'); params.push(updates.dueDate); }\n if (updates.completedAt !== undefined) { sets.push('completed_at = ?'); params.push(updates.completedAt); }\n\n if (sets.length === 0) return;\n\n sets.push('updated_at = ?');\n params.push(Date.now());\n params.push(id);\n\n this.db.prepare(`UPDATE pm_tasks SET ${sets.join(', ')} WHERE id = ?`).run(...params);\n }\n\n deleteTask(id: string): void {\n this.db.prepare('DELETE FROM pm_tasks WHERE id = ?').run(id);\n }\n\n listTasks(projectId?: string, status?: TaskStatus): PmTask[] {\n const conditions: string[] = [];\n const params: unknown[] = [];\n\n if (projectId) {\n conditions.push('project_id = ?');\n params.push(projectId);\n }\n if (status) {\n conditions.push('status = ?');\n params.push(status);\n }\n\n const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';\n const rows = this.db\n .prepare(`SELECT * FROM pm_tasks ${where} ORDER BY sort_order ASC`)\n .all(...params) as PmTaskRow[];\n\n return rows.map(r => this.mapTaskRow(r));\n }\n\n reorderTask(id: string, status: TaskStatus, sortOrder: number): void {\n const now = Date.now();\n const completedAt = status === 'done' ? now : null;\n this.db.prepare(`\n UPDATE pm_tasks SET status = ?, sort_order = ?, updated_at = ?, completed_at = COALESCE(?, completed_at)\n WHERE id = ?\n `).run(status, sortOrder, now, completedAt, id);\n }\n\n private mapTaskRow(row: PmTaskRow): PmTask {\n return {\n id: row.id,\n projectId: row.project_id ?? undefined,\n title: row.title,\n description: row.description ?? undefined,\n status: row.status as TaskStatus,\n priority: row.priority as PmTask['priority'],\n labels: row.labels ? JSON.parse(row.labels) : [],\n source: (row.source ?? 'manual') as PmTask['source'],\n sourceRef: row.source_ref ?? undefined,\n sortOrder: row.sort_order,\n assignedTo: row.assigned_to ?? undefined,\n dueDate: row.due_date ?? undefined,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n completedAt: row.completed_at ?? undefined,\n };\n }\n\n // ============================================================\n // Sessions\n // ============================================================\n\n upsertSession(session: PmSession): void {\n this.db.prepare(`\n INSERT INTO pm_sessions (id, project_id, jsonl_path, jsonl_size, first_prompt,\n summary, slug, model, version, git_branch,\n message_count, user_message_count, assistant_message_count,\n total_input_tokens, total_output_tokens,\n total_cache_creation_tokens, total_cache_read_tokens,\n cost_microdollars, started_at, ended_at, active_minutes,\n compaction_count, pre_compaction_tokens, permission_mode,\n created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(id) DO UPDATE SET\n jsonl_size = excluded.jsonl_size,\n first_prompt = COALESCE(excluded.first_prompt, pm_sessions.first_prompt),\n summary = COALESCE(excluded.summary, pm_sessions.summary),\n slug = COALESCE(excluded.slug, pm_sessions.slug),\n model = COALESCE(excluded.model, pm_sessions.model),\n version = COALESCE(excluded.version, pm_sessions.version),\n git_branch = COALESCE(excluded.git_branch, pm_sessions.git_branch),\n message_count = excluded.message_count,\n user_message_count = excluded.user_message_count,\n assistant_message_count = excluded.assistant_message_count,\n total_input_tokens = excluded.total_input_tokens,\n total_output_tokens = excluded.total_output_tokens,\n total_cache_creation_tokens = excluded.total_cache_creation_tokens,\n total_cache_read_tokens = excluded.total_cache_read_tokens,\n cost_microdollars = excluded.cost_microdollars,\n ended_at = excluded.ended_at,\n active_minutes = excluded.active_minutes,\n compaction_count = excluded.compaction_count,\n pre_compaction_tokens = excluded.pre_compaction_tokens,\n permission_mode = excluded.permission_mode,\n updated_at = excluded.updated_at\n `).run(\n session.id,\n session.projectId,\n session.jsonlPath,\n session.jsonlSize ?? null,\n session.firstPrompt ?? null,\n session.summary ?? null,\n session.slug ?? null,\n session.model ?? null,\n session.version ?? null,\n session.gitBranch ?? null,\n session.messageCount,\n session.userMessageCount,\n session.assistantMessageCount,\n session.totalInputTokens,\n session.totalOutputTokens,\n session.totalCacheCreationTokens,\n session.totalCacheReadTokens,\n session.costMicrodollars,\n session.startedAt,\n session.endedAt ?? null,\n session.activeMinutes,\n session.compactionCount,\n session.preCompactionTokens ?? null,\n session.permissionMode ?? null,\n session.createdAt,\n session.updatedAt,\n );\n }\n\n getSession(id: string): PmSession | null {\n const row = this.db\n .prepare('SELECT * FROM pm_sessions WHERE id = ?')\n .get(id) as PmSessionRow | undefined;\n return row ? this.mapSessionRow(row) : null;\n }\n\n listSessions(projectId?: string, opts?: { limit?: number; offset?: number; startDate?: string; endDate?: string; hideEmpty?: boolean }): PmSession[] {\n const limit = opts?.limit ?? 100;\n const offset = opts?.offset ?? 0;\n\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n if (projectId) {\n conditions.push('project_id = ?');\n params.push(projectId);\n }\n if (opts?.startDate) {\n conditions.push('started_at >= ?');\n params.push(new Date(opts.startDate).getTime());\n }\n if (opts?.endDate) {\n conditions.push('started_at <= ?');\n params.push(new Date(opts.endDate + 'T23:59:59.999Z').getTime());\n }\n if (opts?.hideEmpty) {\n conditions.push('(message_count > 0 OR total_input_tokens > 0 OR total_output_tokens > 0 OR cost_microdollars > 0 OR active_minutes > 0)');\n }\n\n const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';\n const rows = this.db\n .prepare(`SELECT * FROM pm_sessions ${where} ORDER BY started_at DESC LIMIT ? OFFSET ?`)\n .all(...params, limit, offset) as PmSessionRow[];\n return rows.map(r => this.mapSessionRow(r));\n }\n\n getSessionStats(projectId?: string, opts?: { startDate?: string; endDate?: string; hideEmpty?: boolean }): SessionStats {\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n if (projectId) {\n conditions.push('project_id = ?');\n params.push(projectId);\n }\n if (opts?.startDate) {\n conditions.push('started_at >= ?');\n params.push(new Date(opts.startDate).getTime());\n }\n if (opts?.endDate) {\n conditions.push('started_at <= ?');\n params.push(new Date(opts.endDate + 'T23:59:59.999Z').getTime());\n }\n if (opts?.hideEmpty) {\n conditions.push('(message_count > 0 OR total_input_tokens > 0 OR total_output_tokens > 0 OR cost_microdollars > 0 OR active_minutes > 0)');\n }\n\n const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';\n const row = this.db.prepare(`\n SELECT\n COUNT(*) as total_sessions,\n COALESCE(SUM(active_minutes), 0) as total_active_minutes,\n COALESCE(SUM(cost_microdollars), 0) as total_cost,\n COALESCE(SUM(total_input_tokens), 0) as total_input,\n COALESCE(SUM(total_output_tokens), 0) as total_output,\n COALESCE(AVG(active_minutes), 0) as avg_minutes\n FROM pm_sessions ${whereClause}\n `).get(...params) as {\n total_sessions: number;\n total_active_minutes: number;\n total_cost: number;\n total_input: number;\n total_output: number;\n avg_minutes: number;\n };\n\n const modelConditions = [...conditions];\n if (!modelConditions.some(c => c.includes('model'))) {\n modelConditions.push('model IS NOT NULL');\n }\n const modelWhere = `WHERE ${modelConditions.join(' AND ')}`;\n const modelRows = this.db.prepare(`\n SELECT model, COUNT(*) as sessions, SUM(cost_microdollars) as cost\n FROM pm_sessions\n ${modelWhere}\n GROUP BY model\n ORDER BY cost DESC\n `).all(...params) as { model: string; sessions: number; cost: number }[];\n\n return {\n totalSessions: row.total_sessions,\n totalActiveMinutes: row.total_active_minutes,\n totalCostMicrodollars: row.total_cost,\n totalInputTokens: row.total_input,\n totalOutputTokens: row.total_output,\n avgSessionMinutes: row.avg_minutes,\n modelBreakdown: modelRows,\n };\n }\n\n getProjectSummaries(opts?: { startDate?: string; endDate?: string; hideEmpty?: boolean }): ProjectSummaryRow[] {\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n if (opts?.startDate) {\n conditions.push('s.started_at >= ?');\n params.push(new Date(opts.startDate).getTime());\n }\n if (opts?.endDate) {\n conditions.push('s.started_at <= ?');\n params.push(new Date(opts.endDate + 'T23:59:59.999Z').getTime());\n }\n if (opts?.hideEmpty) {\n conditions.push('(s.message_count > 0 OR s.total_input_tokens > 0 OR s.total_output_tokens > 0 OR s.cost_microdollars > 0 OR s.active_minutes > 0)');\n }\n\n const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';\n const rows = this.db.prepare(`\n SELECT\n p.id,\n p.name,\n p.path,\n p.category,\n p.sdk_installed,\n p.runtimescope_project,\n COUNT(s.id) as session_count,\n COALESCE(SUM(s.cost_microdollars), 0) as total_cost,\n COALESCE(SUM(s.active_minutes), 0) as total_active_minutes,\n MAX(s.started_at) as last_session_at,\n COALESCE(SUM(s.message_count), 0) as total_messages\n FROM pm_projects p\n LEFT JOIN pm_sessions s ON s.project_id = p.id ${where ? 'AND ' + conditions.join(' AND ') : ''}\n GROUP BY p.id\n ORDER BY last_session_at DESC NULLS LAST\n `).all(...params) as ProjectSummaryRow[];\n\n return rows;\n }\n\n private mapSessionRow(row: PmSessionRow): PmSession {\n return {\n id: row.id,\n projectId: row.project_id,\n jsonlPath: row.jsonl_path,\n jsonlSize: row.jsonl_size ?? undefined,\n firstPrompt: row.first_prompt ?? undefined,\n summary: row.summary ?? undefined,\n slug: row.slug ?? undefined,\n model: row.model ?? undefined,\n version: row.version ?? undefined,\n gitBranch: row.git_branch ?? undefined,\n messageCount: row.message_count,\n userMessageCount: row.user_message_count,\n assistantMessageCount: row.assistant_message_count,\n totalInputTokens: row.total_input_tokens,\n totalOutputTokens: row.total_output_tokens,\n totalCacheCreationTokens: row.total_cache_creation_tokens,\n totalCacheReadTokens: row.total_cache_read_tokens,\n costMicrodollars: row.cost_microdollars,\n startedAt: row.started_at,\n endedAt: row.ended_at ?? undefined,\n activeMinutes: row.active_minutes,\n compactionCount: row.compaction_count,\n preCompactionTokens: row.pre_compaction_tokens ?? undefined,\n permissionMode: row.permission_mode ?? undefined,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n }\n\n // ============================================================\n // Notes\n // ============================================================\n\n createNote(note: PmNote): PmNote {\n this.db.prepare(`\n INSERT INTO pm_notes (id, project_id, session_id, title, content, pinned, tags, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n `).run(\n note.id,\n note.projectId ?? null,\n note.sessionId ?? null,\n note.title,\n note.content,\n note.pinned ? 1 : 0,\n JSON.stringify(note.tags),\n note.createdAt,\n note.updatedAt,\n );\n return note;\n }\n\n updateNote(id: string, updates: Partial<PmNote>): void {\n const sets: string[] = [];\n const params: unknown[] = [];\n\n if (updates.title !== undefined) { sets.push('title = ?'); params.push(updates.title); }\n if (updates.content !== undefined) { sets.push('content = ?'); params.push(updates.content); }\n if (updates.pinned !== undefined) { sets.push('pinned = ?'); params.push(updates.pinned ? 1 : 0); }\n if (updates.tags !== undefined) { sets.push('tags = ?'); params.push(JSON.stringify(updates.tags)); }\n\n if (sets.length === 0) return;\n\n sets.push('updated_at = ?');\n params.push(Date.now());\n params.push(id);\n\n this.db.prepare(`UPDATE pm_notes SET ${sets.join(', ')} WHERE id = ?`).run(...params);\n }\n\n deleteNote(id: string): void {\n this.db.prepare('DELETE FROM pm_notes WHERE id = ?').run(id);\n }\n\n listNotes(opts?: { projectId?: string; pinned?: boolean }): PmNote[] {\n const conditions: string[] = [];\n const params: unknown[] = [];\n\n if (opts?.projectId) {\n conditions.push('project_id = ?');\n params.push(opts.projectId);\n }\n if (opts?.pinned !== undefined) {\n conditions.push('pinned = ?');\n params.push(opts.pinned ? 1 : 0);\n }\n\n const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';\n const rows = this.db\n .prepare(`SELECT * FROM pm_notes ${where} ORDER BY pinned DESC, updated_at DESC`)\n .all(...params) as PmNoteRow[];\n\n return rows.map(r => this.mapNoteRow(r));\n }\n\n private mapNoteRow(row: PmNoteRow): PmNote {\n return {\n id: row.id,\n projectId: row.project_id ?? undefined,\n sessionId: row.session_id ?? undefined,\n title: row.title,\n content: row.content,\n pinned: row.pinned === 1,\n tags: row.tags ? JSON.parse(row.tags) : [],\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n }\n\n // ============================================================\n // CapEx\n // ============================================================\n\n upsertCapexEntry(entry: PmCapexEntry): void {\n this.db.prepare(`\n INSERT INTO pm_capex_entries (id, project_id, session_id, classification, work_type,\n active_minutes, cost_microdollars, adjustment_factor, adjusted_cost_microdollars,\n confirmed, confirmed_at, confirmed_by, notes, period, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(id) DO UPDATE SET\n classification = excluded.classification,\n work_type = excluded.work_type,\n active_minutes = excluded.active_minutes,\n cost_microdollars = excluded.cost_microdollars,\n adjustment_factor = excluded.adjustment_factor,\n adjusted_cost_microdollars = excluded.adjusted_cost_microdollars,\n confirmed = excluded.confirmed,\n confirmed_at = excluded.confirmed_at,\n confirmed_by = excluded.confirmed_by,\n notes = excluded.notes,\n updated_at = excluded.updated_at\n `).run(\n entry.id,\n entry.projectId,\n entry.sessionId,\n entry.classification,\n entry.workType ?? null,\n entry.activeMinutes,\n entry.costMicrodollars,\n entry.adjustmentFactor,\n entry.adjustedCostMicrodollars,\n entry.confirmed ? 1 : 0,\n entry.confirmedAt ?? null,\n entry.confirmedBy ?? null,\n entry.notes ?? null,\n entry.period,\n entry.createdAt,\n entry.updatedAt,\n );\n }\n\n listCapexEntries(projectId: string, opts?: { month?: string; confirmed?: boolean }): PmCapexEntry[] {\n const conditions: string[] = ['project_id = ?'];\n const params: unknown[] = [projectId];\n\n if (opts?.month) {\n conditions.push('period = ?');\n params.push(opts.month);\n }\n if (opts?.confirmed !== undefined) {\n conditions.push('confirmed = ?');\n params.push(opts.confirmed ? 1 : 0);\n }\n\n const where = conditions.join(' AND ');\n const rows = this.db\n .prepare(`SELECT * FROM pm_capex_entries WHERE ${where} ORDER BY period DESC, created_at DESC`)\n .all(...params) as PmCapexRow[];\n\n return rows.map(r => this.mapCapexRow(r));\n }\n\n updateCapexEntry(id: string, updates: Partial<PmCapexEntry>): void {\n const sets: string[] = [];\n const params: unknown[] = [];\n\n if (updates.classification !== undefined) { sets.push('classification = ?'); params.push(updates.classification); }\n if (updates.workType !== undefined) { sets.push('work_type = ?'); params.push(updates.workType); }\n if (updates.adjustmentFactor !== undefined) {\n sets.push('adjustment_factor = ?');\n params.push(updates.adjustmentFactor);\n // Recalculate adjusted cost\n if (updates.costMicrodollars !== undefined) {\n sets.push('adjusted_cost_microdollars = ?');\n params.push(Math.round(updates.costMicrodollars * updates.adjustmentFactor));\n }\n }\n if (updates.notes !== undefined) { sets.push('notes = ?'); params.push(updates.notes); }\n\n if (sets.length === 0) return;\n\n sets.push('updated_at = ?');\n params.push(Date.now());\n params.push(id);\n\n this.db.prepare(`UPDATE pm_capex_entries SET ${sets.join(', ')} WHERE id = ?`).run(...params);\n }\n\n confirmCapexEntry(id: string, confirmedBy?: string): void {\n const now = Date.now();\n this.db.prepare(`\n UPDATE pm_capex_entries SET confirmed = 1, confirmed_at = ?, confirmed_by = ?, updated_at = ?\n WHERE id = ?\n `).run(now, confirmedBy ?? null, now, id);\n }\n\n getCapexSummary(projectId: string, opts?: { startDate?: string; endDate?: string }): CapexSummary {\n const conditions: string[] = ['project_id = ?'];\n const params: unknown[] = [projectId];\n\n if (opts?.startDate) {\n conditions.push('period >= ?');\n params.push(opts.startDate);\n }\n if (opts?.endDate) {\n conditions.push('period <= ?');\n params.push(opts.endDate);\n }\n\n const where = conditions.join(' AND ');\n\n const totals = this.db.prepare(`\n SELECT\n COUNT(*) as total_sessions,\n COALESCE(SUM(active_minutes), 0) as total_active_minutes,\n COALESCE(SUM(adjusted_cost_microdollars), 0) as total_cost,\n COALESCE(SUM(CASE WHEN classification = 'capitalizable' THEN adjusted_cost_microdollars ELSE 0 END), 0) as cap_cost,\n COALESCE(SUM(CASE WHEN classification = 'expensed' THEN adjusted_cost_microdollars ELSE 0 END), 0) as exp_cost,\n COALESCE(SUM(CASE WHEN confirmed = 1 THEN 1 ELSE 0 END), 0) as confirmed_count,\n COALESCE(SUM(CASE WHEN confirmed = 0 THEN 1 ELSE 0 END), 0) as unconfirmed_count\n FROM pm_capex_entries WHERE ${where}\n `).get(...params) as {\n total_sessions: number;\n total_active_minutes: number;\n total_cost: number;\n cap_cost: number;\n exp_cost: number;\n confirmed_count: number;\n unconfirmed_count: number;\n };\n\n const monthlyRows = this.db.prepare(`\n SELECT\n period,\n SUM(CASE WHEN classification = 'capitalizable' THEN adjusted_cost_microdollars ELSE 0 END) as capitalizable,\n SUM(CASE WHEN classification = 'expensed' THEN adjusted_cost_microdollars ELSE 0 END) as expensed,\n SUM(active_minutes) as activeMinutes\n FROM pm_capex_entries\n WHERE ${where}\n GROUP BY period\n ORDER BY period ASC\n `).all(...params) as { period: string; capitalizable: number; expensed: number; activeMinutes: number }[];\n\n return {\n projectId,\n period: opts?.startDate || opts?.endDate ? { start: opts.startDate ?? '', end: opts.endDate ?? '' } : undefined,\n totalSessions: totals.total_sessions,\n totalActiveMinutes: totals.total_active_minutes,\n totalCostMicrodollars: totals.total_cost,\n capitalizableCostMicrodollars: totals.cap_cost,\n expensedCostMicrodollars: totals.exp_cost,\n confirmedCount: totals.confirmed_count,\n unconfirmedCount: totals.unconfirmed_count,\n byMonth: monthlyRows,\n };\n }\n\n exportCapexCsv(projectId: string, opts?: { startDate?: string; endDate?: string }): string {\n const entries = this.listCapexEntries(projectId, { month: opts?.startDate });\n const sessions = new Map<string, PmSession>();\n for (const entry of entries) {\n if (!sessions.has(entry.sessionId)) {\n const s = this.getSession(entry.sessionId);\n if (s) sessions.set(entry.sessionId, s);\n }\n }\n\n const headers = [\n 'Period', 'Session ID', 'Session Slug', 'Date', 'Model',\n 'Active Minutes', 'Active Hours', 'Cost (USD)',\n 'Classification', 'Work Type', 'Adjustment Factor',\n 'Adjusted Cost (USD)', 'Confirmed', 'Confirmed By', 'Notes',\n ];\n\n const rows = entries.map(e => {\n const s = sessions.get(e.sessionId);\n const date = s?.startedAt ? new Date(s.startedAt).toISOString().split('T')[0] : '';\n return [\n e.period,\n e.sessionId,\n s?.slug ?? '',\n date,\n s?.model ?? '',\n e.activeMinutes.toFixed(2),\n (e.activeMinutes / 60).toFixed(2),\n (e.costMicrodollars / 1_000_000).toFixed(4),\n e.classification,\n e.workType ?? '',\n e.adjustmentFactor.toFixed(2),\n (e.adjustedCostMicrodollars / 1_000_000).toFixed(4),\n e.confirmed ? 'Yes' : 'No',\n e.confirmedBy ?? '',\n (e.notes ?? '').replace(/\"/g, '\"\"'),\n ].map(v => `\"${v}\"`).join(',');\n });\n\n return [headers.join(','), ...rows].join('\\n');\n }\n\n private mapCapexRow(row: PmCapexRow): PmCapexEntry {\n return {\n id: row.id,\n projectId: row.project_id,\n sessionId: row.session_id,\n classification: row.classification as PmCapexEntry['classification'],\n workType: (row.work_type ?? undefined) as PmCapexEntry['workType'],\n activeMinutes: row.active_minutes,\n costMicrodollars: row.cost_microdollars,\n adjustmentFactor: row.adjustment_factor,\n adjustedCostMicrodollars: row.adjusted_cost_microdollars,\n confirmed: row.confirmed === 1,\n confirmedAt: row.confirmed_at ?? undefined,\n confirmedBy: row.confirmed_by ?? undefined,\n notes: row.notes ?? undefined,\n period: row.period,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n }\n\n // ============================================================\n // Cleanup\n // ============================================================\n\n close(): void {\n this.db.close();\n }\n}\n\n// ============================================================\n// Row types (SQLite column names)\n// ============================================================\n\ninterface PmProjectRow {\n id: string;\n name: string;\n path: string | null;\n claude_project_key: string | null;\n runtimescope_project: string | null;\n phase: string;\n management_authorized: number;\n probable_to_complete: number;\n project_status: string;\n category: string | null;\n sdk_installed: number;\n created_at: number;\n updated_at: number;\n metadata: string | null;\n}\n\ninterface PmTaskRow {\n id: string;\n project_id: string | null;\n title: string;\n description: string | null;\n status: string;\n priority: string;\n labels: string | null;\n source: string | null;\n source_ref: string | null;\n sort_order: number;\n assigned_to: string | null;\n due_date: string | null;\n created_at: number;\n updated_at: number;\n completed_at: number | null;\n}\n\nexport interface ProjectSummaryRow {\n id: string;\n name: string;\n path: string | null;\n category: string | null;\n sdk_installed: number;\n runtimescope_project: string | null;\n session_count: number;\n total_cost: number;\n total_active_minutes: number;\n last_session_at: number | null;\n total_messages: number;\n}\n\ninterface PmSessionRow {\n id: string;\n project_id: string;\n jsonl_path: string;\n jsonl_size: number | null;\n first_prompt: string | null;\n summary: string | null;\n slug: string | null;\n model: string | null;\n version: string | null;\n git_branch: string | null;\n message_count: number;\n user_message_count: number;\n assistant_message_count: number;\n total_input_tokens: number;\n total_output_tokens: number;\n total_cache_creation_tokens: number;\n total_cache_read_tokens: number;\n cost_microdollars: number;\n started_at: number;\n ended_at: number | null;\n active_minutes: number;\n compaction_count: number;\n pre_compaction_tokens: number | null;\n permission_mode: string | null;\n created_at: number;\n updated_at: number;\n}\n\ninterface PmNoteRow {\n id: string;\n project_id: string | null;\n session_id: string | null;\n title: string;\n content: string;\n pinned: number;\n tags: string | null;\n created_at: number;\n updated_at: number;\n}\n\ninterface PmCapexRow {\n id: string;\n project_id: string;\n session_id: string;\n classification: string;\n work_type: string | null;\n active_minutes: number;\n cost_microdollars: number;\n adjustment_factor: number;\n adjusted_cost_microdollars: number;\n confirmed: number;\n confirmed_at: number | null;\n confirmed_by: string | null;\n notes: string | null;\n period: string;\n created_at: number;\n updated_at: number;\n}\n","import { createReadStream } from 'node:fs';\nimport { createInterface } from 'node:readline';\nimport type { PmSession } from './pm-types.js';\n\n// ============================================================\n// Model pricing in microdollars per million tokens\n// ============================================================\n\nexport const MODEL_PRICING: Record<string, { input: number; output: number; cacheWrite: number; cacheRead: number }> = {\n 'claude-opus-4-6': { input: 15_000_000, output: 75_000_000, cacheWrite: 18_750_000, cacheRead: 1_500_000 },\n 'claude-sonnet-4-6': { input: 3_000_000, output: 15_000_000, cacheWrite: 3_750_000, cacheRead: 300_000 },\n 'claude-sonnet-4-5': { input: 3_000_000, output: 15_000_000, cacheWrite: 3_750_000, cacheRead: 300_000 },\n 'claude-haiku-4-5': { input: 800_000, output: 4_000_000, cacheWrite: 1_000_000, cacheRead: 80_000 },\n 'claude-haiku-3-5': { input: 800_000, output: 4_000_000, cacheWrite: 1_000_000, cacheRead: 80_000 },\n};\n\n// ============================================================\n// Types\n// ============================================================\n\nexport interface ParseResult {\n session: Partial<PmSession>;\n messageTimestamps: number[];\n}\n\n// ============================================================\n// Active time calculation\n// ============================================================\n\n/**\n * Sum gaps between consecutive timestamps where gap < idle threshold.\n * Returns total active time in minutes.\n */\nexport function calculateActiveMinutes(timestamps: number[], idleThresholdMs = 900_000): number {\n if (timestamps.length < 2) return 0;\n\n const sorted = [...timestamps].sort((a, b) => a - b);\n let activeMs = 0;\n\n for (let i = 1; i < sorted.length; i++) {\n const gap = sorted[i] - sorted[i - 1];\n if (gap < idleThresholdMs) {\n activeMs += gap;\n }\n }\n\n return activeMs / 60_000;\n}\n\n// ============================================================\n// Cost calculation\n// ============================================================\n\n/**\n * Fuzzy-match a model string to a pricing entry.\n * Strips date suffixes (e.g., \"claude-sonnet-4-20250514\" → \"claude-sonnet-4\")\n * and tries progressively shorter prefixes.\n */\nfunction lookupPricing(model: string): { input: number; output: number; cacheWrite: number; cacheRead: number } | undefined {\n // Direct match first\n if (MODEL_PRICING[model]) return MODEL_PRICING[model];\n\n // Strip date suffix pattern (e.g., -20250514, -20250805)\n const stripped = model.replace(/-\\d{8}$/, '');\n if (MODEL_PRICING[stripped]) return MODEL_PRICING[stripped];\n\n // Try matching by key prefix — find the longest matching key\n const keys = Object.keys(MODEL_PRICING);\n let bestMatch: string | undefined;\n let bestLen = 0;\n for (const key of keys) {\n if (stripped.startsWith(key) && key.length > bestLen) {\n bestMatch = key;\n bestLen = key.length;\n }\n }\n if (bestMatch) return MODEL_PRICING[bestMatch];\n\n // Reverse: key starts with stripped model name\n for (const key of keys) {\n if (key.startsWith(stripped) && key.length > bestLen) {\n bestMatch = key;\n bestLen = key.length;\n }\n }\n if (bestMatch) return MODEL_PRICING[bestMatch];\n\n return undefined;\n}\n\n/**\n * Calculate cost in microdollars from token counts and model name.\n * Formula: (input * pricing.input + output * pricing.output\n * + cacheCreation * pricing.cacheWrite + cacheRead * pricing.cacheRead) / 1_000_000\n */\nexport function calculateCostMicrodollars(\n model: string,\n inputTokens: number,\n outputTokens: number,\n cacheCreationTokens: number,\n cacheReadTokens: number,\n): number {\n const pricing = lookupPricing(model);\n if (!pricing) return 0;\n\n return Math.round(\n (inputTokens * pricing.input\n + outputTokens * pricing.output\n + cacheCreationTokens * pricing.cacheWrite\n + cacheReadTokens * pricing.cacheRead)\n / 1_000_000,\n );\n}\n\n// ============================================================\n// JSONL stream parser\n// ============================================================\n\n/**\n * Parse an ISO 8601 timestamp or epoch-ms number into epoch milliseconds.\n * Returns 0 if unparseable.\n */\nfunction parseTimestamp(value: unknown): number {\n if (typeof value === 'number') return value;\n if (typeof value === 'string') {\n const ms = Date.parse(value);\n return Number.isNaN(ms) ? 0 : ms;\n }\n return 0;\n}\n\n/**\n * Extract text content from a message's content field.\n * Content can be a plain string or an array of content blocks.\n */\nfunction extractTextContent(content: unknown): string {\n if (typeof content === 'string') return content;\n if (Array.isArray(content)) {\n for (const block of content) {\n if (typeof block === 'object' && block !== null && (block as Record<string, unknown>).type === 'text') {\n const text = (block as Record<string, unknown>).text;\n if (typeof text === 'string') return text;\n }\n }\n }\n return '';\n}\n\n/**\n * Stream-parse a Claude Code session JSONL file and extract metadata.\n *\n * Uses Node.js readline + createReadStream for memory-safe line-by-line\n * parsing of files up to ~192 MB. Each line is independently JSON.parse'd;\n * malformed lines are silently skipped.\n */\nexport async function parseSessionJsonl(\n jsonlPath: string,\n sessionId: string,\n projectId: string,\n): Promise<ParseResult> {\n const session: Partial<PmSession> = {\n id: sessionId,\n projectId,\n jsonlPath,\n messageCount: 0,\n userMessageCount: 0,\n assistantMessageCount: 0,\n totalInputTokens: 0,\n totalOutputTokens: 0,\n totalCacheCreationTokens: 0,\n totalCacheReadTokens: 0,\n costMicrodollars: 0,\n activeMinutes: 0,\n compactionCount: 0,\n };\n\n const messageTimestamps: number[] = [];\n let firstHumanSeen = false;\n let earliestTs = Infinity;\n let latestTs = 0;\n\n const rl = createInterface({\n input: createReadStream(jsonlPath, { encoding: 'utf-8' }),\n crlfDelay: Infinity,\n });\n\n for await (const line of rl) {\n // Skip empty lines\n if (!line.trim()) continue;\n\n let obj: Record<string, unknown>;\n try {\n obj = JSON.parse(line);\n } catch {\n // Malformed line — skip\n continue;\n }\n\n if (typeof obj !== 'object' || obj === null) continue;\n\n const type = obj.type as string | undefined;\n const ts = parseTimestamp(obj.timestamp);\n\n // Collect timestamp for active-time calculation\n if (ts > 0) {\n messageTimestamps.push(ts);\n if (ts < earliestTs) earliestTs = ts;\n if (ts > latestTs) latestTs = ts;\n }\n\n // ---- Extract session-level metadata (from any message) ----\n\n if (!session.version && typeof obj.version === 'string') {\n session.version = obj.version;\n }\n if (!session.slug && typeof obj.slug === 'string') {\n session.slug = obj.slug;\n }\n if (!session.gitBranch && typeof obj.gitBranch === 'string') {\n session.gitBranch = obj.gitBranch;\n }\n if (!session.permissionMode && typeof obj.permissionMode === 'string') {\n session.permissionMode = obj.permissionMode;\n }\n\n // ---- Handle by message type ----\n\n if (type === 'user') {\n session.messageCount = (session.messageCount ?? 0) + 1;\n\n // Only count real user messages (not tool-use results) for userMessageCount\n if (!obj.toolUseResult) {\n session.userMessageCount = (session.userMessageCount ?? 0) + 1;\n\n // Extract first real human prompt\n if (!firstHumanSeen) {\n firstHumanSeen = true;\n const msg = obj.message as Record<string, unknown> | undefined;\n if (msg) {\n const text = extractTextContent(msg.content);\n if (text) {\n session.firstPrompt = text.slice(0, 500);\n }\n }\n }\n }\n } else if (type === 'assistant') {\n session.messageCount = (session.messageCount ?? 0) + 1;\n session.assistantMessageCount = (session.assistantMessageCount ?? 0) + 1;\n\n const msg = obj.message as Record<string, unknown> | undefined;\n\n // Extract model\n const model = (msg?.model ?? obj.model) as string | undefined;\n if (model && typeof model === 'string') {\n // Use the last model seen as the session's model\n session.model = model;\n }\n\n // Extract token usage\n const usage = (msg?.usage ?? obj.usage) as Record<string, unknown> | undefined;\n if (usage && typeof usage === 'object') {\n const inputTokens = typeof usage.input_tokens === 'number' ? usage.input_tokens : 0;\n const outputTokens = typeof usage.output_tokens === 'number' ? usage.output_tokens : 0;\n const cacheCreation = typeof usage.cache_creation_input_tokens === 'number'\n ? usage.cache_creation_input_tokens : 0;\n const cacheRead = typeof usage.cache_read_input_tokens === 'number'\n ? usage.cache_read_input_tokens : 0;\n\n session.totalInputTokens = (session.totalInputTokens ?? 0) + inputTokens;\n session.totalOutputTokens = (session.totalOutputTokens ?? 0) + outputTokens;\n session.totalCacheCreationTokens = (session.totalCacheCreationTokens ?? 0) + cacheCreation;\n session.totalCacheReadTokens = (session.totalCacheReadTokens ?? 0) + cacheRead;\n\n // Calculate incremental cost if we have a model\n if (model) {\n session.costMicrodollars = (session.costMicrodollars ?? 0)\n + calculateCostMicrodollars(model, inputTokens, outputTokens, cacheCreation, cacheRead);\n }\n }\n } else if (type === 'summary') {\n // Compaction summary — appears at the start of compacted sessions\n session.compactionCount = (session.compactionCount ?? 0) + 1;\n if (!session.summary && typeof obj.summary === 'string') {\n session.summary = obj.summary;\n }\n } else if (type === 'system') {\n session.messageCount = (session.messageCount ?? 0) + 1;\n\n // Detect compaction events via subtype or content\n const subtype = obj.subtype as string | undefined;\n if (subtype === 'compact_boundary') {\n session.compactionCount = (session.compactionCount ?? 0) + 1;\n\n // Extract pre-compaction token count\n const meta = obj.compactMetadata as Record<string, unknown> | undefined;\n if (meta) {\n const preTokens = typeof meta.preTokens === 'string'\n ? parseInt(meta.preTokens, 10)\n : typeof meta.preTokens === 'number' ? meta.preTokens : undefined;\n if (preTokens && !Number.isNaN(preTokens)) {\n session.preCompactionTokens = preTokens;\n }\n }\n } else {\n // Check content for compaction keywords\n const content = typeof obj.content === 'string' ? obj.content : '';\n if (content.toLowerCase().includes('compact')) {\n session.compactionCount = (session.compactionCount ?? 0) + 1;\n }\n }\n }\n\n // ---- Direct cost fields on any line ----\n const directCost = obj.costUSD ?? obj.cost_usd;\n if (typeof directCost === 'number') {\n // costUSD is in dollars; convert to microdollars and add\n session.costMicrodollars = (session.costMicrodollars ?? 0) + Math.round(directCost * 1_000_000);\n }\n }\n\n // ---- Finalize session metadata ----\n\n if (earliestTs < Infinity) {\n session.startedAt = earliestTs;\n }\n if (latestTs > 0) {\n session.endedAt = latestTs;\n }\n // Don't fallback to Date.now() here — let the caller provide a better\n // fallback (e.g. file mtime) so sessions aren't all stamped \"today\"\n\n // Calculate active minutes from collected timestamps\n session.activeMinutes = calculateActiveMinutes(messageTimestamps);\n\n // Set audit timestamps\n const now = Date.now();\n session.createdAt = session.startedAt ?? now;\n session.updatedAt = now;\n\n return { session, messageTimestamps };\n}\n","import { readdir, readFile, stat } from 'node:fs/promises';\nimport { join, basename } from 'node:path';\nimport { existsSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport type { PmStore } from './pm-store.js';\nimport type { ProjectManager } from '../project-manager.js';\nimport type { PmProject, PmSession, PmCapexEntry } from './pm-types.js';\nimport { parseSessionJsonl, calculateActiveMinutes, calculateCostMicrodollars } from './session-parser.js';\n\n// ============================================================\n// Project Discovery — scans filesystem for Claude Code and\n// RuntimeScope projects, merges them, populates PM database\n// ============================================================\n\nconst LOG_PREFIX = '[RuntimeScope PM]';\n\nexport interface DiscoveryResult {\n projectsDiscovered: number;\n projectsUpdated: number;\n sessionsDiscovered: number;\n sessionsUpdated: number;\n errors: string[];\n}\n\n/** Shape of entries in ~/.claude/projects/<key>/sessions-index.json */\ninterface ClaudeSessionIndexEntry {\n sessionId: string;\n fullPath: string;\n fileMtime: number;\n firstPrompt?: string;\n summary?: string;\n messageCount: number;\n created: string;\n modified: string;\n gitBranch?: string;\n projectPath?: string;\n isSidechain?: boolean;\n}\n\ninterface ClaudeSessionIndex {\n version: number;\n entries: ClaudeSessionIndexEntry[];\n}\n\n/**\n * Check if a project has @runtimescope/sdk or @runtimescope/server-sdk installed.\n * Checks package.json deps first, then falls back to checking node_modules.\n */\nasync function detectSdkInstalled(projectPath: string): Promise<boolean> {\n // Check package.json dependencies\n try {\n const pkgPath = join(projectPath, 'package.json');\n const pkg = JSON.parse(await readFile(pkgPath, 'utf-8'));\n const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };\n if ('@runtimescope/sdk' in allDeps || '@runtimescope/server-sdk' in allDeps) {\n return true;\n }\n // Check workspace packages (monorepo root may list it as a workspace)\n if (pkg.workspaces) {\n const workspaces: string[] = Array.isArray(pkg.workspaces) ? pkg.workspaces : pkg.workspaces.packages ?? [];\n for (const ws of workspaces) {\n // Resolve simple workspace patterns like \"packages/*\"\n const wsBase = ws.replace(/\\/?\\*$/, '');\n const wsDir = join(projectPath, wsBase);\n try {\n const entries = await readdir(wsDir, { withFileTypes: true });\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n try {\n const wsPkg = JSON.parse(await readFile(join(wsDir, entry.name, 'package.json'), 'utf-8'));\n const wsDeps = { ...wsPkg.dependencies, ...wsPkg.devDependencies };\n if ('@runtimescope/sdk' in wsDeps || '@runtimescope/server-sdk' in wsDeps) {\n return true;\n }\n } catch { /* skip */ }\n }\n } catch { /* skip */ }\n }\n }\n } catch { /* no package.json */ }\n\n // Fallback: check if node_modules/@runtimescope exists\n try {\n await stat(join(projectPath, 'node_modules', '@runtimescope'));\n return true;\n } catch {\n return false;\n }\n}\n\nfunction emptyResult(): DiscoveryResult {\n return {\n projectsDiscovered: 0,\n projectsUpdated: 0,\n sessionsDiscovered: 0,\n sessionsUpdated: 0,\n errors: [],\n };\n}\n\nfunction mergeResults(a: Partial<DiscoveryResult>, b: Partial<DiscoveryResult>): DiscoveryResult {\n return {\n projectsDiscovered: (a.projectsDiscovered ?? 0) + (b.projectsDiscovered ?? 0),\n projectsUpdated: (a.projectsUpdated ?? 0) + (b.projectsUpdated ?? 0),\n sessionsDiscovered: (a.sessionsDiscovered ?? 0) + (b.sessionsDiscovered ?? 0),\n sessionsUpdated: (a.sessionsUpdated ?? 0) + (b.sessionsUpdated ?? 0),\n errors: [...(a.errors ?? []), ...(b.errors ?? [])],\n };\n}\n\n/**\n * Slugify a filesystem path into a unique project ID.\n * Uses the last two path segments to avoid collisions between projects\n * with the same directory name in different locations.\n * e.g. `/Users/edwinlovettiii/runtime-profiler` → `edwinlovettiii--runtime-profiler`\n * e.g. `/Users/alice/frontend` vs `/Users/bob/frontend` → different IDs\n *\n * Single-segment paths (e.g. just `frontend`) use basename only.\n * The double-hyphen `--` separates parent from basename for readability.\n */\nfunction slugifyPath(fsPath: string): string {\n const parts = fsPath.replace(/\\/+$/, '').split('/').filter(Boolean);\n // Use last 2 segments for uniqueness (parent + basename)\n const segments = parts.length >= 2 ? parts.slice(-2) : parts;\n return segments\n .join('--')\n .toLowerCase()\n .replace(/[^a-z0-9_-]/g, '-')\n .replace(/-{3,}/g, '--')\n .replace(/^-|-$/g, '');\n}\n\n/**\n * Decode a Claude project key back to a filesystem path.\n *\n * Claude Code encodes project paths by replacing `/` with `-`.\n * e.g. `-Users-edwinlovettiii-runtime-profiler` → `/Users/edwinlovettiii/runtime-profiler`\n *\n * The tricky part: real directory names can also contain hyphens.\n * Strategy: try the naive full replacement first (`-` → `/`), then\n * progressively try keeping certain `-` characters intact by checking\n * if candidate paths actually exist on the filesystem.\n *\n * Returns null if no valid path can be resolved.\n */\nfunction decodeClaudeKey(key: string): string | null {\n // The key starts with `-` which represents the root `/`\n // Naive decode: replace all `-` with `/`\n const naive = '/' + key.slice(1).replace(/-/g, '/');\n if (existsSync(naive)) return naive;\n\n // The naive approach fails when directory names contain hyphens.\n // Try a segment-based approach: split by `-`, then greedily combine\n // segments to form valid directory paths.\n const parts = key.slice(1).split('-'); // remove leading `-`\n return resolvePathSegments(parts);\n}\n\n/**\n * Greedy path resolver: given parts split by `-`, try to reconstruct the\n * filesystem path by testing whether joining adjacent segments with `-`\n * forms an existing directory at each level.\n */\nfunction resolvePathSegments(parts: string[]): string | null {\n if (parts.length === 0) return null;\n\n function tryResolve(prefix: string, remaining: string[]): string | null {\n if (remaining.length === 0) {\n return existsSync(prefix) ? prefix : null;\n }\n\n // Try consuming 1..N segments as the next directory component\n // Prefer longer matches first (greedy) to handle names with hyphens\n for (let count = remaining.length; count >= 1; count--) {\n const segment = remaining.slice(0, count).join('-');\n const candidate = join(prefix, segment);\n\n if (count === remaining.length) {\n // Last segment(s) — this would be the full path\n if (existsSync(candidate)) return candidate;\n } else {\n // Intermediate segment — must be a directory\n try {\n if (existsSync(candidate)) {\n const result = tryResolve(candidate, remaining.slice(count));\n if (result) return result;\n }\n } catch {\n // stat failed, skip\n }\n }\n }\n\n return null;\n }\n\n return tryResolve('/', parts);\n}\n\n/**\n * Derive the YYYY-MM period string from a timestamp.\n */\nfunction toPeriod(timestampMs: number): string {\n const d = new Date(timestampMs);\n const year = d.getFullYear();\n const month = String(d.getMonth() + 1).padStart(2, '0');\n return `${year}-${month}`;\n}\n\nexport class ProjectDiscovery {\n private readonly claudeBaseDir: string;\n\n constructor(\n private pmStore: PmStore,\n private projectManager: ProjectManager,\n claudeBaseDir?: string,\n ) {\n this.claudeBaseDir = claudeBaseDir ?? join(homedir(), '.claude');\n }\n\n /**\n * Run full discovery: Claude Code projects + RuntimeScope projects.\n * Never throws — all errors are captured in the result.\n */\n async discoverAll(): Promise<DiscoveryResult> {\n const result = emptyResult();\n\n try {\n const [claudeResult, runtimeResult] = await Promise.all([\n this.discoverClaudeProjects(),\n this.discoverRuntimeScopeProjects(),\n ]);\n return mergeResults(claudeResult, runtimeResult);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`${LOG_PREFIX} Fatal discovery error: ${msg}`);\n result.errors.push(`Fatal discovery error: ${msg}`);\n return result;\n }\n }\n\n /**\n * Discover Claude Code projects from ~/.claude/projects/.\n */\n async discoverClaudeProjects(): Promise<Partial<DiscoveryResult>> {\n const result: Partial<DiscoveryResult> = {\n projectsDiscovered: 0,\n projectsUpdated: 0,\n sessionsDiscovered: 0,\n sessionsUpdated: 0,\n errors: [],\n };\n\n const projectsDir = join(this.claudeBaseDir, 'projects');\n\n try {\n await stat(projectsDir);\n } catch {\n // ~/.claude/projects/ doesn't exist — nothing to discover\n return result;\n }\n\n let entries: string[];\n try {\n const dirEntries = await readdir(projectsDir, { withFileTypes: true });\n entries = dirEntries.filter((d) => d.isDirectory()).map((d) => d.name);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`${LOG_PREFIX} Failed to read Claude projects dir: ${msg}`);\n result.errors!.push(`Failed to read Claude projects dir: ${msg}`);\n return result;\n }\n\n for (const key of entries) {\n try {\n await this.processClaudeProject(key, result);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`${LOG_PREFIX} Error processing Claude project ${key}: ${msg}`);\n result.errors!.push(`Claude project ${key}: ${msg}`);\n }\n }\n\n return result;\n }\n\n /**\n * Discover RuntimeScope projects from ~/.runtimescope/projects/.\n */\n async discoverRuntimeScopeProjects(): Promise<Partial<DiscoveryResult>> {\n const result: Partial<DiscoveryResult> = {\n projectsDiscovered: 0,\n projectsUpdated: 0,\n sessionsDiscovered: 0,\n sessionsUpdated: 0,\n errors: [],\n };\n\n try {\n const runtimeProjects = this.projectManager.listProjects();\n\n for (const projectName of runtimeProjects) {\n try {\n const projectDir = this.projectManager.getProjectDir(projectName);\n\n const id = projectName.toLowerCase().replace(/[^a-z0-9_-]/g, '-');\n\n // Check if this project already exists in PM store (may have been\n // created by Claude discovery and needs merging)\n const existingProjects = await this.pmStore.listProjects();\n const nameLower = projectName.toLowerCase();\n const existing = existingProjects.find(\n (p) => p.id === id\n || p.runtimescopeProject === projectName\n || p.name.toLowerCase() === nameLower,\n );\n\n const now = Date.now();\n // Use the existing source path if available (projectDir is the data dir, not source)\n const sourcePath = existing?.path ?? projectDir;\n const sdkInstalled = await detectSdkInstalled(sourcePath);\n\n if (existing) {\n // Merge: add runtimescopeProject reference\n const updated: PmProject = {\n ...existing,\n runtimescopeProject: projectName,\n sdkInstalled: sdkInstalled || existing.sdkInstalled,\n updatedAt: now,\n };\n await this.pmStore.upsertProject(updated);\n result.projectsUpdated = (result.projectsUpdated ?? 0) + 1;\n } else {\n // Resolve filesystem path from project dir\n const fsPath = projectDir;\n\n const project: PmProject = {\n id,\n name: projectName,\n path: fsPath,\n runtimescopeProject: projectName,\n phase: 'application_development',\n managementAuthorized: false,\n probableToComplete: true,\n projectStatus: 'active',\n sdkInstalled,\n createdAt: now,\n updatedAt: now,\n };\n await this.pmStore.upsertProject(project);\n result.projectsDiscovered = (result.projectsDiscovered ?? 0) + 1;\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`${LOG_PREFIX} Error processing RuntimeScope project ${projectName}: ${msg}`);\n result.errors!.push(`RuntimeScope project ${projectName}: ${msg}`);\n }\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`${LOG_PREFIX} Failed to list RuntimeScope projects: ${msg}`);\n result.errors!.push(`Failed to list RuntimeScope projects: ${msg}`);\n }\n\n return result;\n }\n\n /**\n * Index all sessions for a given project.\n * Returns the number of sessions indexed (new or updated).\n */\n async indexProjectSessions(projectId: string): Promise<number> {\n const existingProjects = await this.pmStore.listProjects();\n const project = existingProjects.find((p) => p.id === projectId);\n if (!project) {\n console.error(`${LOG_PREFIX} Project not found: ${projectId}`);\n return 0;\n }\n\n if (!project.claudeProjectKey) {\n // No Claude project associated — nothing to index\n return 0;\n }\n\n const projectDir = join(this.claudeBaseDir, 'projects', project.claudeProjectKey);\n let sessionsIndexed = 0;\n\n try {\n // Try sessions-index.json first for fast metadata\n const indexPath = join(projectDir, 'sessions-index.json');\n let indexEntries: ClaudeSessionIndexEntry[] | null = null;\n\n try {\n const indexContent = await readFile(indexPath, 'utf-8');\n const index: ClaudeSessionIndex = JSON.parse(indexContent);\n indexEntries = index.entries ?? [];\n } catch {\n // No index file — will fall back to scanning .jsonl files\n }\n\n // Scan for .jsonl files\n const dirEntries = await readdir(projectDir, { withFileTypes: true });\n const jsonlFiles = dirEntries\n .filter((d) => d.isFile() && d.name.endsWith('.jsonl'))\n .map((d) => d.name);\n\n for (const jsonlFile of jsonlFiles) {\n try {\n const sessionId = jsonlFile.replace('.jsonl', '');\n const jsonlPath = join(projectDir, jsonlFile);\n\n // Check if session already exists and if file size changed\n const fileStat = await stat(jsonlPath);\n const fileSize = fileStat.size;\n\n const existingSession = await this.pmStore.getSession(sessionId);\n if (existingSession && existingSession.jsonlSize === fileSize) {\n // File hasn't changed — skip\n continue;\n }\n\n // Try to get metadata from sessions-index.json first\n const indexEntry = indexEntries?.find((e) => e.sessionId === sessionId);\n\n let session: PmSession;\n\n if (indexEntry) {\n // Build session from index metadata (fast path)\n session = this.buildSessionFromIndex(indexEntry, projectId, jsonlPath, fileSize);\n } else {\n // Full parse of the JSONL file\n session = await this.buildSessionFromJsonl(sessionId, projectId, jsonlPath, fileSize);\n }\n\n await this.pmStore.upsertSession(session);\n\n // Create a CapEx entry stub for this session\n await this.upsertCapexStub(session);\n\n sessionsIndexed++;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`${LOG_PREFIX} Error indexing session ${jsonlFile}: ${msg}`);\n }\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`${LOG_PREFIX} Error indexing sessions for project ${projectId}: ${msg}`);\n }\n\n return sessionsIndexed;\n }\n\n // ---------------------------------------------------------------\n // Private helpers\n // ---------------------------------------------------------------\n\n /**\n * Process a single Claude project directory key.\n */\n private async processClaudeProject(\n key: string,\n result: Partial<DiscoveryResult>,\n ): Promise<void> {\n const projectDir = join(this.claudeBaseDir, 'projects', key);\n\n // Decode key to filesystem path\n let fsPath = decodeClaudeKey(key);\n\n // If decoding fails, try to get path from sessions-index.json\n if (!fsPath) {\n fsPath = await this.resolvePathFromIndex(projectDir);\n }\n\n const id = fsPath ? slugifyPath(fsPath) : slugifyPath(key);\n const name = fsPath ? basename(fsPath) : key;\n const now = Date.now();\n\n // Check if this project already exists (by ID, Claude key, or name)\n const existingProjects = await this.pmStore.listProjects();\n const nameLower = name.toLowerCase();\n const existing = existingProjects.find(\n (p) => p.id === id || p.claudeProjectKey === key || p.name.toLowerCase() === nameLower,\n );\n\n // Detect SDK installation if we have a filesystem path\n const resolvedPath = fsPath ?? existing?.path;\n const sdkInstalled = resolvedPath ? await detectSdkInstalled(resolvedPath) : false;\n\n if (existing) {\n // Update/merge: add Claude key, path, SDK status\n const updated: PmProject = {\n ...existing,\n claudeProjectKey: key,\n path: fsPath ?? existing.path,\n sdkInstalled: sdkInstalled || existing.sdkInstalled,\n updatedAt: now,\n };\n await this.pmStore.upsertProject(updated);\n result.projectsUpdated = (result.projectsUpdated ?? 0) + 1;\n } else {\n const project: PmProject = {\n id,\n name,\n path: fsPath ?? undefined,\n claudeProjectKey: key,\n phase: 'application_development',\n managementAuthorized: false,\n probableToComplete: true,\n projectStatus: 'active',\n sdkInstalled,\n createdAt: now,\n updatedAt: now,\n };\n await this.pmStore.upsertProject(project);\n result.projectsDiscovered = (result.projectsDiscovered ?? 0) + 1;\n }\n\n // Index sessions for this project — use the actual stored project ID\n const actualId = existing ? existing.id : id;\n const sessionsIndexed = await this.indexSessionsForClaudeProject(actualId, key);\n result.sessionsDiscovered = (result.sessionsDiscovered ?? 0) + sessionsIndexed.discovered;\n result.sessionsUpdated = (result.sessionsUpdated ?? 0) + sessionsIndexed.updated;\n }\n\n /**\n * Try to resolve the filesystem path from the sessions-index.json projectPath field.\n */\n private async resolvePathFromIndex(projectDir: string): Promise<string | null> {\n try {\n const indexPath = join(projectDir, 'sessions-index.json');\n const content = await readFile(indexPath, 'utf-8');\n const index: ClaudeSessionIndex = JSON.parse(content);\n\n // Find the first entry with a projectPath\n const entry = index.entries?.find((e) => e.projectPath);\n return entry?.projectPath ?? null;\n } catch {\n return null;\n }\n }\n\n /**\n * Index sessions for a Claude project directly (used during discovery).\n * Returns counts of discovered and updated sessions.\n */\n private async indexSessionsForClaudeProject(\n projectId: string,\n claudeKey: string,\n ): Promise<{ discovered: number; updated: number }> {\n const counts = { discovered: 0, updated: 0 };\n const projectDir = join(this.claudeBaseDir, 'projects', claudeKey);\n\n try {\n // Try sessions-index.json first\n let indexEntries: ClaudeSessionIndexEntry[] | null = null;\n try {\n const indexPath = join(projectDir, 'sessions-index.json');\n const indexContent = await readFile(indexPath, 'utf-8');\n const index: ClaudeSessionIndex = JSON.parse(indexContent);\n indexEntries = index.entries ?? [];\n } catch {\n // No index file\n }\n\n // Scan for .jsonl files (only top-level, skip subdirectories)\n const dirEntries = await readdir(projectDir, { withFileTypes: true });\n const jsonlFiles = dirEntries\n .filter((d) => d.isFile() && d.name.endsWith('.jsonl'))\n .map((d) => d.name);\n\n for (const jsonlFile of jsonlFiles) {\n try {\n const sessionId = jsonlFile.replace('.jsonl', '');\n const jsonlPath = join(projectDir, jsonlFile);\n\n const fileStat = await stat(jsonlPath);\n const fileSize = fileStat.size;\n\n const existingSession = await this.pmStore.getSession(sessionId);\n\n if (existingSession && existingSession.jsonlSize === fileSize) {\n // Unchanged — skip\n continue;\n }\n\n const indexEntry = indexEntries?.find((e) => e.sessionId === sessionId);\n\n let session: PmSession;\n if (indexEntry) {\n session = this.buildSessionFromIndex(indexEntry, projectId, jsonlPath, fileSize);\n } else {\n session = await this.buildSessionFromJsonl(sessionId, projectId, jsonlPath, fileSize);\n }\n\n await this.pmStore.upsertSession(session);\n await this.upsertCapexStub(session);\n\n if (existingSession) {\n counts.updated++;\n } else {\n counts.discovered++;\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`${LOG_PREFIX} Error indexing session ${jsonlFile} in ${claudeKey}: ${msg}`);\n }\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`${LOG_PREFIX} Error scanning sessions for ${claudeKey}: ${msg}`);\n }\n\n return counts;\n }\n\n /**\n * Build a PmSession from the fast sessions-index.json entry.\n * Token counts and cost are zeroed since the index doesn't contain them;\n * they will be populated on a subsequent full parse if needed.\n */\n private buildSessionFromIndex(\n entry: ClaudeSessionIndexEntry,\n projectId: string,\n jsonlPath: string,\n jsonlSize: number,\n ): PmSession {\n const now = Date.now();\n const startedAt = new Date(entry.created).getTime();\n const endedAt = entry.modified ? new Date(entry.modified).getTime() : undefined;\n const activeMinutes = endedAt\n ? Math.max(1, Math.round((endedAt - startedAt) / 60_000))\n : 0;\n\n return {\n id: entry.sessionId,\n projectId,\n jsonlPath,\n jsonlSize,\n firstPrompt: entry.firstPrompt ?? undefined,\n summary: entry.summary ?? undefined,\n slug: undefined,\n model: undefined,\n version: undefined,\n gitBranch: entry.gitBranch ?? undefined,\n messageCount: entry.messageCount ?? 0,\n userMessageCount: 0,\n assistantMessageCount: 0,\n totalInputTokens: 0,\n totalOutputTokens: 0,\n totalCacheCreationTokens: 0,\n totalCacheReadTokens: 0,\n costMicrodollars: 0,\n startedAt,\n endedAt,\n activeMinutes,\n compactionCount: 0,\n preCompactionTokens: undefined,\n permissionMode: undefined,\n createdAt: now,\n updatedAt: now,\n };\n }\n\n /**\n * Build a PmSession by fully parsing a .jsonl file.\n */\n private async buildSessionFromJsonl(\n sessionId: string,\n projectId: string,\n jsonlPath: string,\n jsonlSize: number,\n ): Promise<PmSession> {\n const now = Date.now();\n\n // Use file mtime as fallback timestamp (more accurate than Date.now())\n let fileMtime = now;\n try {\n const fstat = await stat(jsonlPath);\n fileMtime = fstat.mtimeMs;\n } catch { /* use now */ }\n\n try {\n const { session: parsed, messageTimestamps } = await parseSessionJsonl(jsonlPath, sessionId, projectId);\n const activeMinutes = calculateActiveMinutes(messageTimestamps);\n const costMicrodollars = parsed.costMicrodollars ?? calculateCostMicrodollars(\n parsed.model ?? '',\n parsed.totalInputTokens ?? 0,\n parsed.totalOutputTokens ?? 0,\n parsed.totalCacheCreationTokens ?? 0,\n parsed.totalCacheReadTokens ?? 0,\n );\n\n return {\n id: sessionId,\n projectId,\n jsonlPath,\n jsonlSize,\n firstPrompt: parsed.firstPrompt ?? undefined,\n summary: parsed.summary ?? undefined,\n slug: parsed.slug ?? undefined,\n model: parsed.model ?? undefined,\n version: parsed.version ?? undefined,\n gitBranch: parsed.gitBranch ?? undefined,\n messageCount: parsed.messageCount ?? 0,\n userMessageCount: parsed.userMessageCount ?? 0,\n assistantMessageCount: parsed.assistantMessageCount ?? 0,\n totalInputTokens: parsed.totalInputTokens ?? 0,\n totalOutputTokens: parsed.totalOutputTokens ?? 0,\n totalCacheCreationTokens: parsed.totalCacheCreationTokens ?? 0,\n totalCacheReadTokens: parsed.totalCacheReadTokens ?? 0,\n costMicrodollars,\n startedAt: parsed.startedAt ?? fileMtime,\n endedAt: parsed.endedAt ?? undefined,\n activeMinutes,\n compactionCount: parsed.compactionCount ?? 0,\n preCompactionTokens: parsed.preCompactionTokens ?? undefined,\n permissionMode: parsed.permissionMode ?? undefined,\n createdAt: now,\n updatedAt: now,\n };\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`${LOG_PREFIX} Failed to parse session ${sessionId}: ${msg}`);\n\n // Return a minimal session record — use file mtime instead of Date.now()\n // to avoid all failed sessions appearing with today's date\n return {\n id: sessionId,\n projectId,\n jsonlPath,\n jsonlSize,\n messageCount: 0,\n userMessageCount: 0,\n assistantMessageCount: 0,\n totalInputTokens: 0,\n totalOutputTokens: 0,\n totalCacheCreationTokens: 0,\n totalCacheReadTokens: 0,\n costMicrodollars: 0,\n startedAt: fileMtime,\n activeMinutes: 0,\n compactionCount: 0,\n createdAt: now,\n updatedAt: now,\n };\n }\n }\n\n /**\n * Create or update a CapEx entry stub for a session.\n * Defaults to 'expensed' classification, unconfirmed.\n */\n private async upsertCapexStub(session: PmSession): Promise<void> {\n try {\n const now = Date.now();\n const entry: PmCapexEntry = {\n id: `capex-${session.id}`,\n projectId: session.projectId,\n sessionId: session.id,\n classification: 'expensed',\n workType: undefined,\n activeMinutes: session.activeMinutes,\n costMicrodollars: session.costMicrodollars,\n adjustmentFactor: 1.0,\n adjustedCostMicrodollars: session.costMicrodollars,\n confirmed: false,\n confirmedAt: undefined,\n confirmedBy: undefined,\n notes: undefined,\n period: toPeriod(session.startedAt),\n createdAt: now,\n updatedAt: now,\n };\n\n await this.pmStore.upsertCapexEntry(entry);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`${LOG_PREFIX} Failed to create CapEx stub for session ${session.id}: ${msg}`);\n }\n }\n}\n"],"mappings":"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","names":["resolve","readFileSync","resolve","errorCount","createHttpsServer","readFileSync","existsSync","WebSocketServer","existsSync","join","homedir","result","existsSync","resolve","createHttpsServer","WebSocketServer","readFileSync","Database","readdir","readFile","stat","join","basename","existsSync","homedir","join","readFile","readdir","stat","existsSync","homedir","basename"]}