mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-05-10 17:58:15 +00:00
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
58 lines
1.5 KiB
TypeScript
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()
|
|
},
|
|
}
|
|
}
|