codeburn/src/sqlite.ts
AgentSeal 70931b7269 feat: add Cursor IDE provider with SQLite adapter
Reads token usage from Cursor's local state.vscdb database.
Supports per-request input/output tokens, model tracking,
and incremental caching for large databases.

- better-sqlite3 as optionalDependency (lazy-loaded, no impact on Claude/Codex)
- Parameterized SQL queries, read-only mode, per-row error handling
- Schema detection with clear error on format changes
- Cache layer with timestamp watermark for incremental reads
- Provider colors and [p] key cycling in dashboard
- 39 tests passing, zero regressions
2026-04-15 03:44:43 -07:00

58 lines
1.5 KiB
TypeScript

import { createRequire } from 'node:module'
const require = createRequire(import.meta.url)
type Row = Record<string, unknown>
export type SqliteDatabase = {
query<T extends Row = Row>(sql: string, params?: unknown[]): T[]
close(): void
}
let BetterSqlite3: unknown = null
let loadAttempted = false
let loadError: string | null = null
function loadDriver(): boolean {
if (loadAttempted) return BetterSqlite3 !== null
loadAttempted = true
try {
BetterSqlite3 = require('better-sqlite3')
return true
} catch {
loadError = 'SQLite-based providers (Cursor, OpenCode) require the better-sqlite3 package.\n' +
'Install it with: npm install -g better-sqlite3\n' +
'Then run codeburn again.'
return false
}
}
export function isSqliteAvailable(): boolean {
return loadDriver()
}
export function getSqliteLoadError(): string {
return loadError ?? 'SQLite driver not available'
}
export function openDatabase(path: string): SqliteDatabase {
if (!loadDriver()) {
throw new Error(getSqliteLoadError())
}
const Database = BetterSqlite3 as new (path: string, options?: Record<string, unknown>) => {
prepare(sql: string): { all(...params: unknown[]): Row[] }
close(): void
}
const db = new Database(path, { readonly: true, fileMustExist: true })
return {
query<T extends Row = Row>(sql: string, params: unknown[] = []): T[] {
return db.prepare(sql).all(...params) as T[]
},
close() {
db.close()
},
}
}