@runtimescope/collector 0.9.2 → 0.10.0
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.
- package/dist/{chunk-GENCCHYK.js → chunk-WWFIEANS.js} +792 -73
- package/dist/chunk-WWFIEANS.js.map +1 -0
- package/dist/dashboard.js +2 -2
- package/dist/dashboard.js.map +1 -1
- package/dist/index.d.ts +134 -12
- package/dist/index.js +8 -72
- package/dist/index.js.map +1 -1
- package/dist/standalone.js +31 -6
- package/dist/standalone.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-GENCCHYK.js.map +0 -1
|
@@ -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/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 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 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 } 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\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 // 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 );\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 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 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// 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 { 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\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 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\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 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 return this.tryStart(port, host, maxRetries, retryDelayMs, tls);\n }\n\n private tryStart(\n port: number,\n host: string,\n retriesLeft: number,\n retryDelayMs: number,\n tls?: TlsConfig | null\n ): Promise<void> {\n return new Promise((resolve, reject) => {\n let wss: WebSocketServer;\n\n if (tls) {\n // TLS mode: create HTTPS server, then attach WebSocket to it\n const httpsServer = createHttpsServer(loadTlsOptions(tls));\n wss = new WebSocketServer({ server: httpsServer });\n\n httpsServer.on('listening', () => {\n this.wss = wss;\n this.setupConnectionHandler(wss);\n 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 console.error(\n `[RuntimeScope] Port ${port} in use, retrying in ${retryDelayMs}ms (${retriesLeft} attempts left)...`\n );\n setTimeout(() => {\n this.tryStart(port, host, retriesLeft - 1, retryDelayMs, tls)\n .then(resolve)\n .catch(reject);\n }, retryDelayMs);\n } else {\n console.error('[RuntimeScope] WebSocket server error:', err.message);\n reject(err);\n }\n }\n\n private ensureSqliteStore(projectName: string): SqliteStore | null {\n if (!this.projectManager) return null;\n 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 /** 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', () => {\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\n if (sqliteStore) {\n const sessionInfo: SessionInfoExtended = {\n sessionId: payload.sessionId,\n project: projectName,\n appName: payload.appName,\n connectedAt: msg.timestamp,\n sdkVersion: payload.sdkVersion,\n eventCount: 0,\n isConnected: true,\n };\n sqliteStore.saveSession(sessionInfo);\n }\n\n // 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)) {\n for (const event of payload.events) {\n // Rate limit per session\n if (clientInfo && !this.rateLimiter.allow(clientInfo.sessionId)) {\n break; // drop remaining events in this batch\n }\n this.store.addEvent(event);\n }\n }\n break;\n }\n case 'command_response': {\n const resp = msg as unknown as CommandResponse;\n const pending = this.pendingCommands.get(resp.requestId);\n if (pending) {\n clearTimeout(pending.timer);\n this.pendingCommands.delete(resp.requestId);\n pending.resolve(resp.payload);\n }\n break;\n }\n case 'heartbeat':\n break;\n }\n }\n\n /** Find the WebSocket for a given sessionId */\n private findWsBySessionId(sessionId: string): WebSocket | undefined {\n for (const [ws, info] of this.clients) {\n if (info.sessionId === sessionId) return ws;\n }\n return undefined;\n }\n\n /** Get the first connected session ID (for single-app use) */\n getFirstSessionId(): string | undefined {\n for (const [, info] of this.clients) {\n return info.sessionId;\n }\n return undefined;\n }\n\n /** Get the project name for a session */\n getProjectForSession(sessionId: string): string | undefined {\n for (const [, info] of this.clients) {\n if (info.sessionId === sessionId) return info.projectName;\n }\n return undefined;\n }\n\n /** Get all connected session IDs with their project names */\n getConnectedSessions(): { sessionId: string; projectName: string; 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 // Close all SQLite stores\n for (const [name, sqliteStore] of this.sqliteStores) {\n try {\n sqliteStore.close();\n console.error(`[RuntimeScope] SQLite store closed for \"${name}\"`);\n } catch {\n // Ignore close errors during shutdown\n }\n }\n this.sqliteStores.clear();\n\n if (this.wss) {\n this.wss.close();\n this.wss = null;\n console.error('[RuntimeScope] Collector stopped');\n }\n }\n}\n","import { mkdirSync, readFileSync, writeFileSync, existsSync, readdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport type { ApiKeyEntry } from './auth.js';\n\n// ============================================================\n// Project Manager — manages ~/.runtimescope/ directory structure\n// ============================================================\n\nexport interface GlobalConfig {\n defaultPort: number;\n bufferSize: number;\n httpPort: number;\n /** Authentication configuration */\n auth?: {\n enabled: boolean;\n apiKeys: ApiKeyEntry[];\n };\n /** TLS certificate paths */\n tls?: {\n certPath: string;\n keyPath: string;\n caPath?: string;\n };\n /** CORS allowed origins (defaults to '*' when not set) */\n corsOrigins?: string[];\n /** Rate limiting configuration */\n rateLimits?: {\n maxEventsPerSecond?: number;\n maxEventsPerMinute?: number;\n };\n /** Payload redaction configuration */\n redaction?: {\n enabled: boolean;\n rules?: { name: string; pattern: string; replacement: string }[];\n };\n}\n\nexport interface ProjectConfig {\n name: string;\n createdAt: string;\n sdkVersion?: string;\n 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\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 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 ) {\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.registerRoutes();\n\n // Register PM routes if PM store is available\n if (options?.pmStore && options?.discovery) {\n this.pmRouter = createPmRouter(options.pmStore, options.discovery, {\n json: (res, data, status) => this.json(res, data, status),\n readBody: (req, maxBytes) => this.readBody(req, maxBytes),\n }, (msg) => this.broadcastDevServer(msg));\n }\n }\n\n private registerRoutes(): void {\n // Health check — always unauthenticated for load balancer probes\n this.routes.set('GET /api/health', (_req, res) => {\n this.json(res, {\n status: 'ok',\n 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 // 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 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: params.get('project_id') ?? undefined,\n });\n this.json(res, { data: events, count: events.length });\n });\n\n // Console events\n this.routes.set('GET /api/events/console', (_req, res, params) => {\n const events = this.store.getConsoleMessages({\n sinceSeconds: numParam(params, 'since_seconds'),\n level: params.get('level') ?? undefined,\n search: params.get('search') ?? undefined,\n sessionId: params.get('session_id') ?? undefined,\n projectId: params.get('project_id') ?? undefined,\n });\n this.json(res, { data: events, count: events.length });\n });\n\n // State events\n this.routes.set('GET /api/events/state', (_req, res, params) => {\n const events = this.store.getStateEvents({\n sinceSeconds: numParam(params, 'since_seconds'),\n storeId: params.get('store_id') ?? undefined,\n sessionId: params.get('session_id') ?? undefined,\n projectId: params.get('project_id') ?? undefined,\n });\n this.json(res, { data: events, count: events.length });\n });\n\n // Render events\n this.routes.set('GET /api/events/renders', (_req, res, params) => {\n const events = this.store.getRenderEvents({\n sinceSeconds: numParam(params, 'since_seconds'),\n componentName: params.get('component') ?? undefined,\n sessionId: params.get('session_id') ?? undefined,\n projectId: params.get('project_id') ?? undefined,\n });\n this.json(res, { data: events, count: events.length });\n });\n\n // Performance events\n this.routes.set('GET /api/events/performance', (_req, res, params) => {\n const events = this.store.getPerformanceMetrics({\n sinceSeconds: numParam(params, 'since_seconds'),\n metricName: params.get('metric') ?? undefined,\n sessionId: params.get('session_id') ?? undefined,\n projectId: params.get('project_id') ?? undefined,\n });\n this.json(res, { data: events, count: events.length });\n });\n\n // Database events\n this.routes.set('GET /api/events/database', (_req, res, params) => {\n const events = this.store.getDatabaseEvents({\n sinceSeconds: numParam(params, 'since_seconds'),\n table: params.get('table') ?? undefined,\n minDurationMs: numParam(params, 'min_duration_ms'),\n search: params.get('search') ?? undefined,\n sessionId: params.get('session_id') ?? undefined,\n projectId: params.get('project_id') ?? undefined,\n });\n this.json(res, { data: events, count: events.length });\n });\n\n // Timeline\n this.routes.set('GET /api/events/timeline', (_req, res, params) => {\n const eventTypes = params.get('event_types')?.split(',') ?? undefined;\n const events = this.store.getEventTimeline({\n sinceSeconds: numParam(params, 'since_seconds'),\n eventTypes: eventTypes as RuntimeEvent['eventType'][] | undefined,\n sessionId: params.get('session_id') ?? undefined,\n projectId: params.get('project_id') ?? undefined,\n });\n this.json(res, { data: events, count: events.length });\n });\n\n this.routes.set('GET /api/events/custom', (_req, res, params) => {\n const events = this.store.getCustomEvents({\n name: params.get('name') ?? undefined,\n sinceSeconds: numParam(params, 'since_seconds'),\n sessionId: params.get('session_id') ?? undefined,\n projectId: params.get('project_id') ?? undefined,\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 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: params.get('project_id') ?? undefined,\n });\n this.json(res, { data: events, count: events.length });\n });\n\n // Clear events\n this.routes.set('DELETE /api/events', (_req, res) => {\n const result = this.store.clear();\n this.json(res, result);\n });\n\n // POST event ingestion — HTTP alternative to WebSocket for serverless environments\n this.routes.set('POST /api/events', async (req, res) => {\n const body = await this.readBody(req, 1_048_576); // 1MB limit\n if (!body) {\n this.json(res, { error: 'Request body required', code: 'EMPTY_BODY' }, 400);\n return;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(body);\n } catch {\n this.json(res, { error: 'Invalid JSON', code: 'PARSE_ERROR' }, 400);\n return;\n }\n\n const payload = parsed as {\n sessionId?: string;\n appName?: string;\n sdkVersion?: string;\n 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 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 this.activePort = port;\n this.startedAt = Date.now();\n const proto = tls ? 'https' : 'http';\n console.error(`[RuntimeScope] HTTP API listening on ${proto}://${host}:${port}`);\n resolve();\n });\n\n server.on('error', (err) => {\n // Clean up the WebSocket server on failure\n this.wss?.close();\n this.wss = null;\n if (this.eventListener) {\n this.store.removeEventListener(this.eventListener);\n this.eventListener = null;\n }\n reject(err);\n });\n\n server.listen(port, host);\n });\n }\n\n async stop(): Promise<void> {\n if (this.eventListener) {\n this.store.removeEventListener(this.eventListener);\n this.eventListener = null;\n }\n\n for (const ws of this.dashboardClients) {\n ws.close();\n }\n this.dashboardClients.clear();\n\n if (this.wss) {\n this.wss.close();\n this.wss = null;\n }\n\n if (this.server) {\n return new Promise((resolve) => {\n this.server!.close(() => {\n this.server = null;\n console.error('[RuntimeScope] HTTP API stopped');\n resolve();\n });\n });\n }\n }\n\n broadcastEvent(event: RuntimeEvent): void {\n if (this.dashboardClients.size === 0) return;\n\n const message = JSON.stringify({ type: 'event', data: event });\n for (const ws of this.dashboardClients) {\n if (ws.readyState === 1 /* OPEN */) {\n try {\n ws.send(message);\n } catch {\n this.dashboardClients.delete(ws);\n }\n }\n }\n }\n\n broadcastSessionChange(type: 'session_connected' | 'session_disconnected', sessionId: string, appName: string): void {\n if (this.dashboardClients.size === 0) return;\n const message = JSON.stringify({ type, sessionId, appName });\n for (const ws of this.dashboardClients) {\n if (ws.readyState === 1) {\n try { ws.send(message); } catch { this.dashboardClients.delete(ws); }\n }\n }\n }\n\n private broadcastDevServer(msg: unknown): void {\n if (this.dashboardClients.size === 0) return;\n\n const message = JSON.stringify(msg);\n for (const ws of this.dashboardClients) {\n if (ws.readyState === 1 /* OPEN */) {\n try {\n ws.send(message);\n } catch {\n this.dashboardClients.delete(ws);\n }\n }\n }\n }\n\n private handleRequest(req: IncomingMessage, res: ServerResponse): void {\n const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);\n\n // CORS headers — use specific origin when configured, wildcard for dev\n this.setCorsHeaders(req, res);\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n // Auth check — skip for health endpoint and static assets\n const isPublic = url.pathname === '/api/health'\n || url.pathname === '/runtimescope.js'\n || url.pathname === '/snippet';\n\n if (!isPublic && this.authManager?.isEnabled()) {\n const token = AuthManager.extractBearer(req.headers.authorization);\n // Accept either a global API key OR a workspace-scoped key from pmStore.\n // This lets SDKs with workspace tokens post events without the user\n // needing to configure a global token on the collector too.\n const isGlobal = this.authManager.isAuthorized(token);\n const isWorkspaceToken = !!(token && this.pmStore?.getWorkspaceByApiKey(token));\n if (!isGlobal && !isWorkspaceToken) {\n this.json(res, { error: 'Unauthorized', code: 'AUTH_FAILED' }, 401);\n return;\n }\n }\n\n // Serve SDK IIFE bundle — works in any HTML page via <script> tag\n if (req.method === 'GET' && url.pathname === '/runtimescope.js') {\n const sdkPath = this.resolveSdkPath();\n if (sdkPath) {\n const bundle = readFileSync(sdkPath, 'utf-8');\n res.writeHead(200, {\n 'Content-Type': 'application/javascript',\n 'Cache-Control': 'no-cache',\n });\n res.end(bundle);\n } else {\n res.writeHead(404, { 'Content-Type': 'text/plain' });\n res.end('SDK bundle not found. Run: npm run build -w packages/sdk');\n }\n return;\n }\n\n // Serve a ready-to-paste snippet for any tech stack\n if (req.method === 'GET' && url.pathname === '/snippet') {\n const appName = (url.searchParams.get('app') || 'my-app').replace(/[^a-zA-Z0-9_-]/g, '');\n const 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\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\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 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 route('GET', '/api/pm/workspaces', (_req, res) => {\n helpers.json(res, { data: pmStore.listWorkspaces() });\n });\n\n route('POST', '/api/pm/workspaces', async (req, res) => {\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 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 (!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 route('DELETE', '/api/pm/workspaces/:id', (_req, res, params) => {\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 (!pmStore.getWorkspace(id)) { helpers.json(res, { error: 'Workspace not found' }, 404); return; }\n // Return keys WITH the secret — callers can re-read their keys.\n // If we ever serve this endpoint to non-owners, mask all but the creator's most recent key.\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 (!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 // Returned ONCE — callers should store the `key` field; we don't expose it again\n helpers.json(res, key, 201);\n } catch (err) {\n helpers.json(res, { error: (err as Error).message }, 400);\n }\n });\n\n route('DELETE', '/api/pm/api-keys/:key', (_req, res, params) => {\n pmStore.revokeApiKey(params.get('key')!);\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 } 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 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 const workspaceId =\n project.workspaceId ?? this.db\n .prepare('SELECT id FROM pm_workspaces WHERE is_default = 1 LIMIT 1')\n .get() as { id: string } | undefined;\n const resolvedWorkspaceId =\n typeof workspaceId === 'string' ? workspaceId : workspaceId?.id ?? null;\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 key = `tk_${randomBytes(24).toString('hex')}`;\n const now = Date.now();\n this.db\n .prepare(\n `INSERT INTO pm_api_keys (key, workspace_id, label, created_at, expires_at)\n VALUES (?, ?, ?, ?, ?)`,\n )\n .run(key, workspaceId, label, now, expiresAt ?? null);\n return { key, 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 return rows.map(mapApiKeyRow);\n }\n\n revokeApiKey(key: string): void {\n this.db.prepare('UPDATE pm_api_keys SET revoked_at = ? WHERE key = ?').run(Date.now(), key);\n }\n\n getWorkspaceByApiKey(key: string): PmWorkspace | null {\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(key, 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(), key);\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}\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 key: row.key,\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","resolve","ws","readFileSync","existsSync","resolve","readFileSync","existsSync","writeFileSync","mkdirSync","join","join","existsSync","readFileSync","mkdirSync","writeFileSync","randomBytes","readdirSync","join","errorCount","createHttpsServer","readFileSync","existsSync","WebSocketServer","existsSync","join","homedir","execFileSync","join","homedir","execFileSync","existsSync","readFileSync","result","existsSync","resolve","createHttpsServer","WebSocketServer","randomBytes","apps","basename","readdir","readFile","stat","join","basename","existsSync","homedir","join","readFile","readdir","stat","existsSync","homedir","basename"]}
|