@runtimescope/collector 0.10.0 → 0.10.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ring-buffer.ts","../src/store.ts","../src/project-id.ts","../src/sqlite-store.ts","../src/sqlite-check.ts","../src/wal.ts","../src/metrics.ts","../src/otel-exporter.ts","../src/rate-limiter.ts","../src/tls.ts","../src/server.ts","../src/project-manager.ts","../src/project-config.ts","../src/auth.ts","../src/redactor.ts","../src/platform.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 UIInteractionEvent,\n UIInteractionFilter,\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\n/**\n * Check if a sessionId matches a filter value.\n * Supports comma-separated session IDs for multi-app project queries.\n */\nfunction matchesSessionFilter(eventSessionId: string, filterSessionId: string): boolean {\n if (filterSessionId.includes(',')) {\n return filterSessionId.split(',').includes(eventSessionId);\n }\n return eventSessionId === filterSessionId;\n}\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 /**\n * Pre-load recent events from a SqliteStore into the in-memory ring buffer.\n * Used at collector startup to make MCP tools immediately useful after a\n * restart — without this, the buffer is empty until the SDK reconnects and\n * generates new traffic.\n *\n * Also rehydrates the session→projectId map from SqliteStore's `sessions`\n * table. Without this, queries filtered by `project_id` after a crash\n * return nothing because the in-memory map is empty: events are tagged\n * by sessionId, and matchesProjectId() looks up the session record to\n * find the project. (Pre-fix: only sessions whose `session` event happens\n * to be in the warmed window got rehydrated, which is rare in long runs.)\n *\n * Does NOT propagate to SqliteStore (we just read FROM it). Ring-buffer\n * eviction handles overflow naturally if multiple projects are warmed.\n */\n warmFromSqlite(sqliteStore: SqliteStore, project: string, limit: number): void {\n // 1. Rehydrate the session map so projectId-filtered queries work\n // immediately after a crash, even for sessions whose original\n // SessionEvent isn't among the warmed events.\n for (const stored of sqliteStore.getStoredSessions(project)) {\n if (this.sessions.has(stored.sessionId)) continue; // live session wins\n this.sessions.set(stored.sessionId, {\n sessionId: stored.sessionId,\n appName: stored.appName,\n connectedAt: stored.connectedAt,\n sdkVersion: stored.sdkVersion,\n eventCount: 0, // recompute from warmed events below\n isConnected: false,\n projectId: stored.projectId,\n });\n }\n\n // 2. Warm the ring buffer with the most recent N events.\n const events = sqliteStore.getRecentEvents(project, limit);\n for (const event of events) {\n if (event.eventType === 'session') {\n const se = event as SessionEvent;\n if (!this.sessions.has(se.sessionId)) {\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: false,\n projectId: se.projectId,\n });\n }\n }\n const session = this.sessions.get(event.sessionId);\n if (session) session.eventCount++;\n this.buffer.push(event);\n // No SqliteStore dual-write, no listener notification — this is a load,\n // not a new event.\n }\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 projectId: se.projectId,\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 && !matchesSessionFilter(e.sessionId, filter.sessionId)) return false;\n if (filter.projectId && !this.matchesProjectId(e.sessionId, filter.projectId)) 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 && !matchesSessionFilter(e.sessionId, filter.sessionId)) return false;\n if (filter.projectId && !this.matchesProjectId(e.sessionId, filter.projectId)) 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 /**\n * Fast O(sessions) event count for a specific projectId. Callers that just\n * want to know \"have any events arrived for project X yet?\" should use this\n * instead of `getAllEvents(..., projectId).length`, which allocates and\n * filters the entire 10K-event ring buffer on every call.\n */\n eventCountForProject(projectId: string): number {\n let total = 0;\n for (const session of this.sessions.values()) {\n if (session.projectId === projectId) total += session.eventCount;\n }\n return total;\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 && !matchesSessionFilter(e.sessionId, filter.sessionId)) return false;\n if (filter.projectId && !this.matchesProjectId(e.sessionId, filter.projectId)) return false;\n if (typeSet && !typeSet.has(e.eventType)) return false;\n return true;\n });\n }\n\n getAllEvents(sinceSeconds?: number, sessionId?: string, projectId?: 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 if (projectId && !this.matchesProjectId(e.sessionId, projectId)) 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 getSessionIdsForProjectId(projectId: string): string[] {\n return Array.from(this.sessions.values())\n .filter((s) => s.projectId === projectId)\n .map((s) => s.sessionId);\n }\n\n /** Re-tag all sessions with oldProjectId to use newProjectId. */\n retagSessions(oldProjectId: string, newProjectId: string): void {\n for (const [, session] of this.sessions) {\n if (session.projectId === oldProjectId) {\n session.projectId = newProjectId;\n }\n }\n }\n\n /** Check if an event belongs to the given projectId (via its session). */\n private matchesProjectId(sessionId: string, projectId: string): boolean {\n const session = this.sessions.get(sessionId);\n if (!session?.projectId) return false;\n if (projectId.includes(',')) {\n return projectId.split(',').includes(session.projectId);\n }\n return session.projectId === projectId;\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 && !matchesSessionFilter(e.sessionId, filter.sessionId)) return false;\n if (filter.projectId && !this.matchesProjectId(e.sessionId, filter.projectId)) 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 && !matchesSessionFilter(e.sessionId, filter.sessionId)) return false;\n if (filter.projectId && !this.matchesProjectId(e.sessionId, filter.projectId)) 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 && !matchesSessionFilter(e.sessionId, filter.sessionId)) return false;\n if (filter.projectId && !this.matchesProjectId(e.sessionId, filter.projectId)) 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 && !matchesSessionFilter(e.sessionId, filter.sessionId)) return false;\n if (filter.projectId && !this.matchesProjectId(e.sessionId, filter.projectId)) 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 && !matchesSessionFilter(e.sessionId, filter.sessionId)) return false;\n if (filter.projectId && !this.matchesProjectId(e.sessionId, filter.projectId)) 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 getUIInteractions(filter: UIInteractionFilter = {}): UIInteractionEvent[] {\n const since = filter.sinceSeconds\n ? Date.now() - filter.sinceSeconds * 1000\n : 0;\n\n return this.buffer.query((e) => {\n if (e.eventType !== 'ui') return false;\n if (filter.sessionId && !matchesSessionFilter(e.sessionId, filter.sessionId)) return false;\n if (filter.projectId && !this.matchesProjectId(e.sessionId, filter.projectId)) return false;\n const ue = e as UIInteractionEvent;\n if (ue.timestamp < since) return false;\n if (filter.action && ue.action !== filter.action) return false;\n return true;\n }) as UIInteractionEvent[];\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 && !matchesSessionFilter(e.sessionId, filter.sessionId)) return false;\n if (filter.projectId && !this.matchesProjectId(e.sessionId, filter.projectId)) 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 && !matchesSessionFilter(e.sessionId, filter.sessionId)) return false;\n if (filter.projectId && !this.matchesProjectId(e.sessionId, filter.projectId)) 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 { randomBytes } from 'node:crypto';\nimport type { ProjectManager } from './project-manager.js';\n\n/** Minimal interface for PmStore to avoid circular dependencies. */\nexport interface PmStoreLike {\n findProjectIdByApp(appName: string): string | null;\n getWorkspaceByApiKey?(key: string): { id: string; slug: string; name: string } | null;\n listProjects?(): Array<{ id: string; runtimeProjectId?: string; workspaceId?: string }>;\n setProjectWorkspace?(projectId: string, workspaceId: string): void;\n autoLinkApp?(appName: string, projectId?: string): string | null;\n}\n\n// ============================================================\n// Project ID — stable identifier for grouping all SDKs in a project\n// Format: proj_ + 12 alphanumeric chars (e.g., proj_a1b2c3d4e5f6)\n// ============================================================\n\nconst PROJECT_ID_PREFIX = 'proj_';\nconst PROJECT_ID_CHARS = 'abcdefghijklmnopqrstuvwxyz0123456789';\nconst PROJECT_ID_LENGTH = 12;\nconst PROJECT_ID_REGEX = /^proj_[a-z0-9]{12}$/;\n\n/** Generate a new project ID (proj_ + 12 random alphanumeric chars). */\nexport function generateProjectId(): string {\n const bytes = randomBytes(PROJECT_ID_LENGTH);\n let id = PROJECT_ID_PREFIX;\n for (let i = 0; i < PROJECT_ID_LENGTH; i++) {\n id += PROJECT_ID_CHARS[bytes[i] % PROJECT_ID_CHARS.length];\n }\n return id;\n}\n\n/** Validate that a string is a well-formed project ID. */\nexport function isValidProjectId(id: string): boolean {\n return PROJECT_ID_REGEX.test(id);\n}\n\n/**\n * Look up or create a project ID for a given appName.\n * Persists the ID in the ProjectManager config so the same appName\n * always returns the same project ID (idempotent).\n */\nexport function getOrCreateProjectId(projectManager: ProjectManager, appName: string): string {\n // Check if this app already has a project ID\n const existing = projectManager.getProjectIdForApp(appName);\n if (existing) return existing;\n\n // Generate and persist a new one\n const projectId = generateProjectId();\n projectManager.ensureProjectDir(appName);\n projectManager.setProjectIdForApp(appName, projectId);\n return projectId;\n}\n\n/**\n * Resolve a projectId for an appName using a priority chain:\n * 1. ProjectManager cached index (fastest — scans project configs)\n * 2. PmStore lookup (PM project's runtimeProjectId)\n * 3. Existing per-appName config (~/.runtimescope/projects/<appName>/)\n * 4. Generate new (last resort)\n */\nexport function resolveProjectId(\n projectManager: ProjectManager,\n appName: string,\n pmStore?: PmStoreLike | null,\n): string {\n // Step 1: Check cached reverse index\n const fromIndex = projectManager.resolveAppProjectId(appName);\n if (fromIndex) return fromIndex;\n\n // Step 2: Check PM store\n if (pmStore) {\n const fromPm = pmStore.findProjectIdByApp(appName);\n if (fromPm) {\n // Cache it for next time\n projectManager.setProjectIdForApp(appName, fromPm);\n return fromPm;\n }\n }\n\n // Step 3+4: Existing or generate new (original behavior)\n return getOrCreateProjectId(projectManager, appName);\n}\n","import { renameSync, existsSync, statSync } from 'node:fs';\nimport { createRequire } from 'node:module';\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// Loads better-sqlite3 lazily — if not available, SqliteStore\n// cannot be instantiated (guarded by isSqliteAvailable()).\n// ============================================================\n\n// Lazy-load better-sqlite3 to avoid crashing when the native module isn't installed\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nlet DatabaseConstructor: any;\nfunction getDatabase(): any {\n if (!DatabaseConstructor) {\n const require = createRequire(import.meta.url);\n DatabaseConstructor = require('better-sqlite3');\n }\n return DatabaseConstructor;\n}\n\n// Use 'any' for DB instance since we lazy-load the module\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype DatabaseInstance = any;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Statement = any;\n\nexport interface SqliteStoreOptions {\n dbPath: string;\n walMode?: boolean;\n flushIntervalMs?: number;\n batchSize?: number;\n}\n\nexport class SqliteStore {\n private db: DatabaseInstance;\n private writeBuffer: { event: RuntimeEvent; project: string }[] = [];\n private flushTimer: ReturnType<typeof setInterval> | null = null;\n private readonly batchSize: number;\n private readonly dbPath: string;\n\n private static readonly MAX_SNAPSHOTS_PER_SESSION = 50;\n\n private insertEventStmt!: Statement;\n private insertSessionStmt!: Statement;\n private updateSessionDisconnectedStmt!: Statement;\n\n constructor(options: SqliteStoreOptions) {\n this.dbPath = options.dbPath;\n this.batchSize = options.batchSize ?? 50;\n\n this.db = this.openDatabase(options);\n\n // Start flush timer\n const flushInterval = options.flushIntervalMs ?? 100;\n this.flushTimer = setInterval(() => this.flush(), flushInterval);\n }\n\n private openDatabase(options: SqliteStoreOptions): DatabaseInstance {\n const Db = getDatabase();\n try {\n const db = new Db(options.dbPath);\n if (options.walMode !== false) {\n db.pragma('journal_mode = WAL');\n }\n db.pragma('synchronous = NORMAL');\n\n // Quick integrity check — if this fails, the DB is corrupt\n const check = db.pragma('integrity_check') as { integrity_check: string }[];\n if (check[0]?.integrity_check !== 'ok') {\n throw new Error('Integrity check failed');\n }\n\n this.createSchema(db);\n this.prepareStatements(db);\n return db;\n } catch (err) {\n // Corruption recovery: rename the bad DB and create a fresh one\n console.error(\n `[RuntimeScope] SQLite database corrupt or unreadable (${(err as Error).message}), recreating...`\n );\n try {\n if (existsSync(options.dbPath)) {\n const backupPath = `${options.dbPath}.corrupt.${Date.now()}`;\n renameSync(options.dbPath, backupPath);\n console.error(`[RuntimeScope] Renamed corrupt DB to ${backupPath}`);\n }\n // Also clean up WAL/SHM files\n for (const suffix of ['-wal', '-shm']) {\n const p = options.dbPath + suffix;\n if (existsSync(p)) {\n renameSync(p, `${p}.corrupt.${Date.now()}`);\n }\n }\n } catch { /* best effort */ }\n\n const db = new Db(options.dbPath);\n if (options.walMode !== false) {\n db.pragma('journal_mode = WAL');\n }\n db.pragma('synchronous = NORMAL');\n this.createSchema(db);\n this.prepareStatements(db);\n return db;\n }\n }\n\n private prepareStatements(db: DatabaseInstance): void {\n this.insertEventStmt = db.prepare(`\n INSERT INTO events (event_id, session_id, project, event_type, timestamp, data)\n VALUES (?, ?, ?, ?, ?, ?)\n `);\n\n this.insertSessionStmt = 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, project_id\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n\n this.updateSessionDisconnectedStmt = db.prepare(`\n UPDATE sessions SET is_connected = 0, disconnected_at = ? WHERE session_id = ?\n `);\n }\n\n private createSchema(db?: DatabaseInstance): void {\n const d = db ?? this.db;\n d.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 // v0.10.2 — `project_id` (the runtime projectId, e.g. proj_xxx) needed to\n // rehydrate the in-memory session→projectId map after a crash so that\n // `/api/events/*?project_id=...` queries work post-recovery. Idempotent.\n try { d.exec('ALTER TABLE sessions ADD COLUMN project_id TEXT'); } catch { /* already exists */ }\n try { d.exec('CREATE INDEX IF NOT EXISTS idx_sessions_project_id ON sessions(project_id)'); } catch { /* ignore */ }\n\n // Migrate from old session_metrics table if it exists\n this.migrateSessionMetrics(d);\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 info.projectId ?? null,\n );\n }\n\n /**\n * Read every session record stored for a project. Used by the in-memory\n * EventStore at startup to rehydrate the session→projectId map after a\n * crash, so post-recovery `?project_id=...` queries return the right rows.\n * Each row is returned as a `SessionInfo` shape compatible with EventStore.\n */\n getStoredSessions(project: string): 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, project_id\n FROM sessions WHERE project = ?`,\n )\n .all(project) 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 project_id: string | null;\n }[];\n return rows.map((r) => ({\n sessionId: r.session_id,\n project: r.project,\n appName: r.app_name,\n connectedAt: r.connected_at,\n sdkVersion: r.sdk_version,\n eventCount: r.event_count,\n // Recovered sessions are NEVER live — only an active WS reconnect\n // flips this back to true. The persisted `is_connected = 1` only\n // means \"was connected when last written\"; if we crashed, it's stale.\n isConnected: false,\n buildMeta: r.build_meta ? JSON.parse(r.build_meta) : undefined,\n projectId: r.project_id ?? undefined,\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 // Enforce retention: keep only the most recent N snapshots per session\n this.pruneSnapshots(sessionId);\n }\n\n /** Remove oldest snapshots for a session beyond the retention limit */\n private pruneSnapshots(sessionId: string): void {\n const count = (this.db\n .prepare('SELECT COUNT(*) as cnt FROM session_snapshots WHERE session_id = ?')\n .get(sessionId) as { cnt: number }).cnt;\n\n if (count > SqliteStore.MAX_SNAPSHOTS_PER_SESSION) {\n this.db.prepare(`\n DELETE FROM session_snapshots WHERE id IN (\n SELECT id FROM session_snapshots\n WHERE session_id = ?\n ORDER BY created_at ASC\n LIMIT ?\n )\n `).run(sessionId, count - SqliteStore.MAX_SNAPSHOTS_PER_SESSION);\n }\n }\n\n // --- Read Operations ---\n\n /**\n * Return the most recent `limit` events for a project, in chronological\n * order (oldest-first). Used at startup to warm the in-memory ring buffer\n * so MCP tools see recent activity immediately after a collector restart.\n */\n getRecentEvents(project: string, limit: number): RuntimeEvent[] {\n const rows = this.db\n .prepare(\n `SELECT data FROM events WHERE project = ? ORDER BY timestamp DESC LIMIT ?`,\n )\n .all(project, limit) as { data: string }[];\n // Reverse: caller wants oldest-first so ring-buffer push order matches\n // wall-clock order.\n return rows.reverse().map((row) => JSON.parse(row.data) as RuntimeEvent);\n }\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(db: DatabaseInstance): void {\n const hasOldTable = db.prepare(\n \"SELECT name FROM sqlite_master WHERE type='table' AND name='session_metrics'\"\n ).get();\n\n if (hasOldTable) {\n 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 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 /**\n * Atomically copy the live database to `targetPath` using SQLite's\n * `VACUUM INTO`. The destination is a self-contained, defragmented copy of\n * the DB at the moment the statement runs — no readers are blocked beyond\n * the duration of the copy, and the WAL/journal contents are folded in.\n * The target file must not already exist; SQLite refuses to overwrite.\n *\n * Returns the size in bytes of the resulting file.\n */\n snapshotTo(targetPath: string): number {\n // Drain any pending writes first so the snapshot reflects the latest\n // events the caller has handed us.\n this.flush();\n // Parameterized path — `prepare(...).run(targetPath)` keeps the path out\n // of the SQL text and away from any string-concatenation injection risk.\n this.db.prepare('VACUUM INTO ?').run(targetPath);\n try {\n return statSync(targetPath).size;\n } catch {\n return 0;\n }\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","// Probes whether better-sqlite3 is available at runtime.\n// Returns false if the native module failed to compile or load.\n\nimport { createRequire } from 'node:module';\n\nlet _checked = false;\nlet _available = false;\n\nexport function isSqliteAvailable(): boolean {\n if (_checked) return _available;\n _checked = true;\n\n try {\n const require = createRequire(import.meta.url);\n require('better-sqlite3');\n _available = true;\n } catch {\n _available = false;\n console.error(\n '[RuntimeScope] better-sqlite3 is not available — running in memory-only mode.\\n' +\n '[RuntimeScope] Historical data persistence is disabled. To fix this:\\n' +\n '[RuntimeScope] macOS: xcode-select --install\\n' +\n '[RuntimeScope] Ubuntu: sudo apt-get install build-essential python3\\n' +\n '[RuntimeScope] Windows: npm install --global windows-build-tools\\n' +\n '[RuntimeScope] Then run: npm rebuild better-sqlite3'\n );\n }\n\n return _available;\n}\n","/**\n * Write-ahead log for the collector. One WAL directory per project, holding\n * an append-only JSONL file (`active.jsonl`) plus zero or more sealed files\n * that have been rotated out. The durability contract is:\n *\n * append(events) + commit() → events are on disk (fsync'd).\n *\n * `append` buffers into the OS file table (a plain `write` syscall); `commit`\n * calls `fsync` to force the bytes to stable storage. Callers should batch\n * appends within a single SDK message and call `commit` once per batch.\n *\n * On rotation, the active file is renamed to `sealed-<ts>-<seq>.jsonl` and a\n * fresh active file is opened. Sealed files stay on disk until the collector\n * confirms the corresponding events are durable in SqliteStore, at which\n * point they're safe to delete.\n *\n * On crash + restart, any remaining sealed or active files are evidence of\n * events that may not have reached SqliteStore. Replay feeds them back into\n * the store path, then deletes the WAL files.\n */\n\nimport {\n closeSync,\n existsSync,\n fsyncSync,\n mkdirSync,\n openSync,\n readFileSync,\n readdirSync,\n renameSync,\n statSync,\n unlinkSync,\n writeSync,\n} from 'node:fs';\nimport { join } from 'node:path';\nimport type { RuntimeEvent } from './types.js';\n\nexport interface WalOptions {\n /** Directory to hold WAL files — created if missing. */\n dir: string;\n /** Rotate the active file once it grows past this size in bytes. */\n rotateSizeBytes?: number;\n}\n\ninterface WalEntry {\n seq: number;\n event: RuntimeEvent;\n}\n\nexport class Wal {\n private dir: string;\n private rotateSize: number;\n private fd: number | null = null;\n private activeSize = 0;\n private seq = 0;\n private closed = false;\n\n constructor(options: WalOptions) {\n this.dir = options.dir;\n this.rotateSize = options.rotateSizeBytes ?? 8 * 1024 * 1024;\n mkdirSync(this.dir, { recursive: true });\n this.openActive();\n }\n\n private openActive(): void {\n const path = this.activePath();\n // O_APPEND: atomic append even under concurrent writers (we only use one,\n // but keeps behavior correct if a stale handle is still around).\n this.fd = openSync(path, 'a');\n try {\n this.activeSize = statSync(path).size;\n } catch {\n this.activeSize = 0;\n }\n }\n\n private activePath(): string {\n return join(this.dir, 'active.jsonl');\n }\n\n /**\n * Buffer events into the active file. Not durable yet — must be followed by\n * `commit()` before the caller treats the events as persisted.\n */\n append(events: RuntimeEvent[]): void {\n if (this.closed || this.fd === null || events.length === 0) return;\n const lines: string[] = [];\n for (const ev of events) {\n this.seq++;\n const entry: WalEntry = { seq: this.seq, event: ev };\n lines.push(JSON.stringify(entry));\n }\n const payload = Buffer.from(lines.join('\\n') + '\\n', 'utf8');\n writeSync(this.fd, payload);\n this.activeSize += payload.length;\n }\n\n /** fsync the active file. Durability contract: returns only after bytes are on stable storage. */\n commit(): void {\n if (this.closed || this.fd === null) return;\n try {\n fsyncSync(this.fd);\n } catch {\n // A broken disk will surface on the next write; there's nothing useful\n // to do here that the caller doesn't already expect.\n }\n }\n\n /** True if the active file has exceeded the rotate threshold. */\n shouldRotate(): boolean {\n return this.activeSize >= this.rotateSize;\n }\n\n /**\n * Rotate the active file: fsync, close, rename to `sealed-<ts>-<seq>.jsonl`,\n * then open a fresh active file. Returns the sealed path so the caller can\n * schedule deletion after confirming persistence elsewhere.\n */\n rotate(): string | null {\n if (this.closed || this.fd === null) return null;\n this.commit();\n closeSync(this.fd);\n this.fd = null;\n\n const active = this.activePath();\n if (!existsSync(active)) {\n // Nothing to rotate — reopen and return.\n this.openActive();\n return null;\n }\n const sealed = join(this.dir, `sealed-${Date.now()}-${this.seq}.jsonl`);\n renameSync(active, sealed);\n this.activeSize = 0;\n this.openActive();\n return sealed;\n }\n\n /** Remove a sealed file once its events are durable in the downstream store. */\n static deleteSealed(path: string): void {\n try {\n unlinkSync(path);\n } catch {\n // Already gone or permissions issue — non-fatal; retry on next sweep.\n }\n }\n\n /** List sealed files in this WAL's directory, sorted oldest-first. */\n listSealed(): string[] {\n try {\n return readdirSync(this.dir)\n .filter((f) => f.startsWith('sealed-') && f.endsWith('.jsonl'))\n .sort()\n .map((f) => join(this.dir, f));\n } catch {\n return [];\n }\n }\n\n /**\n * Parse a WAL file back into its events, skipping any corrupt or truncated\n * trailing line. A crash mid-append can leave a partial line; the parser\n * tolerates it because fsync had never completed for that line, which means\n * the event was never treated as durable.\n */\n static readFile(path: string): RuntimeEvent[] {\n let raw: string;\n try {\n raw = readFileSync(path, 'utf8');\n } catch {\n return [];\n }\n if (!raw) return [];\n const events: RuntimeEvent[] = [];\n for (const line of raw.split('\\n')) {\n if (!line.trim()) continue;\n try {\n const parsed = JSON.parse(line) as WalEntry;\n if (parsed && parsed.event) events.push(parsed.event);\n } catch {\n // Torn tail — stop here; everything after the last fsync may be garbage.\n break;\n }\n }\n return events;\n }\n\n /**\n * List the WAL files that need recovery for a project: any sealed files\n * plus the active file if it has content. Returned in ingestion order\n * (sealed oldest-first, then active last).\n */\n static listRecoveryFiles(dir: string): string[] {\n if (!existsSync(dir)) return [];\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return [];\n }\n const sealed = entries\n .filter((f) => f.startsWith('sealed-') && f.endsWith('.jsonl'))\n .sort();\n const files = sealed.map((f) => join(dir, f));\n const active = join(dir, 'active.jsonl');\n try {\n const s = statSync(active);\n if (s.size > 0) files.push(active);\n } catch {\n // no active file, or can't stat it — skip\n }\n return files;\n }\n\n close(): void {\n if (this.closed || this.fd === null) return;\n this.commit();\n try {\n closeSync(this.fd);\n } catch {\n // ignore\n }\n this.fd = null;\n this.closed = true;\n }\n\n /**\n * Copy the active file (post-fsync) and any sealed files into `targetDir`.\n * Returns the total bytes copied. Used by snapshot endpoints — pairing the\n * SQLite snapshot with the WAL means a restore captures any events that\n * hadn't yet been drained into SQLite at snapshot time.\n */\n snapshotTo(targetDir: string): number {\n this.commit();\n mkdirSync(targetDir, { recursive: true });\n\n let total = 0;\n const copyOne = (src: string, dstName: string) => {\n try {\n const data = readFileSync(src);\n const dst = join(targetDir, dstName);\n // 'wx' refuses to overwrite — the snapshot dir is fresh, so this\n // catches any accidental name reuse loudly instead of silently.\n const fd = openSync(dst, 'wx');\n try {\n writeSync(fd, data);\n } finally {\n closeSync(fd);\n }\n total += data.length;\n } catch {\n // Source missing or unreadable — skip; the manifest will reflect what\n // actually copied.\n }\n };\n\n const active = this.activePath();\n try {\n if (statSync(active).size > 0) copyOne(active, 'active.jsonl');\n } catch {\n // no active file\n }\n for (const sealed of this.listSealed()) {\n copyOne(sealed, sealed.split('/').pop() ?? 'sealed.jsonl');\n }\n return total;\n }\n}\n","/**\n * Minimal Prometheus-compatible metrics registry. Zero dependencies — the\n * exposition format is small enough that pulling in `prom-client` would be\n * heavier than just emitting the text ourselves.\n *\n * Two metric kinds are supported:\n * - Counter (monotonic) for things like total events, dropped events.\n * - Gauge (point-in-time) for things like current sessions connected,\n * ring-buffer occupancy. Gauges accept a `collect` callback so they\n * compute their value at scrape time rather than tracking state.\n *\n * Spec reference: https://prometheus.io/docs/instrumenting/exposition_formats/\n */\n\nexport type LabelValues = Record<string, string>;\n\ninterface Sample {\n labels: LabelValues;\n value: number;\n}\n\nabstract class Metric {\n constructor(\n public readonly name: string,\n public readonly help: string,\n public readonly labelNames: string[] = [],\n ) {\n if (!/^[a-zA-Z_:][a-zA-Z0-9_:]*$/.test(name)) {\n throw new Error(`Invalid metric name \"${name}\" — must match Prometheus naming rules`);\n }\n }\n\n abstract type(): 'counter' | 'gauge';\n abstract collect(): Sample[];\n}\n\nexport class Counter extends Metric {\n private values = new Map<string, Sample>();\n\n type(): 'counter' { return 'counter'; }\n\n inc(value: number = 1, labels: LabelValues = {}): void {\n if (!Number.isFinite(value) || value < 0) return;\n const key = labelKey(labels, this.labelNames);\n const existing = this.values.get(key);\n if (existing) {\n existing.value += value;\n } else {\n this.values.set(key, { labels: orderLabels(labels, this.labelNames), value });\n }\n }\n\n reset(): void {\n this.values.clear();\n }\n\n collect(): Sample[] {\n return Array.from(this.values.values());\n }\n}\n\nexport class Gauge extends Metric {\n private values = new Map<string, Sample>();\n private collectFn: (() => Sample[]) | null = null;\n\n type(): 'gauge' { return 'gauge'; }\n\n /**\n * Set a fixed value for the given label set. Per the Prometheus spec, gauges\n * may take any numeric value including +Inf / -Inf / NaN — the renderer\n * handles formatting. Only actually-not-a-number inputs (`undefined`, etc.)\n * are silently dropped.\n */\n set(value: number, labels: LabelValues = {}): void {\n if (typeof value !== 'number') return;\n const key = labelKey(labels, this.labelNames);\n this.values.set(key, { labels: orderLabels(labels, this.labelNames), value });\n }\n\n /**\n * Compute the gauge dynamically at scrape time. Useful for things like\n * \"currently-connected sessions\" where caching a stale value is wrong.\n * Mutually exclusive with `set()` — last writer wins.\n */\n setCollect(fn: () => number | Sample[]): void {\n this.collectFn = () => {\n const result = fn();\n if (typeof result === 'number') return [{ labels: {}, value: result }];\n return result;\n };\n }\n\n collect(): Sample[] {\n if (this.collectFn) return this.collectFn();\n return Array.from(this.values.values());\n }\n}\n\nexport class MetricsRegistry {\n private metrics: Metric[] = [];\n\n counter(name: string, help: string, labelNames: string[] = []): Counter {\n const c = new Counter(name, help, labelNames);\n this.metrics.push(c);\n return c;\n }\n\n gauge(name: string, help: string, labelNames: string[] = []): Gauge {\n const g = new Gauge(name, help, labelNames);\n this.metrics.push(g);\n return g;\n }\n\n /** Serialize every registered metric in Prometheus exposition format. */\n render(): string {\n const out: string[] = [];\n for (const metric of this.metrics) {\n out.push(`# HELP ${metric.name} ${escapeHelp(metric.help)}`);\n out.push(`# TYPE ${metric.name} ${metric.type()}`);\n for (const sample of metric.collect()) {\n out.push(formatSample(metric.name, sample));\n }\n }\n // Prometheus requires a trailing newline.\n return out.join('\\n') + '\\n';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers — kept private to discourage callers from hand-rolling exposition.\n// ---------------------------------------------------------------------------\n\nfunction labelKey(labels: LabelValues, expected: string[]): string {\n // Stable key for in-memory dedup. Sorted by label name for determinism.\n const ordered = orderLabels(labels, expected);\n const pairs: string[] = [];\n for (const name of Object.keys(ordered).sort()) {\n pairs.push(`${name}=${ordered[name]}`);\n }\n return pairs.join('|');\n}\n\nfunction orderLabels(labels: LabelValues, expected: string[]): LabelValues {\n // Drop unexpected labels and provide stable presence for the rendered output.\n // Strict-mode would throw; we're permissive so a typo on inc() doesn't take\n // down the collector.\n const out: LabelValues = {};\n for (const name of expected) {\n out[name] = labels[name] ?? '';\n }\n return out;\n}\n\nfunction formatSample(name: string, sample: Sample): string {\n const labelPart = renderLabels(sample.labels);\n return `${name}${labelPart} ${formatNumber(sample.value)}`;\n}\n\nfunction renderLabels(labels: LabelValues): string {\n const keys = Object.keys(labels);\n if (keys.length === 0) return '';\n const parts: string[] = [];\n for (const k of keys.sort()) {\n parts.push(`${k}=\"${escapeLabelValue(labels[k])}\"`);\n }\n return `{${parts.join(',')}}`;\n}\n\nfunction escapeLabelValue(v: string): string {\n // Escape backslash, double-quote, and newline per spec.\n return v.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"').replace(/\\n/g, '\\\\n');\n}\n\nfunction escapeHelp(v: string): string {\n return v.replace(/\\\\/g, '\\\\\\\\').replace(/\\n/g, '\\\\n');\n}\n\nfunction formatNumber(n: number): string {\n if (Number.isNaN(n)) return 'NaN';\n if (n === Infinity) return '+Inf';\n if (n === -Infinity) return '-Inf';\n // Integers render without a decimal point — matches prom-client output and\n // avoids surprising users grepping for \"events_total 5\" not \"events_total 5.0\".\n if (Number.isInteger(n)) return String(n);\n return n.toString();\n}\n","/**\n * OpenTelemetry exporter — one-way bridge from RuntimeScope events to OTLP/HTTP.\n *\n * Each RuntimeScope event maps to one OTel signal:\n * - `network` → trace span (HTTP client, kind=CLIENT)\n * - `database` → trace span (DB client, kind=CLIENT)\n * - `render` → trace span (kind=INTERNAL, attr `react.component`)\n * - `console` → log record. `level=error` is severity=ERROR, with the\n * stack trace as an `exception.stacktrace` attribute.\n * - `performance` → gauge metric (Web Vitals: LCP / FCP / CLS / INP / TTFB).\n *\n * State, session, ui-interaction, dom_snapshot, custom, navigation, and recon\n * events have no clean OTel mapping and are skipped — downstream tools can\n * still see everything via the RuntimeScope HTTP API directly.\n *\n * Session = trace. All events for one `sessionId` share a deterministic 16-byte\n * trace ID derived from `sha256(sessionId)`, so a downstream OTel UI groups\n * them. Span IDs are random per event.\n *\n * Wire format is OTLP/HTTP with JSON encoding — chosen specifically because it\n * needs no Protobuf compiler, no deps, and is supported by every modern\n * collector (otel-collector, Honeycomb refinery, Vector, Datadog, Tempo, etc.).\n *\n * Configuration is opt-in via env vars:\n * - RUNTIMESCOPE_OTEL_ENDPOINT — e.g. \"http://localhost:4318\" (no path)\n * - RUNTIMESCOPE_OTEL_HEADERS — \"k1=v1,k2=v2\" — for bearer tokens etc.\n * - RUNTIMESCOPE_OTEL_SERVICE_NAME — defaults to \"runtimescope-collector\"\n *\n * Failure semantics: best-effort. We batch in memory, flush on a timer or\n * size threshold, and drop on POST failure (logging at most once per failure).\n * If the OTel endpoint is down, RuntimeScope itself is unaffected — that's\n * the whole point of running this as a sidecar exporter.\n */\n\nimport { createHash, randomBytes } from 'node:crypto';\nimport type {\n RuntimeEvent,\n NetworkEvent,\n DatabaseEvent,\n ConsoleEvent,\n RenderEvent,\n PerformanceEvent,\n} from './types.js';\n\nexport interface OtelExporterOptions {\n /** Base URL of the OTel collector — no path. e.g. \"http://localhost:4318\". */\n endpoint: string;\n /** `service.name` resource attribute. Default \"runtimescope-collector\". */\n serviceName?: string;\n /** `service.namespace` resource attribute — useful for projectId. */\n serviceNamespace?: string;\n /** Extra HTTP headers (auth tokens, tenant IDs, etc.). */\n headers?: Record<string, string>;\n /** Force flush after this many milliseconds even if the buffer is small. Default 10s. */\n flushIntervalMs?: number;\n /** Force flush once we've accumulated this many signals. Default 1000. */\n maxBatchSize?: number;\n}\n\ninterface OtlpAttribute {\n key: string;\n value:\n | { stringValue: string }\n | { intValue: string }\n | { doubleValue: number }\n | { boolValue: boolean };\n}\n\ninterface OtlpSpan {\n traceId: string;\n spanId: string;\n parentSpanId?: string;\n name: string;\n kind: number;\n startTimeUnixNano: string;\n endTimeUnixNano: string;\n attributes: OtlpAttribute[];\n status: { code: number; message?: string };\n events?: { timeUnixNano: string; name: string; attributes: OtlpAttribute[] }[];\n}\n\ninterface OtlpLogRecord {\n timeUnixNano: string;\n severityNumber: number;\n severityText: string;\n body: { stringValue: string };\n attributes: OtlpAttribute[];\n traceId?: string;\n spanId?: string;\n}\n\ninterface OtlpMetric {\n name: string;\n unit?: string;\n gauge: {\n dataPoints: {\n asDouble?: number;\n asInt?: string;\n timeUnixNano: string;\n attributes: OtlpAttribute[];\n }[];\n };\n}\n\n/** OTel span kind enum. */\nconst SpanKind = {\n INTERNAL: 1,\n SERVER: 2,\n CLIENT: 3,\n} as const;\n\n/** OTel status code enum. */\nconst StatusCode = {\n UNSET: 0,\n OK: 1,\n ERROR: 2,\n} as const;\n\nexport class OtelExporter {\n private spans: OtlpSpan[] = [];\n private logs: OtlpLogRecord[] = [];\n private metrics: OtlpMetric[] = [];\n private flushTimer: ReturnType<typeof setInterval> | null = null;\n private closed = false;\n private lastFailureLog = 0;\n private readonly endpoint: string;\n private readonly serviceName: string;\n private readonly serviceNamespace: string | undefined;\n private readonly headers: Record<string, string>;\n private readonly maxBatchSize: number;\n\n constructor(options: OtelExporterOptions) {\n if (!options.endpoint) throw new Error('OtelExporter requires an endpoint');\n this.endpoint = options.endpoint.replace(/\\/$/, '');\n this.serviceName = options.serviceName ?? 'runtimescope-collector';\n this.serviceNamespace = options.serviceNamespace;\n this.headers = options.headers ?? {};\n this.maxBatchSize = options.maxBatchSize ?? 1000;\n\n const interval = options.flushIntervalMs ?? 10_000;\n this.flushTimer = setInterval(() => {\n void this.flush().catch(() => { /* logged inside flush */ });\n }, interval);\n // Don't keep the process alive solely for flushes — the collector owns\n // its lifecycle, and shutdown calls close() to drain.\n this.flushTimer.unref?.();\n }\n\n /** Convert a RuntimeScope event to its OTel signal(s) and buffer. */\n ingest(event: RuntimeEvent): void {\n if (this.closed) return;\n switch (event.eventType) {\n case 'network':\n this.spans.push(this.networkToSpan(event as NetworkEvent));\n break;\n case 'database':\n this.spans.push(this.databaseToSpan(event as DatabaseEvent));\n break;\n case 'render':\n this.spans.push(...this.renderToSpans(event as RenderEvent));\n break;\n case 'console':\n this.logs.push(this.consoleToLog(event as ConsoleEvent));\n break;\n case 'performance': {\n const m = this.performanceToMetric(event as PerformanceEvent);\n if (m) this.metrics.push(m);\n break;\n }\n default:\n // Other event types (state, session, ui-interaction, dom-snapshot) don't\n // map cleanly onto OTel signals. Skip rather than guess.\n break;\n }\n if (this.spans.length + this.logs.length + this.metrics.length >= this.maxBatchSize) {\n void this.flush().catch(() => { /* swallowed inside */ });\n }\n }\n\n /** POST any buffered signals to the OTLP endpoint. */\n async flush(): Promise<void> {\n if (this.closed) return;\n const spans = this.spans;\n const logs = this.logs;\n const metrics = this.metrics;\n this.spans = [];\n this.logs = [];\n this.metrics = [];\n\n const tasks: Promise<void>[] = [];\n if (spans.length) tasks.push(this.send('/v1/traces', { resourceSpans: this.wrapSpans(spans) }));\n if (logs.length) tasks.push(this.send('/v1/logs', { resourceLogs: this.wrapLogs(logs) }));\n if (metrics.length) tasks.push(this.send('/v1/metrics', { resourceMetrics: this.wrapMetrics(metrics) }));\n await Promise.all(tasks);\n }\n\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n await this.flush();\n }\n\n // ------------------------------------------------------------------------\n // Event-to-signal converters\n // ------------------------------------------------------------------------\n\n private networkToSpan(e: NetworkEvent): OtlpSpan {\n const start = (e.timestamp - (e.duration ?? 0)) * 1_000_000;\n const end = e.timestamp * 1_000_000;\n const isError = e.status >= 400 || e.status === 0;\n return {\n traceId: traceIdFromSession(e.sessionId),\n spanId: randomSpanId(),\n name: `${e.method} ${stripQuery(e.url)}`,\n kind: SpanKind.CLIENT,\n startTimeUnixNano: String(start),\n endTimeUnixNano: String(end),\n attributes: [\n attrStr('http.request.method', e.method),\n attrStr('url.full', e.url),\n attrInt('http.response.status_code', e.status),\n attrInt('http.request.body.size', e.requestBodySize ?? 0),\n attrInt('http.response.body.size', e.responseBodySize ?? 0),\n attrStr('runtimescope.session_id', e.sessionId),\n ],\n status: {\n code: isError ? StatusCode.ERROR : StatusCode.OK,\n message: isError ? `HTTP ${e.status}` : undefined,\n },\n };\n }\n\n private databaseToSpan(e: DatabaseEvent): OtlpSpan {\n const start = (e.timestamp - (e.duration ?? 0)) * 1_000_000;\n const end = e.timestamp * 1_000_000;\n const primaryTable = e.tablesAccessed?.[0] ?? '';\n return {\n traceId: traceIdFromSession(e.sessionId),\n spanId: randomSpanId(),\n name: `${(e.operation ?? 'query').toUpperCase()} ${primaryTable}`.trim(),\n kind: SpanKind.CLIENT,\n startTimeUnixNano: String(start),\n endTimeUnixNano: String(end),\n attributes: [\n attrStr('db.system', e.source ?? 'unknown'),\n attrStr('db.operation', e.operation ?? ''),\n attrStr('db.statement', e.query ?? ''),\n attrStr('db.sql.table', primaryTable),\n attrInt('db.response.returned_rows', e.rowsReturned ?? 0),\n attrStr('runtimescope.session_id', e.sessionId),\n ],\n status: { code: e.error ? StatusCode.ERROR : StatusCode.OK, message: e.error },\n };\n }\n\n /**\n * Render events bundle multiple component profiles in one event. We emit\n * one span per profile so the OTel UI can show each component's render\n * timing independently — sharing the same trace via the sessionId.\n */\n private renderToSpans(e: RenderEvent): OtlpSpan[] {\n const traceId = traceIdFromSession(e.sessionId);\n const profiles = e.profiles ?? [];\n const out: OtlpSpan[] = [];\n for (const p of profiles) {\n const dur = p.totalDuration ?? 0;\n const end = e.timestamp * 1_000_000;\n const start = end - Math.round(dur * 1_000_000);\n out.push({\n traceId,\n spanId: randomSpanId(),\n name: `render ${p.componentName}`,\n kind: SpanKind.INTERNAL,\n startTimeUnixNano: String(start),\n endTimeUnixNano: String(end),\n attributes: [\n attrStr('react.component', p.componentName),\n attrStr('react.phase', p.lastRenderPhase ?? ''),\n attrInt('react.render_count', p.renderCount ?? 0),\n attrStr('react.last_render_cause', p.lastRenderCause ?? ''),\n attrStr('runtimescope.session_id', e.sessionId),\n ],\n status: { code: p.suspicious ? StatusCode.ERROR : StatusCode.UNSET },\n });\n }\n return out;\n }\n\n /**\n * `console` events with `level: 'error'` carry stack traces; we surface them\n * via OTel's exception attributes so log-search backends can show them\n * inline. Lower-severity console events become plain log records.\n */\n private consoleToLog(e: ConsoleEvent): OtlpLogRecord {\n const attrs: OtlpAttribute[] = [attrStr('runtimescope.session_id', e.sessionId)];\n if (e.source) attrs.push(attrStr('runtimescope.source', e.source));\n if (e.level === 'error' && e.stackTrace) {\n attrs.push(attrStr('exception.message', e.message ?? ''));\n attrs.push(attrStr('exception.stacktrace', e.stackTrace));\n }\n return {\n timeUnixNano: String(e.timestamp * 1_000_000),\n severityNumber: severityFromConsole(e.level),\n severityText: e.level,\n body: { stringValue: e.message ?? '' },\n attributes: attrs,\n traceId: traceIdFromSession(e.sessionId),\n };\n }\n\n private performanceToMetric(e: PerformanceEvent): OtlpMetric | null {\n if (!e.metricName || typeof e.value !== 'number') return null;\n // Web Vitals (CLS / LCP / FCP / INP / FID / TTFB) are browser-side; the\n // rest are Node server metrics. Use distinct namespaces so dashboards\n // don't accidentally aggregate \"memory.rss\" into the same panel as LCP.\n const isWebVital = WEB_VITALS.has(e.metricName);\n const name = isWebVital\n ? `runtimescope.web_vitals.${e.metricName.toLowerCase()}`\n : `runtimescope.server.${e.metricName.toLowerCase()}`;\n return {\n name,\n unit: e.unit ?? webVitalUnit(e.metricName),\n gauge: {\n dataPoints: [\n {\n asDouble: e.value,\n timeUnixNano: String(e.timestamp * 1_000_000),\n attributes: [\n attrStr('rating', e.rating ?? 'unknown'),\n attrStr('runtimescope.session_id', e.sessionId),\n ],\n },\n ],\n },\n };\n }\n\n // ------------------------------------------------------------------------\n // OTLP envelope wrapping + transport\n // ------------------------------------------------------------------------\n\n private resourceAttributes(): OtlpAttribute[] {\n const attrs: OtlpAttribute[] = [\n attrStr('service.name', this.serviceName),\n attrStr('telemetry.sdk.name', 'runtimescope'),\n attrStr('telemetry.sdk.language', 'nodejs'),\n ];\n if (this.serviceNamespace) attrs.push(attrStr('service.namespace', this.serviceNamespace));\n return attrs;\n }\n\n private wrapSpans(spans: OtlpSpan[]) {\n return [\n {\n resource: { attributes: this.resourceAttributes() },\n scopeSpans: [{ scope: { name: 'runtimescope' }, spans }],\n },\n ];\n }\n\n private wrapLogs(logs: OtlpLogRecord[]) {\n return [\n {\n resource: { attributes: this.resourceAttributes() },\n scopeLogs: [{ scope: { name: 'runtimescope' }, logRecords: logs }],\n },\n ];\n }\n\n private wrapMetrics(metrics: OtlpMetric[]) {\n return [\n {\n resource: { attributes: this.resourceAttributes() },\n scopeMetrics: [{ scope: { name: 'runtimescope' }, metrics }],\n },\n ];\n }\n\n private async send(path: string, body: unknown): Promise<void> {\n try {\n const res = await fetch(this.endpoint + path, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n ...this.headers,\n },\n body: JSON.stringify(body),\n });\n if (!res.ok) {\n this.logFailure(`OTel POST ${path} → ${res.status} ${res.statusText}`);\n }\n } catch (err) {\n this.logFailure(`OTel POST ${path} failed: ${(err as Error).message}`);\n }\n }\n\n /** Rate-limit failure logs to once every 60s — don't spam stderr. */\n private logFailure(msg: string): void {\n const now = Date.now();\n if (now - this.lastFailureLog < 60_000) return;\n this.lastFailureLog = now;\n console.error(`[RuntimeScope] ${msg}`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Build a deterministic 16-byte trace ID from the sessionId so every event\n * for one session belongs to the same trace in the downstream UI.\n */\nexport function traceIdFromSession(sessionId: string): string {\n return createHash('sha256').update(sessionId).digest('hex').slice(0, 32);\n}\n\nexport function randomSpanId(): string {\n return randomBytes(8).toString('hex');\n}\n\nfunction attrStr(key: string, value: string): OtlpAttribute {\n return { key, value: { stringValue: value } };\n}\n\nfunction attrInt(key: string, value: number): OtlpAttribute {\n return { key, value: { intValue: String(Math.trunc(value)) } };\n}\n\nfunction severityFromConsole(level: string): number {\n // OTel SeverityNumber: TRACE=1, DEBUG=5, INFO=9, WARN=13, ERROR=17, FATAL=21.\n switch (level) {\n case 'debug': return 5;\n case 'log':\n case 'info': return 9;\n case 'warn': return 13;\n case 'error': return 17;\n default: return 9;\n }\n}\n\n/** The Web Vital metric names browser SDKs emit. Server metrics route to a different namespace. */\nconst WEB_VITALS = new Set(['LCP', 'FCP', 'CLS', 'TTFB', 'FID', 'INP']);\n\nfunction webVitalUnit(name: string): string {\n // CLS is unitless ratio; the rest are milliseconds.\n return name.toUpperCase() === 'CLS' ? '1' : 'ms';\n}\n\nfunction stripQuery(url: string): string {\n const i = url.indexOf('?');\n return i >= 0 ? url.slice(0, i) : url;\n}\n\n/** Parse `RUNTIMESCOPE_OTEL_HEADERS=\"k1=v1,k2=v2\"` into a header map. */\nexport function parseOtelHeaders(raw: string | undefined): Record<string, string> {\n if (!raw) return {};\n const out: Record<string, string> = {};\n for (const pair of raw.split(',')) {\n const eq = pair.indexOf('=');\n if (eq <= 0) continue;\n const k = pair.slice(0, eq).trim();\n const v = pair.slice(eq + 1).trim();\n if (k) out[k] = v;\n }\n return out;\n}\n\n/** Build options from environment variables. Returns null if OTel is not configured. */\nexport function otelOptionsFromEnv(): OtelExporterOptions | null {\n const endpoint = process.env.RUNTIMESCOPE_OTEL_ENDPOINT;\n if (!endpoint) return null;\n return {\n endpoint,\n serviceName: process.env.RUNTIMESCOPE_OTEL_SERVICE_NAME,\n headers: parseOtelHeaders(process.env.RUNTIMESCOPE_OTEL_HEADERS),\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 { getOrCreateProjectId, resolveProjectId } from './project-id.js';\nimport type { PmStoreLike } from './project-id.js';\nimport { SqliteStore } from './sqlite-store.js';\nimport { isSqliteAvailable } from './sqlite-check.js';\nimport { Wal } from './wal.js';\nimport { dirname, join } from 'node:path';\nimport { mkdirSync, writeFileSync } from 'node:fs';\nimport { MetricsRegistry, Counter } from './metrics.js';\nimport { OtelExporter, otelOptionsFromEnv, type OtelExporterOptions } from './otel-exporter.js';\n\nexport interface SnapshotResult {\n /** Absolute path to the snapshot directory. */\n path: string;\n /** ISO-like timestamp embedded in the directory name. */\n timestamp: string;\n /** Per-project sizes + event counts. */\n projects: { name: string; sqliteBytes: number; walBytes: number; eventCount: number }[];\n /** Total bytes written across all projects. */\n totalBytes: number;\n}\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 RuntimeEvent,\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 * Optional OpenTelemetry exporter config. When set (or when\n * `RUNTIMESCOPE_OTEL_ENDPOINT` is in the environment), every event the\n * store accepts is converted into an OTLP signal and shipped to the\n * configured endpoint. Failures are logged but never break ingestion.\n */\n otel?: OtelExporterOptions;\n}\n\ninterface ClientInfo {\n sessionId: string;\n projectName: string;\n projectId?: string;\n workspaceId?: 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 wals: Map<string, Wal> = new Map();\n private ready = false;\n private metrics: MetricsRegistry = new MetricsRegistry();\n private startedAt: number = Date.now();\n private counters: {\n eventsTotal: Counter;\n eventsDropped: Counter;\n wsDisconnects: Counter;\n };\n private otelExporter: OtelExporter | null = null;\n private connectCallbacks: ((sessionId: string, projectName: string, projectId?: string) => void)[] = [];\n private disconnectCallbacks: ((sessionId: string, projectName: string, projectId?: string) => void)[] = [];\n private pruneTimer: ReturnType<typeof setInterval> | null = null;\n private heartbeatTimer: ReturnType<typeof setInterval> | null = null;\n private tlsConfig: TlsConfig | null = null;\n private pmStore: PmStoreLike | 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 // --- Metrics registry ---\n // Counters increment as work happens; gauges read live state at scrape\n // time so we don't risk drift between the counter and the underlying map.\n this.counters = {\n eventsTotal: this.metrics.counter(\n 'runtimescope_events_total',\n 'Total events accepted by the collector since start.',\n ['type'],\n ),\n eventsDropped: this.metrics.counter(\n 'runtimescope_events_dropped_total',\n 'Total events dropped before reaching the in-memory store.',\n ['reason'],\n ),\n wsDisconnects: this.metrics.counter(\n 'runtimescope_ws_disconnects_total',\n 'WebSocket disconnects (clean + abnormal) since start.',\n ['cause'],\n ),\n };\n\n // Hot path: every event the store accepts increments the per-type counter.\n // Listener errors are swallowed by EventStore.addEvent, so a bad metric\n // can't break ingestion.\n this.store.onEvent((event) => {\n this.counters.eventsTotal.inc(1, { type: event.eventType });\n });\n\n const uptime = this.metrics.gauge(\n 'runtimescope_collector_uptime_seconds',\n 'Seconds since the collector process started.',\n );\n uptime.setCollect(() => Math.floor((Date.now() - this.startedAt) / 1000));\n\n const sessionsConnected = this.metrics.gauge(\n 'runtimescope_sessions_connected',\n 'SDK sessions currently connected via WebSocket.',\n );\n sessionsConnected.setCollect(\n () => this.store.getSessionInfo().filter((s) => s.isConnected).length,\n );\n\n const bufferSize = this.metrics.gauge(\n 'runtimescope_buffer_size',\n 'Events currently held in the in-memory ring buffer.',\n );\n bufferSize.setCollect(() => this.store.eventCount);\n\n const projectsGauge = this.metrics.gauge(\n 'runtimescope_projects',\n 'Distinct projects (apps) the collector has seen.',\n );\n projectsGauge.setCollect(() => this.projectManager?.listProjects().length ?? 0);\n\n const workspacesGauge = this.metrics.gauge(\n 'runtimescope_workspaces',\n 'Workspaces (multi-tenant containers) registered in PmStore.',\n );\n workspacesGauge.setCollect(() => {\n const pm = this.pmStore as { listWorkspaces?: () => unknown[] } | null;\n return pm?.listWorkspaces ? pm.listWorkspaces().length : 0;\n });\n\n // --- OpenTelemetry exporter (opt-in) ---\n // Explicit constructor option wins; otherwise fall back to env-vars so\n // operators can configure it at deploy time without code changes.\n const otelOptions = options.otel ?? otelOptionsFromEnv();\n if (otelOptions) {\n this.otelExporter = new OtelExporter(otelOptions);\n this.store.onEvent((event) => {\n // ingest is fire-and-forget — the exporter buffers + flushes on its\n // own timer. Errors are logged inside the exporter, never thrown out.\n this.otelExporter?.ingest(event);\n });\n console.error(\n `[RuntimeScope] OpenTelemetry export enabled → ${otelOptions.endpoint}`,\n );\n }\n }\n\n /** Public access to the metrics registry — HttpServer renders this at /metrics. */\n getMetricsRegistry(): MetricsRegistry {\n return this.metrics;\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 /** Set the PmStore for project ID resolution (called after construction when PmStore is available). */\n setPmStore(pmStore: PmStoreLike | null): void {\n this.pmStore = pmStore;\n }\n\n onConnect(cb: (sessionId: string, projectName: string, projectId?: string) => void): void {\n this.connectCallbacks.push(cb);\n }\n\n onDisconnect(cb: (sessionId: string, projectName: string, projectId?: string) => void): void {\n this.disconnectCallbacks.push(cb);\n }\n\n /** True after start() finishes recovery. False during startup or after stop(). */\n isReady(): boolean {\n return this.ready;\n }\n\n /**\n * Snapshot every project's SQLite DB and WAL into a fresh directory under\n * `<runtimescope-root>/snapshots/<ISO>/`. Atomic via SQLite's `VACUUM INTO`;\n * non-blocking for ongoing event ingestion (the live DB keeps accepting\n * writes during the copy).\n *\n * Returns metadata for the admin endpoint to serialize.\n */\n createSnapshot(): SnapshotResult {\n if (!this.projectManager) {\n throw new Error('Cannot snapshot — no projectManager configured');\n }\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const root = join(this.projectManager.rootDir, 'snapshots', timestamp);\n mkdirSync(root, { recursive: true });\n\n const projects: SnapshotResult['projects'] = [];\n let totalBytes = 0;\n\n for (const projectName of this.projectManager.listProjects()) {\n const projectDir = join(root, projectName);\n mkdirSync(projectDir, { recursive: true });\n\n // SQLite: drain any pending writes first, then VACUUM INTO. Skip if\n // SQLite isn't available for this build.\n let sqliteBytes = 0;\n let eventCount = 0;\n const sqliteStore = this.sqliteStores.get(projectName);\n if (sqliteStore) {\n const sqlitePath = join(projectDir, 'events.db');\n try {\n sqliteBytes = sqliteStore.snapshotTo(sqlitePath);\n eventCount = sqliteStore.getEventCount({ project: projectName });\n } catch (err) {\n console.error(\n `[RuntimeScope] Snapshot of \"${projectName}\" SQLite failed:`,\n (err as Error).message,\n );\n }\n }\n\n // WAL: copy active + sealed files alongside the SQLite copy. Even after\n // a clean drain there can be in-flight events that would otherwise be\n // missing from a snapshot taken between WAL appends and SQLite flushes.\n let walBytes = 0;\n const wal = this.wals.get(projectName);\n if (wal) {\n try {\n walBytes = wal.snapshotTo(join(projectDir, 'wal'));\n } catch (err) {\n console.error(\n `[RuntimeScope] Snapshot of \"${projectName}\" WAL failed:`,\n (err as Error).message,\n );\n }\n }\n\n projects.push({ name: projectName, sqliteBytes, walBytes, eventCount });\n totalBytes += sqliteBytes + walBytes;\n }\n\n const manifest = {\n timestamp,\n createdAt: Date.now(),\n collectorVersion: process.env.npm_package_version ?? '0.0.0',\n projects,\n totalBytes,\n };\n writeFileSync(join(root, 'manifest.json'), JSON.stringify(manifest, null, 2));\n\n return { path: root, timestamp, projects, totalBytes };\n }\n\n async start(options: CollectorServerOptions = {}): Promise<void> {\n const port = options.port ?? 6767;\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 // Recovery before binding the port: any leftover WAL files from a prior\n // crash are replayed into SqliteStore, and the in-memory ring buffer is\n // warmed with the most recent events per project. New WS connections only\n // start arriving after this completes, so there's no race with incoming\n // events overlapping the warm-up.\n try {\n this.runStartupRecovery();\n } catch (err) {\n console.error('[RuntimeScope] Startup recovery failed (non-fatal):', (err as Error).message);\n }\n this.ready = true;\n\n return this.tryStart(port, host, maxRetries, retryDelayMs, tls);\n }\n\n /**\n * On collector startup, for each known project:\n * 1. Replay any sealed/active WAL files into SqliteStore (mirror of the\n * lazy recovery in `ensureWal`, but proactive — handles the case where\n * a crashed project never reconnects).\n * 2. Warm the in-memory ring buffer with recent events from SqliteStore so\n * MCP tools see history immediately, not just events from the next\n * session that connects.\n *\n * Runs synchronously — better-sqlite3 is sync — so callers can `await\n * collector.start()` and trust the buffer is hot when it returns.\n */\n private runStartupRecovery(): void {\n if (!this.projectManager) return;\n const projects = this.projectManager.listProjects();\n if (projects.length === 0) return;\n\n let walReplayed = 0;\n let warmed = 0;\n\n for (const project of projects) {\n // 1. WAL replay (idempotent — duplicates dropped by event_id PK).\n const dir = this.walDirFor(project);\n if (dir) {\n const files = Wal.listRecoveryFiles(dir);\n if (files.length > 0) {\n const sqliteStore = this.ensureSqliteStore(project);\n if (sqliteStore) {\n for (const file of files) {\n const events = Wal.readFile(file);\n for (const ev of events) {\n try { sqliteStore.addEvent(ev, project); } catch { /* ignore */ }\n }\n walReplayed += events.length;\n }\n sqliteStore.flush();\n for (const file of files) Wal.deleteSealed(file);\n }\n }\n }\n\n // 2. Ring-buffer warm. Cap per-project so a project with millions of\n // historical events doesn't monopolize the (shared) ring; let the\n // buffer's natural eviction handle distribution across projects.\n const sqliteStore = this.ensureSqliteStore(project);\n if (sqliteStore) {\n const before = this.store.eventCount;\n this.store.warmFromSqlite(sqliteStore, project, 1000);\n warmed += this.store.eventCount - before;\n }\n }\n\n if (walReplayed > 0 || warmed > 0) {\n console.error(\n `[RuntimeScope] Recovery: ${walReplayed} WAL events replayed, ${warmed} events warmed into ring buffer.`,\n );\n }\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 this.setupPersistentErrorHandler(wss);\n this.startHeartbeat(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 this.setupPersistentErrorHandler(wss);\n this.startHeartbeat(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 const nextPort = port + 1;\n console.error(\n `[RuntimeScope] Port ${port} in use, trying ${nextPort}...`\n );\n this.tryStart(nextPort, host, retriesLeft - 1, retryDelayMs, tls)\n .then(resolve)\n .catch(reject);\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 if (!isSqliteAvailable()) 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 walDirFor(projectName: string): string | null {\n if (!this.projectManager) return null;\n try {\n this.projectManager.ensureProjectDir(projectName);\n const dbPath = this.projectManager.getProjectDbPath(projectName);\n // Co-locate with the SQLite DB so backups that copy the project dir\n // capture both files. `getProjectDbPath` returns the db file path; the\n // WAL lives in a sibling directory.\n return join(dirname(dbPath), 'wal');\n } catch {\n return null;\n }\n }\n\n /**\n * Open (or return) the WAL for a project. Every event ingested for this\n * project is first appended + fsync'd here before being pushed to the ring\n * buffer and SqliteStore, so a crash between receipt and SqliteStore flush\n * doesn't lose acknowledged events.\n */\n private ensureWal(projectName: string): Wal | null {\n if (!this.projectManager) return null;\n let wal = this.wals.get(projectName);\n if (wal) return wal;\n\n const dir = this.walDirFor(projectName);\n if (!dir) return null;\n try {\n // Recover any files left behind by a prior crash before we open a fresh\n // active WAL — otherwise those events would be stuck forever.\n this.recoverWalForProject(projectName, dir);\n wal = new Wal({ dir });\n this.wals.set(projectName, wal);\n return wal;\n } catch (err) {\n console.error(\n `[RuntimeScope] Failed to open WAL for \"${projectName}\":`,\n (err as Error).message,\n );\n return null;\n }\n }\n\n /**\n * Replay any sealed or non-empty active WAL files into SqliteStore, then\n * delete them. Called lazily the first time a project's WAL is opened — if\n * the prior collector crashed mid-batch, those events survive in the WAL\n * and we'd otherwise leave them stranded on disk forever.\n */\n private recoverWalForProject(projectName: string, dir: string): void {\n const files = Wal.listRecoveryFiles(dir);\n if (files.length === 0) return;\n\n const sqliteStore = this.ensureSqliteStore(projectName);\n let replayed = 0;\n for (const file of files) {\n const events = Wal.readFile(file);\n if (events.length === 0) {\n // Empty file — safe to drop.\n Wal.deleteSealed(file);\n continue;\n }\n if (sqliteStore) {\n for (const ev of events) {\n try { sqliteStore.addEvent(ev, projectName); } catch { /* non-fatal */ }\n }\n replayed += events.length;\n }\n // Don't delete the file until SqliteStore has actually committed.\n }\n if (sqliteStore && replayed > 0) {\n sqliteStore.flush();\n // SqliteStore.flush is synchronous — after this returns events are in\n // SQLite. Now safe to drop the WAL files.\n for (const file of files) Wal.deleteSealed(file);\n console.error(\n `[RuntimeScope] WAL recovery: replayed ${replayed} events for \"${projectName}\"`,\n );\n }\n }\n\n /**\n * Rotate the project's WAL, flush SqliteStore so the rotated file's events\n * are persisted, then delete the sealed file. Called when the active file\n * has grown past its rotate threshold.\n */\n private checkpointWal(projectName: string, wal: Wal): void {\n const sealed = wal.rotate();\n if (!sealed) return;\n const sqliteStore = this.sqliteStores.get(projectName);\n sqliteStore?.flush();\n // Grace period keeps the file around briefly in case a concurrent reader\n // is still parsing it (nobody does today, but defensive).\n setTimeout(() => Wal.deleteSealed(sealed), 5000).unref();\n }\n\n /** Catch runtime errors on the WSS so an unhandled error doesn't crash the process */\n private setupPersistentErrorHandler(wss: WebSocketServer): void {\n wss.on('error', (err) => {\n console.error('[RuntimeScope] WebSocket server runtime error:', err.message);\n });\n }\n\n /** Ping all connected clients every 15s — terminate those that don't respond */\n private startHeartbeat(wss: WebSocketServer): void {\n this.heartbeatTimer = setInterval(() => {\n for (const ws of wss.clients) {\n const ext = ws as WebSocket & { _rsAlive?: boolean };\n if (ext._rsAlive === false) {\n // Missed a heartbeat — terminate dead connection\n ws.terminate();\n continue;\n }\n ext._rsAlive = false;\n ws.ping();\n }\n }, 15_000);\n }\n\n private setupConnectionHandler(wss: WebSocketServer): void {\n wss.on('connection', (ws) => {\n // Mark alive for heartbeat — pong response resets this\n const ext = ws as WebSocket & { _rsAlive?: boolean };\n ext._rsAlive = true;\n ws.on('pong', () => { ext._rsAlive = true; });\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', (code) => {\n // 1000 / 1001 are clean closes; anything else is abnormal. The label\n // lets dashboards alert on spikes in abnormal disconnects.\n const cause = code === 1000 || code === 1001 ? 'clean' : 'abnormal';\n this.counters.wsDisconnects.inc(1, { cause });\n\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, clientInfo.projectId);\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 // Authentication — two layers:\n // 1. Workspace-scoped API keys stored in pmStore. Present when the\n // token is a `tk_xxx` generated by the workspaces API.\n // 2. Global API keys from ~/.runtimescope/config.json or the\n // RUNTIMESCOPE_AUTH_TOKEN env var (handled by AuthManager).\n //\n // We accept either. The workspace key also tells us which workspace\n // this session's project should live in, so we record that below.\n let workspaceFromKey: { id: string; slug: string } | null = null;\n if (payload.authToken && this.pmStore?.getWorkspaceByApiKey) {\n try {\n const ws = this.pmStore.getWorkspaceByApiKey(payload.authToken);\n if (ws) workspaceFromKey = { id: ws.id, slug: ws.slug };\n } catch { /* non-fatal — fall through to global auth */ }\n }\n\n if (this.authManager?.isEnabled() && !workspaceFromKey) {\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 }\n this.pendingHandshakes.delete(ws);\n\n const projectName = payload.appName;\n // Auto-generate a projectId if the SDK didn't send one (backwards compat)\n const projectId = payload.projectId\n ?? (this.projectManager ? resolveProjectId(this.projectManager, projectName, this.pmStore) : undefined);\n\n this.clients.set(ws, {\n sessionId: payload.sessionId,\n projectName,\n projectId,\n workspaceId: workspaceFromKey?.id,\n });\n\n // If we authenticated via a workspace key, auto-assign this session's\n // project (by runtimeProjectId) to that workspace. New projects land\n // in the key's workspace; existing projects are left alone unless\n // they had no workspace yet.\n if (workspaceFromKey && projectId && this.pmStore?.listProjects && this.pmStore.setProjectWorkspace) {\n try {\n const existing = this.pmStore.listProjects().find(\n (p) => p.runtimeProjectId === projectId,\n );\n if (existing && !existing.workspaceId) {\n this.pmStore.setProjectWorkspace(existing.id, workspaceFromKey.id);\n }\n } catch { /* non-fatal */ }\n }\n\n // Initialize SQLite for this project\n const sqliteStore = this.ensureSqliteStore(projectName);\n\n // Save session info to SQLite. Including projectId is required so\n // post-crash recovery can rehydrate the session→projectId map and\n // serve `/api/events/*?project_id=...` queries against warmed data.\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 projectId,\n };\n sqliteStore.saveSession(sessionInfo);\n }\n\n // Register session in EventStore so /api/projects can list it\n this.store.addEvent({\n eventId: `session-${payload.sessionId}`,\n sessionId: payload.sessionId,\n timestamp: msg.timestamp,\n eventType: 'session',\n appName: payload.appName,\n projectId,\n connectedAt: msg.timestamp,\n sdkVersion: payload.sdkVersion,\n } as RuntimeEvent);\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, projectId); } 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)) break;\n\n // Rate-limit first so dropped events don't hit the WAL.\n const accepted: RuntimeEvent[] = [];\n let rateLimited = 0;\n for (const event of payload.events) {\n if (clientInfo && !this.rateLimiter.allow(clientInfo.sessionId)) {\n // Rate limiter rejected — count remaining events in the batch as\n // dropped (we break, so the rest are dropped too).\n rateLimited = payload.events.length - accepted.length;\n break;\n }\n accepted.push(event);\n }\n if (rateLimited > 0) {\n this.counters.eventsDropped.inc(rateLimited, { reason: 'rate_limit' });\n }\n if (accepted.length === 0) break;\n\n // Durability: write to WAL and fsync once per batch BEFORE we hand the\n // events off to the in-memory store. If the process crashes after this\n // point, recovery will replay the batch into SqliteStore.\n const wal = clientInfo?.projectName ? this.ensureWal(clientInfo.projectName) : null;\n if (wal) {\n try {\n wal.append(accepted);\n wal.commit();\n } catch (err) {\n console.error('[RuntimeScope] WAL append/commit failed:', (err as Error).message);\n this.counters.eventsDropped.inc(accepted.length, { reason: 'wal_backpressure' });\n // Continue — dropping an event is worse than proceeding without the\n // durability guarantee on this one batch.\n }\n }\n\n for (const event of accepted) {\n this.store.addEvent(event);\n }\n\n // Rotate if the active file is getting big; checkpoint asynchronously.\n if (wal?.shouldRotate() && clientInfo?.projectName) {\n try {\n this.checkpointWal(clientInfo.projectName, wal);\n } catch (err) {\n console.error('[RuntimeScope] WAL checkpoint failed:', (err as Error).message);\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; projectId?: string }[] {\n const sessions: { sessionId: string; projectName: string; projectId?: string }[] = [];\n for (const [, info] of this.clients) {\n sessions.push({ sessionId: info.sessionId, projectName: info.projectName, projectId: info.projectId });\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 heartbeat\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n\n // Stop rate limiter pruning\n if (this.pruneTimer) {\n clearInterval(this.pruneTimer);\n this.pruneTimer = null;\n }\n\n // Notify connected SDKs that the server is restarting — SDK resets backoff for fast reconnect\n if (this.wss) {\n for (const client of this.wss.clients) {\n if (client.readyState === 1 /* OPEN */) {\n try {\n client.send(JSON.stringify({\n type: '__server_restart',\n timestamp: Date.now(),\n }));\n } catch { /* best-effort */ }\n }\n }\n }\n\n // Drain the OTel exporter before closing storage so any in-flight signals\n // get one last flush attempt. Failures are non-fatal — we're stopping.\n if (this.otelExporter) {\n try {\n // Synchronous-ish fire-and-forget — the close() promise may still\n // resolve after stop() returns, but the timer is already cleared.\n void this.otelExporter.close();\n } catch { /* ignore */ }\n this.otelExporter = null;\n }\n\n // Close WALs before SqliteStores — one final fsync of any in-flight bytes\n // and a clean rename of the active handle so recovery on next start is a\n // no-op rather than a forced replay of the same events.\n for (const [name, wal] of this.wals) {\n try {\n wal.close();\n } catch {\n // Non-fatal during shutdown — the next start will recover.\n console.error(`[RuntimeScope] WAL close error for \"${name}\" (non-fatal)`);\n }\n }\n this.wals.clear();\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 this.ready = false;\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 projectId?: 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: 6767,\n bufferSize: 10_000,\n httpPort: 6768,\n};\n\n/** Minimal interface for PmStore used by rebuildAppIndex (avoids circular deps). */\nexport interface PmStoreIndexSource {\n listProjects(): Array<{ runtimeApps?: string[]; runtimeProjectId?: string; path?: string }>;\n}\n\nexport class ProjectManager {\n private readonly baseDir: string;\n private appProjectIndex: Map<string, string> = new Map();\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 // --- Project ID helpers ---\n\n /** Look up the stored projectId for an appName. Returns null if none set. */\n getProjectIdForApp(appName: string): string | null {\n const config = this.getProjectConfig(appName);\n return config?.projectId ?? null;\n }\n\n /** Persist a projectId for an appName in its project config. */\n setProjectIdForApp(appName: string, projectId: string): void {\n this.ensureProjectDir(appName);\n const config = this.getProjectConfig(appName);\n if (config) {\n config.projectId = projectId;\n this.saveProjectConfig(appName, config);\n }\n }\n\n /** Resolve a projectId to an appName by scanning all project configs. Returns null if not found. */\n getAppForProjectId(projectId: string): string | null {\n for (const name of this.listProjects()) {\n const config = this.getProjectConfig(name);\n if (config?.projectId === projectId) return name;\n }\n return null;\n }\n\n // --- Reverse index: appName → projectId ---\n\n /**\n * Build reverse index: appName -> projectId.\n * Scans all project configs, PM projects with runtimeApps + runtimeProjectId,\n * and project-level .runtimescope/config.json files from PM project paths.\n */\n rebuildAppIndex(pmStore?: PmStoreIndexSource): void {\n this.appProjectIndex.clear();\n\n // Source 1: ~/.runtimescope/projects/*/config.json\n for (const name of this.listProjects()) {\n const config = this.getProjectConfig(name);\n if (config?.projectId) {\n this.appProjectIndex.set(name.toLowerCase(), config.projectId);\n }\n }\n\n // Source 2: PM projects with runtimeApps + runtimeProjectId\n if (pmStore) {\n for (const p of pmStore.listProjects()) {\n if (p.runtimeProjectId && p.runtimeApps) {\n for (const app of p.runtimeApps) {\n this.appProjectIndex.set(app.toLowerCase(), p.runtimeProjectId);\n }\n }\n }\n }\n\n // Source 3: Project-level .runtimescope/config.json files\n if (pmStore) {\n for (const p of pmStore.listProjects()) {\n if (p.path) {\n try {\n const configPath = join(p.path, '.runtimescope', 'config.json');\n if (existsSync(configPath)) {\n const content = readFileSync(configPath, 'utf-8');\n const config = JSON.parse(content);\n if (config.projectId) {\n // Index the top-level appName\n if (config.appName) {\n this.appProjectIndex.set(config.appName.toLowerCase(), config.projectId);\n }\n // Index all SDK appNames\n if (Array.isArray(config.sdks)) {\n for (const sdk of config.sdks) {\n if (sdk.appName) {\n this.appProjectIndex.set(sdk.appName.toLowerCase(), config.projectId);\n }\n }\n }\n }\n }\n } catch { /* non-fatal */ }\n }\n }\n }\n }\n\n /** O(1) lookup from the cached index. */\n resolveAppProjectId(appName: string): string | null {\n return this.appProjectIndex.get(appName.toLowerCase()) ?? null;\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 { readFileSync, existsSync, writeFileSync, mkdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { generateProjectId } from './project-id.js';\nimport type { ProjectManager } from './project-manager.js';\n\n// ============================================================\n// .runtimescope/config.json — project-level config\n//\n// Lives at <project-root>/.runtimescope/config.json\n// Committable to git (no secrets). Secrets go in ~/.runtimescope/.\n// ============================================================\n\nexport interface RuntimeScopeProjectConfig {\n /** Stable project identifier (proj_xxx). Groups all SDKs for this project. */\n projectId: string;\n\n /** DSN connection string: runtimescope://<projectId>@<host>:<port>/<appName> */\n dsn?: string;\n\n /** Human-readable project name. */\n appName: string;\n\n /** Optional description for dashboard/reports. */\n description?: string;\n\n /** SDKs installed in this project. Each entry is a target (browser, server, worker, etc.). */\n sdks: SdkEntry[];\n\n /** Default capture settings. Used by /setup to generate snippets and by server-sdk for auto-config. */\n capture: CaptureConfig;\n\n /** Project phase for CapEx tracking. */\n phase?: 'preliminary' | 'application_development' | 'post_implementation';\n\n /** Category for grouping in dashboard (e.g., \"work\", \"personal\", \"internal\"). */\n category?: string;\n\n /** Infrastructure references (no tokens — those live in ~/.runtimescope/config.json). */\n infra?: {\n vercel?: { projectId?: string };\n cloudflare?: { workerName?: string; accountId?: string };\n railway?: { projectId?: string };\n };\n}\n\nexport interface SdkEntry {\n /** Which SDK is installed. */\n type: 'browser' | 'server' | 'workers';\n\n /** Where the SDK init code lives (e.g., \"src/main.tsx\", \"src/server.ts\"). */\n entryFile?: string;\n\n /** Framework detected during setup. */\n framework?: string;\n\n /** The appName used in this SDK's init (can differ from project appName for multi-SDK). */\n appName?: string;\n}\n\nexport interface CaptureConfig {\n network?: boolean;\n console?: boolean;\n xhr?: boolean;\n body?: boolean;\n performance?: boolean;\n renders?: boolean;\n navigation?: boolean;\n clicks?: boolean;\n /** Server SDK specific */\n http?: boolean;\n errors?: boolean;\n stackTraces?: boolean;\n}\n\nconst DEFAULT_CAPTURE: CaptureConfig = {\n network: true,\n console: true,\n xhr: true,\n body: false,\n performance: true,\n renders: true,\n navigation: true,\n clicks: false,\n http: false,\n errors: true,\n stackTraces: false,\n};\n\n// ============================================================\n// Read / Write / Scaffold\n// ============================================================\n\n/** Read .runtimescope/config.json from a project directory. Returns null if not found. */\nexport function readProjectConfig(projectDir: string): RuntimeScopeProjectConfig | null {\n const configPath = join(projectDir, '.runtimescope', 'config.json');\n if (!existsSync(configPath)) return null;\n try {\n const content = readFileSync(configPath, 'utf-8');\n return JSON.parse(content) as RuntimeScopeProjectConfig;\n } catch {\n return null;\n }\n}\n\n/** Write .runtimescope/config.json to a project directory. Creates the directory if needed. */\nexport function writeProjectConfig(projectDir: string, config: RuntimeScopeProjectConfig): void {\n const dir = join(projectDir, '.runtimescope');\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n writeFileSync(join(dir, 'config.json'), JSON.stringify(config, null, 2) + '\\n', 'utf-8');\n}\n\n/**\n * Scaffold the .runtimescope/ directory for a project.\n * Creates config.json with a generated projectId and default capture settings.\n * If config already exists, returns the existing one (idempotent).\n */\nexport function scaffoldProjectConfig(\n projectDir: string,\n opts: {\n appName: string;\n framework?: string;\n sdkType?: SdkEntry['type'];\n description?: string;\n category?: string;\n },\n): RuntimeScopeProjectConfig {\n const existing = readProjectConfig(projectDir);\n if (existing) {\n // If a new SDK type is being added, merge it in\n if (opts.sdkType) {\n const alreadyHas = existing.sdks.some((s) => s.type === opts.sdkType);\n if (!alreadyHas) {\n existing.sdks.push({\n type: opts.sdkType,\n framework: opts.framework,\n appName: opts.appName !== existing.appName ? opts.appName : undefined,\n });\n writeProjectConfig(projectDir, existing);\n }\n }\n return existing;\n }\n\n const projectId = generateProjectId();\n const httpPort = process.env.RUNTIMESCOPE_HTTP_PORT ?? '6768';\n const dsn = `runtimescope://${projectId}@localhost:${httpPort}/${opts.appName}`;\n\n const config: RuntimeScopeProjectConfig = {\n projectId,\n dsn,\n appName: opts.appName,\n description: opts.description,\n sdks: opts.sdkType\n ? [{ type: opts.sdkType, framework: opts.framework }]\n : [],\n capture: { ...DEFAULT_CAPTURE },\n category: opts.category,\n };\n\n writeProjectConfig(projectDir, config);\n\n // Also create .gitignore for the directory (keep config, ignore local state)\n const gitignorePath = join(projectDir, '.runtimescope', '.gitignore');\n if (!existsSync(gitignorePath)) {\n writeFileSync(gitignorePath, '# Keep config.json committed, ignore local state\\n*.log\\n*.db\\n.env\\n', 'utf-8');\n }\n\n return config;\n}\n\n/**\n * Resolve all appNames associated with a project config.\n * Returns the main appName plus any SDK-specific appNames.\n */\nexport function resolveProjectAppNames(config: RuntimeScopeProjectConfig): string[] {\n const names = new Set<string>([config.appName]);\n for (const sdk of config.sdks) {\n if (sdk.appName) names.add(sdk.appName);\n }\n return Array.from(names);\n}\n\n// ============================================================\n// Project ID Migration\n// ============================================================\n\ninterface MigratablePmStore {\n listProjects(): Array<{\n id: string;\n name: string;\n path?: string;\n runtimeApps?: string[];\n runtimeProjectId?: string;\n }>;\n updateProject(id: string, updates: Record<string, unknown>): void;\n}\n\ninterface MigrationResult {\n unified: number;\n skipped: number;\n details: string[];\n}\n\n/**\n * Scan PM projects with multiple runtimeApps and ensure all their\n * appNames share the same canonical projectId (from .runtimescope/config.json).\n */\nexport function migrateProjectIds(\n projectManager: ProjectManager,\n pmStore?: MigratablePmStore | null,\n): MigrationResult {\n const result: MigrationResult = { unified: 0, skipped: 0, details: [] };\n if (!pmStore) return result;\n\n for (const project of pmStore.listProjects()) {\n // Only migrate multi-app projects\n if (!project.runtimeApps || project.runtimeApps.length < 2) continue;\n if (!project.path) { result.skipped++; continue; }\n\n // Read the project-level .runtimescope/config.json\n let canonicalId: string | null = null;\n try {\n const configPath = join(project.path, '.runtimescope', 'config.json');\n if (existsSync(configPath)) {\n const config = JSON.parse(readFileSync(configPath, 'utf-8'));\n if (config.projectId) canonicalId = config.projectId;\n }\n } catch { /* non-fatal */ }\n\n // If no project-level config, try the PM project's runtimeProjectId\n if (!canonicalId && project.runtimeProjectId) {\n canonicalId = project.runtimeProjectId;\n }\n\n // If still no canonical ID, use the first appName's existing projectId\n if (!canonicalId) {\n for (const appName of project.runtimeApps) {\n const existing = projectManager.getProjectIdForApp(appName);\n if (existing) { canonicalId = existing; break; }\n }\n }\n\n if (!canonicalId) { result.skipped++; continue; }\n\n // Unify: set ALL appNames to the canonical projectId\n for (const appName of project.runtimeApps) {\n const current = projectManager.getProjectIdForApp(appName);\n if (current !== canonicalId) {\n projectManager.setProjectIdForApp(appName, canonicalId);\n result.details.push(`Unified ${appName}: ${current ?? 'none'} → ${canonicalId}`);\n result.unified++;\n }\n }\n\n // Update PM project's runtimeProjectId if different\n if (project.runtimeProjectId !== canonicalId) {\n pmStore.updateProject(project.id, { runtimeProjectId: canonicalId });\n result.details.push(`PM project ${project.name}: runtimeProjectId → ${canonicalId}`);\n }\n }\n\n return result;\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 { execFileSync, execSync } from 'node:child_process';\nimport { readlinkSync, readdirSync } from 'node:fs';\nimport { join } from 'node:path';\n\n// ============================================================\n// Cross-platform process utilities (macOS, Linux, Windows)\n// Replaces macOS-only lsof calls throughout the codebase.\n//\n// All inputs to shell commands are numeric PIDs/ports or\n// internal paths — never user-supplied strings.\n// ============================================================\n\nconst IS_WIN = process.platform === 'win32';\nconst IS_LINUX = process.platform === 'linux';\n\n/** Run execFileSync, return trimmed stdout or null on failure. */\nfunction runFile(cmd: string, args: string[], timeoutMs = 5000): string | null {\n try {\n return execFileSync(cmd, args, { encoding: 'utf-8', timeout: timeoutMs }).trim();\n } catch {\n return null;\n }\n}\n\n/**\n * Run a shell command (needed when piping). All inputs must be\n * trusted internal values (PIDs, ports, paths).\n */\nfunction runShell(cmd: string, timeoutMs = 5000): string | null {\n try {\n return execSync(cmd, { encoding: 'utf-8', timeout: timeoutMs }).trim();\n } catch {\n return null;\n }\n}\n\n// ------------------------------------------------------------------\n// getPidsOnPort — find which PIDs are listening on a given port\n// ------------------------------------------------------------------\n\nfunction getPidsOnPort_unix(port: number): number[] {\n // Try lsof first (macOS always, Linux usually)\n const lsof = runFile('lsof', ['-ti', `:${port}`], 5000);\n if (lsof) {\n return lsof.split('\\n').map(Number).filter((n) => n > 0);\n }\n // Fallback: ss (Linux, typically pre-installed)\n if (IS_LINUX) {\n // ss requires piping through grep, so use shell\n const ss = runShell(`ss -tlnp 2>/dev/null | grep ':${port} '`);\n if (ss) {\n const pids: number[] = [];\n for (const match of ss.matchAll(/pid=(\\d+)/g)) {\n pids.push(parseInt(match[1], 10));\n }\n return [...new Set(pids)];\n }\n }\n return [];\n}\n\nfunction getPidsOnPort_win(port: number): number[] {\n // netstat + findstr requires shell piping\n const out = runShell(`netstat -ano | findstr :${port} | findstr LISTENING`);\n if (!out) return [];\n const pids: number[] = [];\n for (const line of out.split('\\n')) {\n const parts = line.trim().split(/\\s+/);\n const pid = parseInt(parts[parts.length - 1], 10);\n if (pid > 0) pids.push(pid);\n }\n return [...new Set(pids)];\n}\n\n/** Find PIDs listening on `port`. Cross-platform. */\nexport function getPidsOnPort(port: number): number[] {\n return IS_WIN ? getPidsOnPort_win(port) : getPidsOnPort_unix(port);\n}\n\n// ------------------------------------------------------------------\n// getListenPorts — find which ports a PID is listening on\n// ------------------------------------------------------------------\n\nfunction getListenPorts_unix(pid: number): number[] {\n // lsof + grep requires piping\n const lsof = runShell(`lsof -nP -p ${pid} 2>/dev/null | grep LISTEN`, 3000);\n if (lsof) {\n const ports: number[] = [];\n for (const line of lsof.split('\\n')) {\n const match = line.match(/:(\\d+)\\s+\\(LISTEN\\)/);\n if (match) ports.push(parseInt(match[1], 10));\n }\n return [...new Set(ports)];\n }\n // Fallback: ss (Linux)\n if (IS_LINUX) {\n const ss = runShell(`ss -tlnp 2>/dev/null | grep 'pid=${pid},'`, 3000);\n if (ss) {\n const ports: number[] = [];\n for (const match of ss.matchAll(/:(\\d+)\\s/g)) {\n ports.push(parseInt(match[1], 10));\n }\n return [...new Set(ports)];\n }\n }\n return [];\n}\n\nfunction getListenPorts_win(pid: number): number[] {\n const out = runShell(`netstat -ano | findstr ${pid} | findstr LISTENING`);\n if (!out) return [];\n const ports: number[] = [];\n for (const line of out.split('\\n')) {\n // Format: TCP 0.0.0.0:3000 0.0.0.0:0 LISTENING 12345\n const match = line.match(/:(\\d+)\\s/);\n if (match) {\n const port = parseInt(match[1], 10);\n const linePid = parseInt(line.trim().split(/\\s+/).pop()!, 10);\n if (linePid === pid) ports.push(port);\n }\n }\n return [...new Set(ports)];\n}\n\n/** Find which ports a PID is listening on. Cross-platform. */\nexport function getListenPorts(pid: number): number[] {\n return IS_WIN ? getListenPorts_win(pid) : getListenPorts_unix(pid);\n}\n\n// ------------------------------------------------------------------\n// getProcessCwd — get the working directory of a process\n// ------------------------------------------------------------------\n\nfunction getProcessCwd_mac(pid: number): string | undefined {\n const out = runShell(`lsof -p ${pid} 2>/dev/null | grep cwd`, 3000);\n if (!out) return undefined;\n const match = out.match(/cwd\\s+\\w+\\s+\\w+\\s+\\d+\\w?\\s+\\d+\\s+\\d+\\s+\\d+\\s+(.+)/);\n return match?.[1]?.trim();\n}\n\nfunction getProcessCwd_linux(pid: number): string | undefined {\n try {\n return readlinkSync(`/proc/${pid}/cwd`);\n } catch {\n // Fallback to lsof on Linux too (works if installed)\n return getProcessCwd_mac(pid);\n }\n}\n\n/** Get the working directory of a process. Returns undefined on Windows or failure. */\nexport function getProcessCwd(pid: number): string | undefined {\n if (IS_WIN) return undefined; // No reliable cross-platform equivalent\n if (IS_LINUX) return getProcessCwd_linux(pid);\n return getProcessCwd_mac(pid); // macOS\n}\n\n// ------------------------------------------------------------------\n// findPidsInDirectory — find PIDs with open files in a directory\n// ------------------------------------------------------------------\n\nfunction findPidsInDir_lsof(dir: string): number[] {\n // lsof + head requires piping\n const out = runShell(`lsof -t +D \"${dir}\" 2>/dev/null | head -20`);\n if (!out) return [];\n return out.split('\\n').map(Number).filter((n) => n > 0);\n}\n\nfunction findPidsInDir_linux(dir: string): number[] {\n // Try lsof first (most reliable)\n const lsofResult = findPidsInDir_lsof(dir);\n if (lsofResult.length > 0) return lsofResult;\n\n // Fallback: scan /proc/*/cwd for processes rooted in this directory\n try {\n const pids: number[] = [];\n for (const entry of readdirSync('/proc')) {\n const pid = parseInt(entry, 10);\n if (isNaN(pid) || pid <= 1) continue;\n try {\n const cwd = readlinkSync(join('/proc', entry, 'cwd'));\n if (cwd.startsWith(dir)) pids.push(pid);\n } catch {\n // Permission denied or process exited — skip\n }\n }\n return pids;\n } catch {\n return [];\n }\n}\n\n/** Find PIDs with open files/cwd in a directory. Best-effort on Windows (returns []). */\nexport function findPidsInDirectory(dir: string): number[] {\n if (IS_WIN) return []; // No reliable equivalent\n if (IS_LINUX) return findPidsInDir_linux(dir);\n return findPidsInDir_lsof(dir); // macOS\n}\n\n// ------------------------------------------------------------------\n// parseProcessList — cross-platform process listing\n// ------------------------------------------------------------------\n\nexport interface ProcessInfo {\n pid: number;\n cpu: number;\n mem: number;\n command: string;\n}\n\nfunction parseProcessList_unix(): ProcessInfo[] {\n const output = runFile('ps', ['aux']);\n if (!output) return [];\n const lines = output.split('\\n').slice(1); // Skip header\n const results: ProcessInfo[] = [];\n for (const line of lines) {\n const parts = line.trim().split(/\\s+/);\n if (parts.length < 11) continue;\n const pid = parseInt(parts[1], 10);\n const cpu = parseFloat(parts[2]);\n const mem = parseFloat(parts[3]);\n const command = parts.slice(10).join(' ');\n if (isNaN(pid)) continue;\n results.push({ pid, cpu, mem, command });\n }\n return results;\n}\n\nfunction parseProcessList_win(): ProcessInfo[] {\n const output = runFile('tasklist', ['/FO', 'CSV', '/NH'], 10000);\n if (!output) return [];\n const results: ProcessInfo[] = [];\n for (const line of output.split('\\n')) {\n // Format: \"Image Name\",\"PID\",\"Session Name\",\"Session#\",\"Mem Usage\"\n const parts = line.match(/\"([^\"]*)\"/g);\n if (!parts || parts.length < 5) continue;\n const pid = parseInt(parts[1].replace(/\"/g, ''), 10);\n const command = parts[0].replace(/\"/g, '');\n const memStr = parts[4].replace(/\"/g, '').replace(/[, K]/gi, '');\n const mem = parseInt(memStr, 10) / 1024; // KB → MB rough\n if (isNaN(pid)) continue;\n results.push({ pid, cpu: 0, mem, command }); // CPU not available from tasklist\n }\n return results;\n}\n\n/** List all running processes. Cross-platform. */\nexport function parseProcessList(): ProcessInfo[] {\n return IS_WIN ? parseProcessList_win() : parseProcessList_unix();\n}\n\n// ------------------------------------------------------------------\n// getProcessMemoryMB — get RSS of a process in MB\n// ------------------------------------------------------------------\n\nfunction getProcessMemoryMB_unix(pid: number): number {\n const out = runFile('ps', ['-o', 'rss=', '-p', String(pid)], 2000);\n if (!out) return 0;\n const rss = parseInt(out, 10);\n return isNaN(rss) ? 0 : rss / 1024;\n}\n\nfunction getProcessMemoryMB_win(pid: number): number {\n const out = runFile('tasklist', ['/FI', `PID eq ${pid}`, '/FO', 'CSV', '/NH'], 3000);\n if (!out) return 0;\n const parts = out.match(/\"([^\"]*)\"/g);\n if (!parts || parts.length < 5) return 0;\n const memStr = parts[4].replace(/\"/g, '').replace(/[, K]/gi, '');\n const kb = parseInt(memStr, 10);\n return isNaN(kb) ? 0 : kb / 1024;\n}\n\n/** Get RSS memory in MB for a given PID. Cross-platform. */\nexport function getProcessMemoryMB(pid: number): number {\n return IS_WIN ? getProcessMemoryMB_win(pid) : getProcessMemoryMB_unix(pid);\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 type { ProjectManager } from './project-manager.js';\nimport { getOrCreateProjectId, resolveProjectId } from './project-id.js';\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\n// Collector version — loaded from the package.json sibling to dist/.\n// Used by /api/health so clients can detect out-of-date collectors.\nconst COLLECTOR_VERSION = (() => {\n try {\n const here = dirname(fileURLToPath(import.meta.url));\n // dist/http-server.js lives in dist/; package.json is one level up\n const pkgJson = readFileSync(resolve(here, '..', 'package.json'), 'utf-8');\n const pkg = JSON.parse(pkgJson) as { version?: string };\n return pkg.version ?? 'unknown';\n } catch {\n return 'unknown';\n }\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\n/** Request augmented with the resolved caller — populated by the auth gate in handleRequest. */\nexport interface AuthedRequest extends IncomingMessage {\n _rsCaller?: { isAdmin: boolean; workspaceId: string | null };\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 = 6768;\n private startedAt = Date.now();\n private connectedSessionsGetter: (() => { sessionId: string; projectName: string }[]) | null = null;\n private pmStore: PmStore | null = null;\n private projectManager: ProjectManager | null = null;\n\n private isReadyGetter: (() => boolean) | null = null;\n private snapshotFn: (() => { path: string; timestamp: string; projects: { name: string; sqliteBytes: number; walBytes: number; eventCount: number }[]; totalBytes: number }) | null = null;\n private lastSnapshotAt = 0;\n private renderMetricsFn: (() => string) | null = null;\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 getConnectedSessions?: () => { sessionId: string; projectName: string }[];\n projectManager?: ProjectManager;\n /**\n * Returns true once the collector has finished startup recovery\n * (WAL replay + ring-buffer warm). Drives the `/readyz` probe — load\n * balancers and attach-mode detection should wait for ready before\n * routing traffic.\n */\n isReady?: () => boolean;\n /**\n * Called by `POST /api/v1/admin/snapshot`. Implementations should copy\n * every project's SQLite + WAL into a fresh directory and return a\n * manifest. Admin-only on the route side; rate-limited to prevent\n * snapshot loops.\n */\n createSnapshot?: () => {\n path: string;\n timestamp: string;\n projects: { name: string; sqliteBytes: number; walBytes: number; eventCount: number }[];\n totalBytes: number;\n };\n /**\n * Returns the Prometheus exposition body for `GET /metrics`. Public\n * (no auth) so a Prometheus scraper can hit it without configuring a\n * bearer token — that's the standard pattern. Opt out via the\n * `RUNTIMESCOPE_DISABLE_METRICS=1` env var.\n */\n renderMetrics?: () => string;\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.connectedSessionsGetter = options?.getConnectedSessions ?? null;\n this.pmStore = options?.pmStore ?? null;\n this.projectManager = options?.projectManager ?? null;\n this.isReadyGetter = options?.isReady ?? null;\n this.snapshotFn = options?.createSnapshot ?? null;\n this.renderMetricsFn = options?.renderMetrics ?? 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 resolveCaller: (req) =>\n (req as AuthedRequest)._rsCaller ?? { isAdmin: !this.authManager?.isEnabled(), workspaceId: null },\n }, (msg) => this.broadcastDevServer(msg));\n }\n }\n\n private registerRoutes(): void {\n // Liveness probe — process is alive and the HTTP layer is responding.\n // Always unauthenticated for load balancers and orchestrators.\n this.routes.set('GET /api/health', (_req, res) => {\n this.json(res, {\n status: 'ok',\n version: COLLECTOR_VERSION,\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 // Readiness probe — startup recovery (WAL replay + ring-buffer warm) is\n // complete and the collector is ready to serve queries. Distinct from\n // `/api/health` so orchestrators can avoid routing to a process that's\n // listening but still warming, and so MCP attach-mode detection only\n // returns \"yes, attach\" when a peer is fully serviceable.\n this.routes.set('GET /readyz', (_req, res) => {\n const ready = this.isReadyGetter ? this.isReadyGetter() : true;\n if (ready) {\n this.json(res, { status: 'ready', timestamp: Date.now() });\n } else {\n this.json(res, { status: 'starting', timestamp: Date.now() }, 503);\n }\n });\n\n // Prometheus metrics — text exposition format. Public (no auth) so a\n // standard Prometheus scrape config works out of the box. Opt out per\n // deployment via `RUNTIMESCOPE_DISABLE_METRICS=1` (e.g. on a sensitive\n // hosted collector that wants metrics behind a separate sidecar).\n this.routes.set('GET /metrics', (_req, res) => {\n if (process.env.RUNTIMESCOPE_DISABLE_METRICS === '1') {\n res.writeHead(404, { 'Content-Type': 'text/plain' });\n res.end('Metrics disabled (RUNTIMESCOPE_DISABLE_METRICS=1).\\n');\n return;\n }\n const body = this.renderMetricsFn ? this.renderMetricsFn() : '';\n res.writeHead(200, { 'Content-Type': 'text/plain; version=0.0.4; charset=utf-8' });\n res.end(body);\n });\n\n // Snapshot endpoint: atomic SQLite VACUUM INTO + WAL copy for every\n // project, written into `<rootDir>/snapshots/<ISO>/`. Admin only — the\n // raw SQLite files include every event we've ever captured. Rate-limited\n // to one call per minute so a runaway loop can't fill the disk.\n this.routes.set('POST /api/v1/admin/snapshot', (req, res) => {\n if (!this.snapshotFn) {\n this.json(res, { error: 'Snapshot is not available on this collector' }, 501);\n return;\n }\n const caller = (req as AuthedRequest)._rsCaller ?? {\n isAdmin: !this.authManager?.isEnabled(),\n workspaceId: null,\n };\n if (!caller.isAdmin) {\n this.json(res, { error: 'Forbidden: snapshot requires admin' }, 403);\n return;\n }\n\n const now = Date.now();\n const sinceLast = now - this.lastSnapshotAt;\n const COOLDOWN_MS = 60_000;\n if (sinceLast < COOLDOWN_MS) {\n const retryAfter = Math.ceil((COOLDOWN_MS - sinceLast) / 1000);\n res.setHeader('Retry-After', String(retryAfter));\n this.json(\n res,\n { error: 'Snapshot rate-limited', retryAfterSeconds: retryAfter },\n 429,\n );\n return;\n }\n this.lastSnapshotAt = now;\n\n try {\n const result = this.snapshotFn();\n this.json(res, result, 201);\n } catch (err) {\n this.json(res, { error: (err as Error).message }, 500);\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, merged with live WS clients)\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; projectId?: string }>();\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 if (!existing.projectId && s.projectId) existing.projectId = s.projectId;\n } else {\n projectMap.set(s.appName, {\n appName: s.appName,\n sessions: [s.sessionId],\n isConnected: s.isConnected,\n eventCount: s.eventCount,\n projectId: s.projectId,\n });\n }\n }\n\n // Merge live WebSocket clients (safety net — ensures connected SDKs always appear)\n if (this.connectedSessionsGetter) {\n for (const cs of this.connectedSessionsGetter()) {\n const existing = projectMap.get(cs.projectName);\n if (existing) {\n if (!existing.sessions.includes(cs.sessionId)) {\n existing.sessions.push(cs.sessionId);\n }\n existing.isConnected = true;\n } else {\n projectMap.set(cs.projectName, {\n appName: cs.projectName,\n sessions: [cs.sessionId],\n isConnected: true,\n eventCount: 0,\n });\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 projectId = this.authorizeProjectIdParam(req, res, params);\n if (projectId === false) return;\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 projectId,\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 projectId = this.authorizeProjectIdParam(req, res, params);\n if (projectId === false) return;\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 projectId,\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 projectId = this.authorizeProjectIdParam(req, res, params);\n if (projectId === false) return;\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 projectId,\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 projectId = this.authorizeProjectIdParam(req, res, params);\n if (projectId === false) return;\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 projectId,\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 projectId = this.authorizeProjectIdParam(req, res, params);\n if (projectId === false) return;\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 projectId,\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 projectId = this.authorizeProjectIdParam(req, res, params);\n if (projectId === false) return;\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 projectId,\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 projectId = this.authorizeProjectIdParam(req, res, params);\n if (projectId === false) return;\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 projectId,\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 projectId = this.authorizeProjectIdParam(req, res, params);\n if (projectId === false) return;\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 projectId,\n });\n this.json(res, { data: events, count: events.length });\n });\n\n // UI interaction events (clicks, breadcrumbs)\n this.routes.set('GET /api/events/ui', (req, res, params) => {\n const projectId = this.authorizeProjectIdParam(req, res, params);\n if (projectId === false) return;\n const action = params.get('action') as 'click' | 'breadcrumb' | undefined;\n const events = this.store.getUIInteractions({\n action: action ?? undefined,\n sinceSeconds: numParam(params, 'since_seconds'),\n sessionId: params.get('session_id') ?? undefined,\n projectId,\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 projectId?: string;\n events?: unknown[];\n };\n\n // Auto-generate projectId if SDK didn't send one (backwards compat)\n const projectId = typeof payload.projectId === 'string'\n ? payload.projectId\n : (payload.appName && this.projectManager\n ? resolveProjectId(this.projectManager, payload.appName, this.pmStore)\n : undefined);\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 projectId,\n connectedAt: Date.now(),\n sdkVersion: payload.sdkVersion ?? 'http',\n } as RuntimeEvent);\n\n // Auto-link SDK appName to PM project\n if (this.pmStore) {\n try { this.pmStore.autoLinkApp(payload.appName, projectId); } catch { /* non-fatal */ }\n }\n\n // If the request came with a workspace-scoped bearer token, assign\n // this project to that workspace (unless it's already in one).\n if (this.pmStore) {\n try {\n const token = AuthManager.extractBearer(req.headers.authorization);\n if (token) {\n const ws = this.pmStore.getWorkspaceByApiKey(token);\n if (ws && projectId) {\n const existing = this.pmStore\n .listProjects()\n .find((p) => p.runtimeProjectId === projectId);\n if (existing && !existing.workspaceId) {\n this.pmStore.setProjectWorkspace(existing.id, ws.id);\n }\n }\n }\n } catch { /* non-fatal */ }\n }\n }\n\n const VALID_EVENT_TYPES = new Set([\n 'network', 'console', 'session', 'state', 'render',\n 'dom_snapshot', 'performance', 'database',\n 'custom', 'navigation', 'ui',\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 getPort(): number {\n return this.activePort;\n }\n\n /**\n * Validate the `project_id` query parameter against the caller's workspace.\n *\n * Returns:\n * - `string` — the caller is authorized; pass to store.\n * - `undefined` — caller is admin and didn't specify a project_id (all projects allowed).\n * - `false` — not authorized (a 400 or 403 response has already been written); the handler must return immediately.\n *\n * Callers with a workspace-scoped token MUST provide `project_id`, and it\n * must resolve to a PM project in the caller's workspace. Runtime projectIds\n * without a PM record (never registered via setup_project) fall through to\n * admin-only; workspace-scoped callers get 403.\n */\n private authorizeProjectIdParam(\n req: IncomingMessage,\n res: ServerResponse,\n params: URLSearchParams,\n ): string | undefined | false {\n const caller = (req as AuthedRequest)._rsCaller ?? {\n isAdmin: !this.authManager?.isEnabled(),\n workspaceId: null,\n };\n const projectId = params.get('project_id') ?? undefined;\n\n if (caller.isAdmin) return projectId;\n\n if (!projectId) {\n this.json(\n res,\n { error: 'project_id query param is required for workspace-scoped callers' },\n 400,\n );\n return false;\n }\n\n const projectWorkspaceId = this.pmStore?.getWorkspaceIdByRuntimeProjectId(projectId) ?? null;\n if (!projectWorkspaceId) {\n this.json(res, { error: 'Forbidden: project is not registered with any workspace' }, 403);\n return false;\n }\n if (projectWorkspaceId !== caller.workspaceId) {\n this.json(res, { error: 'Forbidden: project belongs to a different workspace' }, 403);\n return false;\n }\n return projectId;\n }\n\n async start(options: HttpServerOptions = {}): Promise<void> {\n const basePort = options.port ?? parseInt(process.env.RUNTIMESCOPE_HTTP_PORT ?? '6768', 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 // Use the actual bound port — `port` may be 0 (random), in which case\n // the OS picked one and `port` itself is meaningless.\n const addr = server.address();\n const boundPort =\n addr && typeof addr === 'object' && typeof addr.port === 'number'\n ? addr.port\n : port;\n this.activePort = boundPort;\n this.startedAt = Date.now();\n const proto = tls ? 'https' : 'http';\n console.error(`[RuntimeScope] HTTP API listening on ${proto}://${host}:${boundPort}`);\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 liveness/readiness probes, Prometheus scrape, and\n // static assets so orchestrators and load balancers don't need credentials\n // to monitor us.\n const isPublic = url.pathname === '/api/health'\n || url.pathname === '/readyz'\n || url.pathname === '/metrics'\n || url.pathname === '/runtimescope.js'\n || url.pathname === '/snippet';\n\n // Resolve caller identity:\n // isAdmin → global AuthManager token, OR auth disabled (local trust mode)\n // workspaceId → set when a workspace-scoped token (tk_*) authenticated this request\n // unauthenticated → auth is enabled but no valid token matched → 401\n //\n // Auth is also considered \"enabled\" if any non-revoked workspace API keys\n // exist. Otherwise the user creates a workspace key expecting it to gate\n // access, but AuthManager.isEnabled() reports false (no global keys) and\n // every request goes through unauthenticated — the H5 bypass.\n const workspaceKeysExist = !!this.pmStore?.hasActiveApiKeys?.();\n const authActive = !!this.authManager?.isEnabled() || workspaceKeysExist;\n\n const caller: { isAdmin: boolean; workspaceId: string | null } = {\n isAdmin: !authActive,\n workspaceId: null,\n };\n\n if (!isPublic && authActive) {\n const token = AuthManager.extractBearer(req.headers.authorization);\n // Use `validate()` rather than `isAuthorized()` — the latter returns\n // true when AuthManager is disabled (its \"auth off, everything passes\"\n // semantics), which would falsely classify a workspace token as a\n // global admin token whenever no global keys are configured. We only\n // want isGlobal=true when there's a real entry in AuthManager's set.\n const isGlobal = !!(token && this.authManager?.validate(token));\n const workspace = token ? this.pmStore?.getWorkspaceByApiKey(token) : null;\n if (!isGlobal && !workspace) {\n this.json(res, { error: 'Unauthorized', code: 'AUTH_FAILED' }, 401);\n return;\n }\n caller.isAdmin = isGlobal;\n caller.workspaceId = workspace?.id ?? null;\n }\n (req as AuthedRequest)._rsCaller = caller;\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 projectId = url.searchParams.get('project_id') || 'proj_xxx';\n const dsn = `runtimescope://${projectId}@localhost:${this.activePort}/${appName}`;\n const snippet = `<!-- RuntimeScope SDK — paste before </body> -->\n<script src=\"http://localhost:${this.activePort}/runtimescope.js\"></script>\n<script>\n RuntimeScope.init({ dsn: '${dsn}' });\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';\nimport { findPidsInDirectory } from '../platform.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 * Resolve the caller's identity for per-workspace authorization checks.\n * - isAdmin=true → global AuthManager token (or auth disabled in local dev).\n * - workspaceId set → the caller authenticated with a workspace-scoped tk_* token.\n * Populated by the auth gate in http-server.ts before handlers run.\n */\n resolveCaller: (req: IncomingMessage) => { isAdmin: boolean; workspaceId: string | null };\n}\n\n/**\n * Authorization helper: enforce that the caller is admin OR that their\n * workspace matches the route's target workspace. Returns true if allowed.\n * If denied, writes a 403 response and returns false so the handler can bail.\n */\nfunction requireWorkspaceAccess(\n helpers: RouteHelpers,\n req: IncomingMessage,\n res: ServerResponse,\n targetWorkspaceId: string,\n): boolean {\n const caller = helpers.resolveCaller(req);\n if (caller.isAdmin) return true;\n if (caller.workspaceId && caller.workspaceId === targetWorkspaceId) return true;\n helpers.json(res, { error: 'Forbidden: caller not authorized for this workspace' }, 403);\n return false;\n}\n\nfunction requireAdmin(\n helpers: RouteHelpers,\n req: IncomingMessage,\n res: ServerResponse,\n): boolean {\n const caller = helpers.resolveCaller(req);\n if (caller.isAdmin) return true;\n helpers.json(res, { error: 'Forbidden: admin required' }, 403);\n return false;\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 let projects = pmStore.listProjects();\n // Optional workspace filter — ?workspace_id=ws_xxx\n const workspaceId = params.get('workspace_id');\n if (workspaceId) {\n projects = projects.filter((p) => p.workspaceId === workspaceId);\n }\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 route('DELETE', '/api/pm/projects/:id', (_req, res, params) => {\n const id = params.get('id')!;\n const project = pmStore.getProject(id);\n if (!project) {\n helpers.json(res, { error: 'Project not found' }, 404);\n return;\n }\n pmStore.deleteProject(id);\n helpers.json(res, { ok: true, deleted: project.name });\n });\n\n // Move a project to a different workspace. Caller must own BOTH the source\n // and destination workspace (or be admin) — otherwise a workspace-scoped\n // caller could annex any project simply by knowing its ID.\n route('PUT', '/api/pm/projects/:id/workspace', 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: 'Missing body' }, 400); return; }\n let parsed: { workspace_id?: string };\n try { parsed = JSON.parse(body); } catch { helpers.json(res, { error: 'Invalid JSON' }, 400); return; }\n if (!parsed.workspace_id) { helpers.json(res, { error: 'Missing workspace_id' }, 400); return; }\n\n const project = pmStore.getProject(id);\n if (!project) { helpers.json(res, { error: 'Project not found' }, 404); return; }\n\n const caller = helpers.resolveCaller(req);\n if (!caller.isAdmin) {\n const sourceOk = !!project.workspaceId && project.workspaceId === caller.workspaceId;\n const destOk = parsed.workspace_id === caller.workspaceId;\n if (!sourceOk || !destOk) {\n helpers.json(res, { error: 'Forbidden: caller must own both source and destination workspace' }, 403);\n return;\n }\n }\n\n try {\n pmStore.setProjectWorkspace(id, parsed.workspace_id);\n helpers.json(res, { ok: true });\n } catch (err) {\n helpers.json(res, { error: (err as Error).message }, 400);\n }\n });\n\n // ============================================================\n // Workspaces (multi-tenant)\n // ============================================================\n\n // List workspaces: admins see all; workspace-scoped callers see only their own.\n route('GET', '/api/pm/workspaces', (req, res) => {\n const caller = helpers.resolveCaller(req);\n const all = pmStore.listWorkspaces();\n if (caller.isAdmin) {\n helpers.json(res, { data: all });\n return;\n }\n const filtered = caller.workspaceId ? all.filter((w) => w.id === caller.workspaceId) : [];\n helpers.json(res, { data: filtered });\n });\n\n // Creating a workspace is a cluster-level operation — admin only.\n route('POST', '/api/pm/workspaces', async (req, res) => {\n if (!requireAdmin(helpers, req, res)) return;\n const body = await helpers.readBody(req, 4096);\n if (!body) { helpers.json(res, { error: 'Missing body' }, 400); return; }\n let parsed: { name?: string; slug?: string; description?: string };\n try { parsed = JSON.parse(body); } catch { helpers.json(res, { error: 'Invalid JSON' }, 400); return; }\n if (!parsed.name || typeof parsed.name !== 'string') {\n helpers.json(res, { error: 'Missing name' }, 400);\n return;\n }\n try {\n const ws = pmStore.createWorkspace({ name: parsed.name, slug: parsed.slug, description: parsed.description });\n helpers.json(res, ws, 201);\n } catch (err) {\n helpers.json(res, { error: (err as Error).message }, 400);\n }\n });\n\n route('GET', '/api/pm/workspaces/:id', (req, res, params) => {\n const id = params.get('id')!;\n if (!requireWorkspaceAccess(helpers, req, res, id)) return;\n const ws = pmStore.getWorkspace(id);\n if (!ws) { helpers.json(res, { error: 'Workspace not found' }, 404); return; }\n helpers.json(res, ws);\n });\n\n route('PUT', '/api/pm/workspaces/:id', async (req, res, params) => {\n const id = params.get('id')!;\n if (!requireWorkspaceAccess(helpers, req, res, id)) return;\n if (!pmStore.getWorkspace(id)) { helpers.json(res, { error: 'Workspace not found' }, 404); return; }\n const body = await helpers.readBody(req, 4096);\n if (!body) { helpers.json(res, { error: 'Missing body' }, 400); return; }\n let parsed: { name?: string; slug?: string; description?: string };\n try { parsed = JSON.parse(body); } catch { helpers.json(res, { error: 'Invalid JSON' }, 400); return; }\n try {\n pmStore.updateWorkspace(id, parsed);\n helpers.json(res, pmStore.getWorkspace(id));\n } catch (err) {\n helpers.json(res, { error: (err as Error).message }, 400);\n }\n });\n\n // Deleting a workspace reassigns projects and wipes keys — admin only.\n route('DELETE', '/api/pm/workspaces/:id', (req, res, params) => {\n if (!requireAdmin(helpers, req, res)) return;\n const id = params.get('id')!;\n try {\n pmStore.deleteWorkspace(id);\n helpers.json(res, { ok: true });\n } catch (err) {\n helpers.json(res, { error: (err as Error).message }, 400);\n }\n });\n\n route('GET', '/api/pm/workspaces/:id/api-keys', (req, res, params) => {\n const id = params.get('id')!;\n if (!requireWorkspaceAccess(helpers, req, res, id)) return;\n if (!pmStore.getWorkspace(id)) { helpers.json(res, { error: 'Workspace not found' }, 404); return; }\n // Raw secrets are never returned — listApiKeys only exposes prefix+last4.\n helpers.json(res, { data: pmStore.listApiKeys(id) });\n });\n\n route('POST', '/api/pm/workspaces/:id/api-keys', async (req, res, params) => {\n const id = params.get('id')!;\n if (!requireWorkspaceAccess(helpers, req, res, id)) return;\n if (!pmStore.getWorkspace(id)) { helpers.json(res, { error: 'Workspace not found' }, 404); return; }\n const body = await helpers.readBody(req, 4096);\n if (!body) { helpers.json(res, { error: 'Missing body' }, 400); return; }\n let parsed: { label?: string; expires_at?: number };\n try { parsed = JSON.parse(body); } catch { helpers.json(res, { error: 'Invalid JSON' }, 400); return; }\n if (!parsed.label) { helpers.json(res, { error: 'Missing label' }, 400); return; }\n try {\n const key = pmStore.createApiKey(id, parsed.label, parsed.expires_at);\n // The raw `key` field appears ONCE — the caller is responsible for storing it.\n helpers.json(res, key, 201);\n } catch (err) {\n helpers.json(res, { error: (err as Error).message }, 400);\n }\n });\n\n // Revoke by the public prefix (\"tk_9f2a1c3b\") — the dashboard never sees the raw secret.\n route('DELETE', '/api/pm/api-keys/:prefix', (req, res, params) => {\n const prefix = params.get('prefix')!;\n const key = pmStore.findApiKeyByPrefix(prefix);\n if (!key) { helpers.json(res, { error: 'Key not found' }, 404); return; }\n if (!requireWorkspaceAccess(helpers, req, res, key.workspaceId)) return;\n pmStore.revokeApiKey(prefix);\n helpers.json(res, { ok: true });\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.indexProjectSessions(session.projectId);\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 (cross-platform)\n const pids = findPidsInDirectory(project.path).filter((n) => n > 1 && n !== process.pid);\n if (pids.length > 0) pid = pids[0];\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 // XLSX export — single project\n route('GET', '/api/pm/capex-report/:projectId', async (_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 try {\n const buffer = await pmStore.exportCapexXlsx(projectId, { startDate, endDate });\n res.writeHead(200, {\n 'Content-Type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n 'Content-Disposition': `attachment; filename=\"capex-${projectId}.xlsx\"`,\n });\n res.end(buffer);\n } catch {\n // Fallback to CSV if exceljs not available\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 // JSON capex summary across all projects (for home page dashboard)\n route('GET', '/api/pm/capex-all', (_req, res, params) => {\n const category = params.get('category') ?? undefined;\n const projects = pmStore.listProjects();\n const filteredProjects = category\n ? projects.filter((p) => p.category === category)\n : projects;\n\n const allEntries: Array<Record<string, unknown>> = [];\n let totalCost = 0;\n let totalCapitalizable = 0;\n let totalExpensed = 0;\n let totalMinutes = 0;\n let totalConfirmed = 0;\n let totalUnconfirmed = 0;\n\n const byProject: Array<Record<string, unknown>> = [];\n\n for (const project of filteredProjects) {\n const entries = pmStore.listCapexEntries(project.id);\n let projCost = 0;\n let projCap = 0;\n let projExp = 0;\n let projMins = 0;\n let projConfirmed = 0;\n\n for (const e of entries) {\n allEntries.push({ ...e, projectName: project.name });\n projCost += e.adjustedCostMicrodollars;\n projMins += e.activeMinutes;\n if (e.classification === 'capitalizable') projCap += e.adjustedCostMicrodollars;\n else projExp += e.adjustedCostMicrodollars;\n if (e.confirmed) projConfirmed++;\n }\n\n if (entries.length > 0) {\n byProject.push({\n projectId: project.id,\n projectName: project.name,\n category: project.category,\n totalCost: projCost,\n capitalizable: projCap,\n expensed: projExp,\n activeMinutes: projMins,\n activeHours: +(projMins / 60).toFixed(2),\n confirmed: projConfirmed,\n total: entries.length,\n });\n }\n\n totalCost += projCost;\n totalCapitalizable += projCap;\n totalExpensed += projExp;\n totalMinutes += projMins;\n totalConfirmed += projConfirmed;\n totalUnconfirmed += entries.length - projConfirmed;\n }\n\n helpers.json(res, {\n data: {\n summary: {\n totalCost,\n capitalizable: totalCapitalizable,\n expensed: totalExpensed,\n activeMinutes: totalMinutes,\n activeHours: +(totalMinutes / 60).toFixed(2),\n confirmed: totalConfirmed,\n unconfirmed: totalUnconfirmed,\n projectCount: byProject.length,\n },\n byProject,\n entries: allEntries.sort((a, b) => (b as any).createdAt - (a as any).createdAt),\n },\n });\n });\n\n // XLSX export — all projects (optional ?category= filter)\n route('GET', '/api/pm/capex-report-all', async (_req, res, params) => {\n const startDate = params.get('start_date') ?? undefined;\n const endDate = params.get('end_date') ?? undefined;\n const category = params.get('category') ?? undefined;\n try {\n const buffer = await pmStore.exportCapexXlsxAll({ startDate, endDate, category });\n res.writeHead(200, {\n 'Content-Type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n 'Content-Disposition': 'attachment; filename=\"capex-all-projects.xlsx\"',\n });\n res.end(buffer);\n } catch (err) {\n helpers.json(res, { error: (err as Error).message }, 500);\n }\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 { randomBytes, createHash, timingSafeEqual } from 'node:crypto';\nimport type {\n PmProject,\n PmTask,\n PmSession,\n PmNote,\n PmCapexEntry,\n CapexSummary,\n SessionStats,\n TaskStatus,\n ProjectPhase,\n ProjectStatus,\n PmWorkspace,\n PmApiKey,\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 CREATE TABLE IF NOT EXISTS pm_deleted_projects (\n path TEXT PRIMARY KEY,\n name TEXT,\n deleted_at INTEGER NOT NULL\n );\n CREATE INDEX IF NOT EXISTS idx_deleted_path ON pm_deleted_projects(path);\n\n -- Multi-tenant workspaces (Phase 1) --\n CREATE TABLE IF NOT EXISTS pm_workspaces (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n slug TEXT UNIQUE NOT NULL,\n description TEXT,\n is_default INTEGER NOT NULL DEFAULT 0,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL\n );\n CREATE INDEX IF NOT EXISTS idx_workspaces_slug ON pm_workspaces(slug);\n\n CREATE TABLE IF NOT EXISTS pm_api_keys (\n key TEXT PRIMARY KEY,\n workspace_id TEXT NOT NULL,\n label TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n last_used_at INTEGER,\n expires_at INTEGER,\n revoked_at INTEGER,\n FOREIGN KEY (workspace_id) REFERENCES pm_workspaces(id) ON DELETE CASCADE\n );\n CREATE INDEX IF NOT EXISTS idx_api_keys_workspace ON pm_api_keys(workspace_id);\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 // Add runtime_apps column (added in v3) — JSON array of associated SDK appNames\n try { this.db.exec('ALTER TABLE pm_projects ADD COLUMN runtime_apps TEXT DEFAULT NULL'); } catch { /* already exists */ }\n // Add runtime_project_id column (added in v4) — stable project ID for multi-SDK grouping\n try { this.db.exec('ALTER TABLE pm_projects ADD COLUMN runtime_project_id TEXT DEFAULT NULL'); } catch { /* already exists */ }\n // Multi-tenancy (Phase 1): projects belong to workspaces\n try { this.db.exec('ALTER TABLE pm_projects ADD COLUMN workspace_id TEXT DEFAULT NULL'); } catch { /* already exists */ }\n\n // v0.10.1 — API key storage changed from plaintext to SHA-256 hash.\n // New columns for user-visible identification without exposing the secret.\n let apiKeyColumnsAdded = false;\n try {\n this.db.exec('ALTER TABLE pm_api_keys ADD COLUMN key_prefix TEXT');\n apiKeyColumnsAdded = true;\n } catch { /* already exists */ }\n try { this.db.exec('ALTER TABLE pm_api_keys ADD COLUMN key_last4 TEXT'); } catch { /* already exists */ }\n try { this.db.exec('CREATE INDEX IF NOT EXISTS idx_api_keys_prefix ON pm_api_keys(key_prefix)'); } catch { /* ignore */ }\n\n // If we just added the new columns, any existing rows contain plaintext\n // tokens (v0.10.0 behavior). Revoke them unconditionally — the hash/prefix\n // columns are null so they can't authenticate under the new scheme anyway,\n // and leaving them visible in the DB is the B3 leak we're fixing.\n if (apiKeyColumnsAdded) {\n this.db\n .prepare('UPDATE pm_api_keys SET revoked_at = ? WHERE revoked_at IS NULL AND key_prefix IS NULL')\n .run(Date.now());\n }\n\n this.ensureDefaultWorkspace();\n }\n\n /**\n * Ensure a default \"personal\" workspace exists and every project has a\n * workspace_id. Runs on every startup — idempotent.\n */\n private ensureDefaultWorkspace(): void {\n const existing = this.db.prepare('SELECT id FROM pm_workspaces WHERE is_default = 1').get() as\n | { id: string }\n | undefined;\n\n let defaultId: string;\n if (existing) {\n defaultId = existing.id;\n } else {\n defaultId = generateWorkspaceId();\n const now = Date.now();\n this.db\n .prepare(\n `INSERT INTO pm_workspaces (id, name, slug, description, is_default, created_at, updated_at)\n VALUES (?, ?, ?, ?, 1, ?, ?)`,\n )\n .run(defaultId, 'Personal', 'personal', 'Your personal workspace', now, now);\n }\n\n // Assign any projects without a workspace_id to the default\n this.db.prepare('UPDATE pm_projects SET workspace_id = ? WHERE workspace_id IS NULL').run(defaultId);\n }\n\n // ============================================================\n // Projects\n // ============================================================\n\n upsertProject(project: PmProject): void {\n // New projects without an explicit workspace go to the default workspace.\n // Existing projects keep whatever workspace they already had (COALESCE below).\n //\n // Prior implementation combined the two lookups in a single `??` chain and\n // then tried to disambiguate with `typeof` — but the explicit-string branch\n // was discarded because the ternary resolved to the else side when the\n // value was already a string. Rewritten to make both branches explicit.\n let resolvedWorkspaceId: string | null = project.workspaceId ?? null;\n if (!resolvedWorkspaceId) {\n const row = this.db\n .prepare('SELECT id FROM pm_workspaces WHERE is_default = 1 LIMIT 1')\n .get() as { id: string } | undefined;\n resolvedWorkspaceId = row?.id ?? null;\n }\n\n this.db.prepare(`\n INSERT INTO pm_projects (id, workspace_id, name, path, claude_project_key, runtimescope_project,\n phase, management_authorized, probable_to_complete, project_status,\n category, sdk_installed, runtime_apps,\n created_at, updated_at, metadata)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(id) DO UPDATE SET\n workspace_id = COALESCE(pm_projects.workspace_id, excluded.workspace_id),\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 runtime_apps = COALESCE(excluded.runtime_apps, pm_projects.runtime_apps),\n updated_at = excluded.updated_at,\n metadata = COALESCE(excluded.metadata, pm_projects.metadata)\n `).run(\n project.id,\n resolvedWorkspaceId,\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.runtimeApps?.length ? JSON.stringify(project.runtimeApps) : null,\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.runtimeApps !== undefined) { sets.push('runtime_apps = ?'); params.push(updates.runtimeApps.length ? JSON.stringify(updates.runtimeApps) : null); }\n if (updates.runtimescopeProject !== undefined) { sets.push('runtimescope_project = ?'); params.push(updates.runtimescopeProject ?? null); }\n if (updates.runtimeProjectId !== undefined) { sets.push('runtime_project_id = ?'); params.push(updates.runtimeProjectId ?? null); }\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 /**\n * Auto-link an SDK appName to a PM project.\n * Matches by: exact name, directory basename, runtimescopeProject, or existing runtimeApps.\n * Returns the project ID if linked, null if no match found.\n */\n autoLinkApp(appName: string, projectId?: string): string | null {\n const projects = this.listProjects();\n const appLower = appName.toLowerCase();\n\n // 0. If projectId provided, try exact match first (deterministic, no fuzzy matching)\n if (projectId) {\n const byProjectId = projects.find((p) => p.runtimeProjectId === projectId);\n if (byProjectId) {\n // Ensure appName is in runtimeApps\n const apps = byProjectId.runtimeApps ?? [];\n if (!apps.some((a) => a.toLowerCase() === appLower)) {\n apps.push(appName);\n this.updateProject(byProjectId.id, { runtimeApps: apps });\n }\n return byProjectId.id;\n }\n }\n\n // 1. Already linked by appName?\n const alreadyLinked = projects.find(\n (p) => p.runtimeApps?.some((a) => a.toLowerCase() === appLower),\n );\n if (alreadyLinked) {\n // If projectId provided but not yet stored, store it now\n if (projectId && !alreadyLinked.runtimeProjectId) {\n this.updateProject(alreadyLinked.id, { runtimeProjectId: projectId });\n }\n return alreadyLinked.id;\n }\n\n // 2. Find best match by name/path/runtimescopeProject\n const match = projects.find((p) => {\n // Exact name match\n if (p.name.toLowerCase() === appLower) return true;\n // Directory basename match (e.g., project path /Users/x/my-app → \"my-app\")\n if (p.path) {\n const basename = p.path.replace(/\\/+$/, '').split('/').pop()?.toLowerCase();\n if (basename === appLower) return true;\n }\n // runtimescopeProject match\n if (p.runtimescopeProject?.toLowerCase() === appLower) return true;\n return false;\n });\n\n if (!match) return null;\n\n // Add appName to runtimeApps and store projectId if provided\n const apps = match.runtimeApps ?? [];\n if (!apps.some((a) => a.toLowerCase() === appLower)) {\n apps.push(appName);\n }\n const updates: Partial<PmProject> = { runtimeApps: apps };\n if (projectId && !match.runtimeProjectId) {\n updates.runtimeProjectId = projectId;\n }\n this.updateProject(match.id, updates);\n return match.id;\n }\n\n /** Find a project's runtimeProjectId by checking if appName appears in any project's runtimeApps. */\n findProjectIdByApp(appName: string): string | null {\n const row = this.db\n .prepare(`SELECT runtime_project_id FROM pm_projects WHERE runtime_apps LIKE ? AND runtime_project_id IS NOT NULL LIMIT 1`)\n .get(`%\"${appName}\"%`) as { runtime_project_id: string } | undefined;\n return row?.runtime_project_id ?? null;\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 // ============================================================\n // Workspaces (multi-tenant)\n // ============================================================\n\n listWorkspaces(): PmWorkspace[] {\n const rows = this.db\n .prepare('SELECT * FROM pm_workspaces ORDER BY is_default DESC, name ASC')\n .all() as PmWorkspaceRow[];\n return rows.map(mapWorkspaceRow);\n }\n\n getWorkspace(id: string): PmWorkspace | null {\n const row = this.db\n .prepare('SELECT * FROM pm_workspaces WHERE id = ?')\n .get(id) as PmWorkspaceRow | undefined;\n return row ? mapWorkspaceRow(row) : null;\n }\n\n getWorkspaceBySlug(slug: string): PmWorkspace | null {\n const row = this.db\n .prepare('SELECT * FROM pm_workspaces WHERE slug = ?')\n .get(slug) as PmWorkspaceRow | undefined;\n return row ? mapWorkspaceRow(row) : null;\n }\n\n getDefaultWorkspace(): PmWorkspace {\n const row = this.db\n .prepare('SELECT * FROM pm_workspaces WHERE is_default = 1 LIMIT 1')\n .get() as PmWorkspaceRow | undefined;\n if (!row) {\n throw new Error('Default workspace missing — ensureDefaultWorkspace() must run first');\n }\n return mapWorkspaceRow(row);\n }\n\n createWorkspace(input: { name: string; slug?: string; description?: string }): PmWorkspace {\n const id = generateWorkspaceId();\n const slug = (input.slug ?? input.name)\n .toLowerCase()\n .replace(/[^a-z0-9-]+/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '');\n if (!slug) {\n throw new Error('Workspace slug cannot be empty');\n }\n if (this.getWorkspaceBySlug(slug)) {\n throw new Error(`Workspace with slug \"${slug}\" already exists`);\n }\n const now = Date.now();\n this.db\n .prepare(\n `INSERT INTO pm_workspaces (id, name, slug, description, is_default, created_at, updated_at)\n VALUES (?, ?, ?, ?, 0, ?, ?)`,\n )\n .run(id, input.name, slug, input.description ?? null, now, now);\n return { id, name: input.name, slug, description: input.description, createdAt: now, updatedAt: now, isDefault: false };\n }\n\n updateWorkspace(id: string, updates: Partial<Pick<PmWorkspace, 'name' | 'slug' | 'description'>>): void {\n const sets: string[] = [];\n const params: unknown[] = [];\n if (updates.name !== undefined) { sets.push('name = ?'); params.push(updates.name); }\n if (updates.slug !== undefined) { sets.push('slug = ?'); params.push(updates.slug); }\n if (updates.description !== undefined) { sets.push('description = ?'); params.push(updates.description); }\n if (!sets.length) return;\n sets.push('updated_at = ?');\n params.push(Date.now());\n params.push(id);\n this.db.prepare(`UPDATE pm_workspaces SET ${sets.join(', ')} WHERE id = ?`).run(...params);\n }\n\n deleteWorkspace(id: string): void {\n const ws = this.getWorkspace(id);\n if (!ws) return;\n if (ws.isDefault) {\n throw new Error('Cannot delete the default workspace');\n }\n // Reassign projects back to the default workspace\n const def = this.getDefaultWorkspace();\n this.db.prepare('UPDATE pm_projects SET workspace_id = ? WHERE workspace_id = ?').run(def.id, id);\n this.db.prepare('DELETE FROM pm_api_keys WHERE workspace_id = ?').run(id);\n this.db.prepare('DELETE FROM pm_workspaces WHERE id = ?').run(id);\n }\n\n /** Move a project between workspaces. */\n setProjectWorkspace(projectId: string, workspaceId: string): void {\n const ws = this.getWorkspace(workspaceId);\n if (!ws) throw new Error(`Workspace ${workspaceId} does not exist`);\n this.db\n .prepare('UPDATE pm_projects SET workspace_id = ?, updated_at = ? WHERE id = ?')\n .run(workspaceId, Date.now(), projectId);\n }\n\n // ============================================================\n // API Keys (workspace-scoped)\n // ============================================================\n\n createApiKey(workspaceId: string, label: string, expiresAt?: number): PmApiKey {\n const ws = this.getWorkspace(workspaceId);\n if (!ws) throw new Error(`Workspace ${workspaceId} does not exist`);\n const raw = `tk_${randomBytes(24).toString('hex')}`;\n const hash = hashApiKey(raw);\n const prefix = raw.slice(0, 11); // \"tk_\" + 8 hex = first 11 chars\n const last4 = raw.slice(-4);\n const now = Date.now();\n this.db\n .prepare(\n `INSERT INTO pm_api_keys (key, workspace_id, label, created_at, expires_at, key_prefix, key_last4)\n VALUES (?, ?, ?, ?, ?, ?, ?)`,\n )\n .run(hash, workspaceId, label, now, expiresAt ?? null, prefix, last4);\n // Return the raw token ONCE — the caller must store it; we never persist or\n // re-expose it. Subsequent list/read endpoints see only prefix + last4.\n return { key: raw, keyPrefix: prefix, keyLast4: last4, workspaceId, label, createdAt: now, expiresAt };\n }\n\n listApiKeys(workspaceId: string): PmApiKey[] {\n const rows = this.db\n .prepare(\n `SELECT * FROM pm_api_keys WHERE workspace_id = ? AND revoked_at IS NULL ORDER BY created_at DESC`,\n )\n .all(workspaceId) as PmApiKeyRow[];\n // mapApiKeyRow masks the secret — the `key` field is blank, only prefix + last4 remain.\n return rows.map(mapApiKeyRow);\n }\n\n /** Revoke by the public prefix (what the dashboard shows), not the raw secret. */\n revokeApiKey(prefix: string): void {\n this.db.prepare('UPDATE pm_api_keys SET revoked_at = ? WHERE key_prefix = ?').run(Date.now(), prefix);\n }\n\n /** Look up an API key's workspace by its public prefix. Used for per-workspace authorization on the revoke route. */\n findApiKeyByPrefix(prefix: string): PmApiKey | null {\n const row = this.db\n .prepare(`SELECT * FROM pm_api_keys WHERE key_prefix = ? AND revoked_at IS NULL LIMIT 1`)\n .get(prefix) as PmApiKeyRow | undefined;\n return row ? mapApiKeyRow(row) : null;\n }\n\n /**\n * True if any non-revoked workspace API key exists. The HTTP auth gate uses\n * this to enforce auth whenever workspace keys are in play, even when the\n * global AuthManager has no keys configured. Without this check, a user who\n * creates a workspace key expecting it to gate access gets no auth at all\n * (the H5 bypass).\n */\n hasActiveApiKeys(): boolean {\n const row = this.db\n .prepare('SELECT 1 AS n FROM pm_api_keys WHERE revoked_at IS NULL LIMIT 1')\n .get() as { n: number } | undefined;\n return !!row;\n }\n\n /**\n * Given a runtime projectId (proj_xxx), return the workspaceId it belongs to,\n * or null if the projectId isn't registered with any PM project. Used to\n * enforce per-workspace isolation on event-read routes — the caller can only\n * query events from runtime projects owned by their workspace.\n */\n getWorkspaceIdByRuntimeProjectId(runtimeProjectId: string): string | null {\n if (!runtimeProjectId) return null;\n const row = this.db\n .prepare('SELECT workspace_id FROM pm_projects WHERE runtime_project_id = ? LIMIT 1')\n .get(runtimeProjectId) as { workspace_id: string | null } | undefined;\n return row?.workspace_id ?? null;\n }\n\n getWorkspaceByApiKey(rawKey: string): PmWorkspace | null {\n if (!rawKey || typeof rawKey !== 'string') return null;\n const hash = hashApiKey(rawKey);\n const row = this.db\n .prepare(\n `SELECT w.* FROM pm_api_keys k\n JOIN pm_workspaces w ON w.id = k.workspace_id\n WHERE k.key = ? AND k.revoked_at IS NULL\n AND (k.expires_at IS NULL OR k.expires_at > ?)`,\n )\n .get(hash, Date.now()) as PmWorkspaceRow | undefined;\n if (!row) return null;\n // Update last_used_at — best-effort, non-fatal\n try {\n this.db.prepare('UPDATE pm_api_keys SET last_used_at = ? WHERE key = ?').run(Date.now(), hash);\n } catch { /* ignore */ }\n return mapWorkspaceRow(row);\n }\n\n private mapProjectRow(row: PmProjectRow): PmProject {\n return {\n id: row.id,\n workspaceId: row.workspace_id ?? undefined,\n name: row.name,\n path: row.path ?? undefined,\n claudeProjectKey: row.claude_project_key ?? undefined,\n runtimescopeProject: row.runtimescope_project ?? undefined,\n runtimeProjectId: row.runtime_project_id ?? undefined,\n runtimeApps: row.runtime_apps ? JSON.parse(row.runtime_apps) : 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 p.runtime_apps,\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 // Group by day (period is YYYY-MM-DD)\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 async exportCapexXlsx(\n projectId: string,\n opts?: { startDate?: string; endDate?: string },\n ): Promise<Buffer> {\n const ExcelJS = await import('exceljs');\n const workbook = new ExcelJS.Workbook();\n\n const project = this.getProject(projectId);\n const summary = this.getCapexSummary(projectId, opts);\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 // --- Sheet 1: Summary ---\n const summarySheet = workbook.addWorksheet('Summary');\n const summaryData = [\n ['Project', project?.name ?? projectId],\n ['Phase', project?.phase ?? ''],\n ['Status', project?.projectStatus ?? ''],\n ['Total Active Hours', (summary.totalActiveMinutes / 60).toFixed(2)],\n ['Total Cost', `$${(summary.totalCostMicrodollars / 1_000_000).toFixed(2)}`],\n ['Capitalizable', `$${(summary.capitalizableCostMicrodollars / 1_000_000).toFixed(2)}`],\n ['Expensed', `$${(summary.expensedCostMicrodollars / 1_000_000).toFixed(2)}`],\n ['Sessions', String(summary.totalSessions)],\n ['Confirmed', `${summary.confirmedCount}/${summary.totalSessions}`],\n ];\n summaryData.forEach(([field, value]) => {\n const row = summarySheet.addRow([field, value]);\n row.getCell(1).font = { bold: true };\n });\n summarySheet.getColumn(1).width = 20;\n summarySheet.getColumn(2).width = 30;\n\n // --- Sheet 2: Daily Detail ---\n const detailSheet = workbook.addWorksheet('Daily Detail');\n detailSheet.addRow(['Date', 'Session', 'Model', 'Active Hours', 'Cost (USD)', 'Classification', 'Work Type', 'Confirmed', 'Notes']);\n detailSheet.getRow(1).font = { bold: true };\n for (const e of entries) {\n const s = sessions.get(e.sessionId);\n const date = s?.startedAt ? new Date(s.startedAt).toISOString().split('T')[0] : e.period;\n detailSheet.addRow([\n date,\n s?.slug ?? e.sessionId.slice(0, 12),\n s?.model ?? '',\n Number((e.activeMinutes / 60).toFixed(2)),\n Number((e.costMicrodollars / 1_000_000).toFixed(4)),\n e.classification,\n e.workType ?? '',\n e.confirmed ? 'Yes' : 'No',\n e.notes ?? '',\n ]);\n }\n detailSheet.columns.forEach((col) => { col.width = 16; });\n detailSheet.getColumn(1).width = 12;\n detailSheet.getColumn(2).width = 24;\n\n // --- Sheet 3: Monthly Totals ---\n const monthlySheet = workbook.addWorksheet('Monthly Totals');\n monthlySheet.addRow(['Period', 'Active Hours', 'Capitalizable ($)', 'Expensed ($)', 'Total ($)']);\n monthlySheet.getRow(1).font = { bold: true };\n for (const m of summary.byMonth) {\n monthlySheet.addRow([\n m.period,\n Number((m.activeMinutes / 60).toFixed(2)),\n Number((m.capitalizable / 1_000_000).toFixed(2)),\n Number((m.expensed / 1_000_000).toFixed(2)),\n Number(((m.capitalizable + m.expensed) / 1_000_000).toFixed(2)),\n ]);\n }\n monthlySheet.columns.forEach((col) => { col.width = 18; });\n\n return Buffer.from(await workbook.xlsx.writeBuffer());\n }\n\n async exportCapexXlsxAll(\n opts?: { startDate?: string; endDate?: string; category?: string },\n ): Promise<Buffer> {\n const ExcelJS = await import('exceljs');\n const workbook = new ExcelJS.Workbook();\n\n let projects = this.listProjects();\n if (opts?.category) {\n projects = projects.filter((p) => p.category === opts.category);\n }\n\n // --- Sheet 1: Overview ---\n const overviewSheet = workbook.addWorksheet('Overview');\n overviewSheet.addRow(['Project', 'Category', 'Phase', 'Total Hours', 'Capitalizable ($)', 'Expensed ($)', 'Total ($)']);\n overviewSheet.getRow(1).font = { bold: true };\n for (const p of projects) {\n const summary = this.getCapexSummary(p.id, opts);\n overviewSheet.addRow([\n p.name,\n p.category ?? '',\n p.phase,\n Number((summary.totalActiveMinutes / 60).toFixed(2)),\n Number((summary.capitalizableCostMicrodollars / 1_000_000).toFixed(2)),\n Number((summary.expensedCostMicrodollars / 1_000_000).toFixed(2)),\n Number((summary.totalCostMicrodollars / 1_000_000).toFixed(2)),\n ]);\n }\n overviewSheet.columns.forEach((col) => { col.width = 18; });\n overviewSheet.getColumn(1).width = 28;\n\n // --- Sheet 2: All Entries ---\n const allSheet = workbook.addWorksheet('All Entries');\n allSheet.addRow(['Project', 'Date', 'Session', 'Active Hours', 'Cost (USD)', 'Classification', 'Work Type']);\n allSheet.getRow(1).font = { bold: true };\n for (const p of projects) {\n const entries = this.listCapexEntries(p.id, { month: opts?.startDate });\n for (const e of entries) {\n const s = this.getSession(e.sessionId);\n const date = s?.startedAt ? new Date(s.startedAt).toISOString().split('T')[0] : e.period;\n allSheet.addRow([\n p.name,\n date,\n s?.slug ?? e.sessionId.slice(0, 12),\n Number((e.activeMinutes / 60).toFixed(2)),\n Number((e.costMicrodollars / 1_000_000).toFixed(4)),\n e.classification,\n e.workType ?? '',\n ]);\n }\n }\n allSheet.columns.forEach((col) => { col.width = 16; });\n allSheet.getColumn(1).width = 24;\n allSheet.getColumn(3).width = 24;\n\n return Buffer.from(await workbook.xlsx.writeBuffer());\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 // Deleted Projects Blocklist\n // ============================================================\n\n deleteProject(id: string): void {\n const project = this.getProject(id);\n if (!project) return;\n\n // Add to blocklist so discovery never re-imports\n if (project.path) {\n this.db.prepare(\n 'INSERT OR REPLACE INTO pm_deleted_projects (path, name, deleted_at) VALUES (?, ?, ?)'\n ).run(project.path, project.name, Date.now());\n }\n // Also block by claude project key\n if (project.claudeProjectKey) {\n this.db.prepare(\n 'INSERT OR REPLACE INTO pm_deleted_projects (path, name, deleted_at) VALUES (?, ?, ?)'\n ).run(project.claudeProjectKey, project.name, Date.now());\n }\n\n // Cascade delete all related data\n this.db.prepare('DELETE FROM pm_capex_entries WHERE project_id = ?').run(id);\n this.db.prepare('DELETE FROM pm_notes WHERE project_id = ?').run(id);\n this.db.prepare('DELETE FROM pm_tasks WHERE project_id = ?').run(id);\n this.db.prepare('DELETE FROM pm_sessions WHERE project_id = ?').run(id);\n this.db.prepare('DELETE FROM pm_projects WHERE id = ?').run(id);\n }\n\n isDeletedPath(path: string): boolean {\n const row = this.db.prepare('SELECT 1 FROM pm_deleted_projects WHERE path = ?').get(path);\n return !!row;\n }\n\n recoverProject(path: string): void {\n this.db.prepare('DELETE FROM pm_deleted_projects WHERE path = ?').run(path);\n }\n\n listDeletedProjects(): Array<{ path: string; name: string; deletedAt: number }> {\n return this.db.prepare('SELECT path, name, deleted_at as deletedAt FROM pm_deleted_projects ORDER BY deleted_at DESC').all() as any;\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 runtime_project_id: string | null;\n workspace_id: 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 runtime_apps: string | null;\n created_at: number;\n updated_at: number;\n metadata: string | null;\n}\n\n// --- Workspace helpers ---\n\ninterface PmWorkspaceRow {\n id: string;\n name: string;\n slug: string;\n description: string | null;\n is_default: number;\n created_at: number;\n updated_at: number;\n}\n\ninterface PmApiKeyRow {\n key: string;\n workspace_id: string;\n label: string;\n created_at: number;\n last_used_at: number | null;\n expires_at: number | null;\n revoked_at: number | null;\n key_prefix: string | null;\n key_last4: string | null;\n}\n\nfunction hashApiKey(raw: string): string {\n return createHash('sha256').update(raw, 'utf8').digest('hex');\n}\n\n/**\n * Constant-time comparison of two hex-encoded hashes. Both inputs must be the\n * same length (SHA-256 hex = 64 chars) for timingSafeEqual to work. We use\n * this wherever a partial match could allow a timing side-channel.\n */\nexport function safeHashEqual(a: string, b: string): boolean {\n if (typeof a !== 'string' || typeof b !== 'string' || a.length !== b.length) {\n return false;\n }\n try {\n return timingSafeEqual(Buffer.from(a, 'hex'), Buffer.from(b, 'hex'));\n } catch {\n return false;\n }\n}\n\nfunction generateWorkspaceId(): string {\n return `ws_${randomBytes(8).toString('hex')}`;\n}\n\nfunction mapWorkspaceRow(row: PmWorkspaceRow): PmWorkspace {\n return {\n id: row.id,\n name: row.name,\n slug: row.slug,\n description: row.description ?? undefined,\n isDefault: row.is_default === 1,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n}\n\nfunction mapApiKeyRow(row: PmApiKeyRow): PmApiKey {\n return {\n // Never return the stored value (it's the hash) — list responses expose\n // only prefix + last4 for display. The raw token appears once, at create.\n key: '',\n keyPrefix: row.key_prefix ?? '',\n keyLast4: row.key_last4 ?? '',\n workspaceId: row.workspace_id,\n label: row.label,\n createdAt: row.created_at,\n lastUsedAt: row.last_used_at ?? undefined,\n expiresAt: row.expires_at ?? undefined,\n revokedAt: row.revoked_at ?? undefined,\n };\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 runtime_apps: 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 const day = String(d.getDate()).padStart(2, '0');\n return `${year}-${month}-${day}`;\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 + auto-link to runtimeApps\n const apps = existing.runtimeApps ?? [];\n if (!apps.some((a) => a.toLowerCase() === projectName.toLowerCase())) {\n apps.push(projectName);\n }\n const updated: PmProject = {\n ...existing,\n runtimescopeProject: projectName,\n runtimeApps: apps,\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 // Skip if this project was previously deleted (blocklist)\n if (this.pmStore.isDeletedPath(sourcePath)) continue;\n\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 runtimeApps: [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 // Skip if this project was previously deleted (blocklist)\n if (fsPath && this.pmStore.isDeletedPath(fsPath)) return;\n if (this.pmStore.isDeletedPath(key)) return;\n\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":["require","createRequire","require","existsSync","renameSync","statSync","randomBytes","readFileSync","join","mkdirSync","join","mkdirSync","sqliteStore","resolve","ws","mkdirSync","readFileSync","writeFileSync","existsSync","readdirSync","join","resolve","readFileSync","existsSync","writeFileSync","mkdirSync","join","join","existsSync","readFileSync","mkdirSync","writeFileSync","randomBytes","readdirSync","join","errorCount","createHttpsServer","readFileSync","existsSync","dirname","WebSocketServer","existsSync","join","homedir","execFileSync","join","homedir","execFileSync","existsSync","dirname","readFileSync","result","existsSync","resolve","createHttpsServer","WebSocketServer","randomBytes","createHash","timingSafeEqual","apps","basename","randomBytes","readdir","readFile","stat","join","basename","existsSync","homedir","join","readFile","readdir","stat","existsSync","homedir","basename"]}