tokensniff 0.1.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/LICENSE +21 -0
- package/README.md +809 -0
- package/bin/tokensniff-status.js +8 -0
- package/bin/tokensniff.js +8 -0
- package/dist/cli/run.d.ts +64 -0
- package/dist/cli/run.d.ts.map +1 -0
- package/dist/cli.js +2206 -0
- package/dist/cli.js.map +1 -0
- package/dist/collector/config.d.ts +43 -0
- package/dist/collector/config.d.ts.map +1 -0
- package/dist/collector/index.d.ts +14 -0
- package/dist/collector/index.d.ts.map +1 -0
- package/dist/collector/parse.d.ts +42 -0
- package/dist/collector/parse.d.ts.map +1 -0
- package/dist/collector/store.d.ts +76 -0
- package/dist/collector/store.d.ts.map +1 -0
- package/dist/dashboard/heatmap.d.ts +34 -0
- package/dist/dashboard/heatmap.d.ts.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2474 -0
- package/dist/index.js.map +1 -0
- package/dist/pricing.js +145 -0
- package/dist/pricing.js.map +1 -0
- package/dist/proxy.js +1744 -0
- package/dist/proxy.js.map +1 -0
- package/dist/renderer/format.d.ts +33 -0
- package/dist/renderer/format.d.ts.map +1 -0
- package/dist/renderer/index.d.ts +42 -0
- package/dist/renderer/index.d.ts.map +1 -0
- package/dist/schema.js +90 -0
- package/dist/schema.js.map +1 -0
- package/dist/shared/pricing.d.ts +60 -0
- package/dist/shared/pricing.d.ts.map +1 -0
- package/dist/shared/schema.d.ts +122 -0
- package/dist/shared/schema.d.ts.map +1 -0
- package/dist/status.js +521 -0
- package/dist/status.js.map +1 -0
- package/package.json +105 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/collector/store.ts","../src/shared/pricing.ts","../src/dashboard/heatmap.ts","../src/shared/schema.ts","../src/collector/config.ts","../src/collector/parse.ts","../src/collector/index.ts"],"names":["path","fs"],"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;A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","file":"proxy.js","sourcesContent":["/**\n * tokensniff — Telemetry Storage & Persistence Engine (v1 Production)\n *\n * Implements atomic file writes (temp-file + OS rename), append-only turn streaming,\n * Windows file-locking retry backoff (EPERM/EBUSY), and automated session pruning.\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport type { TokenSniffLatest } from '../shared/schema.js';\n\nexport interface HistoryInput {\n readonly date?: string;\n readonly sessionId: string;\n readonly turn: number;\n readonly model: string;\n readonly tokens: number;\n readonly cost: number;\n readonly cacheRead: number;\n readonly thinkingTokens?: number;\n}\n\n/**\n * Sanitizes session identifiers to guarantee safe filesystem paths.\n * Enforces length limits and restricts characters to [A-Za-z0-9_-].\n */\nexport function sanitizeSessionId(id: unknown): string {\n const normalized = String(id || 'default')\n .trim()\n .slice(0, 64);\n return /^[A-Za-z0-9_-]+$/.test(normalized) ? normalized : 'default';\n}\n\n/**\n * Returns the current local calendar date formatted as YYYY-MM-DD.\n * Aligns daily spending calculations with developer local time rather than UTC midnight.\n */\nexport function getLocalDateString(d: Date = new Date()): string {\n const year = d.getFullYear();\n const month = String(d.getMonth() + 1).padStart(2, '0');\n const day = String(d.getDate()).padStart(2, '0');\n return `${year}-${month}-${day}`;\n}\n\n/**\n * Synchronous thread sleep without busy-waiting.\n * Utilizes SharedArrayBuffer and Atomics.wait for sub-millisecond precision.\n */\nexport function syncSleep(ms: number): void {\n try {\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);\n } catch {\n const end = Date.now() + ms;\n while (Date.now() < end) {\n /* fallback busy-wait */\n }\n }\n}\n\n/**\n * Ensures target directory exists synchronously.\n * Idempotent and resilient against multi-process TOCTOU race conditions.\n */\nexport function ensureDir(dir: string): void {\n try {\n fs.mkdirSync(dir, { recursive: true });\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') {\n throw error;\n }\n }\n}\n\n/**\n * Production-grade atomic file write:\n * 1. Writes to a private temp file in the target directory (same filesystem).\n * 2. Uses atomic OS rename so readers never read partial/corrupt JSON.\n * 3. Retries with exponential backoff on Windows EPERM/EBUSY file locks.\n * 4. Cleans up temp file on failure.\n */\nexport function atomicWrite(filePath: string, data: string, maxRetries = 5): void {\n const dir = path.dirname(filePath);\n ensureDir(dir);\n\n const tmpPath = path.join(\n dir,\n `.${path.basename(filePath)}.tmp.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}`,\n );\n\n fs.writeFileSync(tmpPath, data, 'utf8');\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n fs.renameSync(tmpPath, filePath);\n return;\n } catch (error) {\n const err = error as NodeJS.ErrnoException;\n const isLockError =\n process.platform === 'win32' &&\n (err.code === 'EPERM' || err.code === 'EBUSY' || err.code === 'EACCES');\n\n if (isLockError && attempt < maxRetries) {\n syncSleep((attempt + 1) * 10);\n continue;\n }\n\n try {\n fs.unlinkSync(tmpPath);\n } catch {\n /* ignore cleanup error */\n }\n throw error;\n }\n }\n}\n\n/**\n * Production-grade atomic append for turn telemetry stream (history.ndjson).\n * Retries on Windows transient file locks.\n */\nexport function atomicAppend(filePath: string, line: string, maxRetries = 5): void {\n const dir = path.dirname(filePath);\n ensureDir(dir);\n\n const payload = line.endsWith('\\n') ? line : `${line}\\n`;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n fs.appendFileSync(filePath, payload, { encoding: 'utf8', flag: 'a' });\n return;\n } catch (error) {\n const err = error as NodeJS.ErrnoException;\n const isLockError =\n process.platform === 'win32' &&\n (err.code === 'EPERM' || err.code === 'EBUSY' || err.code === 'EACCES');\n\n if (isLockError && attempt < maxRetries) {\n syncSleep((attempt + 1) * 10);\n continue;\n }\n throw error;\n }\n }\n}\n\n/**\n * Atomically saves latest status snapshots into all designated status directories.\n */\nexport function saveLatest(\n statusDirs: readonly string[],\n sessionId: string,\n latestPayload: TokenSniffLatest,\n): void {\n const safeId = sanitizeSessionId(sessionId);\n const serialized = JSON.stringify(latestPayload);\n\n for (const dir of statusDirs) {\n try {\n ensureDir(dir);\n atomicWrite(path.join(dir, `latest-${safeId}.json`), serialized);\n atomicWrite(path.join(dir, 'latest.json'), serialized);\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n console.error(`[tokensniff] failed saving status in ${dir}: ${msg}`);\n }\n }\n}\n\n/**\n * Persists cumulative spending totals across active sessions, bounding memory to maxSessions.\n */\nexport function saveTotals(\n statusDirs: readonly string[],\n totals: Record<string, number>,\n maxSessions: number,\n): Record<string, number> {\n const sessionKeys = Object.keys(totals);\n if (sessionKeys.length > maxSessions) {\n const expiredKeys = sessionKeys.slice(0, sessionKeys.length - maxSessions);\n for (const key of expiredKeys) {\n delete totals[key];\n }\n }\n\n const serialized = JSON.stringify(totals);\n for (const dir of statusDirs) {\n try {\n ensureDir(dir);\n atomicWrite(path.join(dir, 'totals.json'), serialized);\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n console.error(`[tokensniff] failed saving totals in ${dir}: ${msg}`);\n }\n }\n return totals;\n}\n\n/**\n * Reads cumulative spend totals from disk with directory fallback.\n */\nexport function loadTotals(statusDirs: readonly string[]): Record<string, number> {\n for (const dir of statusDirs) {\n const targetFile = path.join(dir, 'totals.json');\n try {\n if (fs.existsSync(targetFile)) {\n const parsed: unknown = JSON.parse(fs.readFileSync(targetFile, 'utf8'));\n if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {\n return parsed as Record<string, number>;\n }\n }\n } catch {\n /* continue to next directory candidate */\n }\n }\n return {};\n}\n\n/**\n * Appends a compact turn record to the persistent history stream (history.ndjson).\n */\nexport function appendHistory(statusDirs: readonly string[], entry: HistoryInput): void {\n const line = JSON.stringify({\n d: entry.date || getLocalDateString(),\n ts: Date.now(),\n s: sanitizeSessionId(entry.sessionId),\n t: entry.turn || 1,\n m: entry.model || 'unknown',\n tk: entry.tokens || 0,\n c: Number(Number(entry.cost || 0).toFixed(6)),\n cr: entry.cacheRead || 0,\n th: entry.thinkingTokens || 0,\n });\n\n for (const dir of statusDirs) {\n try {\n const historyPath = path.join(dir, 'history.ndjson');\n atomicAppend(historyPath, line);\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n console.error(`[tokensniff] failed appending history in ${dir}: ${msg}`);\n }\n }\n}\n\n/**\n * Computes today's cumulative spend in USD from history.ndjson.\n * Ignores malformed records and handles line-by-line reading safely.\n */\nexport function loadTodaySpend(\n statusDirs: readonly string[],\n targetDate: string = getLocalDateString(),\n): number {\n for (const dir of statusDirs) {\n const historyPath = path.join(dir, 'history.ndjson');\n if (!fs.existsSync(historyPath)) continue;\n\n try {\n const content = fs.readFileSync(historyPath, 'utf8');\n let totalCost = 0;\n const lines = content.split('\\n');\n\n for (const line of lines) {\n if (!line.trim()) continue;\n try {\n const row: unknown = JSON.parse(line);\n if (\n typeof row === 'object' &&\n row !== null &&\n (row as { d?: unknown }).d === targetDate &&\n typeof (row as { c?: unknown }).c === 'number'\n ) {\n totalCost += (row as { c: number }).c;\n }\n } catch {\n /* ignore truncated/malformed record */\n }\n }\n return totalCost;\n } catch {\n /* fallback to next directory */\n }\n }\n return 0;\n}\n\n/**\n * Prunes dead session records exceeding the staleness TTL.\n */\nexport function pruneDeadSessions(statusDirs: readonly string[], ttlMs: number): void {\n const cutoffTime = Date.now() - ttlMs;\n\n for (const dir of statusDirs) {\n if (!fs.existsSync(dir)) continue;\n\n try {\n const files = fs.readdirSync(dir);\n for (const file of files) {\n if (!file.startsWith('latest-') || !file.endsWith('.json')) continue;\n const filePath = path.join(dir, file);\n try {\n const stats = fs.statSync(filePath);\n if (stats.mtimeMs < cutoffTime) {\n fs.unlinkSync(filePath);\n }\n } catch {\n /* continue on per-file error */\n }\n }\n } catch {\n /* continue on unreadable directory */\n }\n }\n}\n","/**\n * tokensniff — Dynamic Universal Pricing & Context Capacity Engine (v1 Production)\n *\n * Provider-agnostic rate resolver mapping 1:1 to OpenRouter's live catalog schema.\n * Dynamically resolves token rates (prompt, completion, cache-read, cache-write)\n * and maximum context length (context_length) without vendor hardcoding or dummy fallbacks.\n */\n\nexport interface ModelRates {\n readonly in: number; // Cost per prompt token in USD\n readonly out: number; // Cost per completion token in USD\n readonly cached: number; // Cost per cache-read token in USD\n readonly cacheWrite: number; // Cost per cache-write token in USD\n readonly window: number; // Maximum context length in tokens\n}\n\nexport interface ModelPricingOverrides {\n readonly [model: string]: Partial<ModelRates>;\n}\n\ninterface OpenRouterPricingPayload {\n readonly prompt?: string;\n readonly completion?: string;\n readonly input_cache_read?: string;\n readonly input_cache_write?: string;\n}\n\ninterface OpenRouterModelItem {\n readonly id?: string;\n readonly context_length?: number;\n readonly pricing?: OpenRouterPricingPayload;\n}\n\n/**\n * In-memory index mapping model identifiers and clean slugs to ModelRates.\n * Guarantees O(1) latency on all subsequent turns after initial resolution.\n */\nconst rateCache = new Map<string, ModelRates>();\n\n/**\n * Concurrency guard preventing multiple redundant requests to OpenRouter.\n */\nlet catalogPromise: Promise<boolean> | null = null;\n\n/**\n * Normalizes vendor model names into canonical matching slugs.\n * Provider-agnostic: does not hardcode vendor names.\n *\n * Normalization rules:\n * 1. Strips provider namespaces (e.g. \"anthropic/...\", \"google/...\", \"~anthropic/...\")\n * 2. Strips variant flags (e.g. \":free\", \":batch\")\n * 3. Strips runtime proxy & mode suffixes (e.g. \"-tiered\", \"-thinking\", \"-preview\", \"-low\", \"-high\", \"-latest\")\n * 4. Strips calendar release dates (e.g. \"-20241022\", \"-2024-08-06\", \"-2407\")\n * 5. Standardizes numeric version separators (e.g. \"3-5\" -> \"3.5\", \"4-6\" -> \"4.6\")\n *\n * Examples:\n * - \"google/gemini-3.8-flash-tiered\" -> \"gemini-3.8-flash\"\n * - \"anthropic/claude-3-5-sonnet-20241022\" -> \"claude-3.5-sonnet\"\n * - \"claude-opus-4-6-thinking\" -> \"claude-opus-4.6\"\n * - \"meta-llama/llama-3.3-70b-instruct:free\" -> \"llama-3.3-70b-instruct\"\n */\nexport function normalizeModelName(raw: unknown): string {\n if (typeof raw !== 'string' || raw.trim().length === 0) {\n return 'unknown';\n }\n\n const trimmed = raw.trim().toLowerCase();\n\n // Strip provider namespace (anything before the first slash, including leading '~' aliases)\n const withoutNamespace = trimmed.replace(/^~?[a-zA-Z0-9_-]+\\//, '');\n\n // Strip variant flags (:free, :batch), proxy/mode tags, dates, and convert digit-dash-digit to dots\n return withoutNamespace\n .replace(/:[a-zA-Z0-9_-]+$/, '')\n .replace(/-(?:tiered|thinking|preview|low|high|latest)$/, '')\n .replace(/-(?:\\d{4}-\\d{2}-\\d{2}|\\d{8}|\\d{4})$/, '')\n .replace(/\\b(\\d+)-(\\d+)\\b/g, '$1.$2');\n}\n\n/**\n * Parses numeric rate strings from OpenRouter safely, guaranteeing non-negative finite numbers.\n */\nfunction toSafeRate(value: string | undefined, fallback = 0): number {\n if (!value) return fallback;\n const parsed = Number.parseFloat(value);\n return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;\n}\n\n/**\n * Fetches and indexes OpenRouter's live models catalog.\n * Uses native Node 24 AbortSignal.timeout to enforce a strict network boundary.\n */\nexport async function syncOpenRouterCatalog(): Promise<boolean> {\n if (catalogPromise) {\n return catalogPromise;\n }\n\n catalogPromise = (async (): Promise<boolean> => {\n try {\n const response = await fetch('https://openrouter.ai/api/v1/models', {\n signal: AbortSignal.timeout(5000),\n headers: { Accept: 'application/json' },\n });\n\n if (!response.ok) {\n return false;\n }\n\n const json = (await response.json()) as {\n readonly data?: readonly OpenRouterModelItem[];\n };\n\n if (!Array.isArray(json?.data)) {\n return false;\n }\n\n for (const item of json.data) {\n if (!item?.id || !item.pricing) continue;\n\n const promptRate = toSafeRate(item.pricing.prompt);\n const completionRate = toSafeRate(item.pricing.completion);\n const cacheReadRate = toSafeRate(\n item.pricing.input_cache_read,\n promptRate > 0 ? promptRate * 0.1 : 0,\n );\n const cacheWriteRate = toSafeRate(item.pricing.input_cache_write, promptRate);\n\n // Dynamically extract real maximum context length from OpenRouter\n const rawWindow = item.context_length;\n const contextWindow =\n typeof rawWindow === 'number' && Number.isFinite(rawWindow) && rawWindow > 0\n ? rawWindow\n : 128_000;\n\n const resolvedRates: ModelRates = {\n in: promptRate,\n out: completionRate,\n cached: cacheReadRate,\n cacheWrite: cacheWriteRate,\n window: contextWindow,\n };\n\n const fullId = item.id.toLowerCase();\n const bareSlug = item.id.includes('/')\n ? (item.id.split('/')[1]?.toLowerCase() ?? fullId)\n : fullId;\n const cleanSlug = normalizeModelName(item.id);\n\n // Multi-key indexing for instant O(1) matching\n rateCache.set(fullId, resolvedRates);\n rateCache.set(bareSlug, resolvedRates);\n rateCache.set(cleanSlug, resolvedRates);\n }\n\n return true;\n } catch {\n return false;\n } finally {\n catalogPromise = null;\n }\n })();\n\n return catalogPromise;\n}\n\n/**\n * Resolves rates and maximum context length dynamically for the active turn model.\n * Seamlessly handles mid-session model switches and user custom overrides.\n */\nexport async function resolveModelRates(\n model: string,\n overrides: ModelPricingOverrides = {},\n): Promise<ModelRates> {\n const clean = normalizeModelName(model);\n const rawKey = model.trim().toLowerCase();\n\n // 1. User config overrides always take highest priority\n const matchedOverride = overrides[rawKey] ?? overrides[clean];\n if (matchedOverride) {\n const existing = rateCache.get(clean) ?? rateCache.get(rawKey);\n const resolvedOverride: ModelRates = {\n in: matchedOverride.in ?? existing?.in ?? 0,\n out: matchedOverride.out ?? existing?.out ?? 0,\n cached: matchedOverride.cached ?? existing?.cached ?? 0,\n cacheWrite: matchedOverride.in ?? existing?.cacheWrite ?? 0,\n window: matchedOverride.window ?? existing?.window ?? 128_000,\n };\n rateCache.set(clean, resolvedOverride);\n rateCache.set(rawKey, resolvedOverride);\n return resolvedOverride;\n }\n\n // 2. Fast-path: Instant in-memory cache hit\n const cached = rateCache.get(clean) ?? rateCache.get(rawKey);\n if (cached) {\n return cached;\n }\n\n // 3. Cache-miss: Fetch OpenRouter catalog on-demand\n await syncOpenRouterCatalog();\n\n const postSync = rateCache.get(clean) ?? rateCache.get(rawKey);\n if (postSync) {\n return postSync;\n }\n\n // 4. Longest-match fallback across indexed models (prioritizes specific variants over shorter base slugs)\n const candidateKeys = Array.from(rateCache.keys()).sort((a, b) => b.length - a.length);\n for (const key of candidateKeys) {\n if (key.length > 3 && (clean.includes(key) || key.includes(clean))) {\n const rates = rateCache.get(key);\n if (rates) {\n rateCache.set(clean, rates);\n return rates;\n }\n }\n }\n\n // 5. Zero-rate boundary if completely unknown to OpenRouter\n const defaultBoundary: ModelRates = {\n in: 0,\n out: 0,\n cached: 0,\n cacheWrite: 0,\n window: 128_000,\n };\n rateCache.set(clean, defaultBoundary);\n return defaultBoundary;\n}\n\n/**\n * Synchronous cache lookup for instantaneous read during statusline rendering.\n */\nexport function ratesFor(model: string): ModelRates {\n const clean = normalizeModelName(model);\n const rawKey = model.trim().toLowerCase();\n\n const hit = rateCache.get(clean) ?? rateCache.get(rawKey);\n if (hit) {\n return hit;\n }\n\n // Kick off background sync for future turns if not yet cached\n void syncOpenRouterCatalog();\n\n return {\n in: 0,\n out: 0,\n cached: 0,\n cacheWrite: 0,\n window: 128_000,\n };\n}\n\n/**\n * Calculates exact monetary cost in USD for a turn interaction.\n * Supports prompt, completion, cache-read, and cache-write token accounting.\n */\nexport function costFor(\n rates: ModelRates,\n usage: {\n readonly input?: number;\n readonly output?: number;\n readonly cacheRead?: number;\n readonly cacheCreate?: number;\n },\n): number {\n const inputTokens = Math.max(0, usage.input ?? 0);\n const outputTokens = Math.max(0, usage.output ?? 0);\n const cacheReadTokens = Math.max(0, usage.cacheRead ?? 0);\n const cacheCreateTokens = Math.max(0, usage.cacheCreate ?? 0);\n\n const cacheWriteRate = rates.cacheWrite > 0 ? rates.cacheWrite : rates.in;\n\n const total =\n inputTokens * rates.in +\n outputTokens * rates.out +\n cacheReadTokens * rates.cached +\n cacheCreateTokens * cacheWriteRate;\n\n if (!Number.isFinite(total) || total < 0) {\n return 0;\n }\n\n // 8-decimal deterministic rounding prevents IEEE 754 floating-point drift across turns\n return Math.round(total * 100_000_000) / 100_000_000;\n}\n","/**\n * tokensniff — Spend Calendar Heatmap Engine (v1 Production)\n *\n * Compiles historical turn telemetry into day-by-day aggregates and renders a\n * standalone, zero-dependency calendar heatmap with an obsidian & emerald aesthetic.\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport * as store from '../collector/store.js';\nimport { costFor, ratesFor } from '../shared/pricing.js';\n\nexport interface DayBucket {\n readonly date: string; // Local date in YYYY-MM-DD\n tokens: number;\n cost: number;\n cacheRead: number;\n thinkingTokens: number;\n turns: number;\n readonly models: Record<string, number>;\n readonly hourlyTurns: number[];\n}\n\n/**\n * Parses history.ndjson into an aggregated day-by-day record map.\n * Recovers cleanly from truncated or malformed trailing records.\n */\nexport function loadDailyRollup(statusDirs: readonly string[]): Record<string, DayBucket> {\n const dailyMap: Record<string, DayBucket> = {};\n\n for (const dir of statusDirs) {\n const historyPath = path.join(dir, 'history.ndjson');\n if (!fs.existsSync(historyPath)) continue;\n\n try {\n const raw = fs.readFileSync(historyPath, 'utf8');\n const lines = raw.split('\\n');\n\n for (const line of lines) {\n if (!line.trim()) continue;\n try {\n const row: unknown = JSON.parse(line);\n if (typeof row !== 'object' || row === null) continue;\n\n const record = row as Record<string, unknown>;\n const dateStr = record.d;\n if (typeof dateStr !== 'string' || !/^\\d{4}-\\d{2}-\\d{2}$/.test(dateStr)) {\n continue;\n }\n\n if (!dailyMap[dateStr]) {\n dailyMap[dateStr] = {\n date: dateStr,\n tokens: 0,\n cost: 0,\n cacheRead: 0,\n thinkingTokens: 0,\n turns: 0,\n models: {},\n hourlyTurns: new Array(24).fill(0) as number[],\n };\n }\n\n const bucket = dailyMap[dateStr];\n if (!bucket) continue;\n\n bucket.tokens += typeof record.tk === 'number' ? record.tk : 0;\n bucket.cost += typeof record.c === 'number' ? record.c : 0;\n bucket.cacheRead += typeof record.cr === 'number' ? record.cr : 0;\n bucket.thinkingTokens += typeof record.th === 'number' ? record.th : 0;\n bucket.turns += 1;\n\n const model = typeof record.m === 'string' && record.m ? record.m : 'unknown';\n bucket.models[model] = (bucket.models[model] ?? 0) + 1;\n\n if (typeof record.ts === 'number') {\n const hour = new Date(record.ts).getHours();\n if (hour >= 0 && hour < 24 && bucket.hourlyTurns[hour] !== undefined) {\n bucket.hourlyTurns[hour] += 1;\n }\n }\n } catch {\n /* ignore corrupted line */\n }\n }\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n console.error(`[tokensniff] failed reading history in ${dir}: ${msg}`);\n }\n }\n\n return dailyMap;\n}\n\n/**\n * Ingests external raw HTTP probe captures and imports them into history.ndjson.\n */\nexport function importCapturesDirectory(\n sourceDir: string,\n targetStatusDirs: readonly string[],\n): { imported: number; skipped: number } {\n if (!fs.existsSync(sourceDir)) {\n return { imported: 0, skipped: 0 };\n }\n\n const files = fs.readdirSync(sourceDir);\n let imported = 0;\n let skipped = 0;\n\n for (const file of files) {\n if (!file.endsWith('.json') || file.startsWith('session_')) continue;\n const fullPath = path.join(sourceDir, file);\n\n try {\n const parsedData: unknown = JSON.parse(fs.readFileSync(fullPath, 'utf8'));\n if (typeof parsedData !== 'object' || parsedData === null) {\n skipped++;\n continue;\n }\n\n const data = parsedData as Record<string, unknown>;\n const timestamp =\n (data.timestamp as string) ||\n ((data.reqLog as Record<string, unknown>)?.timestamp as string);\n if (!timestamp) {\n skipped++;\n continue;\n }\n\n const dateStr = store.getLocalDateString(new Date(timestamp));\n const res = data.response || (data.reqLog as Record<string, unknown>)?.responseHeaders;\n if (!res) {\n skipped++;\n continue;\n }\n\n let rawBody = '';\n if (typeof (data.response as Record<string, unknown>)?.rawBodyString === 'string') {\n rawBody = (data.response as Record<string, unknown>).rawBodyString as string;\n } else if (typeof (data.reqLog as Record<string, unknown>)?.rawResponseBody === 'string') {\n rawBody = (data.reqLog as Record<string, unknown>).rawResponseBody as string;\n }\n\n if (!rawBody) {\n skipped++;\n continue;\n }\n\n const parsedBody: unknown = JSON.parse(rawBody);\n if (typeof parsedBody !== 'object' || parsedBody === null) {\n skipped++;\n continue;\n }\n\n const bodyObj = parsedBody as Record<string, unknown>;\n const usage = (bodyObj.usage as Record<string, unknown>) ?? {};\n const model = typeof bodyObj.model === 'string' ? bodyObj.model : 'gemini-3.7-flash-tiered';\n const rates = ratesFor(model);\n\n const inTokens = typeof usage.input_tokens === 'number' ? usage.input_tokens : 0;\n const outTokens = typeof usage.output_tokens === 'number' ? usage.output_tokens : 0;\n const cacheRead =\n typeof usage.cache_read_input_tokens === 'number' ? usage.cache_read_input_tokens : 0;\n const cacheCreate =\n typeof usage.cache_creation_input_tokens === 'number'\n ? usage.cache_creation_input_tokens\n : 0;\n\n if (inTokens === 0 && outTokens === 0) {\n skipped++;\n continue;\n }\n\n const turnCost = costFor(rates, {\n input: inTokens,\n output: outTokens,\n cacheRead,\n cacheCreate,\n });\n\n store.appendHistory(targetStatusDirs, {\n date: dateStr,\n sessionId: 'imported',\n turn: 1,\n model,\n tokens: inTokens + outTokens + cacheRead,\n cost: turnCost,\n cacheRead,\n thinkingTokens: 0,\n });\n\n imported++;\n } catch {\n skipped++;\n }\n }\n\n return { imported, skipped };\n}\n\n/**\n * Renders the pure, production-grade HTML heatmap dashboard.\n * Zero external web assets or font dependencies; 100% self-contained.\n */\nexport function renderHeatmapHtml(\n dailyMap: Record<string, DayBucket>,\n targetYear: number = new Date().getFullYear(),\n): string {\n let totalTokens = 0;\n let totalCost = 0;\n let totalTurns = 0;\n let totalCacheRead = 0;\n\n for (const bucket of Object.values(dailyMap)) {\n totalTokens += bucket.tokens;\n totalCost += bucket.cost;\n totalTurns += bucket.turns;\n totalCacheRead += bucket.cacheRead;\n }\n\n const cacheHitPct = totalTokens > 0 ? ((totalCacheRead / totalTokens) * 100).toFixed(1) : '0.0';\n const formattedTotalTokens =\n totalTokens >= 1e9\n ? `${(totalTokens / 1e9).toFixed(2)}B`\n : `${(totalTokens / 1e6).toFixed(2)}M`;\n\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\">\n <title>tokensniff • ${targetYear} Token Spend Heatmap</title>\n <style>\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\n :root {\n --bg-deep: #05070c;\n --panel-surface: #0a0e17;\n --panel-border: rgba(255, 255, 255, 0.07);\n --panel-highlight: rgba(255, 255, 255, 0.12);\n --text-main: #f8fafc;\n --text-muted: #64748b;\n --text-subtle: #475569;\n --emerald-bright: #34d399;\n --emerald-vivid: #10b981;\n --emerald-deep: #047857;\n --emerald-dark: #064e3b;\n --tile-size: 16px;\n --tile-gap: 4.5px;\n --tile-radius: 3px;\n }\n\n body {\n overflow-x: hidden;\n background: radial-gradient(circle at 50% -10%, rgba(16, 185, 129, 0.12), transparent 70%),\n radial-gradient(circle at 10% 100%, rgba(5, 150, 105, 0.05), transparent 50%),\n var(--bg-deep);\n color: var(--text-main);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Inter\", \"Geist\", sans-serif;\n min-height: 100vh;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 48px 24px;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n }\n\n .container {\n width: 100%;\n max-width: 1220px;\n background: var(--panel-surface);\n border: 1px solid var(--panel-border);\n border-radius: 20px;\n padding: 38px 44px;\n box-shadow: 0 40px 100px -20px rgba(0, 0, 0, 0.95),\n 0 0 0 1px rgba(255, 255, 255, 0.03) inset,\n 0 0 80px rgba(16, 185, 129, 0.04);\n position: relative;\n overflow: hidden;\n }\n\n .container::before {\n content: \"\";\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n height: 1px;\n background: linear-gradient(90deg, transparent, rgba(52, 211, 153, 0.4), transparent);\n }\n\n /* Header Bar */\n .header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-bottom: 30px;\n border-bottom: 1px solid rgba(255, 255, 255, 0.06);\n flex-wrap: wrap;\n gap: 24px;\n }\n\n .title-group {\n display: flex;\n align-items: center;\n gap: 16px;\n }\n\n .badge-icon {\n width: 44px;\n height: 44px;\n border-radius: 12px;\n background: linear-gradient(135deg, rgba(16, 185, 129, 0.25), rgba(4, 120, 87, 0.08));\n border: 1px solid rgba(52, 211, 153, 0.4);\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--emerald-bright);\n font-weight: 800;\n font-size: 17px;\n font-family: ui-monospace, \"SF Mono\", monospace;\n box-shadow: 0 0 24px rgba(16, 185, 129, 0.2), inset 0 1px 1px rgba(255, 255, 255, 0.2);\n }\n\n h1 {\n font-size: 21px;\n font-weight: 700;\n color: #ffffff;\n letter-spacing: -0.025em;\n line-height: 1.2;\n }\n\n .subtext {\n font-size: 13px;\n color: var(--text-muted);\n margin-top: 4px;\n font-family: ui-monospace, \"SF Mono\", monospace;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .status-pulse {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: 11px;\n color: var(--emerald-bright);\n background: rgba(16, 185, 129, 0.1);\n border: 1px solid rgba(52, 211, 153, 0.25);\n border-radius: 20px;\n padding: 2px 10px;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n font-weight: 600;\n }\n\n .status-pulse-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: var(--emerald-bright);\n box-shadow: 0 0 8px var(--emerald-bright);\n }\n\n /* Structured Metric Bays */\n .summary-metrics {\n display: flex;\n gap: 14px;\n font-family: ui-monospace, \"SF Mono\", monospace;\n flex-wrap: wrap;\n }\n\n .summary-item {\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n background: rgba(255, 255, 255, 0.02);\n border: 1px solid rgba(255, 255, 255, 0.06);\n border-radius: 12px;\n padding: 12px 20px;\n min-width: 125px;\n box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.03);\n }\n\n .summary-label {\n font-size: 10px;\n text-transform: uppercase;\n letter-spacing: 0.1em;\n color: var(--text-muted);\n font-weight: 600;\n }\n\n .summary-val {\n color: #ffffff;\n font-size: 18px;\n font-weight: 700;\n margin-top: 5px;\n letter-spacing: -0.02em;\n }\n\n .summary-val.emerald {\n color: var(--emerald-bright);\n text-shadow: 0 0 20px rgba(52, 211, 153, 0.45);\n }\n\n /* Heatmap Layout & Viewport */\n .heatmap-wrapper {\n padding-top: 34px;\n overflow-x: auto;\n scrollbar-width: thin;\n scrollbar-color: rgba(255, 255, 255, 0.1) transparent;\n }\n\n .heatmap-wrapper::-webkit-scrollbar {\n height: 6px;\n }\n .heatmap-wrapper::-webkit-scrollbar-thumb {\n background: rgba(255, 255, 255, 0.1);\n border-radius: 3px;\n }\n\n .months-row {\n position: relative;\n height: 22px;\n margin-left: 42px;\n margin-bottom: 10px;\n font-size: 12px;\n font-weight: 600;\n color: var(--text-muted);\n font-family: ui-monospace, \"SF Mono\", monospace;\n }\n\n .month-label {\n position: absolute;\n user-select: none;\n letter-spacing: -0.01em;\n }\n\n .grid-container {\n display: flex;\n gap: 12px;\n align-items: start;\n }\n\n .weekday-labels {\n display: grid;\n grid-template-rows: repeat(7, var(--tile-size));\n gap: var(--tile-gap);\n font-size: 11px;\n color: var(--text-subtle);\n font-family: ui-monospace, \"SF Mono\", monospace;\n font-weight: 600;\n user-select: none;\n line-height: var(--tile-size);\n text-align: right;\n padding-right: 6px;\n width: 34px;\n }\n\n .tiles-grid {\n display: grid;\n grid-template-rows: repeat(7, var(--tile-size));\n grid-auto-flow: column;\n grid-auto-columns: var(--tile-size);\n gap: var(--tile-gap);\n }\n\n .tile {\n width: var(--tile-size);\n height: var(--tile-size);\n border-radius: var(--tile-radius);\n cursor: pointer;\n transition: transform 0.16s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.16s ease, border-color 0.16s ease;\n outline: 1px solid transparent;\n position: relative;\n }\n\n .tile:hover {\n transform: scale(1.38);\n z-index: 10;\n outline: 1px solid #ffffff;\n box-shadow: 0 0 16px rgba(255, 255, 255, 0.65);\n }\n\n /* 5-Tier Competitive Millions/Billions Jewel Palette */\n .tier-0 {\n background: #0d121c;\n border: 1px solid rgba(255, 255, 255, 0.04);\n }\n .tier-1 {\n background: #064e3b;\n border: 1px solid #047857;\n box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.1);\n }\n .tier-2 {\n background: #047857;\n border: 1px solid #10b981;\n box-shadow: 0 0 8px rgba(4, 120, 87, 0.45), inset 0 1px 1px rgba(255, 255, 255, 0.2);\n }\n .tier-3 {\n background: #10b981;\n border: 1px solid #34d399;\n box-shadow: 0 0 14px rgba(16, 185, 129, 0.65), inset 0 1px 1px rgba(255, 255, 255, 0.3);\n }\n .tier-4 {\n background: #34d399;\n border: 1px solid #a7f3d0;\n box-shadow: 0 0 20px rgba(52, 211, 153, 0.9), inset 0 0 4px #ffffff;\n }\n\n /* Footer Milestone Legend */\n .footer {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-top: 32px;\n padding-top: 22px;\n border-top: 1px solid rgba(255, 255, 255, 0.06);\n font-size: 12px;\n color: var(--text-muted);\n font-family: ui-monospace, \"SF Mono\", monospace;\n flex-wrap: wrap;\n gap: 16px;\n }\n\n .legend-container {\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .legend-label {\n font-size: 11px;\n color: var(--text-muted);\n text-transform: uppercase;\n letter-spacing: 0.06em;\n font-weight: 600;\n }\n\n .legend {\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .legend-tile {\n width: 15px;\n height: 15px;\n border-radius: var(--tile-radius);\n }\n\n .legend-bounds {\n font-size: 11px;\n color: var(--text-muted);\n }\n\n /* Floating Glassmorphic HUD Tooltip */\n #tooltip {\n position: fixed;\n display: none;\n pointer-events: none;\n background: rgba(10, 14, 23, 0.97);\n backdrop-filter: blur(16px);\n -webkit-backdrop-filter: blur(16px);\n border: 1px solid rgba(255, 255, 255, 0.12);\n border-radius: 12px;\n padding: 14px 18px;\n font-size: 12px;\n color: #f1f5f9;\n box-shadow: 0 20px 40px rgba(0, 0, 0, 0.95), 0 0 30px rgba(16, 185, 129, 0.12);\n font-family: ui-monospace, \"SF Mono\", monospace;\n z-index: 1000;\n line-height: 1.6;\n min-width: 220px;\n }\n\n .tt-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 8px;\n padding-bottom: 6px;\n border-bottom: 1px solid rgba(255, 255, 255, 0.08);\n }\n\n .tt-date {\n font-weight: 700;\n color: #ffffff;\n font-size: 13px;\n }\n\n .tt-row {\n display: flex;\n justify-content: space-between;\n margin-top: 3px;\n }\n\n .tt-label {\n color: var(--text-muted);\n }\n\n .tt-cost {\n color: var(--emerald-bright);\n font-weight: 700;\n }\n\n .tt-val {\n font-weight: 600;\n color: #ffffff;\n }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <div class=\"header\">\n <div class=\"title-group\">\n <div class=\"badge-icon\">TS</div>\n <div>\n <h1>tokensniff dashboard</h1>\n <div class=\"subtext\">\n <span>Year ${targetYear} Token Telemetry & Spend Ledger</span>\n <span class=\"status-pulse\"><span class=\"status-pulse-dot\"></span>Online</span>\n </div>\n </div>\n </div>\n <div class=\"summary-metrics\">\n <div class=\"summary-item\">\n <span class=\"summary-label\">Total Spend</span>\n <span class=\"summary-val emerald\">$${totalCost.toFixed(3)}</span>\n </div>\n <div class=\"summary-item\">\n <span class=\"summary-label\">Total Tokens</span>\n <span class=\"summary-val\">${formattedTotalTokens}</span>\n </div>\n <div class=\"summary-item\">\n <span class=\"summary-label\">Cache Ratio</span>\n <span class=\"summary-val\">${cacheHitPct}%</span>\n </div>\n <div class=\"summary-item\">\n <span class=\"summary-label\">Total Turns</span>\n <span class=\"summary-val\">${totalTurns}</span>\n </div>\n </div>\n </div>\n\n <div class=\"heatmap-wrapper\">\n <div id=\"months-row\" class=\"months-row\"></div>\n <div class=\"grid-container\">\n <div class=\"weekday-labels\">\n <span></span>\n <span>Mon</span>\n <span></span>\n <span>Wed</span>\n <span></span>\n <span>Fri</span>\n <span></span>\n </div>\n <div id=\"tiles\" class=\"tiles-grid\"></div>\n </div>\n </div>\n\n <div class=\"footer\">\n <span>Auto-updated live ledger • port 4000</span>\n <div class=\"legend-container\">\n <span class=\"legend-label\">Daily Volume</span>\n <span class=\"legend-bounds\">0</span>\n <div class=\"legend\">\n <div class=\"legend-tile tier-0\" title=\"0 tokens\"></div>\n <div class=\"legend-tile tier-1\" title=\"1M - 5M tokens\"></div>\n <div class=\"legend-tile tier-2\" title=\"5M - 25M tokens\"></div>\n <div class=\"legend-tile tier-3\" title=\"25M - 100M tokens\"></div>\n <div class=\"legend-tile tier-4\" title=\"> 100M tokens\"></div>\n </div>\n <span class=\"legend-bounds\">100M+</span>\n </div>\n </div>\n </div>\n\n <div id=\"tooltip\"></div>\n\n <script>\n const data = ${JSON.stringify(dailyMap)};\n const year = ${targetYear};\n const tilesContainer = document.getElementById('tiles');\n const monthsRow = document.getElementById('months-row');\n const tooltip = document.getElementById('tooltip');\n\n const startDate = new Date(year, 0, 1);\n const startDay = startDate.getDay(); // 0 is Sunday\n\n // Pad beginning of the year so rows align to Sunday-Saturday\n for (let p = 0; p < startDay; p++) {\n const empty = document.createElement('div');\n empty.style.width = '16px';\n empty.style.height = '16px';\n tilesContainer.appendChild(empty);\n }\n\n const isLeapYear = (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0);\n const totalDays = isLeapYear ? 366 : 365;\n const monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];\n let lastMonth = -1;\n\n function formatHumanTokens(t) {\n if (!t || t === 0) return '0';\n if (t >= 1e9) return (t / 1e9).toFixed(2) + 'B';\n if (t >= 1e6) return (t / 1e6).toFixed(2) + 'M';\n if (t >= 1e3) return (t / 1e3).toFixed(1) + 'k';\n return t.toLocaleString();\n }\n\n for (let i = 0; i < totalDays; i++) {\n const cur = new Date(year, 0, 1 + i);\n const m = cur.getMonth();\n const yearStr = cur.getFullYear();\n const monthStr = String(cur.getMonth() + 1).padStart(2, '0');\n const dayStr = String(cur.getDate()).padStart(2, '0');\n const dStr = yearStr + '-' + monthStr + '-' + dayStr;\n\n // Position month labels dynamically along the horizontal week track\n // 16px tile + 4.5px gap = 20.5px step\n if (m !== lastMonth) {\n lastMonth = m;\n const weekCol = Math.floor((i + startDay) / 7);\n const span = document.createElement('span');\n span.className = 'month-label';\n span.innerText = monthNames[m];\n span.style.left = (weekCol * 20.5) + 'px';\n monthsRow.appendChild(span);\n }\n\n const record = data[dStr];\n const tokens = record ? record.tokens : 0;\n const cost = record ? record.cost : 0;\n const turns = record ? record.turns : 0;\n const cacheRead = record ? record.cacheRead : 0;\n\n // Competitive Millions & Billions Tiers\n let tier = 'tier-0';\n if (tokens >= 100000000) tier = 'tier-4'; // > 100M tokens\n else if (tokens >= 25000000) tier = 'tier-3'; // 25M - 100M tokens\n else if (tokens >= 5000000) tier = 'tier-2'; // 5M - 25M tokens\n else if (tokens > 0) tier = 'tier-1'; // 1M - 5M tokens\n\n const tile = document.createElement('div');\n tile.className = 'tile ' + tier;\n\n function positionTooltip(e) {\n const tipWidth = tooltip.offsetWidth || 240;\n const tipHeight = tooltip.offsetHeight || 160;\n let x = e.clientX + 16;\n let y = e.clientY + 16;\n\n // If hovering near the right edge of the screen (e.g. December), flip to the left of the cursor\n if (x + tipWidth > window.innerWidth - 16) {\n x = e.clientX - tipWidth - 16;\n }\n\n // If hovering near the bottom of the screen, flip upward above the cursor\n if (y + tipHeight > window.innerHeight - 16) {\n y = e.clientY - tipHeight - 16;\n }\n\n tooltip.style.left = Math.max(12, x) + 'px';\n tooltip.style.top = Math.max(12, y) + 'px';\n }\n\n tile.onmouseenter = (e) => {\n const cachePct = tokens > 0 ? ((cacheRead / tokens) * 100).toFixed(1) : '0.0';\n tooltip.innerHTML =\n '<div class=\"tt-header\">' +\n '<span class=\"tt-date\">' + cur.toLocaleDateString(undefined, { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric' }) + '</span>' +\n '<span class=\"tt-cost\">$' + cost.toFixed(4) + '</span>' +\n '</div>' +\n '<div class=\"tt-row\"><span class=\"tt-label\">Volume</span><span class=\"tt-val\">' + formatHumanTokens(tokens) + '</span></div>' +\n '<div class=\"tt-row\"><span class=\"tt-label\">Exact Tokens</span><span class=\"tt-val\">' + tokens.toLocaleString() + '</span></div>' +\n '<div class=\"tt-row\"><span class=\"tt-label\">Cache Leverage</span><span class=\"tt-val\">' + formatHumanTokens(cacheRead) + ' (' + cachePct + '%)</span></div>' +\n '<div class=\"tt-row\"><span class=\"tt-label\">Turns Logged</span><span class=\"tt-val\">' + turns + '</span></div>';\n positionTooltip(e);\n tooltip.style.display = 'block';\n };\n\n tile.onmousemove = (e) => {\n positionTooltip(e);\n };\n\n tile.onmouseleave = () => {\n tooltip.style.display = 'none';\n };\n\n tilesContainer.appendChild(tile);\n }\n </script>\n</body>\n</html>`;\n}\n","/**\n * tokensniff — Canonical Domain Telemetry Schema (v1 Production)\n *\n * Single source of truth for telemetry interfaces, runtime validation guards,\n * safe default factories, and agent state text parsing.\n */\n\nexport const SCHEMA_VERSION = 1 as const;\n\n/**\n * Summary representation of an aggregated tool call across a single turn.\n */\nexport interface ToolSummaryItem {\n readonly name: string;\n readonly count: number;\n readonly arg_tk: number;\n}\n\n/**\n * Granular record of an individual tool call during an execution turn.\n */\nexport interface ToolCallItem {\n readonly name: string;\n readonly argTk: number;\n}\n\n/**\n * Classification of the primary driver of a model turn.\n */\nexport type TurnKind = 'text' | 'tool' | 'thinking' | 'label' | 'error' | 'unknown';\n\n/**\n * Snapshot of provider upstream rate limits and token bucket quotas.\n */\nexport interface QuotaSnapshot {\n readonly remainingPct: number | null;\n readonly remainingFraction: number | null;\n readonly resetTime: string | null;\n readonly tier: string | null;\n readonly isRateLimited: boolean;\n}\n\n/**\n * Raw telemetry extracted directly from a single network interaction.\n */\nexport interface TurnMetrics {\n readonly model: string;\n readonly kind: TurnKind;\n readonly input: number;\n readonly output: number;\n readonly cacheRead: number;\n readonly cacheCreate: number;\n readonly tools: readonly ToolCallItem[];\n readonly toolsSummary: readonly ToolSummaryItem[];\n readonly thinking: boolean;\n readonly thinkingTokens: number;\n readonly stopReason: string;\n readonly label: string;\n}\n\n/**\n * Fully hydrated telemetry state persisted to ~/.tokensniff/status/<session_id>.json.\n * Consumed directly by terminal statusline renderers and CLI dashboards.\n */\nexport interface TokenSniffLatest {\n readonly v: typeof SCHEMA_VERSION;\n readonly session_id: string;\n readonly turn_index: number;\n readonly ts: number;\n readonly model: string;\n readonly is_bg: boolean;\n readonly ctx: number;\n readonly window: number;\n readonly pct: number;\n readonly cache_read: number;\n readonly cache_create: number;\n readonly delta_in: number;\n readonly in_tokens: number;\n readonly out_tokens: number;\n readonly thinking: boolean;\n readonly thinking_tokens: number;\n readonly tool: string | null;\n readonly tool_tk: number;\n readonly tools_summary: readonly ToolSummaryItem[];\n readonly stop_reason: string;\n readonly is_label: boolean;\n readonly label: string;\n readonly ttft_ms: number;\n readonly tps: number;\n readonly dur_s: number;\n readonly cost_turn: number;\n readonly cost_session: number;\n readonly cost_today: number;\n readonly error: number | null;\n readonly status: number;\n readonly quota_pct: number | null;\n readonly quota_reset: string | null;\n readonly tier: string | null;\n}\n\n/**\n * Compact append-only historical turn record for analytics and sparklines.\n */\nexport interface HistoryEntry {\n readonly d: string;\n readonly ts: number;\n readonly s: string;\n readonly t: number;\n readonly m: string;\n readonly tk: number;\n readonly c: number;\n readonly cr: number;\n readonly th: number;\n}\n\n/**\n * Defensive number sanitizer ensuring NaN and Infinity never corrupt telemetry output.\n */\nfunction toFiniteNumber(value: unknown, fallback = 0): number {\n const num = Number(value);\n return Number.isFinite(num) ? num : fallback;\n}\n\n/**\n * Runtime Type Guard to safely validate deserialized disk telemetry without unsafe casts.\n */\nexport function isTokenSniffLatest(value: unknown): value is TokenSniffLatest {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return false;\n }\n\n const candidate = value as Record<string, unknown>;\n\n return (\n candidate.v === SCHEMA_VERSION &&\n typeof candidate.session_id === 'string' &&\n typeof candidate.turn_index === 'number' &&\n typeof candidate.ts === 'number' &&\n typeof candidate.model === 'string' &&\n typeof candidate.ctx === 'number' &&\n typeof candidate.window === 'number' &&\n typeof candidate.pct === 'number'\n );\n}\n\n/**\n * Factory creating fully hydrated TokenSniffLatest records with strictly sanitized defaults.\n */\nexport function buildLatest(\n partial: Partial<TokenSniffLatest> & { readonly session_id: string },\n): TokenSniffLatest {\n return {\n v: SCHEMA_VERSION,\n session_id: partial.session_id,\n turn_index: Math.max(1, Math.floor(toFiniteNumber(partial.turn_index, 1))),\n ts: toFiniteNumber(partial.ts, Date.now()),\n model:\n typeof partial.model === 'string' && partial.model.length > 0 ? partial.model : 'unknown',\n is_bg: Boolean(partial.is_bg),\n ctx: Math.max(0, toFiniteNumber(partial.ctx, 0)),\n window: Math.max(1, toFiniteNumber(partial.window, 1_000_000)),\n pct: Math.max(0, Math.min(100, toFiniteNumber(partial.pct, 0))),\n cache_read: Math.max(0, toFiniteNumber(partial.cache_read, 0)),\n cache_create: Math.max(0, toFiniteNumber(partial.cache_create, 0)),\n delta_in: toFiniteNumber(partial.delta_in, 0),\n in_tokens: Math.max(0, toFiniteNumber(partial.in_tokens, 0)),\n out_tokens: Math.max(0, toFiniteNumber(partial.out_tokens, 0)),\n thinking: Boolean(partial.thinking),\n thinking_tokens: Math.max(0, toFiniteNumber(partial.thinking_tokens, 0)),\n tool: typeof partial.tool === 'string' && partial.tool.length > 0 ? partial.tool : null,\n tool_tk: Math.max(0, toFiniteNumber(partial.tool_tk, 0)),\n tools_summary: Array.isArray(partial.tools_summary) ? partial.tools_summary : [],\n stop_reason: typeof partial.stop_reason === 'string' ? partial.stop_reason : '',\n is_label: Boolean(partial.is_label),\n label: typeof partial.label === 'string' ? partial.label : '',\n ttft_ms: Math.max(0, toFiniteNumber(partial.ttft_ms, 0)),\n tps: Math.max(0, toFiniteNumber(partial.tps, 0)),\n dur_s: Math.max(0, toFiniteNumber(partial.dur_s, 0)),\n cost_turn: Math.max(0, toFiniteNumber(partial.cost_turn, 0)),\n cost_session: Math.max(0, toFiniteNumber(partial.cost_session, 0)),\n cost_today: Math.max(0, toFiniteNumber(partial.cost_today, 0)),\n error:\n typeof partial.error === 'number' && Number.isFinite(partial.error) ? partial.error : null,\n status: Math.max(0, toFiniteNumber(partial.status, 0)),\n quota_pct:\n typeof partial.quota_pct === 'number' && Number.isFinite(partial.quota_pct)\n ? Math.max(0, Math.min(100, partial.quota_pct))\n : null,\n quota_reset: typeof partial.quota_reset === 'string' ? partial.quota_reset : null,\n tier: typeof partial.tier === 'string' ? partial.tier : null,\n };\n}\n\n/**\n * Extracts a concise, human-readable 1-line label from AI agent task descriptions.\n * Handles compound state/detail JSON structures, stringified JSON, and fallback text.\n */\nexport function extractCleanLabel(text: unknown): string {\n if (typeof text !== 'string' || text.length === 0) {\n return '';\n }\n\n const trimmed = text.trim();\n\n if (trimmed.startsWith('{')) {\n if (trimmed.endsWith('}')) {\n try {\n const parsed: unknown = JSON.parse(trimmed);\n if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {\n const record = parsed as Record<string, unknown>;\n\n // High priority: compound state + detail (e.g. \"Running tests: auth.test.ts\")\n if (record.state && record.detail) {\n return `${String(record.state).trim()}: ${String(record.detail).trim()}`;\n }\n\n // Secondary priority: specific action titles or summaries\n const candidate =\n record.title ??\n record.summary ??\n record.label ??\n record.message ??\n record.detail ??\n record.state;\n\n if (typeof candidate === 'string' && candidate.trim().length > 0) {\n return candidate.trim();\n }\n }\n } catch {\n // Fall through to regex extraction for malformed JSON\n }\n }\n\n // Regex fallback for truncated or malformed JSON streams\n const stateMatch = trimmed.match(/\"state\"\\s*:\\s*\"([^\"\\\\]*)(?:\"|$)/);\n const detailMatch = trimmed.match(/\"detail\"\\s*:\\s*\"([^\"\\\\]*)(?:\"|$)/);\n\n if (stateMatch?.[1] && detailMatch?.[1]) {\n return `${stateMatch[1].trim()}: ${detailMatch[1].trim()}`;\n }\n\n const singleMatch = trimmed.match(\n /\"(?:title|summary|label|message|detail|state)\"\\s*:\\s*\"([^\"\\\\]*)(?:\"|$)/,\n );\n if (singleMatch?.[1]) {\n return singleMatch[1].trim();\n }\n }\n\n // Strip wrapping quotation marks and extra whitespace\n return trimmed.replace(/^[\"']+|[\"']+$/g, '').trim();\n}\n","/**\n * tokensniff — Configuration Loader & Resolver (v1 Production)\n *\n * Implements strict hierarchical configuration resolution:\n * process.env > ~/.tokensniff/config.json > embedded defaults.\n *\n * Hardened with defensive path normalization, numeric bounds verification,\n * safe stringified boolean parsing, and corruption fault-tolerance.\n */\n\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nexport interface TokenSniffConfig {\n readonly listenPort: number;\n readonly listenHost: string;\n readonly upstreamHost: string;\n readonly upstreamPort: number;\n readonly upstreamTimeoutMs: number;\n readonly maxBodyBytes: number;\n readonly statusDirs: readonly string[];\n readonly maxSessions: number;\n readonly deadSessionMs: number;\n readonly staleAfterS: number;\n readonly labelMaxIn: number;\n readonly labelMaxOut: number;\n readonly warnPct: number;\n readonly critPct: number;\n readonly color: boolean;\n readonly upstreamCommand: string;\n readonly upstreamStopCommand: string;\n readonly harnessCommand: string;\n}\n\n/**\n * Validates TCP/IP port numbers against standard IANA boundaries [1, 65535].\n */\nfunction toValidPort(value: unknown, fallback: number): number {\n const parsed = Number(value);\n return Number.isInteger(parsed) && parsed >= 1 && parsed <= 65535 ? parsed : fallback;\n}\n\n/**\n * Validates and clamps integer bounds defensively.\n */\nfunction toBoundedInt(\n value: unknown,\n fallback: number,\n min = 0,\n max = Number.MAX_SAFE_INTEGER,\n): number {\n const parsed = Number(value);\n if (!Number.isFinite(parsed)) return fallback;\n const intVal = Math.floor(parsed);\n return Math.max(min, Math.min(max, intVal));\n}\n\n/**\n * Validates non-empty string fields with fallback protection against whitespace-only values.\n */\nfunction toNonEmptyString(value: unknown, fallback: string): string {\n if (typeof value !== 'string') return fallback;\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : fallback;\n}\n\n/**\n * Accurately parses boolean flags, safely handling stringified representations (\"false\", \"0\").\n */\nfunction toSafeBoolean(value: unknown, fallback: boolean): boolean {\n if (typeof value === 'boolean') return value;\n if (typeof value === 'string') {\n const lower = value.trim().toLowerCase();\n if (lower === 'true' || lower === '1') return true;\n if (lower === 'false' || lower === '0') return false;\n }\n return fallback;\n}\n\n/**\n * Normalizes directory paths, resolving cross-platform slashes and relative references to absolute paths.\n */\nfunction normalizeDirectory(targetPath: string): string {\n const resolved = path.resolve(targetPath.trim());\n return path.normalize(resolved);\n}\n\n/**\n * Dynamically resolves the canonical user home directory (~/.tokensniff) across OSes.\n */\nexport function getGlobalDir(): string {\n const home = process.env.USERPROFILE || os.homedir();\n return path.join(home, '.tokensniff');\n}\n\n/**\n * Resolves the configuration file location ~/.tokensniff/config.json.\n */\nexport function getGlobalConfigPath(): string {\n return path.join(getGlobalDir(), 'config.json');\n}\n\n/**\n * Loads configuration with strict precedence:\n * Environment Variables > File Configuration > Embedded Defaults.\n */\nexport function loadConfig(env: NodeJS.ProcessEnv = process.env): TokenSniffConfig {\n const targetConfigFile = env.TOKENSNIFF_CONFIG || getGlobalConfigPath();\n let fileConfig: Record<string, unknown> = {};\n\n // Safe JSON extraction with non-crashing syntax defense\n if (fs.existsSync(targetConfigFile)) {\n try {\n const raw = fs.readFileSync(targetConfigFile, 'utf8');\n const parsed: unknown = JSON.parse(raw);\n if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {\n fileConfig = parsed as Record<string, unknown>;\n }\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n console.error(`[tokensniff] ignoring corrupt config at ${targetConfigFile}: ${msg}`);\n }\n }\n\n const resolve = <T>(envKey: string, configKey: string, defaultValue: T): unknown => {\n return env[envKey] ?? fileConfig[configKey] ?? defaultValue;\n };\n\n const rootDir = getGlobalDir();\n const defaultStatusDir = path.join(rootDir, 'status');\n\n const rawStatusDirs = resolve('TOKENSNIFF_STATUS_DIRS', 'statusDirs', [defaultStatusDir]);\n\n // Handle both array and comma-separated string declarations of status directories\n const parsedDirs: string[] = Array.isArray(rawStatusDirs)\n ? rawStatusDirs.map(String)\n : String(rawStatusDirs).split(',');\n\n const normalizedStatusDirs = parsedDirs\n .map((d) => d.trim())\n .filter((d) => d.length > 0)\n .map(normalizeDirectory);\n\n const statusDirs =\n normalizedStatusDirs.length > 0 ? normalizedStatusDirs : [normalizeDirectory(defaultStatusDir)];\n\n // NO_COLOR standard (https://no-color.org/): any non-empty value disables ANSI formatting\n const isNoColorSet = typeof env.NO_COLOR === 'string' && env.NO_COLOR.length > 0;\n const colorDefault = !isNoColorSet;\n\n return {\n listenPort: toValidPort(resolve('TOKENSNIFF_PORT', 'listenPort', 4000), 4000),\n listenHost: toNonEmptyString(\n resolve('TOKENSNIFF_HOST', 'listenHost', '127.0.0.1'),\n '127.0.0.1',\n ),\n upstreamHost: toNonEmptyString(\n resolve('TOKENSNIFF_UPSTREAM_HOST', 'upstreamHost', 'localhost'),\n 'localhost',\n ),\n upstreamPort: toValidPort(resolve('TOKENSNIFF_UPSTREAM_PORT', 'upstreamPort', 8085), 8085),\n upstreamTimeoutMs: toBoundedInt(\n resolve('TOKENSNIFF_UPSTREAM_TIMEOUT_MS', 'upstreamTimeoutMs', 300_000),\n 300_000,\n 1000,\n 3_600_000,\n ),\n maxBodyBytes: toBoundedInt(\n resolve('TOKENSNIFF_MAX_BODY_BYTES', 'maxBodyBytes', 10 * 1024 * 1024),\n 10_485_760,\n 1024,\n 100 * 1024 * 1024,\n ),\n statusDirs,\n maxSessions: toBoundedInt(resolve('TOKENSNIFF_MAX_SESSIONS', 'maxSessions', 50), 50, 1, 1000),\n deadSessionMs: toBoundedInt(\n resolve('TOKENSNIFF_DEAD_SESSION_MS', 'deadSessionMs', 72 * 3600 * 1000),\n 259_200_000,\n 3600 * 1000,\n ),\n staleAfterS: toBoundedInt(\n resolve('TOKENSNIFF_STALE_AFTER_S', 'staleAfterS', 120),\n 120,\n 5,\n 86400,\n ),\n labelMaxIn: toBoundedInt(resolve('TOKENSNIFF_LABEL_MAX_IN', 'labelMaxIn', 2000), 2000, 100),\n labelMaxOut: toBoundedInt(resolve('TOKENSNIFF_LABEL_MAX_OUT', 'labelMaxOut', 60), 60, 1),\n warnPct: toBoundedInt(resolve('TOKENSNIFF_WARN_PCT', 'warnPct', 60), 60, 1, 99),\n critPct: toBoundedInt(resolve('TOKENSNIFF_CRIT_PCT', 'critPct', 80), 80, 2, 100),\n color: colorDefault && toSafeBoolean(resolve('TOKENSNIFF_COLOR', 'color', true), true),\n upstreamCommand: toNonEmptyString(\n resolve(\n 'TOKENSNIFF_UPSTREAM_CMD',\n 'upstreamCommand',\n 'npx antigravity-claude-proxy@latest start',\n ),\n 'npx antigravity-claude-proxy@latest start',\n ),\n upstreamStopCommand: String(\n resolve('TOKENSNIFF_UPSTREAM_STOP_CMD', 'upstreamStopCommand', ''),\n ).trim(),\n harnessCommand: toNonEmptyString(\n resolve('TOKENSNIFF_HARNESS_CMD', 'harnessCommand', 'claude'),\n 'claude',\n ),\n };\n}\n","/**\n * tokensniff — Telemetry Stream & Payload Parser (v1 Production)\n *\n * Translates raw wire interaction buffers (SSE streams and buffered JSON)\n * into strongly-typed TurnMetrics using battle-tested regex extractors\n * calibrated directly against real-world Claude Code network captures.\n */\n\nimport {\n extractCleanLabel,\n type ToolCallItem,\n type ToolSummaryItem,\n type TurnKind,\n type TurnMetrics,\n} from '../shared/schema.js';\n\nexport interface InteractionPayload {\n readonly reqBody?: string | null;\n readonly resBody?: string | null;\n readonly contentType?: string | null;\n readonly statusCode?: number | null;\n}\n\n/**\n * Safe JSON parser with guaranteed non-throwing semantics and unknown return typing.\n */\nexport function parseSafeJson(raw: unknown): unknown {\n if (typeof raw !== 'string' || raw.trim().length === 0) {\n return null;\n }\n try {\n return JSON.parse(raw);\n } catch {\n return null;\n }\n}\n\n/**\n * Extracts the first positive integer match against an input string.\n */\nfunction extractFirstInt(pattern: RegExp, target: string): number {\n const match = target.match(pattern);\n if (!match?.[1]) return 0;\n const parsed = Number.parseInt(match[1], 10);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;\n}\n\n/**\n * Aggregates a flat list of individual tool calls into a grouped summary,\n * preserving initial encounter order.\n */\nexport function groupTools(tools: readonly ToolCallItem[]): ToolSummaryItem[] {\n if (tools.length === 0) return [];\n\n const map = new Map<string, { name: string; count: number; arg_tk: number }>();\n\n for (const t of tools) {\n if (!t.name) continue;\n const existing = map.get(t.name);\n if (existing) {\n existing.count += 1;\n existing.arg_tk += t.argTk;\n } else {\n map.set(t.name, {\n name: t.name,\n count: 1,\n arg_tk: t.argTk,\n });\n }\n }\n\n return Array.from(map.values()).map((v) => ({\n name: v.name,\n count: v.count,\n arg_tk: v.arg_tk,\n }));\n}\n\n/**\n * Extracts token telemetry, reasoning/thinking, and multi-tool activity from Server-Sent Events (SSE) streams.\n */\nexport function parseSseStream(fallbackModel: string, payload: string): TurnMetrics {\n const input = extractFirstInt(/\"input_tokens\"\\s*:\\s*(\\d+)/, payload);\n\n const cacheReadMatches = Array.from(payload.matchAll(/\"cache_read_input_tokens\"\\s*:\\s*(\\d+)/g));\n const cacheRead =\n cacheReadMatches.length > 0\n ? Math.max(\n ...cacheReadMatches.map((m) => {\n const val = m[1] ? Number.parseInt(m[1], 10) : 0;\n return Number.isFinite(val) ? val : 0;\n }),\n )\n : 0;\n\n const cacheCreateMatches = Array.from(\n payload.matchAll(/\"cache_creation_input_tokens\"\\s*:\\s*(\\d+)/g),\n );\n const cacheCreate =\n cacheCreateMatches.length > 0\n ? Math.max(\n ...cacheCreateMatches.map((m) => {\n const val = m[1] ? Number.parseInt(m[1], 10) : 0;\n return Number.isFinite(val) ? val : 0;\n }),\n )\n : 0;\n\n const outputMatches = Array.from(payload.matchAll(/\"output_tokens\"\\s*:\\s*(\\d+)/g));\n const lastOutputMatch = outputMatches[outputMatches.length - 1];\n const output = lastOutputMatch?.[1] ? Number.parseInt(lastOutputMatch[1], 10) : 0;\n\n const stopMatches = Array.from(payload.matchAll(/\"stop_reason\"\\s*:\\s*\"([^\"]*)\"/g));\n const lastStopMatch = stopMatches[stopMatches.length - 1];\n const stopReason = lastStopMatch?.[1] ?? '';\n\n // Tool parsing: match both tool_use declarations and streaming partial JSON\n const toolNames = Array.from(\n payload.matchAll(/\"type\"\\s*:\\s*\"tool_use\"[^}]*?\"name\"\\s*:\\s*\"([^\"]+)\"/g),\n ).map((m) => m[1] ?? '');\n\n const partialChunks = Array.from(\n payload.matchAll(/\"partial_json\"\\s*:\\s*\"((?:\\\\.|[^\"\\\\])*)\"/g),\n ).map((m) => m[1] ?? '');\n\n let argChars = 0;\n try {\n argChars = partialChunks.join('').replace(/\\\\./g, 'X').length;\n } catch {\n argChars = 0;\n }\n\n const estimatedTotalArgTk = Math.max(0, Math.round(argChars / 4));\n const avgArgTk =\n toolNames.length > 0 ? Math.max(1, Math.round(estimatedTotalArgTk / toolNames.length)) : 0;\n\n const tools: ToolCallItem[] = toolNames.map((name) => ({\n name,\n argTk: avgArgTk,\n }));\n\n // Reasoning / Thinking token extraction\n let thinkingTokens =\n extractFirstInt(/\"thinking_tokens\"\\s*:\\s*(\\d+)/, payload) ||\n extractFirstInt(/\"reasoning_tokens\"\\s*:\\s*(\\d+)/, payload) ||\n extractFirstInt(/\"reasoningTokenCount\"\\s*:\\s*(\\d+)/, payload);\n\n if (thinkingTokens === 0) {\n const thinkingDeltas = Array.from(\n payload.matchAll(/\"thinking\"\\s*:\\s*\"((?:\\\\.|[^\"\\\\])*)\"/g),\n ).map((m) => m[1] ?? '');\n if (thinkingDeltas.length > 0) {\n const thinkingChars = thinkingDeltas.join('').length;\n thinkingTokens = Math.max(1, Math.round(thinkingChars / 4));\n }\n }\n\n const thinking =\n thinkingTokens > 0 ||\n payload.includes('thinking_delta') ||\n payload.includes('\"type\":\"thinking\"') ||\n payload.includes('\"thought\":true');\n\n const kind: TurnKind = tools.length > 0 ? 'tool' : thinking ? 'thinking' : 'text';\n\n return {\n model: fallbackModel,\n kind,\n input,\n output,\n cacheRead,\n cacheCreate,\n tools,\n toolsSummary: groupTools(tools),\n thinking,\n thinkingTokens,\n stopReason,\n label: '',\n };\n}\n\ninterface AnthropicContentBlock {\n readonly type?: string;\n readonly text?: string;\n readonly name?: string;\n readonly input?: unknown;\n readonly thinking?: string;\n}\n\ninterface GeminiCandidatePart {\n readonly text?: string;\n readonly thought?: boolean;\n readonly functionCall?: {\n readonly name?: string;\n readonly args?: unknown;\n };\n}\n\ninterface RawResponsePayload {\n readonly model?: string;\n readonly stop_reason?: string;\n readonly content?: readonly AnthropicContentBlock[] | string;\n readonly candidates?: readonly {\n readonly finishReason?: string;\n readonly content?: {\n readonly parts?: readonly GeminiCandidatePart[];\n };\n }[];\n readonly usage?: {\n readonly input_tokens?: number;\n readonly output_tokens?: number;\n readonly prompt_tokens?: number;\n readonly completion_tokens?: number;\n readonly cache_read_input_tokens?: number;\n readonly cache_creation_input_tokens?: number;\n readonly thinking_tokens?: number;\n readonly reasoning_tokens?: number;\n };\n readonly usageMetadata?: {\n readonly promptTokenCount?: number;\n readonly candidatesTokenCount?: number;\n readonly cachedContentTokenCount?: number;\n readonly candidatesTokenDetails?: readonly {\n readonly reasoningTokenCount?: number;\n }[];\n };\n}\n\n/**\n * Extracts token telemetry from standard buffered JSON responses.\n * Supports Anthropic, Google Gemini (Antigravity), and OpenAI/DeepSeek schema styles.\n */\nexport function parseJsonPayload(fallbackModel: string, payload: string): TurnMetrics | null {\n const parsed = parseSafeJson(payload);\n if (!parsed || typeof parsed !== 'object') return null;\n\n const data = parsed as RawResponsePayload;\n const usage = data.usage ?? {};\n const tools: ToolCallItem[] = [];\n let rawText = '';\n let thinking = false;\n let thinkingChars = 0;\n\n // 1. Anthropic message content structure\n if (Array.isArray(data.content)) {\n for (const item of data.content) {\n if (!item) continue;\n if (item.type === 'text' && typeof item.text === 'string') {\n rawText += item.text;\n } else if (item.type === 'tool_use' && typeof item.name === 'string') {\n const argStr = JSON.stringify(item.input ?? {});\n tools.push({\n name: item.name,\n argTk: Math.max(1, Math.round(argStr.length / 4)),\n });\n } else if (item.type === 'thinking') {\n thinking = true;\n if (typeof item.thinking === 'string') {\n thinkingChars += item.thinking.length;\n }\n }\n }\n } else if (typeof data.content === 'string') {\n rawText = data.content;\n }\n\n // 2. Google Gemini candidates structure (Antigravity harness)\n if (Array.isArray(data.candidates)) {\n for (const candidate of data.candidates) {\n const parts = candidate?.content?.parts;\n if (Array.isArray(parts)) {\n for (const part of parts) {\n if (!part) continue;\n if (typeof part.text === 'string') {\n if (part.thought) {\n thinking = true;\n thinkingChars += part.text.length;\n } else {\n rawText += part.text;\n }\n } else if (part.functionCall?.name) {\n const argStr = JSON.stringify(part.functionCall.args ?? {});\n tools.push({\n name: part.functionCall.name,\n argTk: Math.max(1, Math.round(argStr.length / 4)),\n });\n }\n }\n }\n }\n }\n\n // Exact thinking token accounting\n let thinkingTokens =\n usage.thinking_tokens ??\n usage.reasoning_tokens ??\n data.usageMetadata?.candidatesTokenDetails?.[0]?.reasoningTokenCount ??\n 0;\n\n if (!thinkingTokens && thinkingChars > 0) {\n thinkingTokens = Math.max(1, Math.round(thinkingChars / 4));\n }\n\n if (thinkingTokens > 0) {\n thinking = true;\n }\n\n const input =\n usage.input_tokens ?? usage.prompt_tokens ?? data.usageMetadata?.promptTokenCount ?? 0;\n\n const output =\n usage.output_tokens ?? usage.completion_tokens ?? data.usageMetadata?.candidatesTokenCount ?? 0;\n\n const cacheRead =\n usage.cache_read_input_tokens ?? data.usageMetadata?.cachedContentTokenCount ?? 0;\n\n const cacheCreate = usage.cache_creation_input_tokens ?? 0;\n\n // Background micro-turn detection (e.g. status labels)\n const isMicro = input < 2000 && output < 60 && tools.length === 0 && !thinking;\n\n let kind: TurnKind = 'text';\n if (tools.length > 0) {\n kind = 'tool';\n } else if (thinking) {\n kind = 'thinking';\n } else if (isMicro) {\n kind = 'label';\n }\n\n const cleanLabel = isMicro ? extractCleanLabel(rawText).slice(0, 120) : '';\n\n return {\n model: data.model || fallbackModel,\n kind,\n input,\n output,\n cacheRead,\n cacheCreate,\n tools,\n toolsSummary: groupTools(tools),\n thinking,\n thinkingTokens,\n stopReason: data.stop_reason ?? data.candidates?.[0]?.finishReason ?? 'end_turn',\n label: cleanLabel,\n };\n}\n\n/**\n * Core parsing coordinator. Translates raw network interaction into TurnMetrics.\n * Guaranteed never to throw under invalid or truncated payloads.\n */\nexport function parseTurn(interaction: InteractionPayload): TurnMetrics {\n const fallbackResult: TurnMetrics = {\n model: 'unknown',\n kind: 'unknown',\n input: 0,\n output: 0,\n cacheRead: 0,\n cacheCreate: 0,\n tools: [],\n toolsSummary: [],\n thinking: false,\n thinkingTokens: 0,\n stopReason: '',\n label: '',\n };\n\n try {\n let resolvedModel = 'unknown';\n\n // Priority 1: Extract model from request body\n const parsedReq = parseSafeJson(interaction.reqBody);\n if (\n parsedReq &&\n typeof parsedReq === 'object' &&\n 'model' in parsedReq &&\n typeof (parsedReq as { model: unknown }).model === 'string'\n ) {\n resolvedModel = (parsedReq as { model: string }).model;\n }\n\n const resString = interaction.resBody ?? '';\n const isError = (interaction.statusCode ?? 0) === 429 || (interaction.statusCode ?? 0) >= 500;\n const isSse =\n (interaction.contentType ?? '').includes('text/event-stream') ||\n resString.includes('event: message_start');\n\n if (isError) {\n const base = isSse\n ? parseSseStream(resolvedModel, resString)\n : (parseJsonPayload(resolvedModel, resString) ?? fallbackResult);\n return { ...base, kind: 'error' };\n }\n\n if (isSse) {\n return parseSseStream(resolvedModel, resString);\n }\n\n return parseJsonPayload(resolvedModel, resString) ?? fallbackResult;\n } catch {\n return fallbackResult;\n }\n}\n\n/**\n * Cascading resolution of Claude Code session IDs with safe regex sanitization.\n */\nexport function extractSessionId(\n headers: Record<string, string | string[] | undefined> | undefined,\n rawReqBody: unknown,\n): string {\n let candidate = 'default';\n\n if (headers && typeof headers['x-claude-code-session-id'] === 'string') {\n candidate = headers['x-claude-code-session-id'];\n } else {\n const parsedBody = parseSafeJson(rawReqBody);\n if (\n parsedBody &&\n typeof parsedBody === 'object' &&\n 'metadata' in parsedBody &&\n typeof (parsedBody as { metadata: unknown }).metadata === 'object' &&\n (parsedBody as { metadata: Record<string, unknown> }).metadata !== null\n ) {\n const meta = (parsedBody as { metadata: Record<string, unknown> }).metadata;\n if (typeof meta.session_id === 'string' && meta.session_id.trim().length > 0) {\n candidate = meta.session_id;\n } else if (typeof meta.user_id === 'string') {\n const userMeta = parseSafeJson(meta.user_id);\n if (\n userMeta &&\n typeof userMeta === 'object' &&\n 'session_id' in userMeta &&\n typeof (userMeta as { session_id?: unknown }).session_id === 'string'\n ) {\n candidate = (userMeta as { session_id: string }).session_id;\n }\n }\n }\n }\n\n // Sanitize to safe identifier characters\n const sanitized = candidate.trim().replace(/[^a-zA-Z0-9_-]/g, '');\n return sanitized.length > 0 ? sanitized : 'default';\n}\n","/**\n * tokensniff — Telemetry Collector Proxy Daemon (v1 Production)\n *\n * Transparent HTTP reverse proxy sitting between Claude Code and the upstream provider.\n * Streams response bytes with zero latency overhead while capturing token metrics,\n * calculating turn costs, measuring TTFT/TPS velocity, and persisting telemetry state.\n */\n\nimport http from 'node:http';\nimport { loadDailyRollup, renderHeatmapHtml } from '../dashboard/heatmap.js';\nimport { costFor, resolveModelRates } from '../shared/pricing.js';\nimport { buildLatest, type TokenSniffLatest } from '../shared/schema.js';\nimport { loadConfig, type TokenSniffConfig } from './config.js';\nimport { extractSessionId, parseTurn } from './parse.js';\nimport * as store from './store.js';\n\nconst MAX_TRACKED_SESSIONS = 500;\n\n/**\n * LRU-bounded map insertion to prevent unbounded memory growth in long-running daemons.\n */\nfunction setBoundedLRU<K, V>(map: Map<K, V>, key: K, value: V): void {\n if (!map.has(key) && map.size >= MAX_TRACKED_SESSIONS) {\n const oldestKey = map.keys().next().value;\n if (oldestKey !== undefined) {\n map.delete(oldestKey);\n }\n }\n map.set(key, value);\n}\n\n/**\n * Resolves loopback IP candidates, prioritizing IPv4 before falling back to IPv6.\n */\nfunction getUpstreamCandidates(host: string): readonly string[] {\n if (host === 'localhost' || host === '127.0.0.1' || host === '::1') {\n return ['127.0.0.1', '::1'];\n }\n return [host];\n}\n\n/**\n * Starts the tokensniff HTTP reverse proxy server.\n */\nexport function start(userConfig?: Partial<TokenSniffConfig>): http.Server {\n const config: TokenSniffConfig = { ...loadConfig(), ...(userConfig || {}) };\n\n for (const dir of config.statusDirs) {\n store.ensureDir(dir);\n }\n store.pruneDeadSessions(config.statusDirs, config.deadSessionMs);\n\n const contextHistory = new Map<string, number>();\n const turnIndexBySession = new Map<string, number>();\n const sessionSpending: Record<string, number> = store.loadTotals(config.statusDirs);\n\n // Daily spend tracking with local midnight rollover\n let activeLocalDate = store.getLocalDateString();\n let todayTotalSpend = store.loadTodaySpend(config.statusDirs, activeLocalDate);\n\n let _requestSequence = 0;\n const upstreamHosts = getUpstreamCandidates(config.upstreamHost);\n\n const server = http.createServer((clientReq, clientRes) => {\n const reqUrl = clientReq.url || '/';\n const reqMethod = clientReq.method?.toUpperCase() || 'GET';\n\n // Serve pure heatmap dashboard directly on port 4000\n if (reqMethod === 'GET' && (reqUrl === '/' || reqUrl === '/dashboard')) {\n const dailyMap = loadDailyRollup(config.statusDirs);\n const html = renderHeatmapHtml(dailyMap, new Date().getFullYear());\n clientRes.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });\n clientRes.end(html);\n return;\n }\n\n if (reqMethod === 'GET' && reqUrl === '/api/daily') {\n const dailyMap = loadDailyRollup(config.statusDirs);\n clientRes.writeHead(200, { 'content-type': 'application/json; charset=utf-8' });\n clientRes.end(JSON.stringify(dailyMap));\n return;\n }\n\n ++_requestSequence;\n const startTime = Date.now();\n const headers = clientReq.headers || {};\n const isModelExecution = reqUrl.includes('/v1/messages');\n const isBackgroundAgent = headers['x-app'] === 'cli-bg';\n\n const contentLength = Number(headers['content-length']);\n if (Number.isFinite(contentLength) && contentLength > config.maxBodyBytes) {\n clientRes.writeHead(413, { 'content-type': 'text/plain' });\n clientRes.end('Payload Too Large');\n return;\n }\n\n let resolvedSessionId = store.sanitizeSessionId(\n extractSessionId(headers as Record<string, string | string[] | undefined>, ''),\n );\n const requestChunks: Buffer[] = [];\n let receivedBytes = 0;\n let isPayloadExceeded = false;\n let isRequestComplete = false;\n let proxyClientReq: http.ClientRequest | null = null;\n let isResponseCommitted = false;\n\n const dispatch502 = (failureReason: string): void => {\n console.error(\n `[tokensniff] upstream ${config.upstreamHost}:${config.upstreamPort} unavailable: ${failureReason}`,\n );\n try {\n const fallbackStatus = buildLatest({\n session_id: resolvedSessionId,\n model: 'unknown',\n ctx: contextHistory.get(resolvedSessionId) ?? 0,\n error: 502,\n status: 502,\n cost_session: sessionSpending[resolvedSessionId] ?? 0,\n cost_today: todayTotalSpend,\n is_bg: isBackgroundAgent,\n });\n store.saveLatest(config.statusDirs, resolvedSessionId, fallbackStatus);\n } catch {\n /* ignore error */\n }\n\n if (!clientRes.headersSent) {\n clientRes.writeHead(502, { 'content-type': 'text/plain' });\n }\n try {\n clientRes.end('Bad Gateway: Upstream Unreachable');\n } catch {\n /* ignore error */\n }\n };\n\n const handleProxyResponse = (proxyRes: http.IncomingMessage): void => {\n isResponseCommitted = true;\n const responseChunks: Buffer[] = [];\n let responseBytes = 0;\n let isResponseCapped = false;\n let timeToFirstToken = 0;\n\n const contentType = String(proxyRes.headers['content-type'] || '').toLowerCase();\n const isStreaming = contentType.includes('text/event-stream');\n\n clientRes.writeHead(proxyRes.statusCode || 500, proxyRes.headers);\n\n proxyRes.on('data', (chunk: Buffer) => {\n if (isStreaming && timeToFirstToken === 0) {\n const text = chunk.toString('utf8');\n if (\n text.includes('content_block_start') ||\n text.includes('content_block_delta') ||\n text.includes('\"delta\"')\n ) {\n timeToFirstToken = Date.now();\n }\n }\n\n if (!isResponseCapped) {\n if (responseBytes + chunk.length <= config.maxBodyBytes) {\n responseChunks.push(chunk);\n responseBytes += chunk.length;\n } else {\n isResponseCapped = true;\n }\n }\n clientRes.write(chunk);\n });\n\n proxyRes.on('aborted', () => {\n try {\n clientRes.destroy();\n } catch {\n /* ignore error */\n }\n });\n\n proxyRes.on('error', () => {\n try {\n if (!clientRes.writableEnded) clientRes.end();\n } catch {\n /* ignore error */\n }\n });\n\n proxyRes.on('end', async () => {\n const endTime = Date.now();\n try {\n if (!clientRes.writableEnded) clientRes.end();\n } catch {\n /* ignore error */\n }\n\n if (!isModelExecution) return;\n\n const rawRequestBody = Buffer.concat(requestChunks);\n try {\n resolvedSessionId = store.sanitizeSessionId(\n extractSessionId(\n headers as Record<string, string | string[] | undefined>,\n rawRequestBody.toString('utf8'),\n ),\n );\n } catch {\n /* retain header-derived identifier */\n }\n const rawResponseBody = Buffer.concat(responseChunks);\n\n try {\n const turn = parseTurn({\n reqBody: rawRequestBody.toString('utf8'),\n resBody: rawResponseBody.toString('utf8'),\n contentType: String(proxyRes.headers['content-type'] || ''),\n statusCode: proxyRes.statusCode || 200,\n });\n\n // Dynamically resolve rates and context capacity on-demand\n const rates = await resolveModelRates(turn.model);\n\n const currentContextTokens =\n turn.input > turn.cacheRead\n ? turn.input + turn.cacheCreate\n : turn.input + turn.cacheRead + turn.cacheCreate;\n\n const previousContextTokens = contextHistory.has(resolvedSessionId)\n ? (contextHistory.get(resolvedSessionId) ?? currentContextTokens)\n : currentContextTokens;\n\n const durationSeconds = (endTime - startTime) / 1000;\n const streamGenerationMs =\n timeToFirstToken && isStreaming ? endTime - timeToFirstToken : 0;\n\n // Pure generation velocity: excludes initial network/server wait time (TTFT)\n let tokensPerSecond = 0;\n if (turn.output > 0) {\n if (isStreaming && streamGenerationMs >= 20) {\n tokensPerSecond = Math.round((turn.output / streamGenerationMs) * 1000);\n } else if (endTime > startTime) {\n // Fallback for non-streaming or coalesced socket payloads: use active turn duration\n const totalTurnMs = Math.max(20, endTime - startTime);\n tokensPerSecond = Math.round((turn.output / totalTurnMs) * 1000);\n }\n }\n\n const ttftMs =\n timeToFirstToken && isStreaming ? timeToFirstToken - startTime : endTime - startTime;\n\n const uncachedInput =\n turn.input > turn.cacheRead ? Math.max(0, turn.input - turn.cacheRead) : turn.input;\n\n const turnCostUsd = costFor(rates, {\n input: uncachedInput,\n output: turn.output,\n cacheRead: turn.cacheRead,\n cacheCreate: turn.cacheCreate,\n });\n\n // Monotonic session turn tracking\n const priorTurns = turnIndexBySession.get(resolvedSessionId) || 0;\n const currentTurnIndex = priorTurns + 1;\n setBoundedLRU(turnIndexBySession, resolvedSessionId, currentTurnIndex);\n\n // Session spend accumulation\n const accumulatedSessionCost = (sessionSpending[resolvedSessionId] || 0) + turnCostUsd;\n sessionSpending[resolvedSessionId] = accumulatedSessionCost;\n store.saveTotals(config.statusDirs, sessionSpending, config.maxSessions);\n\n // Local calendar day rollover detection\n const nowLocalDate = store.getLocalDateString();\n if (nowLocalDate !== activeLocalDate) {\n activeLocalDate = nowLocalDate;\n todayTotalSpend = 0;\n }\n todayTotalSpend += turnCostUsd;\n\n const isMicroLabel =\n (turn.kind === 'label' ||\n (turn.tools.length === 0 && turn.output > 0 && turn.output < config.labelMaxOut)) &&\n currentContextTokens < config.labelMaxIn;\n\n if (!isMicroLabel) {\n setBoundedLRU(contextHistory, resolvedSessionId, currentContextTokens);\n }\n\n const displayCtxTokens =\n isMicroLabel && contextHistory.has(resolvedSessionId)\n ? (contextHistory.get(resolvedSessionId) ?? currentContextTokens)\n : currentContextTokens;\n\n const statusSnapshot: TokenSniffLatest = buildLatest({\n session_id: resolvedSessionId,\n turn_index: currentTurnIndex,\n model: turn.model,\n is_bg: isBackgroundAgent,\n ctx: displayCtxTokens,\n window: rates.window,\n pct: Number(((displayCtxTokens / rates.window) * 100).toFixed(1)),\n cache_read: turn.cacheRead,\n cache_create: turn.cacheCreate,\n delta_in: isMicroLabel ? 0 : currentContextTokens - previousContextTokens,\n in_tokens: uncachedInput,\n out_tokens: turn.output,\n tool: turn.tools[0]?.name ?? null,\n tool_tk: turn.tools[0]?.argTk ?? 0,\n tools_summary: turn.toolsSummary,\n thinking: turn.thinking,\n thinking_tokens: turn.thinkingTokens,\n stop_reason: turn.stopReason,\n is_label: isMicroLabel,\n label: isMicroLabel ? turn.label : '',\n ttft_ms: ttftMs,\n tps: tokensPerSecond,\n dur_s: Number(durationSeconds.toFixed(1)),\n cost_turn: turnCostUsd,\n cost_session: accumulatedSessionCost,\n cost_today: todayTotalSpend,\n error: null,\n status: proxyRes.statusCode || 200,\n });\n\n store.saveLatest(config.statusDirs, resolvedSessionId, statusSnapshot);\n\n // Append to history.ndjson stream for heatmap & daily ledger\n if (!isMicroLabel && (turn.input > 0 || turn.output > 0)) {\n store.appendHistory(config.statusDirs, {\n date: nowLocalDate,\n sessionId: resolvedSessionId,\n turn: currentTurnIndex,\n model: turn.model,\n tokens: currentContextTokens + turn.output,\n cost: turnCostUsd,\n cacheRead: turn.cacheRead,\n thinkingTokens: turn.thinkingTokens,\n });\n }\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n console.error(`[tokensniff] turn parse/record failure: ${msg}`);\n }\n });\n };\n\n const tryForwardToHost = (hostIndex: number): void => {\n if (hostIndex >= upstreamHosts.length) {\n dispatch502('all loopback candidates exhausted');\n return;\n }\n\n const hostCandidate = upstreamHosts[hostIndex] ?? '127.0.0.1';\n const forwardHeaders = { ...headers };\n forwardHeaders.host = `${hostCandidate}:${config.upstreamPort}`;\n\n const proxyRequestOptions: http.RequestOptions = {\n hostname: hostCandidate,\n port: config.upstreamPort,\n path: clientReq.url,\n method: clientReq.method,\n headers: forwardHeaders,\n timeout: config.upstreamTimeoutMs,\n };\n\n proxyClientReq = http.request(proxyRequestOptions, (proxyRes) => {\n handleProxyResponse(proxyRes);\n });\n\n proxyClientReq.on('timeout', () => {\n try {\n proxyClientReq?.destroy();\n } catch {\n /* ignore error */\n }\n if (!isResponseCommitted) {\n tryForwardToHost(hostIndex + 1);\n }\n });\n\n proxyClientReq.on('error', () => {\n if (!isResponseCommitted) {\n tryForwardToHost(hostIndex + 1);\n }\n });\n\n for (const chunk of requestChunks) {\n proxyClientReq.write(chunk);\n }\n if (isRequestComplete) {\n proxyClientReq.end();\n }\n };\n\n clientReq.on('data', (chunk: Buffer) => {\n receivedBytes += chunk.length;\n if (receivedBytes > config.maxBodyBytes) {\n if (!isPayloadExceeded) {\n isPayloadExceeded = true;\n clientRes.writeHead(413, { 'content-type': 'text/plain' });\n clientRes.end('Payload Too Large');\n }\n return;\n }\n requestChunks.push(chunk);\n if (proxyClientReq) {\n proxyClientReq.write(chunk);\n }\n });\n\n clientReq.on('end', () => {\n isRequestComplete = true;\n if (proxyClientReq) {\n proxyClientReq.end();\n } else if (!isPayloadExceeded) {\n tryForwardToHost(0);\n }\n });\n\n clientRes.on('close', () => {\n if (!clientRes.writableEnded && proxyClientReq) {\n try {\n proxyClientReq.destroy();\n } catch {\n /* ignore error */\n }\n }\n });\n });\n\n server.listen(config.listenPort, config.listenHost, () => {\n console.log(\n `[tokensniff] proxy listening on http://${config.listenHost}:${config.listenPort} -> ${config.upstreamHost}:${config.upstreamPort}`,\n );\n });\n\n return server;\n}\n"]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tokensniff — Dynamic Responsive Statusline Formatter (v1 Production)
|
|
3
|
+
*
|
|
4
|
+
* Sequence: Turn -> Model -> [bg] -> Context [Cache %] -> Tool/Input/Output -> TTFT/Speed -> Cost.
|
|
5
|
+
* Zero emojis. Explicit "tok" token units.
|
|
6
|
+
* Responsive layout: dynamically adapts to terminal width (process.stdout.columns)
|
|
7
|
+
* ensuring the line NEVER wraps and the cost is NEVER cut off.
|
|
8
|
+
*/
|
|
9
|
+
import { type TokenSniffLatest } from '../shared/schema.js';
|
|
10
|
+
export interface FormatOptions {
|
|
11
|
+
readonly warnPct?: number;
|
|
12
|
+
readonly critPct?: number;
|
|
13
|
+
readonly color?: boolean;
|
|
14
|
+
readonly staleAfterS?: number;
|
|
15
|
+
readonly now?: number;
|
|
16
|
+
readonly modelFallback?: string;
|
|
17
|
+
readonly columns?: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Formats raw token counts into clean, human-readable numbers without redundant trailing zeros.
|
|
21
|
+
* E.g.: 1500 -> "1.5k", 1000000 -> "1M", 450 -> "450".
|
|
22
|
+
*/
|
|
23
|
+
export declare function formatTokenCount(value: number | null | undefined): string;
|
|
24
|
+
/**
|
|
25
|
+
* Formats tool execution summary with explicit "tok" unit.
|
|
26
|
+
*/
|
|
27
|
+
export declare function formatToolSegment(snapshot: Partial<TokenSniffLatest>, isCompact?: boolean): string;
|
|
28
|
+
/**
|
|
29
|
+
* Formats telemetry snapshot into a single-line status bar that dynamically adapts
|
|
30
|
+
* to terminal columns, preventing line wrapping and preventing cost truncation.
|
|
31
|
+
*/
|
|
32
|
+
export declare function formatStatus(snapshot: Partial<TokenSniffLatest> | null, options?: FormatOptions): string;
|
|
33
|
+
//# sourceMappingURL=format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/renderer/format.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE/E,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAcD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAYzE;AAYD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,OAAO,CAAC,gBAAgB,CAAC,EAAE,SAAS,UAAQ,GAAG,MAAM,CAyChG;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,IAAI,EAC1C,OAAO,GAAE,aAAkB,GAC1B,MAAM,CAoKR"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tokensniff — Statusline CLI Renderer Entrypoint (v1 Production)
|
|
3
|
+
*
|
|
4
|
+
* Executed by Claude Code statusLine hook. Consumes piped stdin JSON from Claude,
|
|
5
|
+
* locates the session-specific latest-<session_id>.json snapshot, and outputs
|
|
6
|
+
* the scannable single-line status bar to stdout.
|
|
7
|
+
*
|
|
8
|
+
* GUARANTEE: Never throws or exits non-zero, preserving terminal UI integrity.
|
|
9
|
+
*/
|
|
10
|
+
import { type TokenSniffLatest } from '../shared/schema.js';
|
|
11
|
+
export interface StdinPayload {
|
|
12
|
+
readonly session_id?: string;
|
|
13
|
+
readonly sessionId?: string;
|
|
14
|
+
readonly model?: string | {
|
|
15
|
+
readonly id?: string;
|
|
16
|
+
readonly display_name?: string;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export interface StatusRunnerOptions {
|
|
20
|
+
readonly statusDirs?: readonly string[];
|
|
21
|
+
readonly staleAfterS?: number;
|
|
22
|
+
readonly color?: boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Consumes Claude Code JSON piped via stdin with a strict non-blocking timeout guard.
|
|
26
|
+
* Unrefs the timer so the process never hangs if stdin stays open indefinitely.
|
|
27
|
+
*/
|
|
28
|
+
export declare function readStdin(timeoutMs?: number): Promise<StdinPayload>;
|
|
29
|
+
/**
|
|
30
|
+
* Resolves the latest telemetry snapshot across global status directories.
|
|
31
|
+
* Prioritizes session-specific state, falling back to universal latest.json.
|
|
32
|
+
*/
|
|
33
|
+
export declare function pickLatest(statusDirs: readonly string[], sessionId?: string): TokenSniffLatest | null;
|
|
34
|
+
/**
|
|
35
|
+
* Extracts the model name string from Claude Code's stdin payload.
|
|
36
|
+
*/
|
|
37
|
+
export declare function extractModel(input: StdinPayload): string | undefined;
|
|
38
|
+
/**
|
|
39
|
+
* Main execution hook for tokensniff-status.
|
|
40
|
+
*/
|
|
41
|
+
export declare function main(opts?: StatusRunnerOptions): Promise<void>;
|
|
42
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/renderer/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,EAAsB,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAGhF,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG;QAAE,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACpF;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,SAAS,SAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CA6ChE;AAED;;;GAGG;AACH,wBAAgB,UAAU,CACxB,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,SAAS,CAAC,EAAE,MAAM,GACjB,gBAAgB,GAAG,IAAI,CAyBzB;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,GAAG,SAAS,CAMpE;AAED;;GAEG;AACH,wBAAsB,IAAI,CAAC,IAAI,GAAE,mBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiCxE"}
|
package/dist/schema.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// src/shared/schema.ts
|
|
2
|
+
var SCHEMA_VERSION = 1;
|
|
3
|
+
function toFiniteNumber(value, fallback = 0) {
|
|
4
|
+
const num = Number(value);
|
|
5
|
+
return Number.isFinite(num) ? num : fallback;
|
|
6
|
+
}
|
|
7
|
+
function isTokenSniffLatest(value) {
|
|
8
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
const candidate = value;
|
|
12
|
+
return candidate.v === SCHEMA_VERSION && typeof candidate.session_id === "string" && typeof candidate.turn_index === "number" && typeof candidate.ts === "number" && typeof candidate.model === "string" && typeof candidate.ctx === "number" && typeof candidate.window === "number" && typeof candidate.pct === "number";
|
|
13
|
+
}
|
|
14
|
+
function buildLatest(partial) {
|
|
15
|
+
return {
|
|
16
|
+
v: SCHEMA_VERSION,
|
|
17
|
+
session_id: partial.session_id,
|
|
18
|
+
turn_index: Math.max(1, Math.floor(toFiniteNumber(partial.turn_index, 1))),
|
|
19
|
+
ts: toFiniteNumber(partial.ts, Date.now()),
|
|
20
|
+
model: typeof partial.model === "string" && partial.model.length > 0 ? partial.model : "unknown",
|
|
21
|
+
is_bg: Boolean(partial.is_bg),
|
|
22
|
+
ctx: Math.max(0, toFiniteNumber(partial.ctx, 0)),
|
|
23
|
+
window: Math.max(1, toFiniteNumber(partial.window, 1e6)),
|
|
24
|
+
pct: Math.max(0, Math.min(100, toFiniteNumber(partial.pct, 0))),
|
|
25
|
+
cache_read: Math.max(0, toFiniteNumber(partial.cache_read, 0)),
|
|
26
|
+
cache_create: Math.max(0, toFiniteNumber(partial.cache_create, 0)),
|
|
27
|
+
delta_in: toFiniteNumber(partial.delta_in, 0),
|
|
28
|
+
in_tokens: Math.max(0, toFiniteNumber(partial.in_tokens, 0)),
|
|
29
|
+
out_tokens: Math.max(0, toFiniteNumber(partial.out_tokens, 0)),
|
|
30
|
+
thinking: Boolean(partial.thinking),
|
|
31
|
+
thinking_tokens: Math.max(0, toFiniteNumber(partial.thinking_tokens, 0)),
|
|
32
|
+
tool: typeof partial.tool === "string" && partial.tool.length > 0 ? partial.tool : null,
|
|
33
|
+
tool_tk: Math.max(0, toFiniteNumber(partial.tool_tk, 0)),
|
|
34
|
+
tools_summary: Array.isArray(partial.tools_summary) ? partial.tools_summary : [],
|
|
35
|
+
stop_reason: typeof partial.stop_reason === "string" ? partial.stop_reason : "",
|
|
36
|
+
is_label: Boolean(partial.is_label),
|
|
37
|
+
label: typeof partial.label === "string" ? partial.label : "",
|
|
38
|
+
ttft_ms: Math.max(0, toFiniteNumber(partial.ttft_ms, 0)),
|
|
39
|
+
tps: Math.max(0, toFiniteNumber(partial.tps, 0)),
|
|
40
|
+
dur_s: Math.max(0, toFiniteNumber(partial.dur_s, 0)),
|
|
41
|
+
cost_turn: Math.max(0, toFiniteNumber(partial.cost_turn, 0)),
|
|
42
|
+
cost_session: Math.max(0, toFiniteNumber(partial.cost_session, 0)),
|
|
43
|
+
cost_today: Math.max(0, toFiniteNumber(partial.cost_today, 0)),
|
|
44
|
+
error: typeof partial.error === "number" && Number.isFinite(partial.error) ? partial.error : null,
|
|
45
|
+
status: Math.max(0, toFiniteNumber(partial.status, 0)),
|
|
46
|
+
quota_pct: typeof partial.quota_pct === "number" && Number.isFinite(partial.quota_pct) ? Math.max(0, Math.min(100, partial.quota_pct)) : null,
|
|
47
|
+
quota_reset: typeof partial.quota_reset === "string" ? partial.quota_reset : null,
|
|
48
|
+
tier: typeof partial.tier === "string" ? partial.tier : null
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function extractCleanLabel(text) {
|
|
52
|
+
if (typeof text !== "string" || text.length === 0) {
|
|
53
|
+
return "";
|
|
54
|
+
}
|
|
55
|
+
const trimmed = text.trim();
|
|
56
|
+
if (trimmed.startsWith("{")) {
|
|
57
|
+
if (trimmed.endsWith("}")) {
|
|
58
|
+
try {
|
|
59
|
+
const parsed = JSON.parse(trimmed);
|
|
60
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
61
|
+
const record = parsed;
|
|
62
|
+
if (record.state && record.detail) {
|
|
63
|
+
return `${String(record.state).trim()}: ${String(record.detail).trim()}`;
|
|
64
|
+
}
|
|
65
|
+
const candidate = record.title ?? record.summary ?? record.label ?? record.message ?? record.detail ?? record.state;
|
|
66
|
+
if (typeof candidate === "string" && candidate.trim().length > 0) {
|
|
67
|
+
return candidate.trim();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} catch {
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const stateMatch = trimmed.match(/"state"\s*:\s*"([^"\\]*)(?:"|$)/);
|
|
74
|
+
const detailMatch = trimmed.match(/"detail"\s*:\s*"([^"\\]*)(?:"|$)/);
|
|
75
|
+
if (stateMatch?.[1] && detailMatch?.[1]) {
|
|
76
|
+
return `${stateMatch[1].trim()}: ${detailMatch[1].trim()}`;
|
|
77
|
+
}
|
|
78
|
+
const singleMatch = trimmed.match(
|
|
79
|
+
/"(?:title|summary|label|message|detail|state)"\s*:\s*"([^"\\]*)(?:"|$)/
|
|
80
|
+
);
|
|
81
|
+
if (singleMatch?.[1]) {
|
|
82
|
+
return singleMatch[1].trim();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return trimmed.replace(/^["']+|["']+$/g, "").trim();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export { SCHEMA_VERSION, buildLatest, extractCleanLabel, isTokenSniffLatest };
|
|
89
|
+
//# sourceMappingURL=schema.js.map
|
|
90
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/schema.ts"],"names":[],"mappings":"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","file":"schema.js","sourcesContent":["/**\n * tokensniff — Canonical Domain Telemetry Schema (v1 Production)\n *\n * Single source of truth for telemetry interfaces, runtime validation guards,\n * safe default factories, and agent state text parsing.\n */\n\nexport const SCHEMA_VERSION = 1 as const;\n\n/**\n * Summary representation of an aggregated tool call across a single turn.\n */\nexport interface ToolSummaryItem {\n readonly name: string;\n readonly count: number;\n readonly arg_tk: number;\n}\n\n/**\n * Granular record of an individual tool call during an execution turn.\n */\nexport interface ToolCallItem {\n readonly name: string;\n readonly argTk: number;\n}\n\n/**\n * Classification of the primary driver of a model turn.\n */\nexport type TurnKind = 'text' | 'tool' | 'thinking' | 'label' | 'error' | 'unknown';\n\n/**\n * Snapshot of provider upstream rate limits and token bucket quotas.\n */\nexport interface QuotaSnapshot {\n readonly remainingPct: number | null;\n readonly remainingFraction: number | null;\n readonly resetTime: string | null;\n readonly tier: string | null;\n readonly isRateLimited: boolean;\n}\n\n/**\n * Raw telemetry extracted directly from a single network interaction.\n */\nexport interface TurnMetrics {\n readonly model: string;\n readonly kind: TurnKind;\n readonly input: number;\n readonly output: number;\n readonly cacheRead: number;\n readonly cacheCreate: number;\n readonly tools: readonly ToolCallItem[];\n readonly toolsSummary: readonly ToolSummaryItem[];\n readonly thinking: boolean;\n readonly thinkingTokens: number;\n readonly stopReason: string;\n readonly label: string;\n}\n\n/**\n * Fully hydrated telemetry state persisted to ~/.tokensniff/status/<session_id>.json.\n * Consumed directly by terminal statusline renderers and CLI dashboards.\n */\nexport interface TokenSniffLatest {\n readonly v: typeof SCHEMA_VERSION;\n readonly session_id: string;\n readonly turn_index: number;\n readonly ts: number;\n readonly model: string;\n readonly is_bg: boolean;\n readonly ctx: number;\n readonly window: number;\n readonly pct: number;\n readonly cache_read: number;\n readonly cache_create: number;\n readonly delta_in: number;\n readonly in_tokens: number;\n readonly out_tokens: number;\n readonly thinking: boolean;\n readonly thinking_tokens: number;\n readonly tool: string | null;\n readonly tool_tk: number;\n readonly tools_summary: readonly ToolSummaryItem[];\n readonly stop_reason: string;\n readonly is_label: boolean;\n readonly label: string;\n readonly ttft_ms: number;\n readonly tps: number;\n readonly dur_s: number;\n readonly cost_turn: number;\n readonly cost_session: number;\n readonly cost_today: number;\n readonly error: number | null;\n readonly status: number;\n readonly quota_pct: number | null;\n readonly quota_reset: string | null;\n readonly tier: string | null;\n}\n\n/**\n * Compact append-only historical turn record for analytics and sparklines.\n */\nexport interface HistoryEntry {\n readonly d: string;\n readonly ts: number;\n readonly s: string;\n readonly t: number;\n readonly m: string;\n readonly tk: number;\n readonly c: number;\n readonly cr: number;\n readonly th: number;\n}\n\n/**\n * Defensive number sanitizer ensuring NaN and Infinity never corrupt telemetry output.\n */\nfunction toFiniteNumber(value: unknown, fallback = 0): number {\n const num = Number(value);\n return Number.isFinite(num) ? num : fallback;\n}\n\n/**\n * Runtime Type Guard to safely validate deserialized disk telemetry without unsafe casts.\n */\nexport function isTokenSniffLatest(value: unknown): value is TokenSniffLatest {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return false;\n }\n\n const candidate = value as Record<string, unknown>;\n\n return (\n candidate.v === SCHEMA_VERSION &&\n typeof candidate.session_id === 'string' &&\n typeof candidate.turn_index === 'number' &&\n typeof candidate.ts === 'number' &&\n typeof candidate.model === 'string' &&\n typeof candidate.ctx === 'number' &&\n typeof candidate.window === 'number' &&\n typeof candidate.pct === 'number'\n );\n}\n\n/**\n * Factory creating fully hydrated TokenSniffLatest records with strictly sanitized defaults.\n */\nexport function buildLatest(\n partial: Partial<TokenSniffLatest> & { readonly session_id: string },\n): TokenSniffLatest {\n return {\n v: SCHEMA_VERSION,\n session_id: partial.session_id,\n turn_index: Math.max(1, Math.floor(toFiniteNumber(partial.turn_index, 1))),\n ts: toFiniteNumber(partial.ts, Date.now()),\n model:\n typeof partial.model === 'string' && partial.model.length > 0 ? partial.model : 'unknown',\n is_bg: Boolean(partial.is_bg),\n ctx: Math.max(0, toFiniteNumber(partial.ctx, 0)),\n window: Math.max(1, toFiniteNumber(partial.window, 1_000_000)),\n pct: Math.max(0, Math.min(100, toFiniteNumber(partial.pct, 0))),\n cache_read: Math.max(0, toFiniteNumber(partial.cache_read, 0)),\n cache_create: Math.max(0, toFiniteNumber(partial.cache_create, 0)),\n delta_in: toFiniteNumber(partial.delta_in, 0),\n in_tokens: Math.max(0, toFiniteNumber(partial.in_tokens, 0)),\n out_tokens: Math.max(0, toFiniteNumber(partial.out_tokens, 0)),\n thinking: Boolean(partial.thinking),\n thinking_tokens: Math.max(0, toFiniteNumber(partial.thinking_tokens, 0)),\n tool: typeof partial.tool === 'string' && partial.tool.length > 0 ? partial.tool : null,\n tool_tk: Math.max(0, toFiniteNumber(partial.tool_tk, 0)),\n tools_summary: Array.isArray(partial.tools_summary) ? partial.tools_summary : [],\n stop_reason: typeof partial.stop_reason === 'string' ? partial.stop_reason : '',\n is_label: Boolean(partial.is_label),\n label: typeof partial.label === 'string' ? partial.label : '',\n ttft_ms: Math.max(0, toFiniteNumber(partial.ttft_ms, 0)),\n tps: Math.max(0, toFiniteNumber(partial.tps, 0)),\n dur_s: Math.max(0, toFiniteNumber(partial.dur_s, 0)),\n cost_turn: Math.max(0, toFiniteNumber(partial.cost_turn, 0)),\n cost_session: Math.max(0, toFiniteNumber(partial.cost_session, 0)),\n cost_today: Math.max(0, toFiniteNumber(partial.cost_today, 0)),\n error:\n typeof partial.error === 'number' && Number.isFinite(partial.error) ? partial.error : null,\n status: Math.max(0, toFiniteNumber(partial.status, 0)),\n quota_pct:\n typeof partial.quota_pct === 'number' && Number.isFinite(partial.quota_pct)\n ? Math.max(0, Math.min(100, partial.quota_pct))\n : null,\n quota_reset: typeof partial.quota_reset === 'string' ? partial.quota_reset : null,\n tier: typeof partial.tier === 'string' ? partial.tier : null,\n };\n}\n\n/**\n * Extracts a concise, human-readable 1-line label from AI agent task descriptions.\n * Handles compound state/detail JSON structures, stringified JSON, and fallback text.\n */\nexport function extractCleanLabel(text: unknown): string {\n if (typeof text !== 'string' || text.length === 0) {\n return '';\n }\n\n const trimmed = text.trim();\n\n if (trimmed.startsWith('{')) {\n if (trimmed.endsWith('}')) {\n try {\n const parsed: unknown = JSON.parse(trimmed);\n if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {\n const record = parsed as Record<string, unknown>;\n\n // High priority: compound state + detail (e.g. \"Running tests: auth.test.ts\")\n if (record.state && record.detail) {\n return `${String(record.state).trim()}: ${String(record.detail).trim()}`;\n }\n\n // Secondary priority: specific action titles or summaries\n const candidate =\n record.title ??\n record.summary ??\n record.label ??\n record.message ??\n record.detail ??\n record.state;\n\n if (typeof candidate === 'string' && candidate.trim().length > 0) {\n return candidate.trim();\n }\n }\n } catch {\n // Fall through to regex extraction for malformed JSON\n }\n }\n\n // Regex fallback for truncated or malformed JSON streams\n const stateMatch = trimmed.match(/\"state\"\\s*:\\s*\"([^\"\\\\]*)(?:\"|$)/);\n const detailMatch = trimmed.match(/\"detail\"\\s*:\\s*\"([^\"\\\\]*)(?:\"|$)/);\n\n if (stateMatch?.[1] && detailMatch?.[1]) {\n return `${stateMatch[1].trim()}: ${detailMatch[1].trim()}`;\n }\n\n const singleMatch = trimmed.match(\n /\"(?:title|summary|label|message|detail|state)\"\\s*:\\s*\"([^\"\\\\]*)(?:\"|$)/,\n );\n if (singleMatch?.[1]) {\n return singleMatch[1].trim();\n }\n }\n\n // Strip wrapping quotation marks and extra whitespace\n return trimmed.replace(/^[\"']+|[\"']+$/g, '').trim();\n}\n"]}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tokensniff — Dynamic Universal Pricing & Context Capacity Engine (v1 Production)
|
|
3
|
+
*
|
|
4
|
+
* Provider-agnostic rate resolver mapping 1:1 to OpenRouter's live catalog schema.
|
|
5
|
+
* Dynamically resolves token rates (prompt, completion, cache-read, cache-write)
|
|
6
|
+
* and maximum context length (context_length) without vendor hardcoding or dummy fallbacks.
|
|
7
|
+
*/
|
|
8
|
+
export interface ModelRates {
|
|
9
|
+
readonly in: number;
|
|
10
|
+
readonly out: number;
|
|
11
|
+
readonly cached: number;
|
|
12
|
+
readonly cacheWrite: number;
|
|
13
|
+
readonly window: number;
|
|
14
|
+
}
|
|
15
|
+
export interface ModelPricingOverrides {
|
|
16
|
+
readonly [model: string]: Partial<ModelRates>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Normalizes vendor model names into canonical matching slugs.
|
|
20
|
+
* Provider-agnostic: does not hardcode vendor names.
|
|
21
|
+
*
|
|
22
|
+
* Normalization rules:
|
|
23
|
+
* 1. Strips provider namespaces (e.g. "anthropic/...", "google/...", "~anthropic/...")
|
|
24
|
+
* 2. Strips variant flags (e.g. ":free", ":batch")
|
|
25
|
+
* 3. Strips runtime proxy & mode suffixes (e.g. "-tiered", "-thinking", "-preview", "-low", "-high", "-latest")
|
|
26
|
+
* 4. Strips calendar release dates (e.g. "-20241022", "-2024-08-06", "-2407")
|
|
27
|
+
* 5. Standardizes numeric version separators (e.g. "3-5" -> "3.5", "4-6" -> "4.6")
|
|
28
|
+
*
|
|
29
|
+
* Examples:
|
|
30
|
+
* - "google/gemini-3.8-flash-tiered" -> "gemini-3.8-flash"
|
|
31
|
+
* - "anthropic/claude-3-5-sonnet-20241022" -> "claude-3.5-sonnet"
|
|
32
|
+
* - "claude-opus-4-6-thinking" -> "claude-opus-4.6"
|
|
33
|
+
* - "meta-llama/llama-3.3-70b-instruct:free" -> "llama-3.3-70b-instruct"
|
|
34
|
+
*/
|
|
35
|
+
export declare function normalizeModelName(raw: unknown): string;
|
|
36
|
+
/**
|
|
37
|
+
* Fetches and indexes OpenRouter's live models catalog.
|
|
38
|
+
* Uses native Node 24 AbortSignal.timeout to enforce a strict network boundary.
|
|
39
|
+
*/
|
|
40
|
+
export declare function syncOpenRouterCatalog(): Promise<boolean>;
|
|
41
|
+
/**
|
|
42
|
+
* Resolves rates and maximum context length dynamically for the active turn model.
|
|
43
|
+
* Seamlessly handles mid-session model switches and user custom overrides.
|
|
44
|
+
*/
|
|
45
|
+
export declare function resolveModelRates(model: string, overrides?: ModelPricingOverrides): Promise<ModelRates>;
|
|
46
|
+
/**
|
|
47
|
+
* Synchronous cache lookup for instantaneous read during statusline rendering.
|
|
48
|
+
*/
|
|
49
|
+
export declare function ratesFor(model: string): ModelRates;
|
|
50
|
+
/**
|
|
51
|
+
* Calculates exact monetary cost in USD for a turn interaction.
|
|
52
|
+
* Supports prompt, completion, cache-read, and cache-write token accounting.
|
|
53
|
+
*/
|
|
54
|
+
export declare function costFor(rates: ModelRates, usage: {
|
|
55
|
+
readonly input?: number;
|
|
56
|
+
readonly output?: number;
|
|
57
|
+
readonly cacheRead?: number;
|
|
58
|
+
readonly cacheCreate?: number;
|
|
59
|
+
}): number;
|
|
60
|
+
//# sourceMappingURL=pricing.d.ts.map
|