@diogonzafe/tokenwatch 0.5.0 → 0.7.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/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../bin/cli.ts","../src/core/sync.ts","../src/core/storage.ts","../src/core/tracker.ts","../src/core/pricing.ts","../src/core/suggestions.ts","../prices.json","../src/dashboard/server.ts","../src/dashboard/data.ts","../src/dashboard/html.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { readFileSync, existsSync } from 'node:fs'\nimport { join, dirname } from 'node:path'\nimport { homedir } from 'node:os'\nimport { fileURLToPath } from 'node:url'\nimport { fetchRemotePrices } from '../src/core/sync.js'\nimport { SqliteStorage } from '../src/core/storage.js'\nimport { createTracker } from '../src/core/tracker.js'\nimport { startDashboardServer } from '../src/dashboard/server.js'\nimport type { PricesFile } from '../src/types/index.js'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nconst DB_PATH = join(homedir(), '.tokenwatch', 'usage.db')\n\nfunction loadBundledPrices(): PricesFile['models'] {\n const pricesPath = join(__dirname, '..', 'prices.json')\n const raw = readFileSync(pricesPath, 'utf8')\n const data = JSON.parse(raw) as PricesFile\n return data.models\n}\n\nasync function cmdSync(): Promise<void> {\n console.log('Fetching latest prices from remote...')\n const result = await fetchRemotePrices()\n if (result) {\n console.log(`✓ Prices updated. ${Object.keys(result.models).length} models cached (updated_at: ${result.updated_at}).`)\n } else {\n console.error('✗ Failed to fetch remote prices. Check your internet connection.')\n process.exit(1)\n }\n}\n\nfunction cmdPrices(): void {\n const models = loadBundledPrices()\n const rows = Object.entries(models).map(([name, price]) => ({\n model: name,\n input: `$${price.input.toFixed(2)}/M`,\n output: `$${price.output.toFixed(2)}/M`,\n }))\n\n const maxName = Math.max(...rows.map((r) => r.model.length), 5)\n const header = `${'Model'.padEnd(maxName)} ${'Input'.padStart(12)} ${'Output'.padStart(12)}`\n const sep = '-'.repeat(header.length)\n\n console.log(header)\n console.log(sep)\n for (const row of rows) {\n console.log(`${row.model.padEnd(maxName)} ${row.input.padStart(12)} ${row.output.padStart(12)}`)\n }\n}\n\nasync function cmdReport(): Promise<void> {\n if (!existsSync(DB_PATH)) {\n console.log(`No SQLite database found at ${DB_PATH}`)\n console.log('Start your app with storage: \\'sqlite\\' to begin recording usage.')\n return\n }\n\n let storage: SqliteStorage\n try {\n storage = new SqliteStorage(DB_PATH)\n } catch {\n console.error('Failed to open SQLite database. Is better-sqlite3 installed?')\n console.error('Run: npm install better-sqlite3')\n process.exit(1)\n }\n\n const tracker = createTracker({ storage, syncPrices: false })\n const report = await tracker.getReport()\n\n if (report.totalCostUSD === 0 && Object.keys(report.byModel).length === 0) {\n console.log('No usage recorded yet.')\n return\n }\n\n console.log('\\n── tokenwatch report ──────────────────────────────')\n console.log(` Total cost: $${report.totalCostUSD.toFixed(6)} USD`)\n console.log(` Total tokens: ${report.totalTokens.input.toLocaleString()} in / ${report.totalTokens.output.toLocaleString()} out`)\n console.log(` Period: ${report.period.from} → ${report.period.to}`)\n if (report.pricesUpdatedAt) {\n console.log(` Prices as of: ${report.pricesUpdatedAt}`)\n }\n\n if (Object.keys(report.byModel).length > 0) {\n console.log('\\n By model:')\n for (const [model, stats] of Object.entries(report.byModel)) {\n console.log(` ${model.padEnd(30)} $${stats.costUSD.toFixed(6)} (${stats.calls} calls)`)\n }\n }\n\n if (Object.keys(report.byUser).length > 0) {\n console.log('\\n By user:')\n for (const [user, stats] of Object.entries(report.byUser)) {\n console.log(` ${user.padEnd(30)} $${stats.costUSD.toFixed(6)} (${stats.calls} calls)`)\n }\n }\n\n if (Object.keys(report.bySession).length > 0) {\n console.log('\\n By session:')\n for (const [session, stats] of Object.entries(report.bySession)) {\n console.log(` ${session.padEnd(30)} $${stats.costUSD.toFixed(6)} (${stats.calls} calls)`)\n }\n }\n\n if (Object.keys(report.byFeature).length > 0) {\n console.log('\\n By feature:')\n for (const [feature, stats] of Object.entries(report.byFeature)) {\n console.log(` ${feature.padEnd(30)} $${stats.costUSD.toFixed(6)} (${stats.calls} calls)`)\n }\n }\n\n console.log('───────────────────────────────────────────────────\\n')\n}\n\nasync function cmdDashboard(port: number): Promise<void> {\n if (!existsSync(DB_PATH)) {\n console.log(`No SQLite database found at ${DB_PATH}`)\n console.log(\"Start your app with storage: 'sqlite' to begin recording usage.\")\n process.exit(1)\n }\n\n let storage: SqliteStorage\n try {\n storage = new SqliteStorage(DB_PATH)\n } catch {\n console.error('Failed to open SQLite database. Is better-sqlite3 installed?')\n console.error('Run: npm install better-sqlite3')\n process.exit(1)\n }\n\n startDashboardServer(storage, port)\n}\n\nfunction cmdHelp(): void {\n console.log(`\ntokenwatch — CLI\n\nCommands:\n sync Fetch and cache latest model prices from remote\n prices List all bundled models and their current prices\n report Show last saved usage report (requires SQLite storage)\n dashboard [--port N] Open local web dashboard (default port: 4242)\n help Show this help message\n`.trim())\n}\n\nasync function main(): Promise<void> {\n const [, , cmd, ...args] = process.argv\n\n switch (cmd) {\n case 'sync':\n await cmdSync()\n break\n case 'prices':\n cmdPrices()\n break\n case 'report':\n await cmdReport()\n break\n case 'dashboard': {\n const portFlagIdx = args.indexOf('--port')\n const port = portFlagIdx !== -1 ? parseInt(args[portFlagIdx + 1] ?? '4242', 10) : 4242\n await cmdDashboard(port)\n break\n }\n case 'help':\n case undefined:\n cmdHelp()\n break\n default:\n console.error(`Unknown command: ${cmd}\\nRun \"tokenwatch help\" for usage.`)\n process.exit(1)\n }\n}\n\nmain().catch((err: unknown) => {\n console.error(err)\n process.exit(1)\n})\n","import { readFile, writeFile, mkdir } from 'node:fs/promises'\nimport { existsSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport type { PricesFile, PriceMap } from '../types/index.js'\n\nconst CACHE_DIR = join(homedir(), '.tokenwatch')\nconst CACHE_FILE = join(CACHE_DIR, 'prices.json')\nconst CACHE_TTL_MS = 24 * 60 * 60 * 1000 // 24 hours\nconst REMOTE_URL =\n 'https://raw.githubusercontent.com/diogonzafe/tokenwatch/main/prices.json'\n\nexport interface PricesResult {\n models: PriceMap\n updated_at: string\n}\n\nexport async function fetchRemotePrices(url = REMOTE_URL): Promise<PricesResult | null> {\n try {\n const res = await fetch(url, { signal: AbortSignal.timeout(8_000) })\n if (!res.ok) return null\n const data = (await res.json()) as PricesFile\n if (!data?.models) return null\n await persistCache(data)\n return { models: data.models, updated_at: data.updated_at ?? '' }\n } catch {\n return null\n }\n}\n\nexport async function loadCachedPrices(): Promise<PricesResult | null> {\n if (!existsSync(CACHE_FILE)) return null\n try {\n const raw = await readFile(CACHE_FILE, 'utf8')\n const data = JSON.parse(raw) as PricesFile & { _cachedAt?: number }\n const age = Date.now() - (data._cachedAt ?? 0)\n if (age > CACHE_TTL_MS) return null\n if (!data.models) return null\n return { models: data.models, updated_at: data.updated_at ?? '' }\n } catch {\n return null\n }\n}\n\nasync function persistCache(data: PricesFile): Promise<void> {\n try {\n await mkdir(CACHE_DIR, { recursive: true })\n const payload = { ...data, _cachedAt: Date.now() }\n await writeFile(CACHE_FILE, JSON.stringify(payload, null, 2), 'utf8')\n } catch {\n // best-effort — never throw\n }\n}\n\n/**\n * Returns the best available remote price result:\n * 1. Valid local cache (< 24h)\n * 2. Fresh remote fetch (also updates cache)\n * 3. null if both fail\n */\nexport async function getRemotePrices(): Promise<PricesResult | null> {\n const cached = await loadCachedPrices()\n if (cached) return cached\n return fetchRemotePrices()\n}\n","import { createRequire } from 'node:module'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { mkdirSync } from 'node:fs'\nimport type { IStorage, UsageEntry } from '../types/index.js'\n\n// ─── Memory storage ───────────────────────────────────────────────────────────\n\nexport class MemoryStorage implements IStorage {\n private entries: UsageEntry[] = []\n\n record(entry: UsageEntry): void {\n this.entries.push(entry)\n }\n\n getAll(): UsageEntry[] {\n return [...this.entries]\n }\n\n clearAll(): void {\n this.entries = []\n }\n\n clearSession(sessionId: string): void {\n this.entries = this.entries.filter((e) => e.sessionId !== sessionId)\n }\n}\n\n// ─── SQLite storage ───────────────────────────────────────────────────────────\n\nconst DB_DIR = join(homedir(), '.tokenwatch')\nconst DB_PATH = join(DB_DIR, 'usage.db')\n\nexport class SqliteStorage implements IStorage {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private db: any\n\n constructor(dbPath = DB_PATH) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let BetterSqlite3: any\n try {\n // In CJS context globalThis.require is the native require; in ESM use createRequire.\n // This makes the lazy load work in both output formats produced by tsup.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const req: NodeRequire =\n typeof (globalThis as any).require === 'function'\n ? // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (globalThis as any).require\n : createRequire(import.meta.url)\n BetterSqlite3 = req('better-sqlite3')\n } catch {\n throw new Error(\n '[tokenwatch] SQLite storage requires better-sqlite3. ' +\n 'Run: npm install better-sqlite3',\n )\n }\n\n mkdirSync(DB_DIR, { recursive: true })\n this.db = new BetterSqlite3(dbPath)\n this.migrate()\n }\n\n private migrate(): void {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS usage (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n model TEXT NOT NULL,\n input_tokens INTEGER NOT NULL,\n output_tokens INTEGER NOT NULL,\n reasoning_tokens INTEGER NOT NULL DEFAULT 0,\n cached_tokens INTEGER NOT NULL DEFAULT 0,\n cache_creation_tokens INTEGER NOT NULL DEFAULT 0,\n cost_usd REAL NOT NULL,\n session_id TEXT,\n user_id TEXT,\n feature TEXT,\n timestamp TEXT NOT NULL\n )\n `)\n // Incremental migrations for databases created before v0.2.0 / v0.3.0\n const cols = (this.db.prepare(`PRAGMA table_info(usage)`).all() as Array<{ name: string }>)\n .map((c) => c.name)\n if (!cols.includes('reasoning_tokens')) {\n this.db.exec(`ALTER TABLE usage ADD COLUMN reasoning_tokens INTEGER NOT NULL DEFAULT 0`)\n }\n if (!cols.includes('feature')) {\n this.db.exec(`ALTER TABLE usage ADD COLUMN feature TEXT`)\n }\n if (!cols.includes('cached_tokens')) {\n this.db.exec(`ALTER TABLE usage ADD COLUMN cached_tokens INTEGER NOT NULL DEFAULT 0`)\n }\n if (!cols.includes('cache_creation_tokens')) {\n this.db.exec(`ALTER TABLE usage ADD COLUMN cache_creation_tokens INTEGER NOT NULL DEFAULT 0`)\n }\n }\n\n record(entry: UsageEntry): void {\n this.db\n .prepare(\n `INSERT INTO usage\n (model, input_tokens, output_tokens, reasoning_tokens, cached_tokens, cache_creation_tokens,\n cost_usd, session_id, user_id, feature, timestamp)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n )\n .run(\n entry.model,\n entry.inputTokens,\n entry.outputTokens,\n entry.reasoningTokens ?? 0,\n entry.cachedTokens ?? 0,\n entry.cacheCreationTokens ?? 0,\n entry.costUSD,\n entry.sessionId ?? null,\n entry.userId ?? null,\n entry.feature ?? null,\n entry.timestamp,\n )\n }\n\n getAll(): UsageEntry[] {\n const rows = this.db.prepare('SELECT * FROM usage ORDER BY timestamp ASC').all() as Array<{\n model: string\n input_tokens: number\n output_tokens: number\n reasoning_tokens: number\n cached_tokens: number\n cache_creation_tokens: number\n cost_usd: number\n session_id: string | null\n user_id: string | null\n feature: string | null\n timestamp: string\n }>\n\n return rows.map((r) => ({\n model: r.model,\n inputTokens: r.input_tokens,\n outputTokens: r.output_tokens,\n ...(r.reasoning_tokens > 0 && { reasoningTokens: r.reasoning_tokens }),\n ...(r.cached_tokens > 0 && { cachedTokens: r.cached_tokens }),\n ...(r.cache_creation_tokens > 0 && { cacheCreationTokens: r.cache_creation_tokens }),\n costUSD: r.cost_usd,\n ...(r.session_id != null && { sessionId: r.session_id }),\n ...(r.user_id != null && { userId: r.user_id }),\n ...(r.feature != null && { feature: r.feature }),\n timestamp: r.timestamp,\n }))\n }\n\n clearAll(): void {\n this.db.exec('DELETE FROM usage')\n }\n\n clearSession(sessionId: string): void {\n this.db.prepare('DELETE FROM usage WHERE session_id = ?').run(sessionId)\n }\n}\n\n// ─── Factory ──────────────────────────────────────────────────────────────────\n\nexport function createStorage(type: 'memory' | 'sqlite'): IStorage {\n if (type === 'sqlite') return new SqliteStorage()\n return new MemoryStorage()\n}\n","import { z } from 'zod'\nimport type {\n Tracker,\n TrackerConfig,\n UsageEntry,\n Report,\n ReportOptions,\n CostForecast,\n ForecastOptions,\n ModelStats,\n SessionStats,\n UserStats,\n FeatureStats,\n ModelPrice,\n PriceMap,\n IStorage,\n BudgetConfig,\n} from '../types/index.js'\nimport { resolvePrice, findPrice, calculateCost } from './pricing.js'\nimport { maybeSuggestCheaperModel } from './suggestions.js'\nimport { createStorage } from './storage.js'\nimport { getRemotePrices } from './sync.js'\nimport bundledPricesFile from '../../prices.json' assert { type: 'json' }\n\nconst bundledPrices: PriceMap = bundledPricesFile.models as PriceMap\nconst bundledUpdatedAt: string = (bundledPricesFile as { updated_at?: string }).updated_at ?? ''\n\n// ─── Config validation schema ─────────────────────────────────────────────────\n\nconst ModelPriceSchema = z.object({\n input: z.number().nonnegative(),\n output: z.number().nonnegative(),\n cachedInput: z.number().nonnegative().optional(),\n cacheCreationInput: z.number().nonnegative().optional(),\n maxInputTokens: z.number().positive().optional(),\n})\n\nconst BudgetConfigSchema = z.object({\n threshold: z.number().positive(),\n webhookUrl: z.string().url(),\n mode: z.enum(['once', 'always']).optional().default('once'),\n})\n\n// storage can be a string enum or an IStorage instance — validated separately\nconst TrackerConfigSchema = z.object({\n storage: z.union([z.enum(['memory', 'sqlite']), z.custom<IStorage>((v) => {\n return (\n v !== null &&\n typeof v === 'object' &&\n typeof (v as IStorage).record === 'function' &&\n typeof (v as IStorage).getAll === 'function' &&\n typeof (v as IStorage).clearAll === 'function' &&\n typeof (v as IStorage).clearSession === 'function'\n )\n })]).optional().default('memory'),\n alertThreshold: z.number().positive().optional(),\n webhookUrl: z.string().url().optional(),\n syncPrices: z.boolean().optional().default(true),\n customPrices: z.record(z.string(), ModelPriceSchema).optional(),\n warnIfStaleAfterHours: z.number().nonnegative().optional().default(72),\n budgets: z.object({\n perUser: BudgetConfigSchema.optional(),\n perSession: BudgetConfigSchema.optional(),\n }).optional(),\n suggestions: z.boolean().optional().default(false),\n})\n\nexport function createTracker(config: TrackerConfig = {}): Tracker {\n const parsed = TrackerConfigSchema.safeParse(config)\n if (!parsed.success) {\n const issues = parsed.error.issues.map((i) => ` ${i.path.join('.')}: ${i.message}`).join('\\n')\n throw new Error(`[tokenwatch] Invalid config:\\n${issues}`)\n }\n\n const {\n storage: storageOption,\n alertThreshold,\n webhookUrl,\n syncPrices,\n customPrices,\n warnIfStaleAfterHours,\n budgets,\n suggestions,\n } = parsed.data\n\n const storage: IStorage =\n typeof storageOption === 'object'\n ? storageOption\n : createStorage(storageOption)\n\n // Fetch remote prices in the background — bundled prices are used as fallback\n // until the sync resolves. Negligible overhead added to createTracker().\n let remotePrices: PriceMap | undefined\n let pricesUpdatedAt: string = bundledUpdatedAt\n if (syncPrices) {\n getRemotePrices()\n .then((result) => {\n if (result) {\n remotePrices = result.models\n pricesUpdatedAt = result.updated_at\n }\n })\n .catch(() => {\n // best-effort — bundled prices remain in use\n })\n }\n\n // Warn if prices are stale (checked lazily on first access)\n let stalenessChecked = false\n function maybeWarnStaleness(): void {\n if (stalenessChecked || !warnIfStaleAfterHours) return\n stalenessChecked = true\n if (!pricesUpdatedAt) return\n try {\n const updatedMs = new Date(pricesUpdatedAt).getTime()\n const ageHours = (Date.now() - updatedMs) / (1000 * 60 * 60)\n if (ageHours > warnIfStaleAfterHours) {\n console.warn(\n `[tokenwatch] Price data is ${Math.round(ageHours)}h old (updated_at: ${pricesUpdatedAt}). ` +\n `Run \"tokenwatch sync\" to refresh, or set warnIfStaleAfterHours: 0 to suppress.`,\n )\n }\n } catch {\n // best-effort\n }\n }\n\n let alertFired = false\n const firedUserAlerts = new Set<string>()\n const firedSessionAlerts = new Set<string>()\n const startedAt = new Date().toISOString()\n\n function resolveModelPrice(model: string) {\n maybeWarnStaleness()\n return resolvePrice(model, {\n bundledPrices,\n ...(customPrices !== undefined && { customPrices: customPrices as PriceMap }),\n ...(remotePrices !== undefined && { remotePrices }),\n })\n }\n\n function track(entry: Omit<UsageEntry, 'costUSD' | 'timestamp'>): void {\n const price = resolveModelPrice(entry.model)\n const costUSD = calculateCost(\n entry.inputTokens,\n entry.outputTokens,\n price,\n entry.cachedTokens,\n entry.cacheCreationTokens,\n )\n const full: UsageEntry = {\n ...entry,\n costUSD,\n timestamp: new Date().toISOString(),\n }\n storage.record(full)\n maybeFireAlerts(full)\n if (suggestions) {\n maybeSuggestCheaperModel(entry.model, costUSD, entry.inputTokens, entry.outputTokens, {\n bundledPrices,\n ...(customPrices !== undefined && { customPrices: customPrices as PriceMap }),\n ...(remotePrices !== undefined && { remotePrices }),\n })\n }\n }\n\n function maybeFireAlerts(entry: UsageEntry): void {\n // Global threshold alert\n if (alertThreshold && webhookUrl && !alertFired) {\n alertFired = true\n Promise.resolve(storage.getAll()).then((entries) => {\n const total = computeTotal(entries)\n if (total < alertThreshold!) {\n alertFired = false\n return\n }\n fireWebhook(webhookUrl!, {\n text: `[tokenwatch] Alert: total cost reached $${total.toFixed(4)} USD (threshold: $${alertThreshold})`,\n })\n }).catch(() => {\n alertFired = false\n })\n }\n\n // Per-user budget alert\n if (budgets?.perUser && entry.userId) {\n const cfg = budgets.perUser\n const uid = entry.userId\n if (cfg.mode === 'always' || !firedUserAlerts.has(uid)) {\n // Claim the slot synchronously before going async — prevents double-fire\n if (cfg.mode !== 'always') firedUserAlerts.add(uid)\n Promise.resolve(storage.getAll()).then((entries) => {\n const userCost = entries\n .filter((e) => e.userId === uid)\n .reduce((s, e) => s + e.costUSD, 0)\n if (userCost >= cfg.threshold) {\n fireWebhook(cfg.webhookUrl, {\n text: `[tokenwatch] Budget alert: user \"${uid}\" reached $${userCost.toFixed(4)} USD (threshold: $${cfg.threshold})`,\n })\n } else {\n if (cfg.mode !== 'always') firedUserAlerts.delete(uid) // release — threshold not yet met\n }\n }).catch(() => {\n if (cfg.mode !== 'always') firedUserAlerts.delete(uid) // release on storage error\n })\n }\n }\n\n // Per-session budget alert\n if (budgets?.perSession && entry.sessionId) {\n const cfg = budgets.perSession\n const sid = entry.sessionId\n if (cfg.mode === 'always' || !firedSessionAlerts.has(sid)) {\n // Claim the slot synchronously before going async — prevents double-fire\n if (cfg.mode !== 'always') firedSessionAlerts.add(sid)\n Promise.resolve(storage.getAll()).then((entries) => {\n const sessionCost = entries\n .filter((e) => e.sessionId === sid)\n .reduce((s, e) => s + e.costUSD, 0)\n if (sessionCost >= cfg.threshold) {\n fireWebhook(cfg.webhookUrl, {\n text: `[tokenwatch] Budget alert: session \"${sid}\" reached $${sessionCost.toFixed(4)} USD (threshold: $${cfg.threshold})`,\n })\n } else {\n if (cfg.mode !== 'always') firedSessionAlerts.delete(sid) // release\n }\n }).catch(() => {\n if (cfg.mode !== 'always') firedSessionAlerts.delete(sid) // release on storage error\n })\n }\n }\n }\n\n function fireWebhook(url: string, payload: { text: string }): void {\n fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n }).catch(() => {\n // fire-and-forget\n })\n }\n\n async function getReport(options?: ReportOptions): Promise<Report> {\n const allEntries = await Promise.resolve(storage.getAll())\n const entries = filterEntries(allEntries, options)\n\n const byModel: Record<string, ModelStats> = {}\n const bySession: Record<string, SessionStats> = {}\n const byUser: Record<string, UserStats> = {}\n const byFeature: Record<string, FeatureStats> = {}\n\n let totalInput = 0\n let totalOutput = 0\n let totalCost = 0\n let periodFrom = options ? (entries[0]?.timestamp ?? startedAt) : startedAt\n let lastTimestamp = periodFrom\n\n for (const e of entries) {\n totalInput += e.inputTokens + (e.cachedTokens ?? 0) + (e.cacheCreationTokens ?? 0)\n totalOutput += e.outputTokens\n totalCost += e.costUSD\n if (e.timestamp > lastTimestamp) lastTimestamp = e.timestamp\n\n // byModel\n const m = (byModel[e.model] ??= {\n costUSD: 0,\n calls: 0,\n tokens: { input: 0, output: 0, reasoning: 0, cached: 0 },\n })\n m.costUSD += e.costUSD\n m.calls += 1\n m.tokens.input += e.inputTokens + (e.cachedTokens ?? 0) + (e.cacheCreationTokens ?? 0)\n m.tokens.output += e.outputTokens\n m.tokens.reasoning += e.reasoningTokens ?? 0\n m.tokens.cached += e.cachedTokens ?? 0\n\n // bySession\n if (e.sessionId) {\n const s = (bySession[e.sessionId] ??= { costUSD: 0, calls: 0 })\n s.costUSD += e.costUSD\n s.calls += 1\n }\n\n // byUser\n if (e.userId) {\n const u = (byUser[e.userId] ??= { costUSD: 0, calls: 0 })\n u.costUSD += e.costUSD\n u.calls += 1\n }\n\n // byFeature\n if (e.feature) {\n const f = (byFeature[e.feature] ??= { costUSD: 0, calls: 0 })\n f.costUSD += e.costUSD\n f.calls += 1\n }\n }\n\n // When filtering, use the actual first entry's timestamp as period.from\n if (options && entries.length > 0) {\n periodFrom = entries[0]?.timestamp ?? periodFrom\n }\n\n return {\n totalCostUSD: totalCost,\n totalTokens: { input: totalInput, output: totalOutput },\n byModel,\n bySession,\n byUser,\n byFeature,\n period: { from: periodFrom, to: lastTimestamp },\n ...(pricesUpdatedAt ? { pricesUpdatedAt } : {}),\n }\n }\n\n async function getCostForecast(options: ForecastOptions = {}): Promise<CostForecast> {\n const windowHours = options.windowHours ?? 24\n const allEntries = await Promise.resolve(storage.getAll())\n\n const now = Date.now()\n const windowStart = now - windowHours * 60 * 60 * 1000\n const windowEntries = allEntries.filter(\n (e) => new Date(e.timestamp).getTime() >= windowStart,\n )\n\n if (windowEntries.length < 2) {\n return {\n burnRatePerHour: 0,\n projectedDailyCostUSD: 0,\n projectedMonthlyCostUSD: 0,\n basedOnHours: 0,\n basedOnPeriod: null,\n }\n }\n\n const first = windowEntries[0]?.timestamp ?? ''\n const last = windowEntries[windowEntries.length - 1]?.timestamp ?? ''\n const actualMs = new Date(last).getTime() - new Date(first).getTime()\n const actualHours = actualMs / (1000 * 60 * 60)\n\n if (actualHours < 0.001) {\n return {\n burnRatePerHour: 0,\n projectedDailyCostUSD: 0,\n projectedMonthlyCostUSD: 0,\n basedOnHours: 0,\n basedOnPeriod: { from: first, to: last },\n }\n }\n\n const totalCost = windowEntries.reduce((s, e) => s + e.costUSD, 0)\n const burnRatePerHour = totalCost / actualHours\n\n return {\n burnRatePerHour,\n projectedDailyCostUSD: burnRatePerHour * 24,\n projectedMonthlyCostUSD: burnRatePerHour * 24 * 30,\n basedOnHours: Math.round(actualHours * 100) / 100,\n basedOnPeriod: { from: first, to: last },\n }\n }\n\n async function reset(): Promise<void> {\n await Promise.resolve(storage.clearAll())\n alertFired = false\n firedUserAlerts.clear()\n firedSessionAlerts.clear()\n }\n\n async function resetSession(sessionId: string): Promise<void> {\n await Promise.resolve(storage.clearSession(sessionId))\n firedSessionAlerts.delete(sessionId)\n }\n\n async function exportJSON(): Promise<string> {\n return JSON.stringify(await getReport(), null, 2)\n }\n\n async function exportCSV(): Promise<string> {\n const entries = await Promise.resolve(storage.getAll())\n const header =\n 'timestamp,model,inputTokens,outputTokens,reasoningTokens,cachedTokens,cacheCreationTokens,costUSD,sessionId,userId,feature'\n const rows = entries.map((e) =>\n [\n csvEscape(e.timestamp),\n csvEscape(e.model),\n e.inputTokens,\n e.outputTokens,\n e.reasoningTokens ?? 0,\n e.cachedTokens ?? 0,\n e.cacheCreationTokens ?? 0,\n e.costUSD.toFixed(8),\n csvEscape(e.sessionId ?? ''),\n csvEscape(e.userId ?? ''),\n csvEscape(e.feature ?? ''),\n ].join(','),\n )\n return [header, ...rows].join('\\n')\n }\n\n function getModelInfo(model: string): ModelPrice | null {\n return findPrice(model, {\n bundledPrices,\n ...(customPrices !== undefined && { customPrices: customPrices as PriceMap }),\n ...(remotePrices !== undefined && { remotePrices }),\n }) ?? null\n }\n\n return {\n track,\n getReport,\n getCostForecast,\n reset,\n resetSession,\n exportJSON,\n exportCSV,\n getModelInfo,\n }\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction computeTotal(entries: UsageEntry[]): number {\n return entries.reduce((sum, e) => sum + e.costUSD, 0)\n}\n\n/** Parse a 'last' shorthand like '24h', '7d' into milliseconds */\nfunction parseLastMs(last: string): number {\n const match = /^(\\d+(?:\\.\\d+)?)(h|d)$/.exec(last.trim())\n if (!match) throw new Error(`[tokenwatch] Invalid \"last\" value: \"${last}\". Use e.g. \"24h\", \"7d\".`)\n const value = parseFloat(match[1] ?? '0')\n const unit = match[2] ?? 'h'\n return unit === 'h' ? value * 60 * 60 * 1000 : value * 24 * 60 * 60 * 1000\n}\n\nfunction filterEntries(entries: UsageEntry[], options?: ReportOptions): UsageEntry[] {\n if (!options) return entries\n\n let sinceMs: number | undefined\n let untilMs: number | undefined\n\n if (options.last) {\n sinceMs = Date.now() - parseLastMs(options.last)\n } else if (options.since) {\n sinceMs = new Date(options.since).getTime()\n }\n if (options.until) {\n untilMs = new Date(options.until).getTime()\n }\n\n if (sinceMs === undefined && untilMs === undefined) return entries\n\n return entries.filter((e) => {\n const ts = new Date(e.timestamp).getTime()\n if (sinceMs !== undefined && ts < sinceMs) return false\n if (untilMs !== undefined && ts > untilMs) return false\n return true\n })\n}\n\n/** Wrap a CSV field value in double-quotes if it contains commas, quotes, or newlines. */\nfunction csvEscape(value: string): string {\n if (value.includes(',') || value.includes('\"') || value.includes('\\n')) {\n return `\"${value.replace(/\"/g, '\"\"')}\"`\n }\n return value\n}\n","import type { ModelPrice, PriceMap } from '../types/index.js'\n\n/**\n * Resolve price for a model using 3-layer priority:\n * 1. customPrices (user override)\n * 2. remotePrices (synced from GitHub, cached 24h)\n * 3. bundledPrices (always-present fallback)\n *\n * Falls back to zero-cost with a console warning when model is not found anywhere.\n */\nexport function resolvePrice(\n model: string,\n layers: {\n customPrices?: PriceMap\n remotePrices?: PriceMap\n bundledPrices: PriceMap\n },\n): ModelPrice {\n const { customPrices, remotePrices, bundledPrices } = layers\n\n const found =\n lookupInMap(model, customPrices) ??\n lookupInMap(model, remotePrices) ??\n lookupInMap(model, bundledPrices)\n\n if (found) return found\n\n console.warn(\n `[tokenwatch] Unknown model \"${model}\". Cost will be recorded as $0. ` +\n `Add it via customPrices or update prices with: tokenwatch sync`,\n )\n return { input: 0, output: 0 }\n}\n\n/**\n * Find price for a model without the zero-cost fallback.\n * Returns undefined if the model is not found in any layer.\n */\nexport function findPrice(\n model: string,\n layers: {\n customPrices?: PriceMap\n remotePrices?: PriceMap\n bundledPrices: PriceMap\n },\n): ModelPrice | undefined {\n const { customPrices, remotePrices, bundledPrices } = layers\n return (\n lookupInMap(model, customPrices) ??\n lookupInMap(model, remotePrices) ??\n lookupInMap(model, bundledPrices)\n )\n}\n\n/**\n * Look up a model in a PriceMap using:\n * 1. exact key match\n * 2. prefix match — map key is a prefix of the model string (e.g. \"gpt-4o\" matches \"gpt-4o-2024-11-20\")\n * 3. reverse prefix — model string is a prefix of a map key (unusual, safety net)\n */\nfunction lookupInMap(model: string, map: PriceMap | undefined): ModelPrice | undefined {\n if (!map) return undefined\n\n if (model in map) return map[model]\n\n // prefix match\n for (const key of Object.keys(map)) {\n if (model.startsWith(key) || key.startsWith(model)) {\n return map[key]\n }\n }\n\n return undefined\n}\n\n/**\n * Calculate cost in USD given token counts and per-million-token prices.\n *\n * - `inputTokens` — regular (non-cached) input tokens\n * - `outputTokens` — output tokens (includes reasoning tokens for OpenAI, which are billed as output)\n * - `cachedTokens` — cache-read input tokens (billed at price.cachedInput or full input price if absent)\n * - `cacheCreationTokens` — cache-creation input tokens, Anthropic only (billed at price.cacheCreationInput\n * or 1.25× input price if absent)\n */\nexport function calculateCost(\n inputTokens: number,\n outputTokens: number,\n price: ModelPrice,\n cachedTokens = 0,\n cacheCreationTokens = 0,\n): number {\n const regularInputCost = (inputTokens / 1_000_000) * price.input\n const cachedReadCost = (cachedTokens / 1_000_000) * (price.cachedInput ?? price.input)\n const cacheCreationCost =\n (cacheCreationTokens / 1_000_000) * (price.cacheCreationInput ?? price.input * 1.25)\n const outputCost = (outputTokens / 1_000_000) * price.output\n return regularInputCost + cachedReadCost + cacheCreationCost + outputCost\n}\n","import type { PriceMap } from '../types/index.js'\nimport { calculateCost } from './pricing.js'\n\nconst PROVIDER_PREFIXES = ['gpt-', 'claude-', 'gemini-', 'deepseek-'] as const\n\nfunction getProviderPrefix(model: string): string | undefined {\n return PROVIDER_PREFIXES.find((p) => model.startsWith(p))\n}\n\n/**\n * After a tracked call, check if there is a cheaper model in the same provider family\n * (defined by model name prefix). Logs a hint if savings are strictly greater than 50%.\n * No-ops if the model is unknown, costUSD is zero, or no cheaper candidate is found.\n */\nexport function maybeSuggestCheaperModel(\n model: string,\n costUSD: number,\n inputTokens: number,\n outputTokens: number,\n layers: { bundledPrices: PriceMap; customPrices?: PriceMap; remotePrices?: PriceMap },\n): void {\n if (costUSD <= 0) return\n\n const prefix = getProviderPrefix(model)\n if (!prefix) return\n\n // Merge layers: bundled < remote < custom (custom wins)\n const mergedMap: PriceMap = {\n ...layers.bundledPrices,\n ...(layers.remotePrices ?? {}),\n ...(layers.customPrices ?? {}),\n }\n\n let cheapestModel: string | undefined\n let cheapestCost = Infinity\n\n for (const key of Object.keys(mergedMap)) {\n if (key === model || !key.startsWith(prefix)) continue\n const price = mergedMap[key]\n if (!price) continue\n const candidateCost = calculateCost(inputTokens, outputTokens, price)\n if (candidateCost < cheapestCost) {\n cheapestCost = candidateCost\n cheapestModel = key\n }\n }\n\n // Must be strictly more than 50% cheaper\n if (cheapestModel === undefined || cheapestCost >= costUSD * 0.5) return\n\n const savingsPct = Math.round((1 - cheapestCost / costUSD) * 100)\n console.log(\n `[tokenwatch] Suggestion: ${cheapestModel} could handle this for ~$${cheapestCost.toFixed(4)} (${savingsPct}% cheaper than ${model})`,\n )\n}\n","{\n \"updated_at\": \"2026-04-22\",\n \"source\": \"https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json\",\n \"models\": {\n \"gpt-4o\": {\n \"input\": 2.5,\n \"output\": 10,\n \"cachedInput\": 1.25,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-mini\": {\n \"input\": 0.15,\n \"output\": 0.6,\n \"cachedInput\": 0.075,\n \"maxInputTokens\": 128000\n },\n \"gpt-5\": {\n \"input\": 1.25,\n \"output\": 10,\n \"cachedInput\": 0.125,\n \"maxInputTokens\": 272000\n },\n \"gpt-5-mini\": {\n \"input\": 0.25,\n \"output\": 2,\n \"cachedInput\": 0.025,\n \"maxInputTokens\": 272000\n },\n \"gpt-5-nano\": {\n \"input\": 0.05,\n \"output\": 0.4,\n \"cachedInput\": 0.005,\n \"maxInputTokens\": 272000\n },\n \"claude-opus-4-6\": {\n \"input\": 5,\n \"output\": 25,\n \"cachedInput\": 0.5,\n \"cacheCreationInput\": 6.25,\n \"maxInputTokens\": 1000000\n },\n \"claude-sonnet-4-6\": {\n \"input\": 3,\n \"output\": 15,\n \"cachedInput\": 0.3,\n \"cacheCreationInput\": 3.75,\n \"maxInputTokens\": 1000000\n },\n \"claude-haiku-4-5\": {\n \"input\": 1,\n \"output\": 5,\n \"cachedInput\": 0.1,\n \"cacheCreationInput\": 1.25,\n \"maxInputTokens\": 200000\n },\n \"gemini-2.5-pro\": {\n \"input\": 1.25,\n \"output\": 10,\n \"cachedInput\": 0.125,\n \"maxInputTokens\": 1048576\n },\n \"gemini-2.5-flash\": {\n \"input\": 0.3,\n \"output\": 2.5,\n \"cachedInput\": 0.03,\n \"maxInputTokens\": 1048576\n },\n \"deepseek-chat\": {\n \"input\": 0.28,\n \"output\": 0.42,\n \"cachedInput\": 0.028,\n \"maxInputTokens\": 131072\n },\n \"deepseek-reasoner\": {\n \"input\": 0.28,\n \"output\": 0.42,\n \"cachedInput\": 0.028,\n \"maxInputTokens\": 131072\n },\n \"claude-opus-4-5\": {\n \"input\": 5,\n \"output\": 25,\n \"cachedInput\": 0.5,\n \"cacheCreationInput\": 6.25,\n \"maxInputTokens\": 200000\n },\n \"claude-opus-4-7\": {\n \"input\": 5,\n \"output\": 25,\n \"cachedInput\": 0.5,\n \"cacheCreationInput\": 6.25,\n \"maxInputTokens\": 1000000\n },\n \"claude-opus-4-1\": {\n \"input\": 15,\n \"output\": 75,\n \"cachedInput\": 1.5,\n \"cacheCreationInput\": 18.75,\n \"maxInputTokens\": 200000\n },\n \"claude-sonnet-4-5\": {\n \"input\": 3,\n \"output\": 15,\n \"cachedInput\": 0.3,\n \"cacheCreationInput\": 3.75,\n \"maxInputTokens\": 200000\n },\n \"gpt-oss-120b\": {\n \"input\": 3,\n \"output\": 4.5,\n \"maxInputTokens\": 131072\n },\n \"gpt-3.5-turbo\": {\n \"input\": 0.5,\n \"output\": 1.5,\n \"maxInputTokens\": 16385\n },\n \"gpt-3.5-turbo-0125\": {\n \"input\": 0.5,\n \"output\": 1.5,\n \"maxInputTokens\": 16385\n },\n \"gpt-35-turbo\": {\n \"input\": 0.5,\n \"output\": 1.5,\n \"maxInputTokens\": 4097\n },\n \"gpt-35-turbo-0125\": {\n \"input\": 0.5,\n \"output\": 1.5,\n \"maxInputTokens\": 16384\n },\n \"gpt-35-turbo-1106\": {\n \"input\": 1,\n \"output\": 2,\n \"maxInputTokens\": 16384\n },\n \"gpt-35-turbo-16k\": {\n \"input\": 3,\n \"output\": 4,\n \"maxInputTokens\": 16385\n },\n \"gpt-35-turbo-16k-0613\": {\n \"input\": 3,\n \"output\": 4,\n \"maxInputTokens\": 16385\n },\n \"gpt-4\": {\n \"input\": 30,\n \"output\": 60,\n \"maxInputTokens\": 8192\n },\n \"gpt-4-0125-preview\": {\n \"input\": 10,\n \"output\": 30,\n \"maxInputTokens\": 128000\n },\n \"gpt-4-0613\": {\n \"input\": 30,\n \"output\": 60,\n \"maxInputTokens\": 8192\n },\n \"gpt-4-1106-preview\": {\n \"input\": 10,\n \"output\": 30,\n \"maxInputTokens\": 128000\n },\n \"gpt-4-32k\": {\n \"input\": 60,\n \"output\": 120,\n \"maxInputTokens\": 32768\n },\n \"gpt-4-32k-0613\": {\n \"input\": 60,\n \"output\": 120,\n \"maxInputTokens\": 32768\n },\n \"gpt-4-turbo\": {\n \"input\": 10,\n \"output\": 30,\n \"maxInputTokens\": 128000\n },\n \"gpt-4-turbo-2024-04-09\": {\n \"input\": 10,\n \"output\": 30,\n \"maxInputTokens\": 128000\n },\n \"gpt-4-turbo-vision-preview\": {\n \"input\": 10,\n \"output\": 30,\n \"maxInputTokens\": 128000\n },\n \"gpt-4.1\": {\n \"input\": 2,\n \"output\": 8,\n \"cachedInput\": 0.5,\n \"maxInputTokens\": 1047576\n },\n \"gpt-4.1-2025-04-14\": {\n \"input\": 2,\n \"output\": 8,\n \"cachedInput\": 0.5,\n \"maxInputTokens\": 1047576\n },\n \"gpt-4.1-mini\": {\n \"input\": 0.4,\n \"output\": 1.6,\n \"cachedInput\": 0.1,\n \"maxInputTokens\": 1047576\n },\n \"gpt-4.1-mini-2025-04-14\": {\n \"input\": 0.4,\n \"output\": 1.6,\n \"cachedInput\": 0.1,\n \"maxInputTokens\": 1047576\n },\n \"gpt-4.1-nano\": {\n \"input\": 0.1,\n \"output\": 0.4,\n \"cachedInput\": 0.025,\n \"maxInputTokens\": 1047576\n },\n \"gpt-4.1-nano-2025-04-14\": {\n \"input\": 0.1,\n \"output\": 0.4,\n \"cachedInput\": 0.025,\n \"maxInputTokens\": 1047576\n },\n \"gpt-4.5-preview\": {\n \"input\": 75,\n \"output\": 150,\n \"cachedInput\": 37.5,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-2024-05-13\": {\n \"input\": 5,\n \"output\": 15,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-2024-08-06\": {\n \"input\": 2.5,\n \"output\": 10,\n \"cachedInput\": 1.25,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-2024-11-20\": {\n \"input\": 2.5,\n \"output\": 10,\n \"cachedInput\": 1.25,\n \"maxInputTokens\": 128000\n },\n \"gpt-audio-2025-08-28\": {\n \"input\": 2.5,\n \"output\": 10,\n \"maxInputTokens\": 128000\n },\n \"gpt-audio-1.5-2026-02-23\": {\n \"input\": 2.5,\n \"output\": 10,\n \"maxInputTokens\": 128000\n },\n \"gpt-audio-mini-2025-10-06\": {\n \"input\": 0.6,\n \"output\": 2.4,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-audio-preview-2024-12-17\": {\n \"input\": 2.5,\n \"output\": 10,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-mini-2024-07-18\": {\n \"input\": 0.15,\n \"output\": 0.6,\n \"cachedInput\": 0.075,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-mini-audio-preview-2024-12-17\": {\n \"input\": 0.15,\n \"output\": 0.6,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-mini-realtime-preview-2024-12-17\": {\n \"input\": 0.6,\n \"output\": 2.4,\n \"cachedInput\": 0.3,\n \"maxInputTokens\": 128000\n },\n \"gpt-realtime-2025-08-28\": {\n \"input\": 4,\n \"output\": 16,\n \"cachedInput\": 0.4,\n \"maxInputTokens\": 32000\n },\n \"gpt-realtime-1.5-2026-02-23\": {\n \"input\": 4,\n \"output\": 16,\n \"cachedInput\": 4,\n \"maxInputTokens\": 32000\n },\n \"gpt-realtime-mini-2025-10-06\": {\n \"input\": 0.6,\n \"output\": 2.4,\n \"cachedInput\": 0.06,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-mini-transcribe\": {\n \"input\": 1.25,\n \"output\": 5,\n \"maxInputTokens\": 16000\n },\n \"gpt-4o-realtime-preview-2024-10-01\": {\n \"input\": 5,\n \"output\": 20,\n \"cachedInput\": 2.5,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-realtime-preview-2024-12-17\": {\n \"input\": 5,\n \"output\": 20,\n \"cachedInput\": 2.5,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-transcribe\": {\n \"input\": 2.5,\n \"output\": 10,\n \"maxInputTokens\": 16000\n },\n \"gpt-4o-transcribe-diarize\": {\n \"input\": 2.5,\n \"output\": 10,\n \"maxInputTokens\": 16000\n },\n \"gpt-5.1-2025-11-13\": {\n \"input\": 1.25,\n \"output\": 10,\n \"cachedInput\": 0.125,\n \"maxInputTokens\": 272000\n },\n \"gpt-5.1-chat-2025-11-13\": {\n \"input\": 1.25,\n \"output\": 10,\n \"cachedInput\": 0.125,\n \"maxInputTokens\": 128000\n },\n \"gpt-5.1-codex-2025-11-13\": {\n \"input\": 1.25,\n \"output\": 10,\n \"cachedInput\": 0.125,\n \"maxInputTokens\": 272000\n },\n \"gpt-5.1-codex-mini-2025-11-13\": {\n \"input\": 0.25,\n \"output\": 2,\n \"cachedInput\": 0.025,\n \"maxInputTokens\": 272000\n },\n \"gpt-5-2025-08-07\": {\n \"input\": 1.25,\n \"output\": 10,\n \"cachedInput\": 0.125,\n \"maxInputTokens\": 272000\n },\n \"gpt-5-chat\": {\n \"input\": 1.25,\n \"output\": 10,\n \"cachedInput\": 0.125,\n \"maxInputTokens\": 128000\n },\n \"gpt-5-chat-latest\": {\n \"input\": 1.25,\n \"output\": 10,\n \"cachedInput\": 0.125,\n \"maxInputTokens\": 128000\n },\n \"gpt-5-codex\": {\n \"input\": 1.25,\n \"output\": 10,\n \"cachedInput\": 0.125,\n \"maxInputTokens\": 272000\n },\n \"gpt-5-mini-2025-08-07\": {\n \"input\": 0.25,\n \"output\": 2,\n \"cachedInput\": 0.025,\n \"maxInputTokens\": 272000\n },\n \"gpt-5-nano-2025-08-07\": {\n \"input\": 0.05,\n \"output\": 0.4,\n \"cachedInput\": 0.005,\n \"maxInputTokens\": 272000\n },\n \"gpt-5-pro\": {\n \"input\": 15,\n \"output\": 120,\n \"maxInputTokens\": 128000\n },\n \"gpt-5.1\": {\n \"input\": 1.25,\n \"output\": 10,\n \"cachedInput\": 0.125,\n \"maxInputTokens\": 272000\n },\n \"gpt-5.1-chat\": {\n \"input\": 1.25,\n \"output\": 10,\n \"cachedInput\": 0.125,\n \"maxInputTokens\": 128000\n },\n \"gpt-5.1-codex\": {\n \"input\": 1.25,\n \"output\": 10,\n \"cachedInput\": 0.125,\n \"maxInputTokens\": 272000\n },\n \"gpt-5.1-codex-max\": {\n \"input\": 1.25,\n \"output\": 10,\n \"cachedInput\": 0.125,\n \"maxInputTokens\": 272000\n },\n \"gpt-5.1-codex-mini\": {\n \"input\": 0.25,\n \"output\": 2,\n \"cachedInput\": 0.025,\n \"maxInputTokens\": 272000\n },\n \"gpt-5.2\": {\n \"input\": 1.75,\n \"output\": 14,\n \"cachedInput\": 0.175,\n \"maxInputTokens\": 272000\n },\n \"gpt-5.2-2025-12-11\": {\n \"input\": 1.75,\n \"output\": 14,\n \"cachedInput\": 0.175,\n \"maxInputTokens\": 272000\n },\n \"gpt-5.2-chat\": {\n \"input\": 1.75,\n \"output\": 14,\n \"cachedInput\": 0.175,\n \"maxInputTokens\": 128000\n },\n \"gpt-5.2-chat-2025-12-11\": {\n \"input\": 1.75,\n \"output\": 14,\n \"cachedInput\": 0.175,\n \"maxInputTokens\": 128000\n },\n \"gpt-5.2-codex\": {\n \"input\": 1.75,\n \"output\": 14,\n \"cachedInput\": 0.175,\n \"maxInputTokens\": 272000\n },\n \"gpt-5.3-chat\": {\n \"input\": 1.75,\n \"output\": 14,\n \"cachedInput\": 0.175,\n \"maxInputTokens\": 128000\n },\n \"gpt-5.3-codex\": {\n \"input\": 1.75,\n \"output\": 14,\n \"cachedInput\": 0.175,\n \"maxInputTokens\": 272000\n },\n \"gpt-5.2-pro\": {\n \"input\": 21,\n \"output\": 168,\n \"maxInputTokens\": 272000\n },\n \"gpt-5.2-pro-2025-12-11\": {\n \"input\": 21,\n \"output\": 168,\n \"maxInputTokens\": 272000\n },\n \"gpt-5.4\": {\n \"input\": 2.5,\n \"output\": 15,\n \"cachedInput\": 0.25,\n \"maxInputTokens\": 1050000\n },\n \"gpt-5.4-2026-03-05\": {\n \"input\": 2.5,\n \"output\": 15,\n \"cachedInput\": 0.25,\n \"maxInputTokens\": 1050000\n },\n \"gpt-5.4-pro\": {\n \"input\": 30,\n \"output\": 180,\n \"cachedInput\": 3,\n \"maxInputTokens\": 1050000\n },\n \"gpt-5.4-pro-2026-03-05\": {\n \"input\": 30,\n \"output\": 180,\n \"cachedInput\": 3,\n \"maxInputTokens\": 1050000\n },\n \"gpt-5.4-mini\": {\n \"input\": 0.75,\n \"output\": 4.5,\n \"cachedInput\": 0.075,\n \"maxInputTokens\": 272000\n },\n \"gpt-5.4-nano\": {\n \"input\": 0.2,\n \"output\": 1.25,\n \"cachedInput\": 0.02,\n \"maxInputTokens\": 272000\n },\n \"o1-2024-12-17\": {\n \"input\": 15,\n \"output\": 60,\n \"cachedInput\": 7.5,\n \"maxInputTokens\": 200000\n },\n \"o1-mini\": {\n \"input\": 1.21,\n \"output\": 4.84,\n \"cachedInput\": 0.605,\n \"maxInputTokens\": 128000\n },\n \"o1-mini-2024-09-12\": {\n \"input\": 1.1,\n \"output\": 4.4,\n \"cachedInput\": 0.55,\n \"maxInputTokens\": 128000\n },\n \"o1-preview\": {\n \"input\": 15,\n \"output\": 60,\n \"cachedInput\": 7.5,\n \"maxInputTokens\": 128000\n },\n \"o1-preview-2024-09-12\": {\n \"input\": 15,\n \"output\": 60,\n \"cachedInput\": 7.5,\n \"maxInputTokens\": 128000\n },\n \"o3-2025-04-16\": {\n \"input\": 2,\n \"output\": 8,\n \"cachedInput\": 0.5,\n \"maxInputTokens\": 200000\n },\n \"o3-mini\": {\n \"input\": 1.1,\n \"output\": 4.4,\n \"cachedInput\": 0.55,\n \"maxInputTokens\": 200000\n },\n \"o3-mini-2025-01-31\": {\n \"input\": 1.1,\n \"output\": 4.4,\n \"cachedInput\": 0.55,\n \"maxInputTokens\": 200000\n },\n \"o3-pro\": {\n \"input\": 20,\n \"output\": 80,\n \"maxInputTokens\": 200000\n },\n \"o3-pro-2025-06-10\": {\n \"input\": 20,\n \"output\": 80,\n \"maxInputTokens\": 200000\n },\n \"o4-mini\": {\n \"input\": 1.1,\n \"output\": 4.4,\n \"cachedInput\": 0.275,\n \"maxInputTokens\": 200000\n },\n \"o4-mini-2025-04-16\": {\n \"input\": 1.1,\n \"output\": 4.4,\n \"cachedInput\": 0.275,\n \"maxInputTokens\": 200000\n },\n \"deepseek-v3.2\": {\n \"input\": 0.28,\n \"output\": 0.4,\n \"maxInputTokens\": 163840\n },\n \"deepseek-v3.2-speciale\": {\n \"input\": 0.58,\n \"output\": 1.68,\n \"maxInputTokens\": 163840\n },\n \"deepseek-r1\": {\n \"input\": 0.55,\n \"output\": 2.19,\n \"maxInputTokens\": 65536\n },\n \"deepseek-v3\": {\n \"input\": 0.27,\n \"output\": 1.1,\n \"cachedInput\": 0.07,\n \"maxInputTokens\": 65536\n },\n \"deepseek-v3-0324\": {\n \"input\": 0.2,\n \"output\": 0.6,\n \"maxInputTokens\": 131072\n },\n \"chatgpt-4o-latest\": {\n \"input\": 5,\n \"output\": 15,\n \"maxInputTokens\": 128000\n },\n \"claude-haiku-4-5-20251001\": {\n \"input\": 1,\n \"output\": 5,\n \"cachedInput\": 0.1,\n \"cacheCreationInput\": 1.25,\n \"maxInputTokens\": 200000\n },\n \"claude-3-7-sonnet-20250219\": {\n \"input\": 3,\n \"output\": 15,\n \"cachedInput\": 0.3,\n \"cacheCreationInput\": 3.75,\n \"maxInputTokens\": 200000\n },\n \"claude-3-haiku-20240307\": {\n \"input\": 0.25,\n \"output\": 1.25,\n \"cachedInput\": 0.03,\n \"cacheCreationInput\": 0.3,\n \"maxInputTokens\": 200000\n },\n \"claude-3-opus-20240229\": {\n \"input\": 15,\n \"output\": 75,\n \"cachedInput\": 1.5,\n \"cacheCreationInput\": 18.75,\n \"maxInputTokens\": 200000\n },\n \"claude-4-opus-20250514\": {\n \"input\": 15,\n \"output\": 75,\n \"cachedInput\": 1.5,\n \"cacheCreationInput\": 18.75,\n \"maxInputTokens\": 200000\n },\n \"claude-4-sonnet-20250514\": {\n \"input\": 3,\n \"output\": 15,\n \"cachedInput\": 0.3,\n \"cacheCreationInput\": 3.75,\n \"maxInputTokens\": 1000000\n },\n \"claude-sonnet-4-5-20250929\": {\n \"input\": 3,\n \"output\": 15,\n \"cachedInput\": 0.3,\n \"cacheCreationInput\": 3.75,\n \"maxInputTokens\": 200000\n },\n \"claude-sonnet-4-5-20250929-v1:0\": {\n \"input\": 3,\n \"output\": 15,\n \"cachedInput\": 0.3,\n \"cacheCreationInput\": 3.75,\n \"maxInputTokens\": 200000\n },\n \"claude-opus-4-1-20250805\": {\n \"input\": 15,\n \"output\": 75,\n \"cachedInput\": 1.5,\n \"cacheCreationInput\": 18.75,\n \"maxInputTokens\": 200000\n },\n \"claude-opus-4-20250514\": {\n \"input\": 15,\n \"output\": 75,\n \"cachedInput\": 1.5,\n \"cacheCreationInput\": 18.75,\n \"maxInputTokens\": 200000\n },\n \"claude-opus-4-5-20251101\": {\n \"input\": 5,\n \"output\": 25,\n \"cachedInput\": 0.5,\n \"cacheCreationInput\": 6.25,\n \"maxInputTokens\": 200000\n },\n \"claude-opus-4-6-20260205\": {\n \"input\": 5,\n \"output\": 25,\n \"cachedInput\": 0.5,\n \"cacheCreationInput\": 6.25,\n \"maxInputTokens\": 1000000\n },\n \"claude-opus-4-7-20260416\": {\n \"input\": 5,\n \"output\": 25,\n \"cachedInput\": 0.5,\n \"cacheCreationInput\": 6.25,\n \"maxInputTokens\": 1000000\n },\n \"claude-sonnet-4-20250514\": {\n \"input\": 3,\n \"output\": 15,\n \"cachedInput\": 0.3,\n \"cacheCreationInput\": 3.75,\n \"maxInputTokens\": 1000000\n },\n \"codex-mini-latest\": {\n \"input\": 1.5,\n \"output\": 6,\n \"cachedInput\": 0.375,\n \"maxInputTokens\": 200000\n },\n \"deepseek-ai/deepseek-r1\": {\n \"input\": 3,\n \"output\": 7,\n \"maxInputTokens\": 128000\n },\n \"deepseek-ai/deepseek-r1-0528\": {\n \"input\": 135000,\n \"output\": 540000,\n \"maxInputTokens\": 161000\n },\n \"deepseek-ai/deepseek-r1-0528-turbo\": {\n \"input\": 1,\n \"output\": 3,\n \"maxInputTokens\": 32768\n },\n \"deepseek-ai/deepseek-r1-distill-llama-70b\": {\n \"input\": 0.25,\n \"output\": 0.75,\n \"maxInputTokens\": 128000\n },\n \"deepseek-ai/deepseek-r1-distill-qwen-32b\": {\n \"input\": 0.15,\n \"output\": 0.15\n },\n \"deepseek-ai/deepseek-r1-turbo\": {\n \"input\": 1,\n \"output\": 3,\n \"maxInputTokens\": 40960\n },\n \"deepseek-ai/deepseek-v3\": {\n \"input\": 1.25,\n \"output\": 1.25,\n \"maxInputTokens\": 65536\n },\n \"deepseek-ai/deepseek-v3-0324\": {\n \"input\": 114000,\n \"output\": 275000,\n \"maxInputTokens\": 161000\n },\n \"deepseek-ai/deepseek-v3.1\": {\n \"input\": 55000,\n \"output\": 165000,\n \"maxInputTokens\": 128000\n },\n \"deepseek-ai/deepseek-v3.1-terminus\": {\n \"input\": 0.27,\n \"output\": 1,\n \"cachedInput\": 0.216,\n \"maxInputTokens\": 163840\n },\n \"deepseek-coder\": {\n \"input\": 0.14,\n \"output\": 0.28,\n \"maxInputTokens\": 128000\n },\n \"gemini-2.0-flash\": {\n \"input\": 0.1,\n \"output\": 0.4,\n \"cachedInput\": 0.025,\n \"maxInputTokens\": 1048576\n },\n \"gemini-2.0-flash-001\": {\n \"input\": 0.1,\n \"output\": 0.4,\n \"cachedInput\": 0.025,\n \"maxInputTokens\": 1048576\n },\n \"gemini-2.0-flash-lite\": {\n \"input\": 0.075,\n \"output\": 0.3,\n \"cachedInput\": 0.01875,\n \"maxInputTokens\": 1048576\n },\n \"gemini-2.0-flash-lite-001\": {\n \"input\": 0.075,\n \"output\": 0.3,\n \"cachedInput\": 0.01875,\n \"maxInputTokens\": 1048576\n },\n \"gemini-2.5-flash-image\": {\n \"input\": 0.3,\n \"output\": 2.5,\n \"cachedInput\": 0.03,\n \"maxInputTokens\": 32768\n },\n \"gemini-3-pro-image-preview\": {\n \"input\": 2,\n \"output\": 12,\n \"maxInputTokens\": 65536\n },\n \"gemini-3.1-flash-image-preview\": {\n \"input\": 0.5,\n \"output\": 3,\n \"maxInputTokens\": 65536\n },\n \"gemini-3.1-flash-lite-preview\": {\n \"input\": 0.25,\n \"output\": 1.5,\n \"cachedInput\": 0.025,\n \"maxInputTokens\": 1048576\n },\n \"gemini-2.5-flash-lite\": {\n \"input\": 0.1,\n \"output\": 0.4,\n \"cachedInput\": 0.01,\n \"maxInputTokens\": 1048576\n },\n \"gemini-2.5-flash-lite-preview-09-2025\": {\n \"input\": 0.1,\n \"output\": 0.4,\n \"cachedInput\": 0.01,\n \"maxInputTokens\": 1048576\n },\n \"gemini-2.5-flash-preview-09-2025\": {\n \"input\": 0.3,\n \"output\": 2.5,\n \"cachedInput\": 0.075,\n \"maxInputTokens\": 1048576\n },\n \"gemini-live-2.5-flash-preview-native-audio-09-2025\": {\n \"input\": 0.3,\n \"output\": 2,\n \"cachedInput\": 0.075,\n \"maxInputTokens\": 1048576\n },\n \"gemini-2.5-flash-lite-preview-06-17\": {\n \"input\": 0.1,\n \"output\": 0.4,\n \"cachedInput\": 0.025,\n \"maxInputTokens\": 1048576\n },\n \"gemini-3-pro-preview\": {\n \"input\": 2,\n \"output\": 12,\n \"cachedInput\": 0.2,\n \"maxInputTokens\": 1048576\n },\n \"gemini-3.1-pro-preview\": {\n \"input\": 2,\n \"output\": 12,\n \"cachedInput\": 0.2,\n \"maxInputTokens\": 1048576\n },\n \"gemini-3.1-pro-preview-customtools\": {\n \"input\": 2,\n \"output\": 12,\n \"cachedInput\": 0.2,\n \"maxInputTokens\": 1048576\n },\n \"gemini-3-flash-preview\": {\n \"input\": 0.5,\n \"output\": 3,\n \"cachedInput\": 0.05,\n \"maxInputTokens\": 1048576\n },\n \"gemini-robotics-er-1.5-preview\": {\n \"input\": 0.3,\n \"output\": 2.5,\n \"maxInputTokens\": 1048576\n },\n \"gemini-2.5-computer-use-preview-10-2025\": {\n \"input\": 1.25,\n \"output\": 10,\n \"maxInputTokens\": 128000\n },\n \"gemini-flash-latest\": {\n \"input\": 0.3,\n \"output\": 2.5,\n \"cachedInput\": 0.03,\n \"maxInputTokens\": 1048576\n },\n \"gemini-flash-lite-latest\": {\n \"input\": 0.1,\n \"output\": 0.4,\n \"cachedInput\": 0.01,\n \"maxInputTokens\": 1048576\n },\n \"gemini-gemma-2-27b-it\": {\n \"input\": 0.35,\n \"output\": 1.05,\n \"maxInputTokens\": 8192\n },\n \"gemini-gemma-2-9b-it\": {\n \"input\": 0.35,\n \"output\": 1.05,\n \"maxInputTokens\": 8192\n },\n \"deepseek-ai/deepseek-v3.2\": {\n \"input\": 0.28,\n \"output\": 0.4,\n \"maxInputTokens\": 163840\n },\n \"gpt-3.5-turbo-1106\": {\n \"input\": 1,\n \"output\": 2,\n \"maxInputTokens\": 16385\n },\n \"gpt-3.5-turbo-16k\": {\n \"input\": 3,\n \"output\": 4,\n \"maxInputTokens\": 16385\n },\n \"gpt-4-0314\": {\n \"input\": 30,\n \"output\": 60,\n \"maxInputTokens\": 8192\n },\n \"gpt-4-turbo-preview\": {\n \"input\": 10,\n \"output\": 30,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-audio-preview\": {\n \"input\": 2.5,\n \"output\": 10,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-audio-preview-2025-06-03\": {\n \"input\": 2.5,\n \"output\": 10,\n \"maxInputTokens\": 128000\n },\n \"gpt-audio\": {\n \"input\": 2.5,\n \"output\": 10,\n \"maxInputTokens\": 128000\n },\n \"gpt-audio-1.5\": {\n \"input\": 2.5,\n \"output\": 10,\n \"maxInputTokens\": 128000\n },\n \"gpt-audio-mini\": {\n \"input\": 0.6,\n \"output\": 2.4,\n \"maxInputTokens\": 128000\n },\n \"gpt-audio-mini-2025-12-15\": {\n \"input\": 0.6,\n \"output\": 2.4,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-mini-audio-preview\": {\n \"input\": 0.15,\n \"output\": 0.6,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-mini-realtime-preview\": {\n \"input\": 0.6,\n \"output\": 2.4,\n \"cachedInput\": 0.3,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-realtime-preview\": {\n \"input\": 5,\n \"output\": 20,\n \"cachedInput\": 2.5,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-realtime-preview-2025-06-03\": {\n \"input\": 5,\n \"output\": 20,\n \"cachedInput\": 2.5,\n \"maxInputTokens\": 128000\n },\n \"gpt-image-1.5\": {\n \"input\": 5,\n \"output\": 10,\n \"cachedInput\": 1.25\n },\n \"gpt-image-1.5-2025-12-16\": {\n \"input\": 5,\n \"output\": 10,\n \"cachedInput\": 1.25\n },\n \"gpt-5.1-chat-latest\": {\n \"input\": 1.25,\n \"output\": 10,\n \"cachedInput\": 0.125,\n \"maxInputTokens\": 128000\n },\n \"gpt-5.2-chat-latest\": {\n \"input\": 1.75,\n \"output\": 14,\n \"cachedInput\": 0.175,\n \"maxInputTokens\": 128000\n },\n \"gpt-5.3-chat-latest\": {\n \"input\": 1.75,\n \"output\": 14,\n \"cachedInput\": 0.175,\n \"maxInputTokens\": 128000\n },\n \"gpt-5-pro-2025-10-06\": {\n \"input\": 15,\n \"output\": 120,\n \"maxInputTokens\": 128000\n },\n \"gpt-realtime\": {\n \"input\": 4,\n \"output\": 16,\n \"cachedInput\": 0.4,\n \"maxInputTokens\": 32000\n },\n \"gpt-realtime-1.5\": {\n \"input\": 4,\n \"output\": 16,\n \"cachedInput\": 0.4,\n \"maxInputTokens\": 32000\n },\n \"gpt-realtime-mini\": {\n \"input\": 0.6,\n \"output\": 2.4,\n \"maxInputTokens\": 128000\n },\n \"deepseek-r1-distill-llama-70b\": {\n \"input\": 0.99,\n \"output\": 0.99,\n \"maxInputTokens\": 8000\n },\n \"deepseek-llama3.3-70b\": {\n \"input\": 0.2,\n \"output\": 0.6,\n \"maxInputTokens\": 131072\n },\n \"deepseek-r1-0528\": {\n \"input\": 0.2,\n \"output\": 0.6,\n \"maxInputTokens\": 131072\n },\n \"deepseek-r1-671b\": {\n \"input\": 0.8,\n \"output\": 0.8,\n \"maxInputTokens\": 131072\n },\n \"deepseek-ai/deepseek-r1-distill-llama-8b\": {\n \"input\": 0.025,\n \"output\": 0.025\n },\n \"deepseek-ai/deepseek-r1-distill-qwen-1.5b\": {\n \"input\": 0.09,\n \"output\": 0.09\n },\n \"deepseek-ai/deepseek-r1-distill-qwen-14b\": {\n \"input\": 0.07,\n \"output\": 0.07\n },\n \"deepseek-ai/deepseek-r1-distill-qwen-7b\": {\n \"input\": 0.2,\n \"output\": 0.2\n },\n \"o1\": {\n \"input\": 15,\n \"output\": 60,\n \"cachedInput\": 7.5,\n \"maxInputTokens\": 200000\n },\n \"o1-pro\": {\n \"input\": 150,\n \"output\": 600,\n \"maxInputTokens\": 200000\n },\n \"o1-pro-2025-03-19\": {\n \"input\": 150,\n \"output\": 600,\n \"maxInputTokens\": 200000\n },\n \"o3\": {\n \"input\": 2,\n \"output\": 8,\n \"cachedInput\": 0.5,\n \"maxInputTokens\": 200000\n },\n \"gpt-oss-20b\": {\n \"input\": 0.09,\n \"output\": 0.36\n },\n \"deepseek-ai/deepseek-r1-0528-tput\": {\n \"input\": 0.55,\n \"output\": 2.19,\n \"maxInputTokens\": 128000\n },\n \"claude-3-5-haiku\": {\n \"input\": 1,\n \"output\": 5,\n \"maxInputTokens\": 200000\n },\n \"claude-3-5-haiku@20241022\": {\n \"input\": 1,\n \"output\": 5,\n \"maxInputTokens\": 200000\n },\n \"claude-haiku-4-5@20251001\": {\n \"input\": 1,\n \"output\": 5,\n \"cachedInput\": 0.1,\n \"cacheCreationInput\": 1.25,\n \"maxInputTokens\": 200000\n },\n \"claude-3-5-sonnet\": {\n \"input\": 3,\n \"output\": 15,\n \"maxInputTokens\": 200000\n },\n \"claude-3-5-sonnet@20240620\": {\n \"input\": 3,\n \"output\": 15,\n \"maxInputTokens\": 200000\n },\n \"claude-3-7-sonnet@20250219\": {\n \"input\": 3,\n \"output\": 15,\n \"cachedInput\": 0.3,\n \"cacheCreationInput\": 3.75,\n \"maxInputTokens\": 200000\n },\n \"claude-3-haiku\": {\n \"input\": 0.25,\n \"output\": 1.25,\n \"maxInputTokens\": 200000\n },\n \"claude-3-haiku@20240307\": {\n \"input\": 0.25,\n \"output\": 1.25,\n \"maxInputTokens\": 200000\n },\n \"claude-3-opus\": {\n \"input\": 15,\n \"output\": 75,\n \"maxInputTokens\": 200000\n },\n \"claude-3-opus@20240229\": {\n \"input\": 15,\n \"output\": 75,\n \"maxInputTokens\": 200000\n },\n \"claude-3-sonnet\": {\n \"input\": 3,\n \"output\": 15,\n \"maxInputTokens\": 200000\n },\n \"claude-3-sonnet@20240229\": {\n \"input\": 3,\n \"output\": 15,\n \"maxInputTokens\": 200000\n },\n \"claude-opus-4\": {\n \"input\": 15,\n \"output\": 75,\n \"cachedInput\": 1.5,\n \"cacheCreationInput\": 18.75,\n \"maxInputTokens\": 200000\n },\n \"claude-opus-4-1@20250805\": {\n \"input\": 15,\n \"output\": 75,\n \"cachedInput\": 1.5,\n \"cacheCreationInput\": 18.75,\n \"maxInputTokens\": 200000\n },\n \"claude-opus-4-5@20251101\": {\n \"input\": 5,\n \"output\": 25,\n \"cachedInput\": 0.5,\n \"cacheCreationInput\": 6.25,\n \"maxInputTokens\": 200000\n },\n \"claude-opus-4-6@default\": {\n \"input\": 5,\n \"output\": 25,\n \"cachedInput\": 0.5,\n \"cacheCreationInput\": 6.25,\n \"maxInputTokens\": 1000000\n },\n \"claude-opus-4-7@default\": {\n \"input\": 5,\n \"output\": 25,\n \"cachedInput\": 0.5,\n \"cacheCreationInput\": 6.25,\n \"maxInputTokens\": 1000000\n },\n \"claude-sonnet-4-5@20250929\": {\n \"input\": 3,\n \"output\": 15,\n \"cachedInput\": 0.3,\n \"cacheCreationInput\": 3.75,\n \"maxInputTokens\": 200000\n },\n \"claude-opus-4@20250514\": {\n \"input\": 15,\n \"output\": 75,\n \"cachedInput\": 1.5,\n \"cacheCreationInput\": 18.75,\n \"maxInputTokens\": 200000\n },\n \"claude-sonnet-4\": {\n \"input\": 3,\n \"output\": 15,\n \"cachedInput\": 0.3,\n \"cacheCreationInput\": 3.75,\n \"maxInputTokens\": 1000000\n },\n \"claude-sonnet-4@20250514\": {\n \"input\": 3,\n \"output\": 15,\n \"cachedInput\": 0.3,\n \"cacheCreationInput\": 3.75,\n \"maxInputTokens\": 1000000\n },\n \"deepseek-ai/deepseek-v3.1-maas\": {\n \"input\": 1.35,\n \"output\": 5.4,\n \"maxInputTokens\": 163840\n },\n \"deepseek-ai/deepseek-v3.2-maas\": {\n \"input\": 0.56,\n \"output\": 1.68,\n \"maxInputTokens\": 163840\n },\n \"deepseek-ai/deepseek-r1-0528-maas\": {\n \"input\": 1.35,\n \"output\": 5.4,\n \"maxInputTokens\": 65336\n },\n \"deepseek-ai/deepseek-ocr-maas\": {\n \"input\": 0.3,\n \"output\": 1.2\n },\n \"deepseek-r1-8b\": {\n \"input\": 0.1,\n \"output\": 0.2,\n \"maxInputTokens\": 65536\n },\n \"deepseek-r1-7b-qwen\": {\n \"input\": 0.08,\n \"output\": 0.15,\n \"maxInputTokens\": 131072\n },\n \"deepseek-coder-6.7b\": {\n \"input\": 0.06,\n \"output\": 0.12,\n \"maxInputTokens\": 16384\n },\n \"gpt-4o-mini-transcribe-2025-03-20\": {\n \"input\": 1.25,\n \"output\": 5,\n \"maxInputTokens\": 16000\n },\n \"gpt-4o-mini-transcribe-2025-12-15\": {\n \"input\": 1.25,\n \"output\": 5,\n \"maxInputTokens\": 16000\n },\n \"gpt-realtime-mini-2025-12-15\": {\n \"input\": 0.6,\n \"output\": 2.4,\n \"cachedInput\": 0.06,\n \"maxInputTokens\": 128000\n },\n \"gemini-2.5-flash-native-audio-latest\": {\n \"input\": 0.3,\n \"output\": 2.5,\n \"maxInputTokens\": 1048576\n },\n \"gemini-2.5-flash-native-audio-preview-09-2025\": {\n \"input\": 0.3,\n \"output\": 2.5,\n \"maxInputTokens\": 1048576\n },\n \"gemini-2.5-flash-native-audio-preview-12-2025\": {\n \"input\": 0.3,\n \"output\": 2.5,\n \"maxInputTokens\": 1048576\n },\n \"gemini-3.1-flash-live-preview\": {\n \"input\": 0.75,\n \"output\": 4.5,\n \"maxInputTokens\": 131072\n },\n \"gemini-pro-latest\": {\n \"input\": 1.25,\n \"output\": 10,\n \"cachedInput\": 0.125,\n \"maxInputTokens\": 1048576\n },\n \"gemini-exp-1206\": {\n \"input\": 0.3,\n \"output\": 2.5,\n \"cachedInput\": 0.03,\n \"maxInputTokens\": 1048576\n },\n \"claude-sonnet-4-6@default\": {\n \"input\": 3,\n \"output\": 15,\n \"cachedInput\": 0.3,\n \"cacheCreationInput\": 3.75,\n \"maxInputTokens\": 1000000\n }\n }\n}\n","import { createServer } from 'node:http'\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport { getDashboardData, getFingerprint } from './data.js'\nimport { getHtml } from './html.js'\nimport type { IStorage } from '../types/index.js'\n\nexport function startDashboardServer(storage: IStorage, port: number): void {\n const server = createServer((req: IncomingMessage, res: ServerResponse) => {\n const url = new URL(req.url ?? '/', `http://localhost:${port}`)\n\n if (req.method === 'GET' && url.pathname === '/') {\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' })\n res.end(getHtml(port))\n return\n }\n\n if (req.method === 'GET' && url.pathname === '/events') {\n const filter = url.searchParams.get('filter') ?? '24h'\n\n res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'Connection': 'keep-alive',\n 'X-Accel-Buffering': 'no',\n })\n res.flushHeaders()\n\n let lastFingerprint = ''\n\n async function sendData(): Promise<void> {\n try {\n const data = await getDashboardData(storage, filter)\n const fp = getFingerprint(data)\n if (fp !== lastFingerprint) {\n lastFingerprint = fp\n res.write(`data: ${JSON.stringify(data)}\\n\\n`)\n }\n } catch {\n // best-effort — storage errors must not crash the server\n }\n }\n\n // Send immediately on connect\n void sendData()\n\n const timer = setInterval(() => { void sendData() }, 3000)\n\n res.on('close', () => {\n clearInterval(timer)\n })\n return\n }\n\n res.writeHead(404, { 'Content-Type': 'text/plain' })\n res.end('Not found')\n })\n\n server.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n console.error(`[tokenwatch] Port ${port} is already in use. Try: tokenwatch dashboard --port <other>`)\n process.exit(1)\n }\n throw err\n })\n\n server.listen(port, () => {\n console.log(`tokenwatch dashboard → http://localhost:${port}`)\n })\n}\n","import type {\n IStorage,\n UsageEntry,\n Report,\n CostForecast,\n ModelStats,\n SessionStats,\n UserStats,\n FeatureStats,\n} from '../types/index.js'\n\nexport interface TimeSeriesBucket {\n bucket: string\n cost: number\n calls: number\n}\n\nexport interface DashboardData {\n report: Report\n forecast: CostForecast\n timeSeries: TimeSeriesBucket[]\n lastUpdated: string\n}\n\n/**\n * Maps a filter string to a Unix ms timestamp (entries before this are excluded).\n * Returns undefined for 'all' or undefined (no filter).\n */\nexport function parseSince(filter: string | undefined): number | undefined {\n if (!filter || filter === 'all') return undefined\n const now = Date.now()\n switch (filter) {\n case '1h': return now - 60 * 60 * 1000\n case '24h': return now - 24 * 60 * 60 * 1000\n case '7d': return now - 7 * 24 * 60 * 60 * 1000\n case '30d': return now - 30 * 24 * 60 * 60 * 1000\n default: return undefined\n }\n}\n\n/**\n * Groups entries into time-series buckets.\n * Bucket size: 1h window → 5min, 24h window → 1h, 7d/30d/all → 1day.\n */\nexport function buildTimeSeries(\n entries: UsageEntry[],\n sinceMs: number | undefined,\n): TimeSeriesBucket[] {\n const now = Date.now()\n const windowMs = sinceMs !== undefined ? now - sinceMs : undefined\n\n let bucketMs: number\n if (windowMs !== undefined && windowMs <= 60 * 60 * 1000) {\n bucketMs = 5 * 60 * 1000 // 5-min buckets for ≤1h window\n } else if (windowMs !== undefined && windowMs <= 24 * 60 * 60 * 1000) {\n bucketMs = 60 * 60 * 1000 // 1h buckets for ≤24h window\n } else {\n bucketMs = 24 * 60 * 60 * 1000 // 1-day buckets for 7d/30d/all\n }\n\n const filtered = sinceMs !== undefined\n ? entries.filter((e) => new Date(e.timestamp).getTime() >= sinceMs)\n : entries\n\n const buckets = new Map<string, TimeSeriesBucket>()\n\n for (const entry of filtered) {\n const ts = new Date(entry.timestamp).getTime()\n const bucketTs = Math.floor(ts / bucketMs) * bucketMs\n const bucketKey = new Date(bucketTs).toISOString()\n const existing = buckets.get(bucketKey)\n if (existing) {\n existing.cost += entry.costUSD\n existing.calls += 1\n } else {\n buckets.set(bucketKey, { bucket: bucketKey, cost: entry.costUSD, calls: 1 })\n }\n }\n\n return Array.from(buckets.values()).sort((a, b) => a.bucket.localeCompare(b.bucket))\n}\n\nexport function getFingerprint(data: DashboardData): string {\n return `${data.report.totalCostUSD.toFixed(8)}-${data.report.totalTokens.input}-${data.timeSeries.length}`\n}\n\nexport async function getDashboardData(\n storage: IStorage,\n filter?: string,\n): Promise<DashboardData> {\n const allEntries = await Promise.resolve(storage.getAll())\n const sinceMs = parseSince(filter)\n\n const entries = sinceMs !== undefined\n ? allEntries.filter((e) => new Date(e.timestamp).getTime() >= sinceMs)\n : allEntries\n\n // ── Build report ────────────────────────────────────────────────────────────\n const byModel: Record<string, ModelStats> = {}\n const bySession: Record<string, SessionStats> = {}\n const byUser: Record<string, UserStats> = {}\n const byFeature: Record<string, FeatureStats> = {}\n let totalInput = 0\n let totalOutput = 0\n let totalCost = 0\n\n for (const e of entries) {\n totalInput += e.inputTokens + (e.cachedTokens ?? 0) + (e.cacheCreationTokens ?? 0)\n totalOutput += e.outputTokens\n totalCost += e.costUSD\n\n const m = (byModel[e.model] ??= {\n costUSD: 0, calls: 0, tokens: { input: 0, output: 0, reasoning: 0, cached: 0 },\n })\n m.costUSD += e.costUSD\n m.calls += 1\n m.tokens.input += e.inputTokens + (e.cachedTokens ?? 0) + (e.cacheCreationTokens ?? 0)\n m.tokens.output += e.outputTokens\n m.tokens.reasoning += e.reasoningTokens ?? 0\n m.tokens.cached += e.cachedTokens ?? 0\n\n if (e.sessionId) {\n const s = (bySession[e.sessionId] ??= { costUSD: 0, calls: 0 })\n s.costUSD += e.costUSD\n s.calls += 1\n }\n\n if (e.userId) {\n const u = (byUser[e.userId] ??= { costUSD: 0, calls: 0 })\n u.costUSD += e.costUSD\n u.calls += 1\n }\n\n if (e.feature) {\n const f = (byFeature[e.feature] ??= { costUSD: 0, calls: 0 })\n f.costUSD += e.costUSD\n f.calls += 1\n }\n }\n\n const now = new Date().toISOString()\n const periodFrom = entries[0]?.timestamp ?? now\n const periodTo = entries[entries.length - 1]?.timestamp ?? now\n\n const report: Report = {\n totalCostUSD: totalCost,\n totalTokens: { input: totalInput, output: totalOutput },\n byModel,\n bySession,\n byUser,\n byFeature,\n period: { from: periodFrom, to: periodTo },\n }\n\n // ── Build forecast (always 24h window over all entries) ─────────────────────\n const forecastWindowMs = 24 * 60 * 60 * 1000\n const windowStart = Date.now() - forecastWindowMs\n const windowEntries = allEntries.filter(\n (e) => new Date(e.timestamp).getTime() >= windowStart,\n )\n\n let forecast: CostForecast\n if (windowEntries.length < 2) {\n forecast = {\n burnRatePerHour: 0,\n projectedDailyCostUSD: 0,\n projectedMonthlyCostUSD: 0,\n basedOnHours: 0,\n basedOnPeriod: null,\n }\n } else {\n const first = windowEntries[0]?.timestamp ?? ''\n const last = windowEntries[windowEntries.length - 1]?.timestamp ?? ''\n const actualMs = new Date(last).getTime() - new Date(first).getTime()\n const actualHours = actualMs / (1000 * 60 * 60)\n if (actualHours < 0.001) {\n forecast = {\n burnRatePerHour: 0,\n projectedDailyCostUSD: 0,\n projectedMonthlyCostUSD: 0,\n basedOnHours: 0,\n basedOnPeriod: { from: first, to: last },\n }\n } else {\n const windowCost = windowEntries.reduce((s, e) => s + e.costUSD, 0)\n const burnRatePerHour = windowCost / actualHours\n forecast = {\n burnRatePerHour,\n projectedDailyCostUSD: burnRatePerHour * 24,\n projectedMonthlyCostUSD: burnRatePerHour * 24 * 30,\n basedOnHours: Math.round(actualHours * 100) / 100,\n basedOnPeriod: { from: first, to: last },\n }\n }\n }\n\n const timeSeries = buildTimeSeries(allEntries, sinceMs)\n\n return { report, forecast, timeSeries, lastUpdated: now }\n}\n","export function getHtml(port: number): string {\n void port // port is used in the SSE URL in the JS below\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\" />\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n<title>tokenwatch dashboard</title>\n<script src=\"https://cdn.jsdelivr.net/npm/chart.js@4/dist/chart.umd.min.js\"></script>\n<style>\n*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\n:root {\n --bg: #0d1117;\n --surface: #161b22;\n --border: #30363d;\n --text: #e6edf3;\n --muted: #8b949e;\n --accent: #58a6ff;\n --green: #3fb950;\n --yellow: #d29922;\n --red: #f85149;\n --r: 6px;\n}\n\nbody {\n background: var(--bg);\n color: var(--text);\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n line-height: 1.5;\n min-height: 100vh;\n}\n\n.container { max-width: 1200px; margin: 0 auto; padding: 24px 16px; }\n\n/* ── Header ──────────────────────────────────────────────────── */\nheader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 24px;\n}\nheader h1 { font-size: 18px; font-weight: 600; letter-spacing: -0.3px; }\nheader h1 span { color: var(--accent); }\n\n.live-badge {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: var(--muted);\n}\n.live-dot {\n width: 8px; height: 8px;\n border-radius: 50%;\n background: var(--green);\n animation: pulse 2s ease-in-out infinite;\n}\n@keyframes pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.3; }\n}\n\n/* ── Tabs ─────────────────────────────────────────────────────── */\n.tabs {\n display: flex;\n gap: 4px;\n margin-bottom: 24px;\n background: var(--surface);\n border: 1px solid var(--border);\n border-radius: var(--r);\n padding: 4px;\n width: fit-content;\n}\n.tab {\n padding: 6px 14px;\n border-radius: calc(var(--r) - 2px);\n border: none;\n background: transparent;\n color: var(--muted);\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.15s, color 0.15s;\n}\n.tab:hover { color: var(--text); background: rgba(255,255,255,0.05); }\n.tab.active { background: var(--accent); color: #fff; }\n\n/* ── Overview cards ───────────────────────────────────────────── */\n.cards {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));\n gap: 12px;\n margin-bottom: 24px;\n}\n.card {\n background: var(--surface);\n border: 1px solid var(--border);\n border-radius: var(--r);\n padding: 16px;\n}\n.card-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; color: var(--muted); margin-bottom: 6px; }\n.card-value { font-size: 22px; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }\n.card-sub { font-size: 11px; color: var(--muted); margin-top: 3px; }\n\n/* ── Chart section ────────────────────────────────────────────── */\n.charts {\n display: grid;\n grid-template-columns: 2fr 1fr;\n gap: 16px;\n margin-bottom: 24px;\n}\n@media (max-width: 768px) { .charts { grid-template-columns: 1fr; } }\n\n.panel {\n background: var(--surface);\n border: 1px solid var(--border);\n border-radius: var(--r);\n padding: 16px;\n}\n.panel-title {\n font-size: 12px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--muted);\n margin-bottom: 14px;\n}\n.chart-wrap { position: relative; height: 220px; }\n\n/* ── Breakdown tables ─────────────────────────────────────────── */\n.section { margin-bottom: 24px; }\n.section-title {\n font-size: 12px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--muted);\n margin-bottom: 10px;\n}\n\ntable {\n width: 100%;\n border-collapse: collapse;\n background: var(--surface);\n border: 1px solid var(--border);\n border-radius: var(--r);\n overflow: hidden;\n font-size: 13px;\n}\nthead { background: rgba(255,255,255,0.03); }\nth {\n padding: 10px 14px;\n text-align: left;\n font-weight: 600;\n color: var(--muted);\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.4px;\n border-bottom: 1px solid var(--border);\n}\nth.num, td.num { text-align: right; }\ntd {\n padding: 10px 14px;\n border-bottom: 1px solid rgba(48,54,61,0.5);\n font-variant-numeric: tabular-nums;\n color: var(--text);\n}\ntbody tr:last-child td { border-bottom: none; }\ntbody tr:hover td { background: rgba(255,255,255,0.02); }\n\n.bar-wrap { width: 80px; height: 6px; background: var(--border); border-radius: 3px; display: inline-block; vertical-align: middle; margin-left: 8px; }\n.bar-fill { height: 100%; border-radius: 3px; background: var(--accent); }\n\n/* ── Forecast ─────────────────────────────────────────────────── */\n.forecast-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n gap: 12px;\n}\n\n/* ── Empty state ──────────────────────────────────────────────── */\n.empty {\n color: var(--muted);\n font-size: 13px;\n padding: 20px 0;\n text-align: center;\n}\n\n/* ── Collapsible ─────────────────────────────────────────────── */\ndetails summary {\n cursor: pointer;\n list-style: none;\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--muted);\n margin-bottom: 10px;\n user-select: none;\n}\ndetails summary::before { content: '▶'; font-size: 10px; transition: transform 0.15s; }\ndetails[open] summary::before { transform: rotate(90deg); }\n\n/* ── Last updated ─────────────────────────────────────────────── */\n.footer {\n font-size: 11px;\n color: var(--muted);\n text-align: center;\n padding: 16px 0 0;\n}\n</style>\n</head>\n<body>\n<div class=\"container\">\n\n <header>\n <h1>token<span>watch</span></h1>\n <div class=\"live-badge\">\n <div class=\"live-dot\"></div>\n <span id=\"live-label\">live</span>\n </div>\n </header>\n\n <div class=\"tabs\">\n <button class=\"tab\" data-filter=\"1h\">1h</button>\n <button class=\"tab active\" data-filter=\"24h\">24h</button>\n <button class=\"tab\" data-filter=\"7d\">7d</button>\n <button class=\"tab\" data-filter=\"30d\">30d</button>\n <button class=\"tab\" data-filter=\"all\">All</button>\n </div>\n\n <div class=\"cards\">\n <div class=\"card\">\n <div class=\"card-label\">Total Cost</div>\n <div class=\"card-value\" id=\"card-cost\">—</div>\n <div class=\"card-sub\" id=\"card-period\"></div>\n </div>\n <div class=\"card\">\n <div class=\"card-label\">Input Tokens</div>\n <div class=\"card-value\" id=\"card-input\">—</div>\n </div>\n <div class=\"card\">\n <div class=\"card-label\">Output Tokens</div>\n <div class=\"card-value\" id=\"card-output\">—</div>\n </div>\n <div class=\"card\">\n <div class=\"card-label\">Total Calls</div>\n <div class=\"card-value\" id=\"card-calls\">—</div>\n </div>\n <div class=\"card\">\n <div class=\"card-label\">Burn Rate</div>\n <div class=\"card-value\" id=\"card-burn\">—</div>\n <div class=\"card-sub\">per hour</div>\n </div>\n </div>\n\n <div class=\"charts\">\n <div class=\"panel\">\n <div class=\"panel-title\">Cost over time</div>\n <div class=\"chart-wrap\">\n <canvas id=\"chart-line\"></canvas>\n </div>\n </div>\n <div class=\"panel\">\n <div class=\"panel-title\">By model</div>\n <div class=\"chart-wrap\">\n <canvas id=\"chart-doughnut\"></canvas>\n </div>\n </div>\n </div>\n\n <div class=\"section\">\n <div class=\"section-title\">Model breakdown</div>\n <div id=\"model-table-wrap\"></div>\n </div>\n\n <details id=\"users-section\" style=\"margin-bottom:24px;\">\n <summary>By user</summary>\n <div id=\"user-table-wrap\"></div>\n </details>\n\n <details id=\"features-section\" style=\"margin-bottom:24px;\">\n <summary>By feature</summary>\n <div id=\"feature-table-wrap\"></div>\n </details>\n\n <div class=\"section\">\n <div class=\"section-title\">Cost forecast</div>\n <div class=\"forecast-grid\">\n <div class=\"card\">\n <div class=\"card-label\">Projected daily</div>\n <div class=\"card-value\" id=\"fc-daily\">—</div>\n <div class=\"card-sub\" id=\"fc-window\"></div>\n </div>\n <div class=\"card\">\n <div class=\"card-label\">Projected monthly</div>\n <div class=\"card-value\" id=\"fc-monthly\">—</div>\n </div>\n <div class=\"card\">\n <div class=\"card-label\">Burn rate / hr</div>\n <div class=\"card-value\" id=\"fc-burn\">—</div>\n </div>\n </div>\n </div>\n\n <div class=\"footer\" id=\"footer-updated\"></div>\n\n</div><!-- /container -->\n\n<script>\n(function () {\n 'use strict';\n\n // ── Palette ───────────────────────────────────────────────────────────\n const PALETTE = [\n '#58a6ff','#3fb950','#f78166','#d29922','#bc8cff',\n '#79c0ff','#56d364','#ffa657','#ff7b72','#a5d6ff',\n ];\n\n // ── State ─────────────────────────────────────────────────────────────\n let evtSource = null;\n let activeFilter = '24h';\n let lineChart = null;\n let doughnutChart = null;\n\n // ── Helpers ───────────────────────────────────────────────────────────\n function escHtml(str) {\n return String(str)\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;');\n }\n\n function fmtUSD(n) {\n if (n === 0) return '$0.00';\n if (n < 0.001) return '$' + n.toFixed(6);\n if (n < 1) return '$' + n.toFixed(4);\n return '$' + n.toFixed(2);\n }\n\n function fmtNum(n) {\n return n.toLocaleString('en-US');\n }\n\n function fmtDate(iso) {\n try { return new Date(iso).toLocaleString(); } catch { return iso; }\n }\n\n function totalCalls(byModel) {\n return Object.values(byModel).reduce(function(s, m) { return s + m.calls; }, 0);\n }\n\n // ── Tab setup ─────────────────────────────────────────────────────────\n document.querySelectorAll('.tab').forEach(function(btn) {\n btn.addEventListener('click', function() {\n document.querySelectorAll('.tab').forEach(function(b) { b.classList.remove('active'); });\n btn.classList.add('active');\n activeFilter = btn.dataset.filter;\n reconnect();\n });\n });\n\n // ── SSE ───────────────────────────────────────────────────────────────\n function reconnect() {\n if (evtSource) { evtSource.close(); evtSource = null; }\n evtSource = new EventSource('/events?filter=' + encodeURIComponent(activeFilter));\n evtSource.onmessage = function(e) {\n try { updateUI(JSON.parse(e.data)); } catch (_) {}\n };\n evtSource.onerror = function() {\n document.getElementById('live-label').textContent = 'reconnecting…';\n };\n evtSource.onopen = function() {\n document.getElementById('live-label').textContent = 'live';\n };\n }\n\n // ── UI update ─────────────────────────────────────────────────────────\n function updateUI(data) {\n const r = data.report;\n const fc = data.forecast;\n const ts = data.timeSeries;\n\n // Cards\n document.getElementById('card-cost').textContent = fmtUSD(r.totalCostUSD);\n document.getElementById('card-input').textContent = fmtNum(r.totalTokens.input);\n document.getElementById('card-output').textContent = fmtNum(r.totalTokens.output);\n document.getElementById('card-calls').textContent = fmtNum(totalCalls(r.byModel));\n document.getElementById('card-burn').textContent = fmtUSD(fc.burnRatePerHour);\n document.getElementById('card-period').textContent =\n r.period.from !== r.period.to\n ? fmtDate(r.period.from) + ' – ' + fmtDate(r.period.to)\n : '';\n\n // Line chart\n const labels = ts.map(function(b) {\n try { return new Date(b.bucket).toLocaleTimeString([], {hour:'2-digit', minute:'2-digit'}); }\n catch { return b.bucket; }\n });\n const costs = ts.map(function(b) { return b.cost; });\n\n if (!lineChart) {\n const ctx = document.getElementById('chart-line').getContext('2d');\n lineChart = new Chart(ctx, {\n type: 'line',\n data: {\n labels: labels,\n datasets: [{\n label: 'Cost (USD)',\n data: costs,\n borderColor: '#58a6ff',\n backgroundColor: 'rgba(88,166,255,0.08)',\n borderWidth: 2,\n pointRadius: 3,\n pointBackgroundColor: '#58a6ff',\n fill: true,\n tension: 0.3,\n }],\n },\n options: {\n responsive: true, maintainAspectRatio: false,\n plugins: { legend: { display: false } },\n scales: {\n x: { ticks: { color: '#8b949e', maxTicksLimit: 8 }, grid: { color: '#21262d' } },\n y: {\n ticks: {\n color: '#8b949e',\n callback: function(v) { return '$' + Number(v).toFixed(4); },\n },\n grid: { color: '#21262d' },\n },\n },\n },\n });\n } else {\n lineChart.data.labels = labels;\n lineChart.data.datasets[0].data = costs;\n lineChart.update('none');\n }\n\n // Doughnut chart\n const modelEntries = Object.entries(r.byModel);\n const dLabels = modelEntries.map(function(x) { return x[0]; });\n const dData = modelEntries.map(function(x) { return x[1].costUSD; });\n const dColors = dLabels.map(function(_, i) { return PALETTE[i % PALETTE.length]; });\n\n if (!doughnutChart) {\n const ctx2 = document.getElementById('chart-doughnut').getContext('2d');\n doughnutChart = new Chart(ctx2, {\n type: 'doughnut',\n data: { labels: dLabels, datasets: [{ data: dData, backgroundColor: dColors, borderWidth: 0, hoverOffset: 4 }] },\n options: {\n responsive: true, maintainAspectRatio: false,\n cutout: '65%',\n plugins: {\n legend: {\n position: 'bottom',\n labels: { color: '#8b949e', boxWidth: 10, padding: 12, font: { size: 11 } },\n },\n },\n },\n });\n } else {\n doughnutChart.data.labels = dLabels;\n doughnutChart.data.datasets[0].data = dData;\n doughnutChart.data.datasets[0].backgroundColor = dColors;\n doughnutChart.update('none');\n }\n\n // Model table\n const modelWrap = document.getElementById('model-table-wrap');\n if (modelEntries.length === 0) {\n modelWrap.innerHTML = '<p class=\"empty\">No data for this period.</p>';\n } else {\n const totalCost = r.totalCostUSD || 1;\n let html = '<table><thead><tr>' +\n '<th>Model</th>' +\n '<th class=\"num\">Calls</th>' +\n '<th class=\"num\">In tokens</th>' +\n '<th class=\"num\">Out tokens</th>' +\n '<th class=\"num\">Cost</th>' +\n '<th class=\"num\">Share</th>' +\n '</tr></thead><tbody>';\n const sorted = modelEntries.slice().sort(function(a, b) { return b[1].costUSD - a[1].costUSD; });\n sorted.forEach(function(entry) {\n const name = entry[0]; const m = entry[1];\n const pct = (m.costUSD / totalCost * 100).toFixed(1);\n const barW = Math.round(m.costUSD / totalCost * 80);\n html += '<tr>' +\n '<td>' + escHtml(name) + '</td>' +\n '<td class=\"num\">' + fmtNum(m.calls) + '</td>' +\n '<td class=\"num\">' + fmtNum(m.tokens.input) + '</td>' +\n '<td class=\"num\">' + fmtNum(m.tokens.output) + '</td>' +\n '<td class=\"num\">' + fmtUSD(m.costUSD) + '</td>' +\n '<td class=\"num\">' + pct + '%' +\n '<span class=\"bar-wrap\"><span class=\"bar-fill\" style=\"width:' + barW + 'px\"></span></span>' +\n '</td></tr>';\n });\n html += '</tbody></table>';\n modelWrap.innerHTML = html;\n }\n\n // User table\n const userEntries = Object.entries(r.byUser);\n const usersSection = document.getElementById('users-section');\n usersSection.style.display = userEntries.length === 0 ? 'none' : '';\n if (userEntries.length > 0) {\n let html = '<table><thead><tr><th>User</th><th class=\"num\">Calls</th><th class=\"num\">Cost</th></tr></thead><tbody>';\n userEntries.slice().sort(function(a,b) { return b[1].costUSD - a[1].costUSD; }).forEach(function(e) {\n html += '<tr><td>' + escHtml(e[0]) + '</td><td class=\"num\">' + fmtNum(e[1].calls) + '</td><td class=\"num\">' + fmtUSD(e[1].costUSD) + '</td></tr>';\n });\n html += '</tbody></table>';\n document.getElementById('user-table-wrap').innerHTML = html;\n }\n\n // Feature table\n const featureEntries = Object.entries(r.byFeature);\n const featuresSection = document.getElementById('features-section');\n featuresSection.style.display = featureEntries.length === 0 ? 'none' : '';\n if (featureEntries.length > 0) {\n let html = '<table><thead><tr><th>Feature</th><th class=\"num\">Calls</th><th class=\"num\">Cost</th></tr></thead><tbody>';\n featureEntries.slice().sort(function(a,b) { return b[1].costUSD - a[1].costUSD; }).forEach(function(e) {\n html += '<tr><td>' + escHtml(e[0]) + '</td><td class=\"num\">' + fmtNum(e[1].calls) + '</td><td class=\"num\">' + fmtUSD(e[1].costUSD) + '</td></tr>';\n });\n html += '</tbody></table>';\n document.getElementById('feature-table-wrap').innerHTML = html;\n }\n\n // Forecast\n document.getElementById('fc-daily').textContent = fmtUSD(fc.projectedDailyCostUSD);\n document.getElementById('fc-monthly').textContent = fmtUSD(fc.projectedMonthlyCostUSD);\n document.getElementById('fc-burn').textContent = fmtUSD(fc.burnRatePerHour);\n document.getElementById('fc-window').textContent =\n fc.basedOnHours > 0 ? 'based on ' + fc.basedOnHours.toFixed(1) + 'h of data' : 'insufficient data';\n\n // Footer\n document.getElementById('footer-updated').textContent =\n 'Last updated: ' + fmtDate(data.lastUpdated);\n }\n\n // ── Boot ──────────────────────────────────────────────────────────────\n reconnect();\n})();\n</script>\n</body>\n</html>`\n}\n"],"mappings":"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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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","names":["existsSync","join","homedir","homedir","join","bundledPrices","DB_PATH","join","homedir","existsSync"]}
1
+ {"version":3,"sources":["../src/adapters/postgres.ts","../src/adapters/mysql.ts","../src/adapters/mongodb.ts","../bin/cli.ts","../src/core/sync.ts","../src/core/storage.ts","../src/core/tracker.ts","../src/core/pricing.ts","../src/core/suggestions.ts","../prices.json","../src/dashboard/server.ts","../src/dashboard/data.ts","../src/dashboard/html.ts"],"sourcesContent":["import type { IStorage, UsageEntry } from '../types/index.js'\n\n/**\n * IStorage adapter for PostgreSQL using the `pg` driver.\n *\n * Install peer dep: npm install pg\n * Types (optional): npm install -D @types/pg\n *\n * @example\n * ```ts\n * import { Pool } from 'pg'\n * import { createTracker } from '@diogonzafe/tokenwatch'\n * import { PostgresStorage } from '@diogonzafe/tokenwatch/adapters'\n *\n * const pool = new Pool({ connectionString: process.env.DATABASE_URL })\n * const storage = new PostgresStorage(pool)\n * await storage.migrate() // create table if it doesn't exist\n *\n * const tracker = createTracker({ storage })\n * ```\n */\n\n// Minimal structural types so the adapter compiles without `pg` installed\ninterface QueryClient {\n query(sql: string, values?: unknown[]): Promise<{ rows: unknown[] }>\n}\n\nexport class PostgresStorage implements IStorage {\n constructor(private readonly client: QueryClient) {}\n\n /** Creates the `tokenwatch_usage` table if it does not already exist.\n * Also adds new columns for databases created before v0.2.0 / v0.3.0. */\n async migrate(): Promise<void> {\n await this.client.query(`\n CREATE TABLE IF NOT EXISTS tokenwatch_usage (\n id BIGSERIAL PRIMARY KEY,\n model TEXT NOT NULL,\n input_tokens INTEGER NOT NULL,\n output_tokens INTEGER NOT NULL,\n reasoning_tokens INTEGER NOT NULL DEFAULT 0,\n cached_tokens INTEGER NOT NULL DEFAULT 0,\n cache_creation_tokens INTEGER NOT NULL DEFAULT 0,\n cost_usd NUMERIC NOT NULL,\n session_id TEXT,\n user_id TEXT,\n feature TEXT,\n timestamp TIMESTAMPTZ NOT NULL\n )\n `)\n // Incremental migrations for databases created before v0.2.0 / v0.3.0\n for (const col of [\n 'ALTER TABLE tokenwatch_usage ADD COLUMN IF NOT EXISTS reasoning_tokens INTEGER NOT NULL DEFAULT 0',\n 'ALTER TABLE tokenwatch_usage ADD COLUMN IF NOT EXISTS feature TEXT',\n 'ALTER TABLE tokenwatch_usage ADD COLUMN IF NOT EXISTS cached_tokens INTEGER NOT NULL DEFAULT 0',\n 'ALTER TABLE tokenwatch_usage ADD COLUMN IF NOT EXISTS cache_creation_tokens INTEGER NOT NULL DEFAULT 0',\n ]) {\n await this.client.query(col).catch(() => { /* column already exists */ })\n }\n }\n\n record(entry: UsageEntry): void {\n this.client\n .query(\n `INSERT INTO tokenwatch_usage\n (model, input_tokens, output_tokens, reasoning_tokens, cached_tokens, cache_creation_tokens,\n cost_usd, session_id, user_id, feature, timestamp)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)`,\n [\n entry.model,\n entry.inputTokens,\n entry.outputTokens,\n entry.reasoningTokens ?? 0,\n entry.cachedTokens ?? 0,\n entry.cacheCreationTokens ?? 0,\n entry.costUSD,\n entry.sessionId ?? null,\n entry.userId ?? null,\n entry.feature ?? null,\n entry.timestamp,\n ],\n )\n .catch((err: unknown) => {\n console.warn('[tokenwatch] PostgresStorage.record failed:', err)\n })\n }\n\n async getAll(): Promise<UsageEntry[]> {\n const result = await this.client.query(\n 'SELECT * FROM tokenwatch_usage ORDER BY timestamp ASC',\n )\n return (result.rows as Array<Record<string, unknown>>).map(rowToEntry)\n }\n\n async clearAll(): Promise<void> {\n await this.client.query('DELETE FROM tokenwatch_usage')\n }\n\n async clearSession(sessionId: string): Promise<void> {\n await this.client.query(\n 'DELETE FROM tokenwatch_usage WHERE session_id = $1',\n [sessionId],\n )\n }\n}\n\nfunction rowToEntry(r: Record<string, unknown>): UsageEntry {\n const reasoningTokens = (r['reasoning_tokens'] as number | null) ?? 0\n const cachedTokens = (r['cached_tokens'] as number | null) ?? 0\n const cacheCreationTokens = (r['cache_creation_tokens'] as number | null) ?? 0\n return {\n model: r['model'] as string,\n inputTokens: r['input_tokens'] as number,\n outputTokens: r['output_tokens'] as number,\n ...(reasoningTokens > 0 && { reasoningTokens }),\n ...(cachedTokens > 0 && { cachedTokens }),\n ...(cacheCreationTokens > 0 && { cacheCreationTokens }),\n costUSD: Number(r['cost_usd']),\n ...(r['session_id'] != null && { sessionId: r['session_id'] as string }),\n ...(r['user_id'] != null && { userId: r['user_id'] as string }),\n ...(r['feature'] != null && { feature: r['feature'] as string }),\n timestamp:\n r['timestamp'] instanceof Date\n ? (r['timestamp'] as Date).toISOString()\n : (r['timestamp'] as string),\n }\n}\n","import type { IStorage, UsageEntry } from '../types/index.js'\n\n/**\n * IStorage adapter for MySQL / MariaDB using the `mysql2` driver.\n *\n * Install peer dep: npm install mysql2\n *\n * @example\n * ```ts\n * import mysql from 'mysql2/promise'\n * import { createTracker } from '@diogonzafe/tokenwatch'\n * import { MySQLStorage } from '@diogonzafe/tokenwatch/adapters'\n *\n * const pool = mysql.createPool({ uri: process.env.MYSQL_URL })\n * const storage = new MySQLStorage(pool)\n * await storage.migrate() // create table if it doesn't exist\n *\n * const tracker = createTracker({ storage })\n * ```\n */\n\n// Minimal structural type so the adapter compiles without `mysql2` installed\ninterface QueryClient {\n execute(sql: string, values?: unknown[]): Promise<[unknown]>\n}\n\nexport class MySQLStorage implements IStorage {\n constructor(private readonly client: QueryClient) {}\n\n /** Creates the `tokenwatch_usage` table if it does not already exist.\n * Also adds new columns for databases created before v0.2.0 / v0.3.0. */\n async migrate(): Promise<void> {\n await this.client.execute(`\n CREATE TABLE IF NOT EXISTS tokenwatch_usage (\n id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,\n model VARCHAR(255) NOT NULL,\n input_tokens INT NOT NULL,\n output_tokens INT NOT NULL,\n reasoning_tokens INT NOT NULL DEFAULT 0,\n cached_tokens INT NOT NULL DEFAULT 0,\n cache_creation_tokens INT NOT NULL DEFAULT 0,\n cost_usd DECIMAL(18,8) NOT NULL,\n session_id VARCHAR(255),\n user_id VARCHAR(255),\n feature VARCHAR(255),\n timestamp DATETIME(3) NOT NULL\n )\n `)\n // Incremental migrations for databases created before v0.2.0 / v0.3.0\n await this.client.execute(`\n ALTER TABLE tokenwatch_usage\n ADD COLUMN IF NOT EXISTS reasoning_tokens INT NOT NULL DEFAULT 0,\n ADD COLUMN IF NOT EXISTS feature VARCHAR(255),\n ADD COLUMN IF NOT EXISTS cached_tokens INT NOT NULL DEFAULT 0,\n ADD COLUMN IF NOT EXISTS cache_creation_tokens INT NOT NULL DEFAULT 0\n `).catch(() => { /* MySQL < 8.0 may not support IF NOT EXISTS — ignore if columns already exist */ })\n }\n\n record(entry: UsageEntry): void {\n this.client\n .execute(\n `INSERT INTO tokenwatch_usage\n (model, input_tokens, output_tokens, reasoning_tokens, cached_tokens, cache_creation_tokens,\n cost_usd, session_id, user_id, feature, timestamp)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [\n entry.model,\n entry.inputTokens,\n entry.outputTokens,\n entry.reasoningTokens ?? 0,\n entry.cachedTokens ?? 0,\n entry.cacheCreationTokens ?? 0,\n entry.costUSD,\n entry.sessionId ?? null,\n entry.userId ?? null,\n entry.feature ?? null,\n entry.timestamp,\n ],\n )\n .catch((err: unknown) => {\n console.warn('[tokenwatch] MySQLStorage.record failed:', err)\n })\n }\n\n async getAll(): Promise<UsageEntry[]> {\n const [rows] = await this.client.execute(\n 'SELECT * FROM tokenwatch_usage ORDER BY timestamp ASC',\n )\n return (rows as Array<Record<string, unknown>>).map(rowToEntry)\n }\n\n async clearAll(): Promise<void> {\n await this.client.execute('DELETE FROM tokenwatch_usage')\n }\n\n async clearSession(sessionId: string): Promise<void> {\n await this.client.execute(\n 'DELETE FROM tokenwatch_usage WHERE session_id = ?',\n [sessionId],\n )\n }\n}\n\nfunction rowToEntry(r: Record<string, unknown>): UsageEntry {\n const reasoningTokens = (r['reasoning_tokens'] as number | null) ?? 0\n const cachedTokens = (r['cached_tokens'] as number | null) ?? 0\n const cacheCreationTokens = (r['cache_creation_tokens'] as number | null) ?? 0\n return {\n model: r['model'] as string,\n inputTokens: r['input_tokens'] as number,\n outputTokens: r['output_tokens'] as number,\n ...(reasoningTokens > 0 && { reasoningTokens }),\n ...(cachedTokens > 0 && { cachedTokens }),\n ...(cacheCreationTokens > 0 && { cacheCreationTokens }),\n costUSD: Number(r['cost_usd']),\n ...(r['session_id'] != null && { sessionId: r['session_id'] as string }),\n ...(r['user_id'] != null && { userId: r['user_id'] as string }),\n ...(r['feature'] != null && { feature: r['feature'] as string }),\n timestamp:\n r['timestamp'] instanceof Date\n ? (r['timestamp'] as Date).toISOString()\n : (r['timestamp'] as string),\n }\n}\n","import type { IStorage, UsageEntry } from '../types/index.js'\n\n/**\n * IStorage adapter for MongoDB using the official `mongodb` driver.\n *\n * Install peer dep: npm install mongodb\n *\n * @example\n * ```ts\n * import { MongoClient } from 'mongodb'\n * import { createTracker } from '@diogonzafe/tokenwatch'\n * import { MongoStorage } from '@diogonzafe/tokenwatch/adapters'\n *\n * const client = new MongoClient(process.env.MONGO_URL!)\n * await client.connect()\n *\n * const storage = new MongoStorage(client.db('myapp'))\n * const tracker = createTracker({ storage })\n * ```\n *\n * Recommended index (run once at startup):\n * ```ts\n * await storage.createIndexes()\n * ```\n */\n\n// Minimal structural types so the adapter compiles without `mongodb` installed\ninterface MongoDocument {\n _id?: unknown\n model: string\n inputTokens: number\n outputTokens: number\n reasoningTokens?: number\n cachedTokens?: number\n cacheCreationTokens?: number\n costUSD: number\n sessionId?: string | null\n userId?: string | null\n feature?: string | null\n timestamp: string\n}\n\ninterface MongoCursor {\n sort(sort: Record<string, unknown>): MongoCursor\n toArray(): Promise<MongoDocument[]>\n}\n\ninterface Collection {\n insertOne(doc: MongoDocument): Promise<unknown>\n find(filter: Record<string, unknown>): MongoCursor\n deleteMany(filter: Record<string, unknown>): Promise<unknown>\n createIndex(index: Record<string, unknown>): Promise<unknown>\n}\n\ninterface Database {\n collection(name: string): Collection\n}\n\nconst COLLECTION = 'tokenwatch_usage'\n\nexport class MongoStorage implements IStorage {\n private readonly col: Collection\n\n constructor(db: Database) {\n this.col = db.collection(COLLECTION)\n }\n\n /** Creates recommended indexes for query performance. Call once at startup. */\n async createIndexes(): Promise<void> {\n await this.col.createIndex({ timestamp: 1 })\n await this.col.createIndex({ sessionId: 1 })\n await this.col.createIndex({ userId: 1 })\n await this.col.createIndex({ model: 1 })\n }\n\n record(entry: UsageEntry): void {\n this.col\n .insertOne({\n model: entry.model,\n inputTokens: entry.inputTokens,\n outputTokens: entry.outputTokens,\n ...(entry.reasoningTokens !== undefined && { reasoningTokens: entry.reasoningTokens }),\n ...(entry.cachedTokens !== undefined && { cachedTokens: entry.cachedTokens }),\n ...(entry.cacheCreationTokens !== undefined && { cacheCreationTokens: entry.cacheCreationTokens }),\n costUSD: entry.costUSD,\n sessionId: entry.sessionId ?? null,\n userId: entry.userId ?? null,\n ...(entry.feature !== undefined && { feature: entry.feature }),\n timestamp: entry.timestamp,\n })\n .catch((err: unknown) => {\n console.warn('[tokenwatch] MongoStorage.record failed:', err)\n })\n }\n\n async getAll(): Promise<UsageEntry[]> {\n const docs = await this.col.find({}).sort({ timestamp: 1 }).toArray()\n return docs.map(docToEntry)\n }\n\n async clearAll(): Promise<void> {\n await this.col.deleteMany({})\n }\n\n async clearSession(sessionId: string): Promise<void> {\n await this.col.deleteMany({ sessionId })\n }\n}\n\nfunction docToEntry(doc: MongoDocument): UsageEntry {\n return {\n model: doc.model,\n inputTokens: doc.inputTokens,\n outputTokens: doc.outputTokens,\n ...(doc.reasoningTokens != null && doc.reasoningTokens > 0 && { reasoningTokens: doc.reasoningTokens }),\n ...(doc.cachedTokens != null && doc.cachedTokens > 0 && { cachedTokens: doc.cachedTokens }),\n ...(doc.cacheCreationTokens != null && doc.cacheCreationTokens > 0 && { cacheCreationTokens: doc.cacheCreationTokens }),\n costUSD: doc.costUSD,\n ...(doc.sessionId != null && { sessionId: doc.sessionId }),\n ...(doc.userId != null && { userId: doc.userId }),\n ...(doc.feature != null && { feature: doc.feature }),\n timestamp: doc.timestamp,\n }\n}\n","#!/usr/bin/env node\nimport { readFileSync, existsSync } from 'node:fs'\nimport { join, dirname } from 'node:path'\nimport { homedir } from 'node:os'\nimport { fileURLToPath } from 'node:url'\nimport { fetchRemotePrices } from '../src/core/sync.js'\nimport { SqliteStorage } from '../src/core/storage.js'\nimport { createTracker } from '../src/core/tracker.js'\nimport { startDashboardServer } from '../src/dashboard/server.js'\nimport type { IStorage, PricesFile } from '../src/types/index.js'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nconst DEFAULT_DB_PATH = join(homedir(), '.tokenwatch', 'usage.db')\n\n// ─── Arg helpers ──────────────────────────────────────────────────────────────\n\nfunction getFlag(args: string[], flag: string): string | undefined {\n const idx = args.indexOf(flag)\n if (idx === -1) return undefined\n const value = args[idx + 1]\n // Treat missing value or another flag as absent (e.g. --db --port would be wrong)\n return value !== undefined && !value.startsWith('--') ? value : undefined\n}\n\n// ─── Storage factory ──────────────────────────────────────────────────────────\n\n// Local interface stubs — avoids compile-time deps on optional peer packages.\n// Same pattern used in src/exporters/otel.ts for @opentelemetry/api.\ninterface PgPoolLike { end(): Promise<void> }\ninterface MysqlPoolLike { end(): Promise<void> }\ninterface MongoClientLike { connect(): Promise<void>; close(): Promise<void>; db(name?: string): unknown }\n\ninterface StorageHandle {\n storage: IStorage\n close: () => Promise<void>\n}\n\nasync function openStorage(dbUrl: string | undefined): Promise<StorageHandle> {\n // ── Default: SQLite ──────────────────────────────────────────────────────\n if (!dbUrl) {\n if (!existsSync(DEFAULT_DB_PATH)) {\n console.error(`No SQLite database found at ${DEFAULT_DB_PATH}`)\n console.error(\"Start your app with storage: 'sqlite' to begin recording usage.\")\n console.error('Or pass --db <url> to connect to Postgres, MySQL, or MongoDB.')\n process.exit(1)\n }\n let storage: SqliteStorage\n try {\n storage = new SqliteStorage(DEFAULT_DB_PATH)\n } catch {\n console.error('Failed to open SQLite database. Is better-sqlite3 installed?')\n console.error('Run: npm install better-sqlite3')\n process.exit(1)\n }\n return { storage, close: async () => {} }\n }\n\n // ── Postgres ─────────────────────────────────────────────────────────────\n if (dbUrl.startsWith('postgres://') || dbUrl.startsWith('postgresql://')) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let pgMod: any\n try {\n pgMod = (await import('pg' as string)).default\n } catch {\n console.error('[tokenwatch] Postgres requires the pg package.')\n console.error('Run: npm install pg')\n process.exit(1)\n }\n const { PostgresStorage } = await import('../src/adapters/postgres.js')\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access\n const pool = new pgMod.Pool({ connectionString: dbUrl }) as PgPoolLike\n const storage = new PostgresStorage(pool as never)\n return { storage, close: () => pool.end() }\n }\n\n // ── MySQL ─────────────────────────────────────────────────────────────────\n if (dbUrl.startsWith('mysql://')) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let mysqlMod: any\n try {\n mysqlMod = await import('mysql2/promise' as string)\n } catch {\n console.error('[tokenwatch] MySQL requires the mysql2 package.')\n console.error('Run: npm install mysql2')\n process.exit(1)\n }\n const { MySQLStorage } = await import('../src/adapters/mysql.js')\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access\n const pool = mysqlMod.createPool(dbUrl) as MysqlPoolLike\n const storage = new MySQLStorage(pool as never)\n return { storage, close: () => pool.end() }\n }\n\n // ── MongoDB ───────────────────────────────────────────────────────────────\n if (dbUrl.startsWith('mongodb://') || dbUrl.startsWith('mongodb+srv://')) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let mongoMod: any\n try {\n mongoMod = await import('mongodb' as string)\n } catch {\n console.error('[tokenwatch] MongoDB requires the mongodb package.')\n console.error('Run: npm install mongodb')\n process.exit(1)\n }\n const { MongoStorage } = await import('../src/adapters/mongodb.js')\n const urlObj = new URL(dbUrl)\n const dbName = urlObj.pathname.replace(/^\\//, '') || 'tokenwatch'\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access\n const client = new mongoMod.MongoClient(dbUrl) as MongoClientLike\n await client.connect()\n const db = client.db(dbName)\n const storage = new MongoStorage(db as never)\n return { storage, close: () => client.close() }\n }\n\n console.error(`[tokenwatch] Unsupported database URL: \"${dbUrl}\"`)\n console.error('Supported protocols: postgres://, mysql://, mongodb://, mongodb+srv://')\n process.exit(1)\n}\n\n// ─── Commands ─────────────────────────────────────────────────────────────────\n\nfunction loadBundledPrices(): PricesFile['models'] {\n const pricesPath = join(__dirname, '..', 'prices.json')\n const raw = readFileSync(pricesPath, 'utf8')\n const data = JSON.parse(raw) as PricesFile\n return data.models\n}\n\nasync function cmdSync(): Promise<void> {\n console.log('Fetching latest prices from remote...')\n const result = await fetchRemotePrices()\n if (result) {\n console.log(`✓ Prices updated. ${Object.keys(result.models).length} models cached (updated_at: ${result.updated_at}).`)\n } else {\n console.error('✗ Failed to fetch remote prices. Check your internet connection.')\n process.exit(1)\n }\n}\n\nfunction cmdPrices(): void {\n const models = loadBundledPrices()\n const rows = Object.entries(models).map(([name, price]) => ({\n model: name,\n input: `$${price.input.toFixed(2)}/M`,\n output: `$${price.output.toFixed(2)}/M`,\n }))\n\n const maxName = Math.max(...rows.map((r) => r.model.length), 5)\n const header = `${'Model'.padEnd(maxName)} ${'Input'.padStart(12)} ${'Output'.padStart(12)}`\n const sep = '-'.repeat(header.length)\n\n console.log(header)\n console.log(sep)\n for (const row of rows) {\n console.log(`${row.model.padEnd(maxName)} ${row.input.padStart(12)} ${row.output.padStart(12)}`)\n }\n}\n\nasync function cmdReport(args: string[]): Promise<void> {\n const dbUrl = getFlag(args, '--db')\n const { storage, close } = await openStorage(dbUrl)\n\n let report: Awaited<ReturnType<ReturnType<typeof createTracker>['getReport']>>\n try {\n const tracker = createTracker({ storage, syncPrices: false })\n report = await tracker.getReport()\n } finally {\n await close()\n }\n\n if (report.totalCostUSD === 0 && Object.keys(report.byModel).length === 0) {\n console.log('No usage recorded yet.')\n return\n }\n\n console.log('\\n── tokenwatch report ──────────────────────────────')\n console.log(` Total cost: $${report.totalCostUSD.toFixed(6)} USD`)\n console.log(` Total tokens: ${report.totalTokens.input.toLocaleString()} in / ${report.totalTokens.output.toLocaleString()} out`)\n console.log(` Period: ${report.period.from} → ${report.period.to}`)\n if (report.pricesUpdatedAt) {\n console.log(` Prices as of: ${report.pricesUpdatedAt}`)\n }\n\n if (Object.keys(report.byModel).length > 0) {\n console.log('\\n By model:')\n for (const [model, stats] of Object.entries(report.byModel)) {\n console.log(` ${model.padEnd(30)} $${stats.costUSD.toFixed(6)} (${stats.calls} calls)`)\n }\n }\n\n if (Object.keys(report.byUser).length > 0) {\n console.log('\\n By user:')\n for (const [user, stats] of Object.entries(report.byUser)) {\n console.log(` ${user.padEnd(30)} $${stats.costUSD.toFixed(6)} (${stats.calls} calls)`)\n }\n }\n\n if (Object.keys(report.bySession).length > 0) {\n console.log('\\n By session:')\n for (const [session, stats] of Object.entries(report.bySession)) {\n console.log(` ${session.padEnd(30)} $${stats.costUSD.toFixed(6)} (${stats.calls} calls)`)\n }\n }\n\n if (Object.keys(report.byFeature).length > 0) {\n console.log('\\n By feature:')\n for (const [feature, stats] of Object.entries(report.byFeature)) {\n console.log(` ${feature.padEnd(30)} $${stats.costUSD.toFixed(6)} (${stats.calls} calls)`)\n }\n }\n\n console.log('───────────────────────────────────────────────────\\n')\n}\n\nasync function cmdDashboard(args: string[]): Promise<void> {\n const portFlag = getFlag(args, '--port')\n const port = portFlag !== undefined ? parseInt(portFlag, 10) : 4242\n if (isNaN(port) || port < 1 || port > 65535) {\n console.error(`[tokenwatch] Invalid port: \"${portFlag}\". Must be a number between 1 and 65535.`)\n process.exit(1)\n }\n const dbUrl = getFlag(args, '--db')\n\n const { storage, close } = await openStorage(dbUrl)\n\n // Graceful shutdown — close DB connection when the process exits\n const shutdown = (): void => { void close().then(() => process.exit(0)) }\n process.on('SIGINT', shutdown)\n process.on('SIGTERM', shutdown)\n\n startDashboardServer(storage, port)\n}\n\nfunction cmdHelp(): void {\n console.log(`\ntokenwatch — CLI\n\nCommands:\n sync Fetch and cache latest model prices from remote\n prices List all bundled models and their current prices\n report [--db <url>] Show usage report (default: SQLite at ~/.tokenwatch/usage.db)\n dashboard [--port N] Open local web dashboard (default port: 4242)\n [--db <url>] Connect to a database instead of the default SQLite\n\nDatabase URL formats:\n (none) ~/.tokenwatch/usage.db (SQLite, default)\n postgres://user:pass@host:5432/dbname\n mysql://user:pass@host:3306/dbname\n mongodb://user:pass@host:27017/dbname\n\nExamples:\n tokenwatch dashboard\n tokenwatch dashboard --port 8080\n tokenwatch dashboard --db postgres://user:pass@localhost:5432/myapp\n tokenwatch report --db mysql://root:pass@localhost:3306/myapp\n`.trim())\n}\n\n// ─── Entry point ──────────────────────────────────────────────────────────────\n\nasync function main(): Promise<void> {\n const [, , cmd, ...args] = process.argv\n\n switch (cmd) {\n case 'sync':\n await cmdSync()\n break\n case 'prices':\n cmdPrices()\n break\n case 'report':\n await cmdReport(args)\n break\n case 'dashboard':\n await cmdDashboard(args)\n break\n case 'help':\n case undefined:\n cmdHelp()\n break\n default:\n console.error(`Unknown command: ${cmd}\\nRun \"tokenwatch help\" for usage.`)\n process.exit(1)\n }\n}\n\nmain().catch((err: unknown) => {\n console.error(err)\n process.exit(1)\n})\n","import { readFile, writeFile, mkdir } from 'node:fs/promises'\nimport { existsSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport type { PricesFile, PriceMap } from '../types/index.js'\n\nconst CACHE_DIR = join(homedir(), '.tokenwatch')\nconst CACHE_FILE = join(CACHE_DIR, 'prices.json')\nconst CACHE_TTL_MS = 24 * 60 * 60 * 1000 // 24 hours\nconst REMOTE_URL =\n 'https://raw.githubusercontent.com/diogonzafe/tokenwatch/main/prices.json'\n\nexport interface PricesResult {\n models: PriceMap\n updated_at: string\n}\n\nexport async function fetchRemotePrices(url = REMOTE_URL): Promise<PricesResult | null> {\n try {\n const res = await fetch(url, { signal: AbortSignal.timeout(8_000) })\n if (!res.ok) return null\n const data = (await res.json()) as PricesFile\n if (!data?.models) return null\n await persistCache(data)\n return { models: data.models, updated_at: data.updated_at ?? '' }\n } catch {\n return null\n }\n}\n\nexport async function loadCachedPrices(): Promise<PricesResult | null> {\n if (!existsSync(CACHE_FILE)) return null\n try {\n const raw = await readFile(CACHE_FILE, 'utf8')\n const data = JSON.parse(raw) as PricesFile & { _cachedAt?: number }\n const age = Date.now() - (data._cachedAt ?? 0)\n if (age > CACHE_TTL_MS) return null\n if (!data.models) return null\n return { models: data.models, updated_at: data.updated_at ?? '' }\n } catch {\n return null\n }\n}\n\nasync function persistCache(data: PricesFile): Promise<void> {\n try {\n await mkdir(CACHE_DIR, { recursive: true })\n const payload = { ...data, _cachedAt: Date.now() }\n await writeFile(CACHE_FILE, JSON.stringify(payload, null, 2), 'utf8')\n } catch {\n // best-effort — never throw\n }\n}\n\n/**\n * Returns the best available remote price result:\n * 1. Valid local cache (< 24h)\n * 2. Fresh remote fetch (also updates cache)\n * 3. null if both fail\n */\nexport async function getRemotePrices(): Promise<PricesResult | null> {\n const cached = await loadCachedPrices()\n if (cached) return cached\n return fetchRemotePrices()\n}\n","import { createRequire } from 'node:module'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { mkdirSync } from 'node:fs'\nimport type { IStorage, UsageEntry } from '../types/index.js'\n\n// ─── Memory storage ───────────────────────────────────────────────────────────\n\nexport class MemoryStorage implements IStorage {\n private entries: UsageEntry[] = []\n\n record(entry: UsageEntry): void {\n this.entries.push(entry)\n }\n\n getAll(): UsageEntry[] {\n return [...this.entries]\n }\n\n clearAll(): void {\n this.entries = []\n }\n\n clearSession(sessionId: string): void {\n this.entries = this.entries.filter((e) => e.sessionId !== sessionId)\n }\n}\n\n// ─── SQLite storage ───────────────────────────────────────────────────────────\n\nconst DB_DIR = join(homedir(), '.tokenwatch')\nconst DB_PATH = join(DB_DIR, 'usage.db')\n\nexport class SqliteStorage implements IStorage {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private db: any\n\n constructor(dbPath = DB_PATH) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let BetterSqlite3: any\n try {\n // In CJS context globalThis.require is the native require; in ESM use createRequire.\n // This makes the lazy load work in both output formats produced by tsup.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const req: NodeRequire =\n typeof (globalThis as any).require === 'function'\n ? // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (globalThis as any).require\n : createRequire(import.meta.url)\n BetterSqlite3 = req('better-sqlite3')\n } catch {\n throw new Error(\n '[tokenwatch] SQLite storage requires better-sqlite3. ' +\n 'Run: npm install better-sqlite3',\n )\n }\n\n mkdirSync(DB_DIR, { recursive: true })\n this.db = new BetterSqlite3(dbPath)\n this.migrate()\n }\n\n private migrate(): void {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS usage (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n model TEXT NOT NULL,\n input_tokens INTEGER NOT NULL,\n output_tokens INTEGER NOT NULL,\n reasoning_tokens INTEGER NOT NULL DEFAULT 0,\n cached_tokens INTEGER NOT NULL DEFAULT 0,\n cache_creation_tokens INTEGER NOT NULL DEFAULT 0,\n cost_usd REAL NOT NULL,\n session_id TEXT,\n user_id TEXT,\n feature TEXT,\n timestamp TEXT NOT NULL\n )\n `)\n // Incremental migrations for databases created before v0.2.0 / v0.3.0\n const cols = (this.db.prepare(`PRAGMA table_info(usage)`).all() as Array<{ name: string }>)\n .map((c) => c.name)\n if (!cols.includes('reasoning_tokens')) {\n this.db.exec(`ALTER TABLE usage ADD COLUMN reasoning_tokens INTEGER NOT NULL DEFAULT 0`)\n }\n if (!cols.includes('feature')) {\n this.db.exec(`ALTER TABLE usage ADD COLUMN feature TEXT`)\n }\n if (!cols.includes('cached_tokens')) {\n this.db.exec(`ALTER TABLE usage ADD COLUMN cached_tokens INTEGER NOT NULL DEFAULT 0`)\n }\n if (!cols.includes('cache_creation_tokens')) {\n this.db.exec(`ALTER TABLE usage ADD COLUMN cache_creation_tokens INTEGER NOT NULL DEFAULT 0`)\n }\n }\n\n record(entry: UsageEntry): void {\n this.db\n .prepare(\n `INSERT INTO usage\n (model, input_tokens, output_tokens, reasoning_tokens, cached_tokens, cache_creation_tokens,\n cost_usd, session_id, user_id, feature, timestamp)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n )\n .run(\n entry.model,\n entry.inputTokens,\n entry.outputTokens,\n entry.reasoningTokens ?? 0,\n entry.cachedTokens ?? 0,\n entry.cacheCreationTokens ?? 0,\n entry.costUSD,\n entry.sessionId ?? null,\n entry.userId ?? null,\n entry.feature ?? null,\n entry.timestamp,\n )\n }\n\n getAll(): UsageEntry[] {\n const rows = this.db.prepare('SELECT * FROM usage ORDER BY timestamp ASC').all() as Array<{\n model: string\n input_tokens: number\n output_tokens: number\n reasoning_tokens: number\n cached_tokens: number\n cache_creation_tokens: number\n cost_usd: number\n session_id: string | null\n user_id: string | null\n feature: string | null\n timestamp: string\n }>\n\n return rows.map((r) => ({\n model: r.model,\n inputTokens: r.input_tokens,\n outputTokens: r.output_tokens,\n ...(r.reasoning_tokens > 0 && { reasoningTokens: r.reasoning_tokens }),\n ...(r.cached_tokens > 0 && { cachedTokens: r.cached_tokens }),\n ...(r.cache_creation_tokens > 0 && { cacheCreationTokens: r.cache_creation_tokens }),\n costUSD: r.cost_usd,\n ...(r.session_id != null && { sessionId: r.session_id }),\n ...(r.user_id != null && { userId: r.user_id }),\n ...(r.feature != null && { feature: r.feature }),\n timestamp: r.timestamp,\n }))\n }\n\n clearAll(): void {\n this.db.exec('DELETE FROM usage')\n }\n\n clearSession(sessionId: string): void {\n this.db.prepare('DELETE FROM usage WHERE session_id = ?').run(sessionId)\n }\n}\n\n// ─── Factory ──────────────────────────────────────────────────────────────────\n\nexport function createStorage(type: 'memory' | 'sqlite'): IStorage {\n if (type === 'sqlite') return new SqliteStorage()\n return new MemoryStorage()\n}\n","import { z } from 'zod'\nimport type {\n Tracker,\n TrackerConfig,\n UsageEntry,\n Report,\n ReportOptions,\n CostForecast,\n ForecastOptions,\n ModelStats,\n SessionStats,\n UserStats,\n FeatureStats,\n ModelPrice,\n PriceMap,\n IStorage,\n BudgetConfig,\n IExporter,\n} from '../types/index.js'\nimport { resolvePrice, findPrice, calculateCost } from './pricing.js'\nimport { maybeSuggestCheaperModel } from './suggestions.js'\nimport { createStorage } from './storage.js'\nimport { getRemotePrices } from './sync.js'\nimport bundledPricesFile from '../../prices.json' assert { type: 'json' }\n\nconst bundledPrices: PriceMap = bundledPricesFile.models as PriceMap\nconst bundledUpdatedAt: string = (bundledPricesFile as { updated_at?: string }).updated_at ?? ''\n\n// ─── Config validation schema ─────────────────────────────────────────────────\n\nconst ModelPriceSchema = z.object({\n input: z.number().nonnegative(),\n output: z.number().nonnegative(),\n cachedInput: z.number().nonnegative().optional(),\n cacheCreationInput: z.number().nonnegative().optional(),\n maxInputTokens: z.number().positive().optional(),\n})\n\nconst BudgetConfigSchema = z.object({\n threshold: z.number().positive(),\n webhookUrl: z.string().url(),\n mode: z.enum(['once', 'always']).optional().default('once'),\n})\n\n// storage can be a string enum or an IStorage instance — validated separately\nconst TrackerConfigSchema = z.object({\n storage: z.union([z.enum(['memory', 'sqlite']), z.custom<IStorage>((v) => {\n return (\n v !== null &&\n typeof v === 'object' &&\n typeof (v as IStorage).record === 'function' &&\n typeof (v as IStorage).getAll === 'function' &&\n typeof (v as IStorage).clearAll === 'function' &&\n typeof (v as IStorage).clearSession === 'function'\n )\n })]).optional().default('memory'),\n alertThreshold: z.number().positive().optional(),\n webhookUrl: z.string().url().optional(),\n syncPrices: z.boolean().optional().default(true),\n customPrices: z.record(z.string(), ModelPriceSchema).optional(),\n warnIfStaleAfterHours: z.number().nonnegative().optional().default(72),\n budgets: z.object({\n perUser: BudgetConfigSchema.optional(),\n perSession: BudgetConfigSchema.optional(),\n }).optional(),\n suggestions: z.boolean().optional().default(false),\n anomalyDetection: z.object({\n multiplierThreshold: z.number().positive(),\n webhookUrl: z.string().url(),\n windowHours: z.number().positive().optional().default(24),\n mode: z.enum(['once', 'always']).optional().default('once'),\n }).optional(),\n exporter: z.custom<IExporter>((v) => (\n v !== null &&\n typeof v === 'object' &&\n typeof (v as IExporter).export === 'function'\n )).optional(),\n})\n\nexport function createTracker(config: TrackerConfig = {}): Tracker {\n const parsed = TrackerConfigSchema.safeParse(config)\n if (!parsed.success) {\n const issues = parsed.error.issues.map((i) => ` ${i.path.join('.')}: ${i.message}`).join('\\n')\n throw new Error(`[tokenwatch] Invalid config:\\n${issues}`)\n }\n\n const {\n storage: storageOption,\n alertThreshold,\n webhookUrl,\n syncPrices,\n customPrices,\n warnIfStaleAfterHours,\n budgets,\n suggestions,\n anomalyDetection,\n exporter,\n } = parsed.data\n\n const storage: IStorage =\n typeof storageOption === 'object'\n ? storageOption\n : createStorage(storageOption)\n\n // Fetch remote prices in the background — bundled prices are used as fallback\n // until the sync resolves. Negligible overhead added to createTracker().\n let remotePrices: PriceMap | undefined\n let pricesUpdatedAt: string = bundledUpdatedAt\n if (syncPrices) {\n getRemotePrices()\n .then((result) => {\n if (result) {\n remotePrices = result.models\n pricesUpdatedAt = result.updated_at\n }\n })\n .catch(() => {\n // best-effort — bundled prices remain in use\n })\n }\n\n // Warn if prices are stale (checked lazily on first access)\n let stalenessChecked = false\n function maybeWarnStaleness(): void {\n if (stalenessChecked || !warnIfStaleAfterHours) return\n stalenessChecked = true\n if (!pricesUpdatedAt) return\n try {\n const updatedMs = new Date(pricesUpdatedAt).getTime()\n const ageHours = (Date.now() - updatedMs) / (1000 * 60 * 60)\n if (ageHours > warnIfStaleAfterHours) {\n console.warn(\n `[tokenwatch] Price data is ${Math.round(ageHours)}h old (updated_at: ${pricesUpdatedAt}). ` +\n `Run \"tokenwatch sync\" to refresh, or set warnIfStaleAfterHours: 0 to suppress.`,\n )\n }\n } catch {\n // best-effort\n }\n }\n\n let alertFired = false\n const firedUserAlerts = new Set<string>()\n const firedSessionAlerts = new Set<string>()\n const firedAnomalyKeys = new Set<string>()\n const startedAt = new Date().toISOString()\n\n function resolveModelPrice(model: string) {\n maybeWarnStaleness()\n return resolvePrice(model, {\n bundledPrices,\n ...(customPrices !== undefined && { customPrices: customPrices as PriceMap }),\n ...(remotePrices !== undefined && { remotePrices }),\n })\n }\n\n function track(entry: Omit<UsageEntry, 'costUSD' | 'timestamp'>): void {\n const price = resolveModelPrice(entry.model)\n const costUSD = calculateCost(\n entry.inputTokens,\n entry.outputTokens,\n price,\n entry.cachedTokens,\n entry.cacheCreationTokens,\n )\n const full: UsageEntry = {\n ...entry,\n costUSD,\n timestamp: new Date().toISOString(),\n }\n storage.record(full)\n if (exporter) {\n Promise.resolve(exporter.export(full)).catch(() => { /* fire-and-forget */ })\n }\n maybeFireAlerts(full)\n if (anomalyDetection) maybeDetectAnomaly(full)\n if (suggestions) {\n maybeSuggestCheaperModel(entry.model, costUSD, entry.inputTokens, entry.outputTokens, {\n bundledPrices,\n ...(customPrices !== undefined && { customPrices: customPrices as PriceMap }),\n ...(remotePrices !== undefined && { remotePrices }),\n })\n }\n }\n\n function maybeFireAlerts(entry: UsageEntry): void {\n // Global threshold alert\n if (alertThreshold && webhookUrl && !alertFired) {\n alertFired = true\n Promise.resolve(storage.getAll()).then((entries) => {\n const total = computeTotal(entries)\n if (total < alertThreshold!) {\n alertFired = false\n return\n }\n fireWebhook(webhookUrl!, {\n text: `[tokenwatch] Alert: total cost reached $${total.toFixed(4)} USD (threshold: $${alertThreshold})`,\n })\n }).catch(() => {\n alertFired = false\n })\n }\n\n // Per-user budget alert\n if (budgets?.perUser && entry.userId) {\n const cfg = budgets.perUser\n const uid = entry.userId\n if (cfg.mode === 'always' || !firedUserAlerts.has(uid)) {\n // Claim the slot synchronously before going async — prevents double-fire\n if (cfg.mode !== 'always') firedUserAlerts.add(uid)\n Promise.resolve(storage.getAll()).then((entries) => {\n const userCost = entries\n .filter((e) => e.userId === uid)\n .reduce((s, e) => s + e.costUSD, 0)\n if (userCost >= cfg.threshold) {\n fireWebhook(cfg.webhookUrl, {\n text: `[tokenwatch] Budget alert: user \"${uid}\" reached $${userCost.toFixed(4)} USD (threshold: $${cfg.threshold})`,\n })\n } else {\n if (cfg.mode !== 'always') firedUserAlerts.delete(uid) // release — threshold not yet met\n }\n }).catch(() => {\n if (cfg.mode !== 'always') firedUserAlerts.delete(uid) // release on storage error\n })\n }\n }\n\n // Per-session budget alert\n if (budgets?.perSession && entry.sessionId) {\n const cfg = budgets.perSession\n const sid = entry.sessionId\n if (cfg.mode === 'always' || !firedSessionAlerts.has(sid)) {\n // Claim the slot synchronously before going async — prevents double-fire\n if (cfg.mode !== 'always') firedSessionAlerts.add(sid)\n Promise.resolve(storage.getAll()).then((entries) => {\n const sessionCost = entries\n .filter((e) => e.sessionId === sid)\n .reduce((s, e) => s + e.costUSD, 0)\n if (sessionCost >= cfg.threshold) {\n fireWebhook(cfg.webhookUrl, {\n text: `[tokenwatch] Budget alert: session \"${sid}\" reached $${sessionCost.toFixed(4)} USD (threshold: $${cfg.threshold})`,\n })\n } else {\n if (cfg.mode !== 'always') firedSessionAlerts.delete(sid) // release\n }\n }).catch(() => {\n if (cfg.mode !== 'always') firedSessionAlerts.delete(sid) // release on storage error\n })\n }\n }\n }\n\n function fireWebhook(url: string, payload: { text: string }): void {\n fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n }).catch(() => {\n // fire-and-forget\n })\n }\n\n async function getReport(options?: ReportOptions): Promise<Report> {\n const allEntries = await Promise.resolve(storage.getAll())\n const entries = filterEntries(allEntries, options)\n\n const byModel: Record<string, ModelStats> = {}\n const bySession: Record<string, SessionStats> = {}\n const byUser: Record<string, UserStats> = {}\n const byFeature: Record<string, FeatureStats> = {}\n\n let totalInput = 0\n let totalOutput = 0\n let totalCost = 0\n let periodFrom = options ? (entries[0]?.timestamp ?? startedAt) : startedAt\n let lastTimestamp = periodFrom\n\n for (const e of entries) {\n totalInput += e.inputTokens + (e.cachedTokens ?? 0) + (e.cacheCreationTokens ?? 0)\n totalOutput += e.outputTokens\n totalCost += e.costUSD\n if (e.timestamp > lastTimestamp) lastTimestamp = e.timestamp\n\n // byModel\n const m = (byModel[e.model] ??= {\n costUSD: 0,\n calls: 0,\n tokens: { input: 0, output: 0, reasoning: 0, cached: 0 },\n })\n m.costUSD += e.costUSD\n m.calls += 1\n m.tokens.input += e.inputTokens + (e.cachedTokens ?? 0) + (e.cacheCreationTokens ?? 0)\n m.tokens.output += e.outputTokens\n m.tokens.reasoning += e.reasoningTokens ?? 0\n m.tokens.cached += e.cachedTokens ?? 0\n\n // bySession\n if (e.sessionId) {\n const s = (bySession[e.sessionId] ??= { costUSD: 0, calls: 0 })\n s.costUSD += e.costUSD\n s.calls += 1\n }\n\n // byUser\n if (e.userId) {\n const u = (byUser[e.userId] ??= { costUSD: 0, calls: 0 })\n u.costUSD += e.costUSD\n u.calls += 1\n }\n\n // byFeature\n if (e.feature) {\n const f = (byFeature[e.feature] ??= { costUSD: 0, calls: 0 })\n f.costUSD += e.costUSD\n f.calls += 1\n }\n }\n\n // When filtering, use the actual first entry's timestamp as period.from\n if (options && entries.length > 0) {\n periodFrom = entries[0]?.timestamp ?? periodFrom\n }\n\n return {\n totalCostUSD: totalCost,\n totalTokens: { input: totalInput, output: totalOutput },\n byModel,\n bySession,\n byUser,\n byFeature,\n period: { from: periodFrom, to: lastTimestamp },\n ...(pricesUpdatedAt ? { pricesUpdatedAt } : {}),\n }\n }\n\n async function getCostForecast(options: ForecastOptions = {}): Promise<CostForecast> {\n const windowHours = options.windowHours ?? 24\n const allEntries = await Promise.resolve(storage.getAll())\n\n const now = Date.now()\n const windowStart = now - windowHours * 60 * 60 * 1000\n const windowEntries = allEntries.filter(\n (e) => new Date(e.timestamp).getTime() >= windowStart,\n )\n\n if (windowEntries.length < 2) {\n return {\n burnRatePerHour: 0,\n projectedDailyCostUSD: 0,\n projectedMonthlyCostUSD: 0,\n basedOnHours: 0,\n basedOnPeriod: null,\n }\n }\n\n const first = windowEntries[0]?.timestamp ?? ''\n const last = windowEntries[windowEntries.length - 1]?.timestamp ?? ''\n const actualMs = new Date(last).getTime() - new Date(first).getTime()\n const actualHours = actualMs / (1000 * 60 * 60)\n\n if (actualHours < 0.001) {\n return {\n burnRatePerHour: 0,\n projectedDailyCostUSD: 0,\n projectedMonthlyCostUSD: 0,\n basedOnHours: 0,\n basedOnPeriod: { from: first, to: last },\n }\n }\n\n const totalCost = windowEntries.reduce((s, e) => s + e.costUSD, 0)\n const burnRatePerHour = totalCost / actualHours\n\n return {\n burnRatePerHour,\n projectedDailyCostUSD: burnRatePerHour * 24,\n projectedMonthlyCostUSD: burnRatePerHour * 24 * 30,\n basedOnHours: Math.round(actualHours * 100) / 100,\n basedOnPeriod: { from: first, to: last },\n }\n }\n\n function maybeDetectAnomaly(entry: UsageEntry): void {\n if (entry.costUSD <= 0) return\n const { multiplierThreshold, webhookUrl: aUrl, windowHours: wh, mode: modeRaw } = anomalyDetection!\n const wHours = wh ?? 24\n const mode = modeRaw ?? 'once'\n const windowStart = Date.now() - wHours * 60 * 60 * 1000\n const entryTs = new Date(entry.timestamp).getTime()\n\n function checkEntity(key: string, label: string, predicate: (e: UsageEntry) => boolean): void {\n if (mode !== 'always' && firedAnomalyKeys.has(key)) return\n if (mode !== 'always') firedAnomalyKeys.add(key)\n Promise.resolve(storage.getAll()).then((all) => {\n const history = all.filter(\n (e) =>\n predicate(e) &&\n new Date(e.timestamp).getTime() >= windowStart &&\n new Date(e.timestamp).getTime() !== entryTs,\n )\n if (history.length === 0) {\n if (mode !== 'always') firedAnomalyKeys.delete(key)\n return\n }\n const avg = history.reduce((s, e) => s + e.costUSD, 0) / history.length\n if (avg <= 0 || entry.costUSD <= avg * multiplierThreshold) {\n if (mode !== 'always') firedAnomalyKeys.delete(key)\n return\n }\n const multiple = (entry.costUSD / avg).toFixed(1)\n fireWebhook(aUrl, {\n text: `[tokenwatch] Anomaly: ${label} call cost $${entry.costUSD.toFixed(4)} is ${multiple}x above ${wHours}h average ($${avg.toFixed(4)})`,\n })\n }).catch(() => {\n if (mode !== 'always') firedAnomalyKeys.delete(key)\n })\n }\n\n if (entry.userId) {\n checkEntity(\n `user:${entry.userId}`,\n `user \"${entry.userId}\"`,\n (e) => e.userId === entry.userId,\n )\n }\n checkEntity(\n `model:${entry.model}`,\n `model \"${entry.model}\"`,\n (e) => e.model === entry.model,\n )\n }\n\n async function reset(): Promise<void> {\n await Promise.resolve(storage.clearAll())\n alertFired = false\n firedUserAlerts.clear()\n firedSessionAlerts.clear()\n firedAnomalyKeys.clear()\n }\n\n async function resetSession(sessionId: string): Promise<void> {\n await Promise.resolve(storage.clearSession(sessionId))\n firedSessionAlerts.delete(sessionId)\n }\n\n async function exportJSON(): Promise<string> {\n return JSON.stringify(await getReport(), null, 2)\n }\n\n async function exportCSV(): Promise<string> {\n const entries = await Promise.resolve(storage.getAll())\n const header =\n 'timestamp,model,inputTokens,outputTokens,reasoningTokens,cachedTokens,cacheCreationTokens,costUSD,sessionId,userId,feature'\n const rows = entries.map((e) =>\n [\n csvEscape(e.timestamp),\n csvEscape(e.model),\n e.inputTokens,\n e.outputTokens,\n e.reasoningTokens ?? 0,\n e.cachedTokens ?? 0,\n e.cacheCreationTokens ?? 0,\n e.costUSD.toFixed(8),\n csvEscape(e.sessionId ?? ''),\n csvEscape(e.userId ?? ''),\n csvEscape(e.feature ?? ''),\n ].join(','),\n )\n return [header, ...rows].join('\\n')\n }\n\n function getModelInfo(model: string): ModelPrice | null {\n return findPrice(model, {\n bundledPrices,\n ...(customPrices !== undefined && { customPrices: customPrices as PriceMap }),\n ...(remotePrices !== undefined && { remotePrices }),\n }) ?? null\n }\n\n return {\n track,\n getReport,\n getCostForecast,\n reset,\n resetSession,\n exportJSON,\n exportCSV,\n getModelInfo,\n }\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction computeTotal(entries: UsageEntry[]): number {\n return entries.reduce((sum, e) => sum + e.costUSD, 0)\n}\n\n/** Parse a 'last' shorthand like '24h', '7d' into milliseconds */\nfunction parseLastMs(last: string): number {\n const match = /^(\\d+(?:\\.\\d+)?)(h|d)$/.exec(last.trim())\n if (!match) throw new Error(`[tokenwatch] Invalid \"last\" value: \"${last}\". Use e.g. \"24h\", \"7d\".`)\n const value = parseFloat(match[1] ?? '0')\n const unit = match[2] ?? 'h'\n return unit === 'h' ? value * 60 * 60 * 1000 : value * 24 * 60 * 60 * 1000\n}\n\nfunction filterEntries(entries: UsageEntry[], options?: ReportOptions): UsageEntry[] {\n if (!options) return entries\n\n let sinceMs: number | undefined\n let untilMs: number | undefined\n\n if (options.last) {\n sinceMs = Date.now() - parseLastMs(options.last)\n } else if (options.since) {\n sinceMs = new Date(options.since).getTime()\n }\n if (options.until) {\n untilMs = new Date(options.until).getTime()\n }\n\n if (sinceMs === undefined && untilMs === undefined) return entries\n\n return entries.filter((e) => {\n const ts = new Date(e.timestamp).getTime()\n if (sinceMs !== undefined && ts < sinceMs) return false\n if (untilMs !== undefined && ts > untilMs) return false\n return true\n })\n}\n\n/** Wrap a CSV field value in double-quotes if it contains commas, quotes, or newlines. */\nfunction csvEscape(value: string): string {\n if (value.includes(',') || value.includes('\"') || value.includes('\\n')) {\n return `\"${value.replace(/\"/g, '\"\"')}\"`\n }\n return value\n}\n","import type { ModelPrice, PriceMap } from '../types/index.js'\n\n/**\n * Resolve price for a model using 3-layer priority:\n * 1. customPrices (user override)\n * 2. remotePrices (synced from GitHub, cached 24h)\n * 3. bundledPrices (always-present fallback)\n *\n * Falls back to zero-cost with a console warning when model is not found anywhere.\n */\nexport function resolvePrice(\n model: string,\n layers: {\n customPrices?: PriceMap\n remotePrices?: PriceMap\n bundledPrices: PriceMap\n },\n): ModelPrice {\n const { customPrices, remotePrices, bundledPrices } = layers\n\n const found =\n lookupInMap(model, customPrices) ??\n lookupInMap(model, remotePrices) ??\n lookupInMap(model, bundledPrices)\n\n if (found) return found\n\n console.warn(\n `[tokenwatch] Unknown model \"${model}\". Cost will be recorded as $0. ` +\n `Add it via customPrices or update prices with: tokenwatch sync`,\n )\n return { input: 0, output: 0 }\n}\n\n/**\n * Find price for a model without the zero-cost fallback.\n * Returns undefined if the model is not found in any layer.\n */\nexport function findPrice(\n model: string,\n layers: {\n customPrices?: PriceMap\n remotePrices?: PriceMap\n bundledPrices: PriceMap\n },\n): ModelPrice | undefined {\n const { customPrices, remotePrices, bundledPrices } = layers\n return (\n lookupInMap(model, customPrices) ??\n lookupInMap(model, remotePrices) ??\n lookupInMap(model, bundledPrices)\n )\n}\n\n/**\n * Look up a model in a PriceMap using:\n * 1. exact key match\n * 2. prefix match — map key is a prefix of the model string (e.g. \"gpt-4o\" matches \"gpt-4o-2024-11-20\")\n * 3. reverse prefix — model string is a prefix of a map key (unusual, safety net)\n */\nfunction lookupInMap(model: string, map: PriceMap | undefined): ModelPrice | undefined {\n if (!map) return undefined\n\n if (model in map) return map[model]\n\n // prefix match\n for (const key of Object.keys(map)) {\n if (model.startsWith(key) || key.startsWith(model)) {\n return map[key]\n }\n }\n\n return undefined\n}\n\n/**\n * Calculate cost in USD given token counts and per-million-token prices.\n *\n * - `inputTokens` — regular (non-cached) input tokens\n * - `outputTokens` — output tokens (includes reasoning tokens for OpenAI, which are billed as output)\n * - `cachedTokens` — cache-read input tokens (billed at price.cachedInput or full input price if absent)\n * - `cacheCreationTokens` — cache-creation input tokens, Anthropic only (billed at price.cacheCreationInput\n * or 1.25× input price if absent)\n */\nexport function calculateCost(\n inputTokens: number,\n outputTokens: number,\n price: ModelPrice,\n cachedTokens = 0,\n cacheCreationTokens = 0,\n): number {\n const regularInputCost = (inputTokens / 1_000_000) * price.input\n const cachedReadCost = (cachedTokens / 1_000_000) * (price.cachedInput ?? price.input)\n const cacheCreationCost =\n (cacheCreationTokens / 1_000_000) * (price.cacheCreationInput ?? price.input * 1.25)\n const outputCost = (outputTokens / 1_000_000) * price.output\n return regularInputCost + cachedReadCost + cacheCreationCost + outputCost\n}\n","import type { PriceMap } from '../types/index.js'\nimport { calculateCost } from './pricing.js'\n\nconst PROVIDER_PREFIXES = ['gpt-', 'claude-', 'gemini-', 'deepseek-'] as const\n\nfunction getProviderPrefix(model: string): string | undefined {\n return PROVIDER_PREFIXES.find((p) => model.startsWith(p))\n}\n\n/**\n * After a tracked call, check if there is a cheaper model in the same provider family\n * (defined by model name prefix). Logs a hint if savings are strictly greater than 50%.\n * No-ops if the model is unknown, costUSD is zero, or no cheaper candidate is found.\n */\nexport function maybeSuggestCheaperModel(\n model: string,\n costUSD: number,\n inputTokens: number,\n outputTokens: number,\n layers: { bundledPrices: PriceMap; customPrices?: PriceMap; remotePrices?: PriceMap },\n): void {\n if (costUSD <= 0) return\n\n const prefix = getProviderPrefix(model)\n if (!prefix) return\n\n // Merge layers: bundled < remote < custom (custom wins)\n const mergedMap: PriceMap = {\n ...layers.bundledPrices,\n ...(layers.remotePrices ?? {}),\n ...(layers.customPrices ?? {}),\n }\n\n let cheapestModel: string | undefined\n let cheapestCost = Infinity\n\n for (const key of Object.keys(mergedMap)) {\n if (key === model || !key.startsWith(prefix)) continue\n const price = mergedMap[key]\n if (!price) continue\n const candidateCost = calculateCost(inputTokens, outputTokens, price)\n if (candidateCost < cheapestCost) {\n cheapestCost = candidateCost\n cheapestModel = key\n }\n }\n\n // Must be strictly more than 50% cheaper\n if (cheapestModel === undefined || cheapestCost >= costUSD * 0.5) return\n\n const savingsPct = Math.round((1 - cheapestCost / costUSD) * 100)\n console.log(\n `[tokenwatch] Suggestion: ${cheapestModel} could handle this for ~$${cheapestCost.toFixed(4)} (${savingsPct}% cheaper than ${model})`,\n )\n}\n","{\n \"updated_at\": \"2026-04-24\",\n \"source\": \"https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json\",\n \"models\": {\n \"gpt-4o\": {\n \"input\": 2.5,\n \"output\": 10,\n \"cachedInput\": 1.25,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-mini\": {\n \"input\": 0.15,\n \"output\": 0.6,\n \"cachedInput\": 0.075,\n \"maxInputTokens\": 128000\n },\n \"gpt-5\": {\n \"input\": 1.25,\n \"output\": 10,\n \"cachedInput\": 0.125,\n \"maxInputTokens\": 272000\n },\n \"gpt-5-mini\": {\n \"input\": 0.25,\n \"output\": 2,\n \"cachedInput\": 0.025,\n \"maxInputTokens\": 272000\n },\n \"gpt-5-nano\": {\n \"input\": 0.05,\n \"output\": 0.4,\n \"cachedInput\": 0.005,\n \"maxInputTokens\": 272000\n },\n \"claude-opus-4-6\": {\n \"input\": 5,\n \"output\": 25,\n \"cachedInput\": 0.5,\n \"cacheCreationInput\": 6.25,\n \"maxInputTokens\": 1000000\n },\n \"claude-sonnet-4-6\": {\n \"input\": 3,\n \"output\": 15,\n \"cachedInput\": 0.3,\n \"cacheCreationInput\": 3.75,\n \"maxInputTokens\": 1000000\n },\n \"claude-haiku-4-5\": {\n \"input\": 1,\n \"output\": 5,\n \"cachedInput\": 0.1,\n \"cacheCreationInput\": 1.25,\n \"maxInputTokens\": 200000\n },\n \"gemini-2.5-pro\": {\n \"input\": 1.25,\n \"output\": 10,\n \"cachedInput\": 0.125,\n \"maxInputTokens\": 1048576\n },\n \"gemini-2.5-flash\": {\n \"input\": 0.3,\n \"output\": 2.5,\n \"cachedInput\": 0.03,\n \"maxInputTokens\": 1048576\n },\n \"deepseek-chat\": {\n \"input\": 0.28,\n \"output\": 0.42,\n \"cachedInput\": 0.028,\n \"maxInputTokens\": 131072\n },\n \"deepseek-reasoner\": {\n \"input\": 0.28,\n \"output\": 0.42,\n \"cachedInput\": 0.028,\n \"maxInputTokens\": 131072\n },\n \"claude-opus-4-5\": {\n \"input\": 5,\n \"output\": 25,\n \"cachedInput\": 0.5,\n \"cacheCreationInput\": 6.25,\n \"maxInputTokens\": 200000\n },\n \"claude-opus-4-7\": {\n \"input\": 5,\n \"output\": 25,\n \"cachedInput\": 0.5,\n \"cacheCreationInput\": 6.25,\n \"maxInputTokens\": 1000000\n },\n \"claude-opus-4-1\": {\n \"input\": 15,\n \"output\": 75,\n \"cachedInput\": 1.5,\n \"cacheCreationInput\": 18.75,\n \"maxInputTokens\": 200000\n },\n \"claude-sonnet-4-5\": {\n \"input\": 3,\n \"output\": 15,\n \"cachedInput\": 0.3,\n \"cacheCreationInput\": 3.75,\n \"maxInputTokens\": 200000\n },\n \"gpt-oss-120b\": {\n \"input\": 3,\n \"output\": 4.5,\n \"maxInputTokens\": 131072\n },\n \"gpt-3.5-turbo\": {\n \"input\": 0.5,\n \"output\": 1.5,\n \"maxInputTokens\": 16385\n },\n \"gpt-3.5-turbo-0125\": {\n \"input\": 0.5,\n \"output\": 1.5,\n \"maxInputTokens\": 16385\n },\n \"gpt-35-turbo\": {\n \"input\": 0.5,\n \"output\": 1.5,\n \"maxInputTokens\": 4097\n },\n \"gpt-35-turbo-0125\": {\n \"input\": 0.5,\n \"output\": 1.5,\n \"maxInputTokens\": 16384\n },\n \"gpt-35-turbo-1106\": {\n \"input\": 1,\n \"output\": 2,\n \"maxInputTokens\": 16384\n },\n \"gpt-35-turbo-16k\": {\n \"input\": 3,\n \"output\": 4,\n \"maxInputTokens\": 16385\n },\n \"gpt-35-turbo-16k-0613\": {\n \"input\": 3,\n \"output\": 4,\n \"maxInputTokens\": 16385\n },\n \"gpt-4\": {\n \"input\": 30,\n \"output\": 60,\n \"maxInputTokens\": 8192\n },\n \"gpt-4-0125-preview\": {\n \"input\": 10,\n \"output\": 30,\n \"maxInputTokens\": 128000\n },\n \"gpt-4-0613\": {\n \"input\": 30,\n \"output\": 60,\n \"maxInputTokens\": 8192\n },\n \"gpt-4-1106-preview\": {\n \"input\": 10,\n \"output\": 30,\n \"maxInputTokens\": 128000\n },\n \"gpt-4-32k\": {\n \"input\": 60,\n \"output\": 120,\n \"maxInputTokens\": 32768\n },\n \"gpt-4-32k-0613\": {\n \"input\": 60,\n \"output\": 120,\n \"maxInputTokens\": 32768\n },\n \"gpt-4-turbo\": {\n \"input\": 10,\n \"output\": 30,\n \"maxInputTokens\": 128000\n },\n \"gpt-4-turbo-2024-04-09\": {\n \"input\": 10,\n \"output\": 30,\n \"maxInputTokens\": 128000\n },\n \"gpt-4-turbo-vision-preview\": {\n \"input\": 10,\n \"output\": 30,\n \"maxInputTokens\": 128000\n },\n \"gpt-4.1\": {\n \"input\": 2,\n \"output\": 8,\n \"cachedInput\": 0.5,\n \"maxInputTokens\": 1047576\n },\n \"gpt-4.1-2025-04-14\": {\n \"input\": 2,\n \"output\": 8,\n \"cachedInput\": 0.5,\n \"maxInputTokens\": 1047576\n },\n \"gpt-4.1-mini\": {\n \"input\": 0.4,\n \"output\": 1.6,\n \"cachedInput\": 0.1,\n \"maxInputTokens\": 1047576\n },\n \"gpt-4.1-mini-2025-04-14\": {\n \"input\": 0.4,\n \"output\": 1.6,\n \"cachedInput\": 0.1,\n \"maxInputTokens\": 1047576\n },\n \"gpt-4.1-nano\": {\n \"input\": 0.1,\n \"output\": 0.4,\n \"cachedInput\": 0.025,\n \"maxInputTokens\": 1047576\n },\n \"gpt-4.1-nano-2025-04-14\": {\n \"input\": 0.1,\n \"output\": 0.4,\n \"cachedInput\": 0.025,\n \"maxInputTokens\": 1047576\n },\n \"gpt-4.5-preview\": {\n \"input\": 75,\n \"output\": 150,\n \"cachedInput\": 37.5,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-2024-05-13\": {\n \"input\": 5,\n \"output\": 15,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-2024-08-06\": {\n \"input\": 2.5,\n \"output\": 10,\n \"cachedInput\": 1.25,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-2024-11-20\": {\n \"input\": 2.5,\n \"output\": 10,\n \"cachedInput\": 1.25,\n \"maxInputTokens\": 128000\n },\n \"gpt-audio-2025-08-28\": {\n \"input\": 2.5,\n \"output\": 10,\n \"maxInputTokens\": 128000\n },\n \"gpt-audio-1.5-2026-02-23\": {\n \"input\": 2.5,\n \"output\": 10,\n \"maxInputTokens\": 128000\n },\n \"gpt-audio-mini-2025-10-06\": {\n \"input\": 0.6,\n \"output\": 2.4,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-audio-preview-2024-12-17\": {\n \"input\": 2.5,\n \"output\": 10,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-mini-2024-07-18\": {\n \"input\": 0.15,\n \"output\": 0.6,\n \"cachedInput\": 0.075,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-mini-audio-preview-2024-12-17\": {\n \"input\": 0.15,\n \"output\": 0.6,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-mini-realtime-preview-2024-12-17\": {\n \"input\": 0.6,\n \"output\": 2.4,\n \"cachedInput\": 0.3,\n \"maxInputTokens\": 128000\n },\n \"gpt-realtime-2025-08-28\": {\n \"input\": 4,\n \"output\": 16,\n \"cachedInput\": 0.4,\n \"maxInputTokens\": 32000\n },\n \"gpt-realtime-1.5-2026-02-23\": {\n \"input\": 4,\n \"output\": 16,\n \"cachedInput\": 4,\n \"maxInputTokens\": 32000\n },\n \"gpt-realtime-mini-2025-10-06\": {\n \"input\": 0.6,\n \"output\": 2.4,\n \"cachedInput\": 0.06,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-mini-transcribe\": {\n \"input\": 1.25,\n \"output\": 5,\n \"maxInputTokens\": 16000\n },\n \"gpt-4o-realtime-preview-2024-10-01\": {\n \"input\": 5,\n \"output\": 20,\n \"cachedInput\": 2.5,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-realtime-preview-2024-12-17\": {\n \"input\": 5,\n \"output\": 20,\n \"cachedInput\": 2.5,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-transcribe\": {\n \"input\": 2.5,\n \"output\": 10,\n \"maxInputTokens\": 16000\n },\n \"gpt-4o-transcribe-diarize\": {\n \"input\": 2.5,\n \"output\": 10,\n \"maxInputTokens\": 16000\n },\n \"gpt-5.1-2025-11-13\": {\n \"input\": 1.25,\n \"output\": 10,\n \"cachedInput\": 0.125,\n \"maxInputTokens\": 272000\n },\n \"gpt-5.1-chat-2025-11-13\": {\n \"input\": 1.25,\n \"output\": 10,\n \"cachedInput\": 0.125,\n \"maxInputTokens\": 128000\n },\n \"gpt-5.1-codex-2025-11-13\": {\n \"input\": 1.25,\n \"output\": 10,\n \"cachedInput\": 0.125,\n \"maxInputTokens\": 272000\n },\n \"gpt-5.1-codex-mini-2025-11-13\": {\n \"input\": 0.25,\n \"output\": 2,\n \"cachedInput\": 0.025,\n \"maxInputTokens\": 272000\n },\n \"gpt-5-2025-08-07\": {\n \"input\": 1.25,\n \"output\": 10,\n \"cachedInput\": 0.125,\n \"maxInputTokens\": 272000\n },\n \"gpt-5-chat\": {\n \"input\": 1.25,\n \"output\": 10,\n \"cachedInput\": 0.125,\n \"maxInputTokens\": 128000\n },\n \"gpt-5-chat-latest\": {\n \"input\": 1.25,\n \"output\": 10,\n \"cachedInput\": 0.125,\n \"maxInputTokens\": 128000\n },\n \"gpt-5-codex\": {\n \"input\": 1.25,\n \"output\": 10,\n \"cachedInput\": 0.125,\n \"maxInputTokens\": 272000\n },\n \"gpt-5-mini-2025-08-07\": {\n \"input\": 0.25,\n \"output\": 2,\n \"cachedInput\": 0.025,\n \"maxInputTokens\": 272000\n },\n \"gpt-5-nano-2025-08-07\": {\n \"input\": 0.05,\n \"output\": 0.4,\n \"cachedInput\": 0.005,\n \"maxInputTokens\": 272000\n },\n \"gpt-5-pro\": {\n \"input\": 15,\n \"output\": 120,\n \"maxInputTokens\": 128000\n },\n \"gpt-5.1\": {\n \"input\": 1.25,\n \"output\": 10,\n \"cachedInput\": 0.125,\n \"maxInputTokens\": 272000\n },\n \"gpt-5.1-chat\": {\n \"input\": 1.25,\n \"output\": 10,\n \"cachedInput\": 0.125,\n \"maxInputTokens\": 128000\n },\n \"gpt-5.1-codex\": {\n \"input\": 1.25,\n \"output\": 10,\n \"cachedInput\": 0.125,\n \"maxInputTokens\": 272000\n },\n \"gpt-5.1-codex-max\": {\n \"input\": 1.25,\n \"output\": 10,\n \"cachedInput\": 0.125,\n \"maxInputTokens\": 272000\n },\n \"gpt-5.1-codex-mini\": {\n \"input\": 0.25,\n \"output\": 2,\n \"cachedInput\": 0.025,\n \"maxInputTokens\": 272000\n },\n \"gpt-5.2\": {\n \"input\": 1.75,\n \"output\": 14,\n \"cachedInput\": 0.175,\n \"maxInputTokens\": 272000\n },\n \"gpt-5.2-2025-12-11\": {\n \"input\": 1.75,\n \"output\": 14,\n \"cachedInput\": 0.175,\n \"maxInputTokens\": 272000\n },\n \"gpt-5.2-chat\": {\n \"input\": 1.75,\n \"output\": 14,\n \"cachedInput\": 0.175,\n \"maxInputTokens\": 128000\n },\n \"gpt-5.2-chat-2025-12-11\": {\n \"input\": 1.75,\n \"output\": 14,\n \"cachedInput\": 0.175,\n \"maxInputTokens\": 128000\n },\n \"gpt-5.2-codex\": {\n \"input\": 1.75,\n \"output\": 14,\n \"cachedInput\": 0.175,\n \"maxInputTokens\": 272000\n },\n \"gpt-5.3-chat\": {\n \"input\": 1.75,\n \"output\": 14,\n \"cachedInput\": 0.175,\n \"maxInputTokens\": 128000\n },\n \"gpt-5.3-codex\": {\n \"input\": 1.75,\n \"output\": 14,\n \"cachedInput\": 0.175,\n \"maxInputTokens\": 272000\n },\n \"gpt-5.2-pro\": {\n \"input\": 21,\n \"output\": 168,\n \"maxInputTokens\": 272000\n },\n \"gpt-5.2-pro-2025-12-11\": {\n \"input\": 21,\n \"output\": 168,\n \"maxInputTokens\": 272000\n },\n \"gpt-5.4\": {\n \"input\": 2.5,\n \"output\": 15,\n \"cachedInput\": 0.25,\n \"maxInputTokens\": 1050000\n },\n \"gpt-5.4-2026-03-05\": {\n \"input\": 2.5,\n \"output\": 15,\n \"cachedInput\": 0.25,\n \"maxInputTokens\": 1050000\n },\n \"gpt-5.4-pro\": {\n \"input\": 30,\n \"output\": 180,\n \"cachedInput\": 3,\n \"maxInputTokens\": 1050000\n },\n \"gpt-5.4-pro-2026-03-05\": {\n \"input\": 30,\n \"output\": 180,\n \"cachedInput\": 3,\n \"maxInputTokens\": 1050000\n },\n \"gpt-5.4-mini\": {\n \"input\": 0.75,\n \"output\": 4.5,\n \"cachedInput\": 0.075,\n \"maxInputTokens\": 272000\n },\n \"gpt-5.4-nano\": {\n \"input\": 0.2,\n \"output\": 1.25,\n \"cachedInput\": 0.02,\n \"maxInputTokens\": 272000\n },\n \"o1-2024-12-17\": {\n \"input\": 15,\n \"output\": 60,\n \"cachedInput\": 7.5,\n \"maxInputTokens\": 200000\n },\n \"o1-mini\": {\n \"input\": 1.21,\n \"output\": 4.84,\n \"cachedInput\": 0.605,\n \"maxInputTokens\": 128000\n },\n \"o1-mini-2024-09-12\": {\n \"input\": 1.1,\n \"output\": 4.4,\n \"cachedInput\": 0.55,\n \"maxInputTokens\": 128000\n },\n \"o1-preview\": {\n \"input\": 15,\n \"output\": 60,\n \"cachedInput\": 7.5,\n \"maxInputTokens\": 128000\n },\n \"o1-preview-2024-09-12\": {\n \"input\": 15,\n \"output\": 60,\n \"cachedInput\": 7.5,\n \"maxInputTokens\": 128000\n },\n \"o3-2025-04-16\": {\n \"input\": 2,\n \"output\": 8,\n \"cachedInput\": 0.5,\n \"maxInputTokens\": 200000\n },\n \"o3-mini\": {\n \"input\": 1.1,\n \"output\": 4.4,\n \"cachedInput\": 0.55,\n \"maxInputTokens\": 200000\n },\n \"o3-mini-2025-01-31\": {\n \"input\": 1.1,\n \"output\": 4.4,\n \"cachedInput\": 0.55,\n \"maxInputTokens\": 200000\n },\n \"o3-pro\": {\n \"input\": 20,\n \"output\": 80,\n \"maxInputTokens\": 200000\n },\n \"o3-pro-2025-06-10\": {\n \"input\": 20,\n \"output\": 80,\n \"maxInputTokens\": 200000\n },\n \"o4-mini\": {\n \"input\": 1.1,\n \"output\": 4.4,\n \"cachedInput\": 0.275,\n \"maxInputTokens\": 200000\n },\n \"o4-mini-2025-04-16\": {\n \"input\": 1.1,\n \"output\": 4.4,\n \"cachedInput\": 0.275,\n \"maxInputTokens\": 200000\n },\n \"deepseek-v3.2\": {\n \"input\": 0.28,\n \"output\": 0.4,\n \"maxInputTokens\": 163840\n },\n \"deepseek-v3.2-speciale\": {\n \"input\": 0.58,\n \"output\": 1.68,\n \"maxInputTokens\": 163840\n },\n \"deepseek-r1\": {\n \"input\": 0.55,\n \"output\": 2.19,\n \"maxInputTokens\": 65536\n },\n \"deepseek-v3\": {\n \"input\": 0.27,\n \"output\": 1.1,\n \"cachedInput\": 0.07,\n \"maxInputTokens\": 65536\n },\n \"deepseek-v3-0324\": {\n \"input\": 0.2,\n \"output\": 0.6,\n \"maxInputTokens\": 131072\n },\n \"chatgpt-4o-latest\": {\n \"input\": 5,\n \"output\": 15,\n \"maxInputTokens\": 128000\n },\n \"claude-haiku-4-5-20251001\": {\n \"input\": 1,\n \"output\": 5,\n \"cachedInput\": 0.1,\n \"cacheCreationInput\": 1.25,\n \"maxInputTokens\": 200000\n },\n \"claude-3-7-sonnet-20250219\": {\n \"input\": 3,\n \"output\": 15,\n \"cachedInput\": 0.3,\n \"cacheCreationInput\": 3.75,\n \"maxInputTokens\": 200000\n },\n \"claude-3-haiku-20240307\": {\n \"input\": 0.25,\n \"output\": 1.25,\n \"cachedInput\": 0.03,\n \"cacheCreationInput\": 0.3,\n \"maxInputTokens\": 200000\n },\n \"claude-3-opus-20240229\": {\n \"input\": 15,\n \"output\": 75,\n \"cachedInput\": 1.5,\n \"cacheCreationInput\": 18.75,\n \"maxInputTokens\": 200000\n },\n \"claude-4-opus-20250514\": {\n \"input\": 15,\n \"output\": 75,\n \"cachedInput\": 1.5,\n \"cacheCreationInput\": 18.75,\n \"maxInputTokens\": 200000\n },\n \"claude-4-sonnet-20250514\": {\n \"input\": 3,\n \"output\": 15,\n \"cachedInput\": 0.3,\n \"cacheCreationInput\": 3.75,\n \"maxInputTokens\": 1000000\n },\n \"claude-sonnet-4-5-20250929\": {\n \"input\": 3,\n \"output\": 15,\n \"cachedInput\": 0.3,\n \"cacheCreationInput\": 3.75,\n \"maxInputTokens\": 200000\n },\n \"claude-sonnet-4-5-20250929-v1:0\": {\n \"input\": 3,\n \"output\": 15,\n \"cachedInput\": 0.3,\n \"cacheCreationInput\": 3.75,\n \"maxInputTokens\": 200000\n },\n \"claude-opus-4-1-20250805\": {\n \"input\": 15,\n \"output\": 75,\n \"cachedInput\": 1.5,\n \"cacheCreationInput\": 18.75,\n \"maxInputTokens\": 200000\n },\n \"claude-opus-4-20250514\": {\n \"input\": 15,\n \"output\": 75,\n \"cachedInput\": 1.5,\n \"cacheCreationInput\": 18.75,\n \"maxInputTokens\": 200000\n },\n \"claude-opus-4-5-20251101\": {\n \"input\": 5,\n \"output\": 25,\n \"cachedInput\": 0.5,\n \"cacheCreationInput\": 6.25,\n \"maxInputTokens\": 200000\n },\n \"claude-opus-4-6-20260205\": {\n \"input\": 5,\n \"output\": 25,\n \"cachedInput\": 0.5,\n \"cacheCreationInput\": 6.25,\n \"maxInputTokens\": 1000000\n },\n \"claude-opus-4-7-20260416\": {\n \"input\": 5,\n \"output\": 25,\n \"cachedInput\": 0.5,\n \"cacheCreationInput\": 6.25,\n \"maxInputTokens\": 1000000\n },\n \"claude-sonnet-4-20250514\": {\n \"input\": 3,\n \"output\": 15,\n \"cachedInput\": 0.3,\n \"cacheCreationInput\": 3.75,\n \"maxInputTokens\": 1000000\n },\n \"codex-mini-latest\": {\n \"input\": 1.5,\n \"output\": 6,\n \"cachedInput\": 0.375,\n \"maxInputTokens\": 200000\n },\n \"deepseek-ai/deepseek-r1\": {\n \"input\": 3,\n \"output\": 7,\n \"maxInputTokens\": 128000\n },\n \"deepseek-ai/deepseek-r1-0528\": {\n \"input\": 135000,\n \"output\": 540000,\n \"maxInputTokens\": 161000\n },\n \"deepseek-ai/deepseek-r1-0528-turbo\": {\n \"input\": 1,\n \"output\": 3,\n \"maxInputTokens\": 32768\n },\n \"deepseek-ai/deepseek-r1-distill-llama-70b\": {\n \"input\": 0.25,\n \"output\": 0.75,\n \"maxInputTokens\": 128000\n },\n \"deepseek-ai/deepseek-r1-distill-qwen-32b\": {\n \"input\": 0.15,\n \"output\": 0.15\n },\n \"deepseek-ai/deepseek-r1-turbo\": {\n \"input\": 1,\n \"output\": 3,\n \"maxInputTokens\": 40960\n },\n \"deepseek-ai/deepseek-v3\": {\n \"input\": 1.25,\n \"output\": 1.25,\n \"maxInputTokens\": 65536\n },\n \"deepseek-ai/deepseek-v3-0324\": {\n \"input\": 114000,\n \"output\": 275000,\n \"maxInputTokens\": 161000\n },\n \"deepseek-ai/deepseek-v3.1\": {\n \"input\": 55000,\n \"output\": 165000,\n \"maxInputTokens\": 128000\n },\n \"deepseek-ai/deepseek-v3.1-terminus\": {\n \"input\": 0.27,\n \"output\": 1,\n \"cachedInput\": 0.216,\n \"maxInputTokens\": 163840\n },\n \"deepseek-coder\": {\n \"input\": 0.14,\n \"output\": 0.28,\n \"maxInputTokens\": 128000\n },\n \"gemini-2.0-flash\": {\n \"input\": 0.1,\n \"output\": 0.4,\n \"cachedInput\": 0.025,\n \"maxInputTokens\": 1048576\n },\n \"gemini-2.0-flash-001\": {\n \"input\": 0.1,\n \"output\": 0.4,\n \"cachedInput\": 0.025,\n \"maxInputTokens\": 1048576\n },\n \"gemini-2.0-flash-lite\": {\n \"input\": 0.075,\n \"output\": 0.3,\n \"cachedInput\": 0.01875,\n \"maxInputTokens\": 1048576\n },\n \"gemini-2.0-flash-lite-001\": {\n \"input\": 0.075,\n \"output\": 0.3,\n \"cachedInput\": 0.01875,\n \"maxInputTokens\": 1048576\n },\n \"gemini-2.5-flash-image\": {\n \"input\": 0.3,\n \"output\": 2.5,\n \"cachedInput\": 0.03,\n \"maxInputTokens\": 32768\n },\n \"gemini-3-pro-image-preview\": {\n \"input\": 2,\n \"output\": 12,\n \"maxInputTokens\": 65536\n },\n \"gemini-3.1-flash-image-preview\": {\n \"input\": 0.5,\n \"output\": 3,\n \"maxInputTokens\": 65536\n },\n \"gemini-3.1-flash-lite-preview\": {\n \"input\": 0.25,\n \"output\": 1.5,\n \"cachedInput\": 0.025,\n \"maxInputTokens\": 1048576\n },\n \"gemini-2.5-flash-lite\": {\n \"input\": 0.1,\n \"output\": 0.4,\n \"cachedInput\": 0.01,\n \"maxInputTokens\": 1048576\n },\n \"gemini-2.5-flash-lite-preview-09-2025\": {\n \"input\": 0.1,\n \"output\": 0.4,\n \"cachedInput\": 0.01,\n \"maxInputTokens\": 1048576\n },\n \"gemini-2.5-flash-preview-09-2025\": {\n \"input\": 0.3,\n \"output\": 2.5,\n \"cachedInput\": 0.075,\n \"maxInputTokens\": 1048576\n },\n \"gemini-live-2.5-flash-preview-native-audio-09-2025\": {\n \"input\": 0.3,\n \"output\": 2,\n \"cachedInput\": 0.075,\n \"maxInputTokens\": 1048576\n },\n \"gemini-2.5-flash-lite-preview-06-17\": {\n \"input\": 0.1,\n \"output\": 0.4,\n \"cachedInput\": 0.025,\n \"maxInputTokens\": 1048576\n },\n \"gemini-3-pro-preview\": {\n \"input\": 2,\n \"output\": 12,\n \"cachedInput\": 0.2,\n \"maxInputTokens\": 1048576\n },\n \"gemini-3.1-pro-preview\": {\n \"input\": 2,\n \"output\": 12,\n \"cachedInput\": 0.2,\n \"maxInputTokens\": 1048576\n },\n \"gemini-3.1-pro-preview-customtools\": {\n \"input\": 2,\n \"output\": 12,\n \"cachedInput\": 0.2,\n \"maxInputTokens\": 1048576\n },\n \"gemini-3-flash-preview\": {\n \"input\": 0.5,\n \"output\": 3,\n \"cachedInput\": 0.05,\n \"maxInputTokens\": 1048576\n },\n \"gemini-robotics-er-1.5-preview\": {\n \"input\": 0.3,\n \"output\": 2.5,\n \"maxInputTokens\": 1048576\n },\n \"gemini-2.5-computer-use-preview-10-2025\": {\n \"input\": 1.25,\n \"output\": 10,\n \"maxInputTokens\": 128000\n },\n \"gemini-flash-latest\": {\n \"input\": 0.3,\n \"output\": 2.5,\n \"cachedInput\": 0.03,\n \"maxInputTokens\": 1048576\n },\n \"gemini-flash-lite-latest\": {\n \"input\": 0.1,\n \"output\": 0.4,\n \"cachedInput\": 0.01,\n \"maxInputTokens\": 1048576\n },\n \"gemini-gemma-2-27b-it\": {\n \"input\": 0.35,\n \"output\": 1.05,\n \"maxInputTokens\": 8192\n },\n \"gemini-gemma-2-9b-it\": {\n \"input\": 0.35,\n \"output\": 1.05,\n \"maxInputTokens\": 8192\n },\n \"deepseek-ai/deepseek-v3.2\": {\n \"input\": 0.28,\n \"output\": 0.4,\n \"maxInputTokens\": 163840\n },\n \"gpt-3.5-turbo-1106\": {\n \"input\": 1,\n \"output\": 2,\n \"maxInputTokens\": 16385\n },\n \"gpt-3.5-turbo-16k\": {\n \"input\": 3,\n \"output\": 4,\n \"maxInputTokens\": 16385\n },\n \"gpt-4-0314\": {\n \"input\": 30,\n \"output\": 60,\n \"maxInputTokens\": 8192\n },\n \"gpt-4-turbo-preview\": {\n \"input\": 10,\n \"output\": 30,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-audio-preview\": {\n \"input\": 2.5,\n \"output\": 10,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-audio-preview-2025-06-03\": {\n \"input\": 2.5,\n \"output\": 10,\n \"maxInputTokens\": 128000\n },\n \"gpt-audio\": {\n \"input\": 2.5,\n \"output\": 10,\n \"maxInputTokens\": 128000\n },\n \"gpt-audio-1.5\": {\n \"input\": 2.5,\n \"output\": 10,\n \"maxInputTokens\": 128000\n },\n \"gpt-audio-mini\": {\n \"input\": 0.6,\n \"output\": 2.4,\n \"maxInputTokens\": 128000\n },\n \"gpt-audio-mini-2025-12-15\": {\n \"input\": 0.6,\n \"output\": 2.4,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-mini-audio-preview\": {\n \"input\": 0.15,\n \"output\": 0.6,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-mini-realtime-preview\": {\n \"input\": 0.6,\n \"output\": 2.4,\n \"cachedInput\": 0.3,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-realtime-preview\": {\n \"input\": 5,\n \"output\": 20,\n \"cachedInput\": 2.5,\n \"maxInputTokens\": 128000\n },\n \"gpt-4o-realtime-preview-2025-06-03\": {\n \"input\": 5,\n \"output\": 20,\n \"cachedInput\": 2.5,\n \"maxInputTokens\": 128000\n },\n \"gpt-image-1.5\": {\n \"input\": 5,\n \"output\": 10,\n \"cachedInput\": 1.25\n },\n \"gpt-image-1.5-2025-12-16\": {\n \"input\": 5,\n \"output\": 10,\n \"cachedInput\": 1.25\n },\n \"gpt-5.1-chat-latest\": {\n \"input\": 1.25,\n \"output\": 10,\n \"cachedInput\": 0.125,\n \"maxInputTokens\": 128000\n },\n \"gpt-5.2-chat-latest\": {\n \"input\": 1.75,\n \"output\": 14,\n \"cachedInput\": 0.175,\n \"maxInputTokens\": 128000\n },\n \"gpt-5.3-chat-latest\": {\n \"input\": 1.75,\n \"output\": 14,\n \"cachedInput\": 0.175,\n \"maxInputTokens\": 128000\n },\n \"gpt-5-pro-2025-10-06\": {\n \"input\": 15,\n \"output\": 120,\n \"maxInputTokens\": 128000\n },\n \"gpt-realtime\": {\n \"input\": 4,\n \"output\": 16,\n \"cachedInput\": 0.4,\n \"maxInputTokens\": 32000\n },\n \"gpt-realtime-1.5\": {\n \"input\": 4,\n \"output\": 16,\n \"cachedInput\": 0.4,\n \"maxInputTokens\": 32000\n },\n \"gpt-realtime-mini\": {\n \"input\": 0.6,\n \"output\": 2.4,\n \"maxInputTokens\": 128000\n },\n \"deepseek-r1-distill-llama-70b\": {\n \"input\": 0.99,\n \"output\": 0.99,\n \"maxInputTokens\": 8000\n },\n \"deepseek-llama3.3-70b\": {\n \"input\": 0.2,\n \"output\": 0.6,\n \"maxInputTokens\": 131072\n },\n \"deepseek-r1-0528\": {\n \"input\": 0.2,\n \"output\": 0.6,\n \"maxInputTokens\": 131072\n },\n \"deepseek-r1-671b\": {\n \"input\": 0.8,\n \"output\": 0.8,\n \"maxInputTokens\": 131072\n },\n \"deepseek-ai/deepseek-r1-distill-llama-8b\": {\n \"input\": 0.025,\n \"output\": 0.025\n },\n \"deepseek-ai/deepseek-r1-distill-qwen-1.5b\": {\n \"input\": 0.09,\n \"output\": 0.09\n },\n \"deepseek-ai/deepseek-r1-distill-qwen-14b\": {\n \"input\": 0.07,\n \"output\": 0.07\n },\n \"deepseek-ai/deepseek-r1-distill-qwen-7b\": {\n \"input\": 0.2,\n \"output\": 0.2\n },\n \"o1\": {\n \"input\": 15,\n \"output\": 60,\n \"cachedInput\": 7.5,\n \"maxInputTokens\": 200000\n },\n \"o1-pro\": {\n \"input\": 150,\n \"output\": 600,\n \"maxInputTokens\": 200000\n },\n \"o1-pro-2025-03-19\": {\n \"input\": 150,\n \"output\": 600,\n \"maxInputTokens\": 200000\n },\n \"o3\": {\n \"input\": 2,\n \"output\": 8,\n \"cachedInput\": 0.5,\n \"maxInputTokens\": 200000\n },\n \"gpt-oss-20b\": {\n \"input\": 0.09,\n \"output\": 0.36\n },\n \"deepseek-ai/deepseek-r1-0528-tput\": {\n \"input\": 0.55,\n \"output\": 2.19,\n \"maxInputTokens\": 128000\n },\n \"claude-3-5-haiku\": {\n \"input\": 1,\n \"output\": 5,\n \"maxInputTokens\": 200000\n },\n \"claude-3-5-haiku@20241022\": {\n \"input\": 1,\n \"output\": 5,\n \"maxInputTokens\": 200000\n },\n \"claude-haiku-4-5@20251001\": {\n \"input\": 1,\n \"output\": 5,\n \"cachedInput\": 0.1,\n \"cacheCreationInput\": 1.25,\n \"maxInputTokens\": 200000\n },\n \"claude-3-5-sonnet\": {\n \"input\": 3,\n \"output\": 15,\n \"maxInputTokens\": 200000\n },\n \"claude-3-5-sonnet@20240620\": {\n \"input\": 3,\n \"output\": 15,\n \"maxInputTokens\": 200000\n },\n \"claude-3-7-sonnet@20250219\": {\n \"input\": 3,\n \"output\": 15,\n \"cachedInput\": 0.3,\n \"cacheCreationInput\": 3.75,\n \"maxInputTokens\": 200000\n },\n \"claude-3-haiku\": {\n \"input\": 0.25,\n \"output\": 1.25,\n \"maxInputTokens\": 200000\n },\n \"claude-3-haiku@20240307\": {\n \"input\": 0.25,\n \"output\": 1.25,\n \"maxInputTokens\": 200000\n },\n \"claude-3-opus\": {\n \"input\": 15,\n \"output\": 75,\n \"maxInputTokens\": 200000\n },\n \"claude-3-opus@20240229\": {\n \"input\": 15,\n \"output\": 75,\n \"maxInputTokens\": 200000\n },\n \"claude-3-sonnet\": {\n \"input\": 3,\n \"output\": 15,\n \"maxInputTokens\": 200000\n },\n \"claude-3-sonnet@20240229\": {\n \"input\": 3,\n \"output\": 15,\n \"maxInputTokens\": 200000\n },\n \"claude-opus-4\": {\n \"input\": 15,\n \"output\": 75,\n \"cachedInput\": 1.5,\n \"cacheCreationInput\": 18.75,\n \"maxInputTokens\": 200000\n },\n \"claude-opus-4-1@20250805\": {\n \"input\": 15,\n \"output\": 75,\n \"cachedInput\": 1.5,\n \"cacheCreationInput\": 18.75,\n \"maxInputTokens\": 200000\n },\n \"claude-opus-4-5@20251101\": {\n \"input\": 5,\n \"output\": 25,\n \"cachedInput\": 0.5,\n \"cacheCreationInput\": 6.25,\n \"maxInputTokens\": 200000\n },\n \"claude-opus-4-6@default\": {\n \"input\": 5,\n \"output\": 25,\n \"cachedInput\": 0.5,\n \"cacheCreationInput\": 6.25,\n \"maxInputTokens\": 1000000\n },\n \"claude-opus-4-7@default\": {\n \"input\": 5,\n \"output\": 25,\n \"cachedInput\": 0.5,\n \"cacheCreationInput\": 6.25,\n \"maxInputTokens\": 1000000\n },\n \"claude-sonnet-4-5@20250929\": {\n \"input\": 3,\n \"output\": 15,\n \"cachedInput\": 0.3,\n \"cacheCreationInput\": 3.75,\n \"maxInputTokens\": 200000\n },\n \"claude-opus-4@20250514\": {\n \"input\": 15,\n \"output\": 75,\n \"cachedInput\": 1.5,\n \"cacheCreationInput\": 18.75,\n \"maxInputTokens\": 200000\n },\n \"claude-sonnet-4\": {\n \"input\": 3,\n \"output\": 15,\n \"cachedInput\": 0.3,\n \"cacheCreationInput\": 3.75,\n \"maxInputTokens\": 1000000\n },\n \"claude-sonnet-4@20250514\": {\n \"input\": 3,\n \"output\": 15,\n \"cachedInput\": 0.3,\n \"cacheCreationInput\": 3.75,\n \"maxInputTokens\": 1000000\n },\n \"deepseek-ai/deepseek-v3.1-maas\": {\n \"input\": 1.35,\n \"output\": 5.4,\n \"maxInputTokens\": 163840\n },\n \"deepseek-ai/deepseek-v3.2-maas\": {\n \"input\": 0.56,\n \"output\": 1.68,\n \"maxInputTokens\": 163840\n },\n \"deepseek-ai/deepseek-r1-0528-maas\": {\n \"input\": 1.35,\n \"output\": 5.4,\n \"maxInputTokens\": 65336\n },\n \"deepseek-ai/deepseek-ocr-maas\": {\n \"input\": 0.3,\n \"output\": 1.2\n },\n \"deepseek-r1-8b\": {\n \"input\": 0.1,\n \"output\": 0.2,\n \"maxInputTokens\": 65536\n },\n \"deepseek-r1-7b-qwen\": {\n \"input\": 0.08,\n \"output\": 0.15,\n \"maxInputTokens\": 131072\n },\n \"deepseek-coder-6.7b\": {\n \"input\": 0.06,\n \"output\": 0.12,\n \"maxInputTokens\": 16384\n },\n \"gpt-4o-mini-transcribe-2025-03-20\": {\n \"input\": 1.25,\n \"output\": 5,\n \"maxInputTokens\": 16000\n },\n \"gpt-4o-mini-transcribe-2025-12-15\": {\n \"input\": 1.25,\n \"output\": 5,\n \"maxInputTokens\": 16000\n },\n \"gpt-realtime-mini-2025-12-15\": {\n \"input\": 0.6,\n \"output\": 2.4,\n \"cachedInput\": 0.06,\n \"maxInputTokens\": 128000\n },\n \"gemini-2.5-flash-native-audio-latest\": {\n \"input\": 0.3,\n \"output\": 2.5,\n \"maxInputTokens\": 1048576\n },\n \"gemini-2.5-flash-native-audio-preview-09-2025\": {\n \"input\": 0.3,\n \"output\": 2.5,\n \"maxInputTokens\": 1048576\n },\n \"gemini-2.5-flash-native-audio-preview-12-2025\": {\n \"input\": 0.3,\n \"output\": 2.5,\n \"maxInputTokens\": 1048576\n },\n \"gemini-3.1-flash-live-preview\": {\n \"input\": 0.75,\n \"output\": 4.5,\n \"maxInputTokens\": 131072\n },\n \"gemini-pro-latest\": {\n \"input\": 1.25,\n \"output\": 10,\n \"cachedInput\": 0.125,\n \"maxInputTokens\": 1048576\n },\n \"gemini-exp-1206\": {\n \"input\": 0.3,\n \"output\": 2.5,\n \"cachedInput\": 0.03,\n \"maxInputTokens\": 1048576\n },\n \"claude-sonnet-4-6@default\": {\n \"input\": 3,\n \"output\": 15,\n \"cachedInput\": 0.3,\n \"cacheCreationInput\": 3.75,\n \"maxInputTokens\": 1000000\n },\n \"gpt-5.5\": {\n \"input\": 5,\n \"output\": 30,\n \"cachedInput\": 0.5,\n \"maxInputTokens\": 272000\n }\n }\n}\n","import { createServer } from 'node:http'\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport { getDashboardData, getFingerprint } from './data.js'\nimport { getHtml } from './html.js'\nimport type { IStorage } from '../types/index.js'\n\nexport function startDashboardServer(storage: IStorage, port: number): void {\n const server = createServer((req: IncomingMessage, res: ServerResponse) => {\n const url = new URL(req.url ?? '/', `http://localhost:${port}`)\n\n if (req.method === 'GET' && url.pathname === '/') {\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' })\n res.end(getHtml(port))\n return\n }\n\n if (req.method === 'GET' && url.pathname === '/events') {\n const filter = url.searchParams.get('filter') ?? '24h'\n\n res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'Connection': 'keep-alive',\n 'X-Accel-Buffering': 'no',\n })\n res.flushHeaders()\n\n let lastFingerprint = ''\n\n async function sendData(): Promise<void> {\n try {\n const data = await getDashboardData(storage, filter)\n const fp = getFingerprint(data)\n if (fp !== lastFingerprint) {\n lastFingerprint = fp\n res.write(`data: ${JSON.stringify(data)}\\n\\n`)\n }\n } catch {\n // best-effort — storage errors must not crash the server\n }\n }\n\n // Send immediately on connect\n void sendData()\n\n const timer = setInterval(() => { void sendData() }, 3000)\n\n res.on('close', () => {\n clearInterval(timer)\n })\n return\n }\n\n res.writeHead(404, { 'Content-Type': 'text/plain' })\n res.end('Not found')\n })\n\n server.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n console.error(`[tokenwatch] Port ${port} is already in use. Try: tokenwatch dashboard --port <other>`)\n process.exit(1)\n }\n throw err\n })\n\n server.listen(port, () => {\n console.log(`tokenwatch dashboard → http://localhost:${port}`)\n })\n}\n","import type {\n IStorage,\n UsageEntry,\n Report,\n CostForecast,\n ModelStats,\n SessionStats,\n UserStats,\n FeatureStats,\n} from '../types/index.js'\n\nexport interface TimeSeriesBucket {\n bucket: string\n cost: number\n calls: number\n}\n\nexport interface DashboardData {\n report: Report\n forecast: CostForecast\n timeSeries: TimeSeriesBucket[]\n lastUpdated: string\n}\n\n/**\n * Maps a filter string to a Unix ms timestamp (entries before this are excluded).\n * Returns undefined for 'all' or undefined (no filter).\n */\nexport function parseSince(filter: string | undefined): number | undefined {\n if (!filter || filter === 'all') return undefined\n const now = Date.now()\n switch (filter) {\n case '1h': return now - 60 * 60 * 1000\n case '24h': return now - 24 * 60 * 60 * 1000\n case '7d': return now - 7 * 24 * 60 * 60 * 1000\n case '30d': return now - 30 * 24 * 60 * 60 * 1000\n default: return undefined\n }\n}\n\n/**\n * Groups entries into time-series buckets.\n * Bucket size: 1h window → 5min, 24h window → 1h, 7d/30d/all → 1day.\n */\nexport function buildTimeSeries(\n entries: UsageEntry[],\n sinceMs: number | undefined,\n): TimeSeriesBucket[] {\n const now = Date.now()\n const windowMs = sinceMs !== undefined ? now - sinceMs : undefined\n\n let bucketMs: number\n if (windowMs !== undefined && windowMs <= 60 * 60 * 1000) {\n bucketMs = 5 * 60 * 1000 // 5-min buckets for ≤1h window\n } else if (windowMs !== undefined && windowMs <= 24 * 60 * 60 * 1000) {\n bucketMs = 60 * 60 * 1000 // 1h buckets for ≤24h window\n } else {\n bucketMs = 24 * 60 * 60 * 1000 // 1-day buckets for 7d/30d/all\n }\n\n const filtered = sinceMs !== undefined\n ? entries.filter((e) => new Date(e.timestamp).getTime() >= sinceMs)\n : entries\n\n const buckets = new Map<string, TimeSeriesBucket>()\n\n for (const entry of filtered) {\n const ts = new Date(entry.timestamp).getTime()\n const bucketTs = Math.floor(ts / bucketMs) * bucketMs\n const bucketKey = new Date(bucketTs).toISOString()\n const existing = buckets.get(bucketKey)\n if (existing) {\n existing.cost += entry.costUSD\n existing.calls += 1\n } else {\n buckets.set(bucketKey, { bucket: bucketKey, cost: entry.costUSD, calls: 1 })\n }\n }\n\n return Array.from(buckets.values()).sort((a, b) => a.bucket.localeCompare(b.bucket))\n}\n\nexport function getFingerprint(data: DashboardData): string {\n return `${data.report.totalCostUSD.toFixed(8)}-${data.report.totalTokens.input}-${data.timeSeries.length}`\n}\n\nexport async function getDashboardData(\n storage: IStorage,\n filter?: string,\n): Promise<DashboardData> {\n const allEntries = await Promise.resolve(storage.getAll())\n const sinceMs = parseSince(filter)\n\n const entries = sinceMs !== undefined\n ? allEntries.filter((e) => new Date(e.timestamp).getTime() >= sinceMs)\n : allEntries\n\n // ── Build report ────────────────────────────────────────────────────────────\n const byModel: Record<string, ModelStats> = {}\n const bySession: Record<string, SessionStats> = {}\n const byUser: Record<string, UserStats> = {}\n const byFeature: Record<string, FeatureStats> = {}\n let totalInput = 0\n let totalOutput = 0\n let totalCost = 0\n\n for (const e of entries) {\n totalInput += e.inputTokens + (e.cachedTokens ?? 0) + (e.cacheCreationTokens ?? 0)\n totalOutput += e.outputTokens\n totalCost += e.costUSD\n\n const m = (byModel[e.model] ??= {\n costUSD: 0, calls: 0, tokens: { input: 0, output: 0, reasoning: 0, cached: 0 },\n })\n m.costUSD += e.costUSD\n m.calls += 1\n m.tokens.input += e.inputTokens + (e.cachedTokens ?? 0) + (e.cacheCreationTokens ?? 0)\n m.tokens.output += e.outputTokens\n m.tokens.reasoning += e.reasoningTokens ?? 0\n m.tokens.cached += e.cachedTokens ?? 0\n\n if (e.sessionId) {\n const s = (bySession[e.sessionId] ??= { costUSD: 0, calls: 0 })\n s.costUSD += e.costUSD\n s.calls += 1\n }\n\n if (e.userId) {\n const u = (byUser[e.userId] ??= { costUSD: 0, calls: 0 })\n u.costUSD += e.costUSD\n u.calls += 1\n }\n\n if (e.feature) {\n const f = (byFeature[e.feature] ??= { costUSD: 0, calls: 0 })\n f.costUSD += e.costUSD\n f.calls += 1\n }\n }\n\n const now = new Date().toISOString()\n const periodFrom = entries[0]?.timestamp ?? now\n const periodTo = entries[entries.length - 1]?.timestamp ?? now\n\n const report: Report = {\n totalCostUSD: totalCost,\n totalTokens: { input: totalInput, output: totalOutput },\n byModel,\n bySession,\n byUser,\n byFeature,\n period: { from: periodFrom, to: periodTo },\n }\n\n // ── Build forecast (always 24h window over all entries) ─────────────────────\n const forecastWindowMs = 24 * 60 * 60 * 1000\n const windowStart = Date.now() - forecastWindowMs\n const windowEntries = allEntries.filter(\n (e) => new Date(e.timestamp).getTime() >= windowStart,\n )\n\n let forecast: CostForecast\n if (windowEntries.length < 2) {\n forecast = {\n burnRatePerHour: 0,\n projectedDailyCostUSD: 0,\n projectedMonthlyCostUSD: 0,\n basedOnHours: 0,\n basedOnPeriod: null,\n }\n } else {\n const first = windowEntries[0]?.timestamp ?? ''\n const last = windowEntries[windowEntries.length - 1]?.timestamp ?? ''\n const actualMs = new Date(last).getTime() - new Date(first).getTime()\n const actualHours = actualMs / (1000 * 60 * 60)\n if (actualHours < 0.001) {\n forecast = {\n burnRatePerHour: 0,\n projectedDailyCostUSD: 0,\n projectedMonthlyCostUSD: 0,\n basedOnHours: 0,\n basedOnPeriod: { from: first, to: last },\n }\n } else {\n const windowCost = windowEntries.reduce((s, e) => s + e.costUSD, 0)\n const burnRatePerHour = windowCost / actualHours\n forecast = {\n burnRatePerHour,\n projectedDailyCostUSD: burnRatePerHour * 24,\n projectedMonthlyCostUSD: burnRatePerHour * 24 * 30,\n basedOnHours: Math.round(actualHours * 100) / 100,\n basedOnPeriod: { from: first, to: last },\n }\n }\n }\n\n const timeSeries = buildTimeSeries(allEntries, sinceMs)\n\n return { report, forecast, timeSeries, lastUpdated: now }\n}\n","export function getHtml(port: number): string {\n void port // port is used in the SSE URL in the JS below\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\" />\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n<title>tokenwatch dashboard</title>\n<script src=\"https://cdn.jsdelivr.net/npm/chart.js@4/dist/chart.umd.min.js\"></script>\n<style>\n*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\n:root {\n --bg: #0d1117;\n --surface: #161b22;\n --border: #30363d;\n --text: #e6edf3;\n --muted: #8b949e;\n --accent: #58a6ff;\n --green: #3fb950;\n --yellow: #d29922;\n --red: #f85149;\n --r: 6px;\n}\n\nbody {\n background: var(--bg);\n color: var(--text);\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n line-height: 1.5;\n min-height: 100vh;\n}\n\n.container { max-width: 1200px; margin: 0 auto; padding: 24px 16px; }\n\n/* ── Header ──────────────────────────────────────────────────── */\nheader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 24px;\n}\nheader h1 { font-size: 18px; font-weight: 600; letter-spacing: -0.3px; }\nheader h1 span { color: var(--accent); }\n\n.live-badge {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: var(--muted);\n}\n.live-dot {\n width: 8px; height: 8px;\n border-radius: 50%;\n background: var(--green);\n animation: pulse 2s ease-in-out infinite;\n}\n@keyframes pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.3; }\n}\n\n/* ── Tabs ─────────────────────────────────────────────────────── */\n.tabs {\n display: flex;\n gap: 4px;\n margin-bottom: 24px;\n background: var(--surface);\n border: 1px solid var(--border);\n border-radius: var(--r);\n padding: 4px;\n width: fit-content;\n}\n.tab {\n padding: 6px 14px;\n border-radius: calc(var(--r) - 2px);\n border: none;\n background: transparent;\n color: var(--muted);\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.15s, color 0.15s;\n}\n.tab:hover { color: var(--text); background: rgba(255,255,255,0.05); }\n.tab.active { background: var(--accent); color: #fff; }\n\n/* ── Overview cards ───────────────────────────────────────────── */\n.cards {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));\n gap: 12px;\n margin-bottom: 24px;\n}\n.card {\n background: var(--surface);\n border: 1px solid var(--border);\n border-radius: var(--r);\n padding: 16px;\n}\n.card-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; color: var(--muted); margin-bottom: 6px; }\n.card-value { font-size: 22px; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }\n.card-sub { font-size: 11px; color: var(--muted); margin-top: 3px; }\n\n/* ── Chart section ────────────────────────────────────────────── */\n.charts {\n display: grid;\n grid-template-columns: 2fr 1fr;\n gap: 16px;\n margin-bottom: 24px;\n}\n@media (max-width: 768px) { .charts { grid-template-columns: 1fr; } }\n\n.panel {\n background: var(--surface);\n border: 1px solid var(--border);\n border-radius: var(--r);\n padding: 16px;\n}\n.panel-title {\n font-size: 12px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--muted);\n margin-bottom: 14px;\n}\n.chart-wrap { position: relative; height: 220px; }\n\n/* ── Breakdown tables ─────────────────────────────────────────── */\n.section { margin-bottom: 24px; }\n.section-title {\n font-size: 12px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--muted);\n margin-bottom: 10px;\n}\n\ntable {\n width: 100%;\n border-collapse: collapse;\n background: var(--surface);\n border: 1px solid var(--border);\n border-radius: var(--r);\n overflow: hidden;\n font-size: 13px;\n}\nthead { background: rgba(255,255,255,0.03); }\nth {\n padding: 10px 14px;\n text-align: left;\n font-weight: 600;\n color: var(--muted);\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.4px;\n border-bottom: 1px solid var(--border);\n}\nth.num, td.num { text-align: right; }\ntd {\n padding: 10px 14px;\n border-bottom: 1px solid rgba(48,54,61,0.5);\n font-variant-numeric: tabular-nums;\n color: var(--text);\n}\ntbody tr:last-child td { border-bottom: none; }\ntbody tr:hover td { background: rgba(255,255,255,0.02); }\n\n.bar-wrap { width: 80px; height: 6px; background: var(--border); border-radius: 3px; display: inline-block; vertical-align: middle; margin-left: 8px; }\n.bar-fill { height: 100%; border-radius: 3px; background: var(--accent); }\n\n/* ── Forecast ─────────────────────────────────────────────────── */\n.forecast-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n gap: 12px;\n}\n\n/* ── Empty state ──────────────────────────────────────────────── */\n.empty {\n color: var(--muted);\n font-size: 13px;\n padding: 20px 0;\n text-align: center;\n}\n\n/* ── Collapsible ─────────────────────────────────────────────── */\ndetails summary {\n cursor: pointer;\n list-style: none;\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--muted);\n margin-bottom: 10px;\n user-select: none;\n}\ndetails summary::before { content: '▶'; font-size: 10px; transition: transform 0.15s; }\ndetails[open] summary::before { transform: rotate(90deg); }\n\n/* ── Last updated ─────────────────────────────────────────────── */\n.footer {\n font-size: 11px;\n color: var(--muted);\n text-align: center;\n padding: 16px 0 0;\n}\n</style>\n</head>\n<body>\n<div class=\"container\">\n\n <header>\n <h1>token<span>watch</span></h1>\n <div class=\"live-badge\">\n <div class=\"live-dot\"></div>\n <span id=\"live-label\">live</span>\n </div>\n </header>\n\n <div class=\"tabs\">\n <button class=\"tab\" data-filter=\"1h\">1h</button>\n <button class=\"tab active\" data-filter=\"24h\">24h</button>\n <button class=\"tab\" data-filter=\"7d\">7d</button>\n <button class=\"tab\" data-filter=\"30d\">30d</button>\n <button class=\"tab\" data-filter=\"all\">All</button>\n </div>\n\n <div class=\"cards\">\n <div class=\"card\">\n <div class=\"card-label\">Total Cost</div>\n <div class=\"card-value\" id=\"card-cost\">—</div>\n <div class=\"card-sub\" id=\"card-period\"></div>\n </div>\n <div class=\"card\">\n <div class=\"card-label\">Input Tokens</div>\n <div class=\"card-value\" id=\"card-input\">—</div>\n </div>\n <div class=\"card\">\n <div class=\"card-label\">Output Tokens</div>\n <div class=\"card-value\" id=\"card-output\">—</div>\n </div>\n <div class=\"card\">\n <div class=\"card-label\">Total Calls</div>\n <div class=\"card-value\" id=\"card-calls\">—</div>\n </div>\n <div class=\"card\">\n <div class=\"card-label\">Burn Rate</div>\n <div class=\"card-value\" id=\"card-burn\">—</div>\n <div class=\"card-sub\">per hour</div>\n </div>\n </div>\n\n <div class=\"charts\">\n <div class=\"panel\">\n <div class=\"panel-title\">Cost over time</div>\n <div class=\"chart-wrap\">\n <canvas id=\"chart-line\"></canvas>\n </div>\n </div>\n <div class=\"panel\">\n <div class=\"panel-title\">By model</div>\n <div class=\"chart-wrap\">\n <canvas id=\"chart-doughnut\"></canvas>\n </div>\n </div>\n </div>\n\n <div class=\"section\">\n <div class=\"section-title\">Model breakdown</div>\n <div id=\"model-table-wrap\"></div>\n </div>\n\n <details id=\"users-section\" style=\"margin-bottom:24px;\">\n <summary>By user</summary>\n <div id=\"user-table-wrap\"></div>\n </details>\n\n <details id=\"features-section\" style=\"margin-bottom:24px;\">\n <summary>By feature</summary>\n <div id=\"feature-table-wrap\"></div>\n </details>\n\n <div class=\"section\">\n <div class=\"section-title\">Cost forecast</div>\n <div class=\"forecast-grid\">\n <div class=\"card\">\n <div class=\"card-label\">Projected daily</div>\n <div class=\"card-value\" id=\"fc-daily\">—</div>\n <div class=\"card-sub\" id=\"fc-window\"></div>\n </div>\n <div class=\"card\">\n <div class=\"card-label\">Projected monthly</div>\n <div class=\"card-value\" id=\"fc-monthly\">—</div>\n </div>\n <div class=\"card\">\n <div class=\"card-label\">Burn rate / hr</div>\n <div class=\"card-value\" id=\"fc-burn\">—</div>\n </div>\n </div>\n </div>\n\n <div class=\"footer\" id=\"footer-updated\"></div>\n\n</div><!-- /container -->\n\n<script>\n(function () {\n 'use strict';\n\n // ── Palette ───────────────────────────────────────────────────────────\n const PALETTE = [\n '#58a6ff','#3fb950','#f78166','#d29922','#bc8cff',\n '#79c0ff','#56d364','#ffa657','#ff7b72','#a5d6ff',\n ];\n\n // ── State ─────────────────────────────────────────────────────────────\n let evtSource = null;\n let activeFilter = '24h';\n let lineChart = null;\n let doughnutChart = null;\n\n // ── Helpers ───────────────────────────────────────────────────────────\n function escHtml(str) {\n return String(str)\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;');\n }\n\n function fmtUSD(n) {\n if (n === 0) return '$0.00';\n if (n < 0.001) return '$' + n.toFixed(6);\n if (n < 1) return '$' + n.toFixed(4);\n return '$' + n.toFixed(2);\n }\n\n function fmtNum(n) {\n return n.toLocaleString('en-US');\n }\n\n function fmtDate(iso) {\n try { return new Date(iso).toLocaleString(); } catch { return iso; }\n }\n\n function totalCalls(byModel) {\n return Object.values(byModel).reduce(function(s, m) { return s + m.calls; }, 0);\n }\n\n // ── Tab setup ─────────────────────────────────────────────────────────\n document.querySelectorAll('.tab').forEach(function(btn) {\n btn.addEventListener('click', function() {\n document.querySelectorAll('.tab').forEach(function(b) { b.classList.remove('active'); });\n btn.classList.add('active');\n activeFilter = btn.dataset.filter;\n reconnect();\n });\n });\n\n // ── SSE ───────────────────────────────────────────────────────────────\n function reconnect() {\n if (evtSource) { evtSource.close(); evtSource = null; }\n evtSource = new EventSource('/events?filter=' + encodeURIComponent(activeFilter));\n evtSource.onmessage = function(e) {\n try { updateUI(JSON.parse(e.data)); } catch (_) {}\n };\n evtSource.onerror = function() {\n document.getElementById('live-label').textContent = 'reconnecting…';\n };\n evtSource.onopen = function() {\n document.getElementById('live-label').textContent = 'live';\n };\n }\n\n // ── UI update ─────────────────────────────────────────────────────────\n function updateUI(data) {\n const r = data.report;\n const fc = data.forecast;\n const ts = data.timeSeries;\n\n // Cards\n document.getElementById('card-cost').textContent = fmtUSD(r.totalCostUSD);\n document.getElementById('card-input').textContent = fmtNum(r.totalTokens.input);\n document.getElementById('card-output').textContent = fmtNum(r.totalTokens.output);\n document.getElementById('card-calls').textContent = fmtNum(totalCalls(r.byModel));\n document.getElementById('card-burn').textContent = fmtUSD(fc.burnRatePerHour);\n document.getElementById('card-period').textContent =\n r.period.from !== r.period.to\n ? fmtDate(r.period.from) + ' – ' + fmtDate(r.period.to)\n : '';\n\n // Line chart\n const labels = ts.map(function(b) {\n try { return new Date(b.bucket).toLocaleTimeString([], {hour:'2-digit', minute:'2-digit'}); }\n catch { return b.bucket; }\n });\n const costs = ts.map(function(b) { return b.cost; });\n\n if (!lineChart) {\n const ctx = document.getElementById('chart-line').getContext('2d');\n lineChart = new Chart(ctx, {\n type: 'line',\n data: {\n labels: labels,\n datasets: [{\n label: 'Cost (USD)',\n data: costs,\n borderColor: '#58a6ff',\n backgroundColor: 'rgba(88,166,255,0.08)',\n borderWidth: 2,\n pointRadius: 3,\n pointBackgroundColor: '#58a6ff',\n fill: true,\n tension: 0.3,\n }],\n },\n options: {\n responsive: true, maintainAspectRatio: false,\n plugins: { legend: { display: false } },\n scales: {\n x: { ticks: { color: '#8b949e', maxTicksLimit: 8 }, grid: { color: '#21262d' } },\n y: {\n ticks: {\n color: '#8b949e',\n callback: function(v) { return '$' + Number(v).toFixed(4); },\n },\n grid: { color: '#21262d' },\n },\n },\n },\n });\n } else {\n lineChart.data.labels = labels;\n lineChart.data.datasets[0].data = costs;\n lineChart.update('none');\n }\n\n // Doughnut chart\n const modelEntries = Object.entries(r.byModel);\n const dLabels = modelEntries.map(function(x) { return x[0]; });\n const dData = modelEntries.map(function(x) { return x[1].costUSD; });\n const dColors = dLabels.map(function(_, i) { return PALETTE[i % PALETTE.length]; });\n\n if (!doughnutChart) {\n const ctx2 = document.getElementById('chart-doughnut').getContext('2d');\n doughnutChart = new Chart(ctx2, {\n type: 'doughnut',\n data: { labels: dLabels, datasets: [{ data: dData, backgroundColor: dColors, borderWidth: 0, hoverOffset: 4 }] },\n options: {\n responsive: true, maintainAspectRatio: false,\n cutout: '65%',\n plugins: {\n legend: {\n position: 'bottom',\n labels: { color: '#8b949e', boxWidth: 10, padding: 12, font: { size: 11 } },\n },\n },\n },\n });\n } else {\n doughnutChart.data.labels = dLabels;\n doughnutChart.data.datasets[0].data = dData;\n doughnutChart.data.datasets[0].backgroundColor = dColors;\n doughnutChart.update('none');\n }\n\n // Model table\n const modelWrap = document.getElementById('model-table-wrap');\n if (modelEntries.length === 0) {\n modelWrap.innerHTML = '<p class=\"empty\">No data for this period.</p>';\n } else {\n const totalCost = r.totalCostUSD || 1;\n let html = '<table><thead><tr>' +\n '<th>Model</th>' +\n '<th class=\"num\">Calls</th>' +\n '<th class=\"num\">In tokens</th>' +\n '<th class=\"num\">Out tokens</th>' +\n '<th class=\"num\">Cost</th>' +\n '<th class=\"num\">Share</th>' +\n '</tr></thead><tbody>';\n const sorted = modelEntries.slice().sort(function(a, b) { return b[1].costUSD - a[1].costUSD; });\n sorted.forEach(function(entry) {\n const name = entry[0]; const m = entry[1];\n const pct = (m.costUSD / totalCost * 100).toFixed(1);\n const barW = Math.round(m.costUSD / totalCost * 80);\n html += '<tr>' +\n '<td>' + escHtml(name) + '</td>' +\n '<td class=\"num\">' + fmtNum(m.calls) + '</td>' +\n '<td class=\"num\">' + fmtNum(m.tokens.input) + '</td>' +\n '<td class=\"num\">' + fmtNum(m.tokens.output) + '</td>' +\n '<td class=\"num\">' + fmtUSD(m.costUSD) + '</td>' +\n '<td class=\"num\">' + pct + '%' +\n '<span class=\"bar-wrap\"><span class=\"bar-fill\" style=\"width:' + barW + 'px\"></span></span>' +\n '</td></tr>';\n });\n html += '</tbody></table>';\n modelWrap.innerHTML = html;\n }\n\n // User table\n const userEntries = Object.entries(r.byUser);\n const usersSection = document.getElementById('users-section');\n usersSection.style.display = userEntries.length === 0 ? 'none' : '';\n if (userEntries.length > 0) {\n let html = '<table><thead><tr><th>User</th><th class=\"num\">Calls</th><th class=\"num\">Cost</th></tr></thead><tbody>';\n userEntries.slice().sort(function(a,b) { return b[1].costUSD - a[1].costUSD; }).forEach(function(e) {\n html += '<tr><td>' + escHtml(e[0]) + '</td><td class=\"num\">' + fmtNum(e[1].calls) + '</td><td class=\"num\">' + fmtUSD(e[1].costUSD) + '</td></tr>';\n });\n html += '</tbody></table>';\n document.getElementById('user-table-wrap').innerHTML = html;\n }\n\n // Feature table\n const featureEntries = Object.entries(r.byFeature);\n const featuresSection = document.getElementById('features-section');\n featuresSection.style.display = featureEntries.length === 0 ? 'none' : '';\n if (featureEntries.length > 0) {\n let html = '<table><thead><tr><th>Feature</th><th class=\"num\">Calls</th><th class=\"num\">Cost</th></tr></thead><tbody>';\n featureEntries.slice().sort(function(a,b) { return b[1].costUSD - a[1].costUSD; }).forEach(function(e) {\n html += '<tr><td>' + escHtml(e[0]) + '</td><td class=\"num\">' + fmtNum(e[1].calls) + '</td><td class=\"num\">' + fmtUSD(e[1].costUSD) + '</td></tr>';\n });\n html += '</tbody></table>';\n document.getElementById('feature-table-wrap').innerHTML = html;\n }\n\n // Forecast\n document.getElementById('fc-daily').textContent = fmtUSD(fc.projectedDailyCostUSD);\n document.getElementById('fc-monthly').textContent = fmtUSD(fc.projectedMonthlyCostUSD);\n document.getElementById('fc-burn').textContent = fmtUSD(fc.burnRatePerHour);\n document.getElementById('fc-window').textContent =\n fc.basedOnHours > 0 ? 'based on ' + fc.basedOnHours.toFixed(1) + 'h of data' : 'insufficient data';\n\n // Footer\n document.getElementById('footer-updated').textContent =\n 'Last updated: ' + fmtDate(data.lastUpdated);\n }\n\n // ── Boot ──────────────────────────────────────────────────────────────\n reconnect();\n})();\n</script>\n</body>\n</html>`\n}\n"],"mappings":"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AA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAsiBT;;;AFliBO,SAAS,qBAAqB,SAAmB,MAAoB;AAC1E,QAAM,SAAS,aAAa,CAAC,KAAsB,QAAwB;AACzE,UAAM,MAAM,IAAI,IAAI,IAAI,OAAO,KAAK,oBAAoB,IAAI,EAAE;AAE9D,QAAI,IAAI,WAAW,SAAS,IAAI,aAAa,KAAK;AAChD,UAAI,UAAU,KAAK,EAAE,gBAAgB,2BAA2B,CAAC;AACjE,UAAI,IAAI,QAAQ,IAAI,CAAC;AACrB;AAAA,IACF;AAEA,QAAI,IAAI,WAAW,SAAS,IAAI,aAAa,WAAW;AACtD,YAAM,SAAS,IAAI,aAAa,IAAI,QAAQ,KAAK;AAEjD,UAAI,UAAU,KAAK;AAAA,QACjB,gBAAgB;AAAA,QAChB,iBAAiB;AAAA,QACjB,cAAc;AAAA,QACd,qBAAqB;AAAA,MACvB,CAAC;AACD,UAAI,aAAa;AAEjB,UAAI,kBAAkB;AAEtB,qBAAe,WAA0B;AACvC,YAAI;AACF,gBAAM,OAAO,MAAM,iBAAiB,SAAS,MAAM;AACnD,gBAAM,KAAK,eAAe,IAAI;AAC9B,cAAI,OAAO,iBAAiB;AAC1B,8BAAkB;AAClB,gBAAI,MAAM,SAAS,KAAK,UAAU,IAAI,CAAC;AAAA;AAAA,CAAM;AAAA,UAC/C;AAAA,QACF,QAAQ;AAAA,QAER;AAAA,MACF;AAGA,WAAK,SAAS;AAEd,YAAM,QAAQ,YAAY,MAAM;AAAE,aAAK,SAAS;AAAA,MAAE,GAAG,GAAI;AAEzD,UAAI,GAAG,SAAS,MAAM;AACpB,sBAAc,KAAK;AAAA,MACrB,CAAC;AACD;AAAA,IACF;AAEA,QAAI,UAAU,KAAK,EAAE,gBAAgB,aAAa,CAAC;AACnD,QAAI,IAAI,WAAW;AAAA,EACrB,CAAC;AAED,SAAO,GAAG,SAAS,CAAC,QAA+B;AACjD,QAAI,IAAI,SAAS,cAAc;AAC7B,cAAQ,MAAM,qBAAqB,IAAI,8DAA8D;AACrG,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,UAAM;AAAA,EACR,CAAC;AAED,SAAO,OAAO,MAAM,MAAM;AACxB,YAAQ,IAAI,gDAA2C,IAAI,EAAE;AAAA,EAC/D,CAAC;AACH;;;APzDA,IAAM,YAAY,QAAQ,cAAc,YAAY,GAAG,CAAC;AACxD,IAAM,kBAAkBC,MAAKC,SAAQ,GAAG,eAAe,UAAU;AAIjE,SAAS,QAAQ,MAAgB,MAAkC;AACjE,QAAM,MAAM,KAAK,QAAQ,IAAI;AAC7B,MAAI,QAAQ,GAAI,QAAO;AACvB,QAAM,QAAQ,KAAK,MAAM,CAAC;AAE1B,SAAO,UAAU,UAAa,CAAC,MAAM,WAAW,IAAI,IAAI,QAAQ;AAClE;AAeA,eAAe,YAAY,OAAmD;AAE5E,MAAI,CAAC,OAAO;AACV,QAAI,CAACC,YAAW,eAAe,GAAG;AAChC,cAAQ,MAAM,+BAA+B,eAAe,EAAE;AAC9D,cAAQ,MAAM,iEAAiE;AAC/E,cAAQ,MAAM,+DAA+D;AAC7E,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,QAAI;AACJ,QAAI;AACF,gBAAU,IAAI,cAAc,eAAe;AAAA,IAC7C,QAAQ;AACN,cAAQ,MAAM,8DAA8D;AAC5E,cAAQ,MAAM,iCAAiC;AAC/C,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,WAAO,EAAE,SAAS,OAAO,YAAY;AAAA,IAAC,EAAE;AAAA,EAC1C;AAGA,MAAI,MAAM,WAAW,aAAa,KAAK,MAAM,WAAW,eAAe,GAAG;AAExE,QAAI;AACJ,QAAI;AACF,eAAS,MAAM,OAAO,IAAc,GAAG;AAAA,IACzC,QAAQ;AACN,cAAQ,MAAM,gDAAgD;AAC9D,cAAQ,MAAM,qBAAqB;AACnC,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,UAAM,EAAE,iBAAAC,iBAAgB,IAAI,MAAM;AAElC,UAAM,OAAO,IAAI,MAAM,KAAK,EAAE,kBAAkB,MAAM,CAAC;AACvD,UAAM,UAAU,IAAIA,iBAAgB,IAAa;AACjD,WAAO,EAAE,SAAS,OAAO,MAAM,KAAK,IAAI,EAAE;AAAA,EAC5C;AAGA,MAAI,MAAM,WAAW,UAAU,GAAG;AAEhC,QAAI;AACJ,QAAI;AACF,iBAAW,MAAM,OAAO,gBAA0B;AAAA,IACpD,QAAQ;AACN,cAAQ,MAAM,iDAAiD;AAC/D,cAAQ,MAAM,yBAAyB;AACvC,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,UAAM,EAAE,cAAAC,cAAa,IAAI,MAAM;AAE/B,UAAM,OAAO,SAAS,WAAW,KAAK;AACtC,UAAM,UAAU,IAAIA,cAAa,IAAa;AAC9C,WAAO,EAAE,SAAS,OAAO,MAAM,KAAK,IAAI,EAAE;AAAA,EAC5C;AAGA,MAAI,MAAM,WAAW,YAAY,KAAK,MAAM,WAAW,gBAAgB,GAAG;AAExE,QAAI;AACJ,QAAI;AACF,iBAAW,MAAM,OAAO,SAAmB;AAAA,IAC7C,QAAQ;AACN,cAAQ,MAAM,oDAAoD;AAClE,cAAQ,MAAM,0BAA0B;AACxC,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,UAAM,EAAE,cAAAC,cAAa,IAAI,MAAM;AAC/B,UAAM,SAAS,IAAI,IAAI,KAAK;AAC5B,UAAM,SAAS,OAAO,SAAS,QAAQ,OAAO,EAAE,KAAK;AAErD,UAAM,SAAS,IAAI,SAAS,YAAY,KAAK;AAC7C,UAAM,OAAO,QAAQ;AACrB,UAAM,KAAK,OAAO,GAAG,MAAM;AAC3B,UAAM,UAAU,IAAIA,cAAa,EAAW;AAC5C,WAAO,EAAE,SAAS,OAAO,MAAM,OAAO,MAAM,EAAE;AAAA,EAChD;AAEA,UAAQ,MAAM,2CAA2C,KAAK,GAAG;AACjE,UAAQ,MAAM,wEAAwE;AACtF,UAAQ,KAAK,CAAC;AAChB;AAIA,SAAS,oBAA0C;AACjD,QAAM,aAAaL,MAAK,WAAW,MAAM,aAAa;AACtD,QAAM,MAAM,aAAa,YAAY,MAAM;AAC3C,QAAM,OAAO,KAAK,MAAM,GAAG;AAC3B,SAAO,KAAK;AACd;AAEA,eAAe,UAAyB;AACtC,UAAQ,IAAI,uCAAuC;AACnD,QAAM,SAAS,MAAM,kBAAkB;AACvC,MAAI,QAAQ;AACV,YAAQ,IAAI,0BAAqB,OAAO,KAAK,OAAO,MAAM,EAAE,MAAM,+BAA+B,OAAO,UAAU,IAAI;AAAA,EACxH,OAAO;AACL,YAAQ,MAAM,uEAAkE;AAChF,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAEA,SAAS,YAAkB;AACzB,QAAM,SAAS,kBAAkB;AACjC,QAAM,OAAO,OAAO,QAAQ,MAAM,EAAE,IAAI,CAAC,CAAC,MAAM,KAAK,OAAO;AAAA,IAC1D,OAAO;AAAA,IACP,OAAO,IAAI,MAAM,MAAM,QAAQ,CAAC,CAAC;AAAA,IACjC,QAAQ,IAAI,MAAM,OAAO,QAAQ,CAAC,CAAC;AAAA,EACrC,EAAE;AAEF,QAAM,UAAU,KAAK,IAAI,GAAG,KAAK,IAAI,CAAC,MAAM,EAAE,MAAM,MAAM,GAAG,CAAC;AAC9D,QAAM,SAAS,GAAG,QAAQ,OAAO,OAAO,CAAC,KAAK,QAAQ,SAAS,EAAE,CAAC,KAAK,SAAS,SAAS,EAAE,CAAC;AAC5F,QAAM,MAAM,IAAI,OAAO,OAAO,MAAM;AAEpC,UAAQ,IAAI,MAAM;AAClB,UAAQ,IAAI,GAAG;AACf,aAAW,OAAO,MAAM;AACtB,YAAQ,IAAI,GAAG,IAAI,MAAM,OAAO,OAAO,CAAC,KAAK,IAAI,MAAM,SAAS,EAAE,CAAC,KAAK,IAAI,OAAO,SAAS,EAAE,CAAC,EAAE;AAAA,EACnG;AACF;AAEA,eAAe,UAAU,MAA+B;AACtD,QAAM,QAAQ,QAAQ,MAAM,MAAM;AAClC,QAAM,EAAE,SAAS,MAAM,IAAI,MAAM,YAAY,KAAK;AAElD,MAAI;AACJ,MAAI;AACF,UAAM,UAAU,cAAc,EAAE,SAAS,YAAY,MAAM,CAAC;AAC5D,aAAS,MAAM,QAAQ,UAAU;AAAA,EACnC,UAAE;AACA,UAAM,MAAM;AAAA,EACd;AAEA,MAAI,OAAO,iBAAiB,KAAK,OAAO,KAAK,OAAO,OAAO,EAAE,WAAW,GAAG;AACzE,YAAQ,IAAI,wBAAwB;AACpC;AAAA,EACF;AAEA,UAAQ,IAAI,uNAAuD;AACnE,UAAQ,IAAI,oBAAoB,OAAO,aAAa,QAAQ,CAAC,CAAC,MAAM;AACpE,UAAQ,IAAI,mBAAmB,OAAO,YAAY,MAAM,eAAe,CAAC,SAAS,OAAO,YAAY,OAAO,eAAe,CAAC,MAAM;AACjI,UAAQ,IAAI,mBAAmB,OAAO,OAAO,IAAI,aAAQ,OAAO,OAAO,EAAE,EAAE;AAC3E,MAAI,OAAO,iBAAiB;AAC1B,YAAQ,IAAI,mBAAmB,OAAO,eAAe,EAAE;AAAA,EACzD;AAEA,MAAI,OAAO,KAAK,OAAO,OAAO,EAAE,SAAS,GAAG;AAC1C,YAAQ,IAAI,eAAe;AAC3B,eAAW,CAAC,OAAO,KAAK,KAAK,OAAO,QAAQ,OAAO,OAAO,GAAG;AAC3D,cAAQ,IAAI,OAAO,MAAM,OAAO,EAAE,CAAC,KAAK,MAAM,QAAQ,QAAQ,CAAC,CAAC,MAAM,MAAM,KAAK,SAAS;AAAA,IAC5F;AAAA,EACF;AAEA,MAAI,OAAO,KAAK,OAAO,MAAM,EAAE,SAAS,GAAG;AACzC,YAAQ,IAAI,cAAc;AAC1B,eAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,OAAO,MAAM,GAAG;AACzD,cAAQ,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC,KAAK,MAAM,QAAQ,QAAQ,CAAC,CAAC,MAAM,MAAM,KAAK,SAAS;AAAA,IAC3F;AAAA,EACF;AAEA,MAAI,OAAO,KAAK,OAAO,SAAS,EAAE,SAAS,GAAG;AAC5C,YAAQ,IAAI,iBAAiB;AAC7B,eAAW,CAAC,SAAS,KAAK,KAAK,OAAO,QAAQ,OAAO,SAAS,GAAG;AAC/D,cAAQ,IAAI,OAAO,QAAQ,OAAO,EAAE,CAAC,KAAK,MAAM,QAAQ,QAAQ,CAAC,CAAC,MAAM,MAAM,KAAK,SAAS;AAAA,IAC9F;AAAA,EACF;AAEA,MAAI,OAAO,KAAK,OAAO,SAAS,EAAE,SAAS,GAAG;AAC5C,YAAQ,IAAI,iBAAiB;AAC7B,eAAW,CAAC,SAAS,KAAK,KAAK,OAAO,QAAQ,OAAO,SAAS,GAAG;AAC/D,cAAQ,IAAI,OAAO,QAAQ,OAAO,EAAE,CAAC,KAAK,MAAM,QAAQ,QAAQ,CAAC,CAAC,MAAM,MAAM,KAAK,SAAS;AAAA,IAC9F;AAAA,EACF;AAEA,UAAQ,IAAI,sTAAuD;AACrE;AAEA,eAAe,aAAa,MAA+B;AACzD,QAAM,WAAW,QAAQ,MAAM,QAAQ;AACvC,QAAM,OAAO,aAAa,SAAY,SAAS,UAAU,EAAE,IAAI;AAC/D,MAAI,MAAM,IAAI,KAAK,OAAO,KAAK,OAAO,OAAO;AAC3C,YAAQ,MAAM,+BAA+B,QAAQ,0CAA0C;AAC/F,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,QAAM,QAAQ,QAAQ,MAAM,MAAM;AAElC,QAAM,EAAE,SAAS,MAAM,IAAI,MAAM,YAAY,KAAK;AAGlD,QAAM,WAAW,MAAY;AAAE,SAAK,MAAM,EAAE,KAAK,MAAM,QAAQ,KAAK,CAAC,CAAC;AAAA,EAAE;AACxE,UAAQ,GAAG,UAAU,QAAQ;AAC7B,UAAQ,GAAG,WAAW,QAAQ;AAE9B,uBAAqB,SAAS,IAAI;AACpC;AAEA,SAAS,UAAgB;AACvB,UAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBZ,KAAK,CAAC;AACR;AAIA,eAAe,OAAsB;AACnC,QAAM,CAAC,EAAE,EAAE,KAAK,GAAG,IAAI,IAAI,QAAQ;AAEnC,UAAQ,KAAK;AAAA,IACX,KAAK;AACH,YAAM,QAAQ;AACd;AAAA,IACF,KAAK;AACH,gBAAU;AACV;AAAA,IACF,KAAK;AACH,YAAM,UAAU,IAAI;AACpB;AAAA,IACF,KAAK;AACH,YAAM,aAAa,IAAI;AACvB;AAAA,IACF,KAAK;AAAA,IACL,KAAK;AACH,cAAQ;AACR;AAAA,IACF;AACE,cAAQ,MAAM,oBAAoB,GAAG;AAAA,iCAAoC;AACzE,cAAQ,KAAK,CAAC;AAAA,EAClB;AACF;AAEA,KAAK,EAAE,MAAM,CAAC,QAAiB;AAC7B,UAAQ,MAAM,GAAG;AACjB,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["rowToEntry","existsSync","join","homedir","homedir","join","bundledPrices","join","homedir","existsSync","PostgresStorage","MySQLStorage","MongoStorage"]}