mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-05-17 03:56:45 +00:00
* Add Kiro provider and transparent auto-model naming - Add Kiro IDE provider: parses .chat JSON files, estimates tokens, normalizes dot-versioned model IDs for cost lookup - Show "Cursor (auto)", "Copilot (auto)", "Kiro (auto)" in CLI dashboard instead of pretending to know which model was used - Route auto model names through BUILTIN_ALIASES for cost estimation * Fix menubar tabs: add missing providers, show period-scoped costs - Add Kiro, OMP to ProviderFilter enum so installed providers appear as tabs - Merge Cursor + Cursor Agent into single Cursor tab - Tab costs now reflect the selected period (7d/30d/month/all) instead of always showing today - Tab visibility still uses today's provider list so tabs don't disappear when switching to periods with no data * Add accent color picker to menubar with Apple system presets - 9 presets using Apple's exact macOS dark-mode accent colors (Ember, Blue, Purple, Pink, Red, Orange, Yellow, Green, Graphite) - Color picker in header, persisted via UserDefaults - "Burn" text stays fixed ember regardless of accent - ThemeState is MainActor-isolated for thread safety - Picker state lifted to AppStore so it survives .id() tree rebuild - Accessibility labels on all color swatches - Renamed brandAccentDark/brandEmberDeep/brandEmberGlow to match their actual light/deep/glow semantics * Fix review findings: case-sensitive cost lookup, Kiro timestamp guard, cache versioning - Normalize provider dictionary keys to lowercase in tab cost lookup so "Cursor Agent" (title-case from CLI) matches providerKeys - Guard against missing/invalid/epoch startTime in Kiro parser to prevent RangeError crash or 1970-01-01 ghost entries - Bump DAILY_CACHE_VERSION to 4 so upgraded users get a clean recompute with the new auto-model naming (cursor-auto vs default) - Add version field to cursor-results.json cache to invalidate stale entries that still use the old 'default' model name
67 lines
1.7 KiB
TypeScript
67 lines
1.7 KiB
TypeScript
import { readFile, writeFile, mkdir, stat } from 'fs/promises'
|
|
import { join } from 'path'
|
|
import { homedir } from 'os'
|
|
|
|
import type { ParsedProviderCall } from './providers/types.js'
|
|
|
|
const CURSOR_CACHE_VERSION = 2
|
|
|
|
type ResultCache = {
|
|
version?: number
|
|
dbMtimeMs: number
|
|
dbSizeBytes: number
|
|
calls: ParsedProviderCall[]
|
|
}
|
|
|
|
const CACHE_FILE = 'cursor-results.json'
|
|
|
|
function getCacheDir(): string {
|
|
return join(homedir(), '.cache', 'codeburn')
|
|
}
|
|
|
|
function getCachePath(): string {
|
|
return join(getCacheDir(), CACHE_FILE)
|
|
}
|
|
|
|
async function getDbFingerprint(dbPath: string): Promise<{ mtimeMs: number; size: number } | null> {
|
|
try {
|
|
const s = await stat(dbPath)
|
|
return { mtimeMs: s.mtimeMs, size: s.size }
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
export async function readCachedResults(dbPath: string): Promise<ParsedProviderCall[] | null> {
|
|
try {
|
|
const fp = await getDbFingerprint(dbPath)
|
|
if (!fp) return null
|
|
|
|
const raw = await readFile(getCachePath(), 'utf-8')
|
|
const cache = JSON.parse(raw) as ResultCache
|
|
|
|
if (cache.version === CURSOR_CACHE_VERSION && cache.dbMtimeMs === fp.mtimeMs && cache.dbSizeBytes === fp.size) {
|
|
return cache.calls
|
|
}
|
|
return null
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
export async function writeCachedResults(dbPath: string, calls: ParsedProviderCall[]): Promise<void> {
|
|
try {
|
|
const fp = await getDbFingerprint(dbPath)
|
|
if (!fp) return
|
|
|
|
const dir = getCacheDir()
|
|
await mkdir(dir, { recursive: true })
|
|
const cache: ResultCache = {
|
|
version: CURSOR_CACHE_VERSION,
|
|
dbMtimeMs: fp.mtimeMs,
|
|
dbSizeBytes: fp.size,
|
|
calls,
|
|
}
|
|
await writeFile(getCachePath(), JSON.stringify(cache), 'utf-8')
|
|
} catch {}
|
|
}
|