tokmon 0.22.7 → 0.23.1

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.
Files changed (57) hide show
  1. package/README.md +1 -0
  2. package/dist/{bootstrap-ink-OHPQZKGB.js → bootstrap-ink-UD3KDTOB.js} +1113 -818
  3. package/dist/{chunk-EMQL5P5J.js → chunk-AON6GE7D.js} +368 -280
  4. package/dist/{chunk-TNIADJ3W.js → chunk-DJPUYMZM.js} +49 -16
  5. package/dist/chunk-HXDAOOSI.js +154 -0
  6. package/dist/{chunk-I4USANOF.js → chunk-PGQK7MOE.js} +879 -391
  7. package/dist/chunk-RM3ZE44Z.js +193 -0
  8. package/dist/cli.js +93 -54
  9. package/dist/{config-WLRQHLVJ.js → config-5CNJN3P7.js} +1 -1
  10. package/dist/daemon-QGCI46US.js +193 -0
  11. package/dist/daemon-handle-4F4TXGXJ.js +163 -0
  12. package/dist/server-4CDZMLH6.js +10 -0
  13. package/dist/web/assets/Area-DGo9K-U3.js +2 -0
  14. package/dist/web/assets/analytics-BOhbhV5Z.js +2 -0
  15. package/dist/web/assets/breakdown-CO04nSN-.js +4 -0
  16. package/dist/web/assets/chart-bNxdfXxA.js +69 -0
  17. package/dist/web/assets/explore-sZnmjzAj.js +22 -0
  18. package/dist/web/assets/index-DSj-Bxov.css +1 -0
  19. package/dist/web/assets/index-DnDr1VUr.js +83 -0
  20. package/dist/web/assets/models-Cl6D8VVF.js +2 -0
  21. package/dist/web/assets/overview-C18nvaAK.js +2 -0
  22. package/dist/web/assets/panel-DTjMnHaz.js +1 -0
  23. package/dist/web/assets/primitives-Dj_9YxTF.js +1 -0
  24. package/dist/web/assets/settings-sheet-0BGb5Ahv.js +1 -0
  25. package/dist/web/assets/share-sheet-BHtt-OmT.js +2 -0
  26. package/dist/web/assets/timeline-DmzB8Z3W.js +1 -0
  27. package/dist/web/assets/use-dialog-trap-DeXFtxx8.js +1 -0
  28. package/dist/web/index.html +3 -2
  29. package/package.json +9 -5
  30. package/dist/daemon-IP3UCTPB.js +0 -213
  31. package/dist/daemon-handle-HLSKLMWU.js +0 -142
  32. package/dist/server-GA2FKYDQ.js +0 -9
  33. package/dist/web/assets/breakdown-DU2j1sQ8.js +0 -4
  34. package/dist/web/assets/index-BCaPGYQA.css +0 -1
  35. package/dist/web/assets/index-ygZyXDLT.js +0 -173
  36. package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
  37. package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
  38. package/dist/web/assets/jetbrains-mono-cyrillic-500-normal-DJqRU3vO.woff +0 -0
  39. package/dist/web/assets/jetbrains-mono-cyrillic-500-normal-DmUKJPL_.woff2 +0 -0
  40. package/dist/web/assets/jetbrains-mono-cyrillic-700-normal-BWTpRfYl.woff2 +0 -0
  41. package/dist/web/assets/jetbrains-mono-cyrillic-700-normal-CEoEElIJ.woff +0 -0
  42. package/dist/web/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
  43. package/dist/web/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
  44. package/dist/web/assets/jetbrains-mono-greek-500-normal-D7SFKleX.woff +0 -0
  45. package/dist/web/assets/jetbrains-mono-greek-500-normal-JpySY46c.woff2 +0 -0
  46. package/dist/web/assets/jetbrains-mono-greek-700-normal-C6CZE3T8.woff2 +0 -0
  47. package/dist/web/assets/jetbrains-mono-greek-700-normal-DEigVDxa.woff +0 -0
  48. package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
  49. package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
  50. package/dist/web/assets/jetbrains-mono-latin-ext-500-normal-Cut-4mMH.woff2 +0 -0
  51. package/dist/web/assets/jetbrains-mono-latin-ext-500-normal-ckzbgY84.woff +0 -0
  52. package/dist/web/assets/jetbrains-mono-latin-ext-700-normal-CZipNAKV.woff2 +0 -0
  53. package/dist/web/assets/jetbrains-mono-latin-ext-700-normal-CxPITLHs.woff +0 -0
  54. package/dist/web/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
  55. package/dist/web/assets/jetbrains-mono-vietnamese-500-normal-DNRqzVM1.woff +0 -0
  56. package/dist/web/assets/jetbrains-mono-vietnamese-700-normal-BDLVIk2r.woff +0 -0
  57. package/dist/web/assets/timeline-CO2B2c-0.js +0 -1
@@ -6,11 +6,14 @@ import {
6
6
  expandHome,
7
7
  isValidTimezone,
8
8
  slugify
9
- } from "./chunk-TNIADJ3W.js";
9
+ } from "./chunk-DJPUYMZM.js";
10
10
 
11
11
  // src/providers/usage-core.ts
12
- import { readFile, writeFile, rename, mkdir, readdir, unlink } from "fs/promises";
13
- import { join } from "path";
12
+ import { readFileSync, readdirSync } from "fs";
13
+ import { readdir as readdir2 } from "fs/promises";
14
+ import { dirname, join as join2, relative } from "path";
15
+ import { createHash as createHash2 } from "crypto";
16
+ import { fileURLToPath } from "url";
14
17
 
15
18
  // src/tz.ts
16
19
  function systemTimezone() {
@@ -146,34 +149,288 @@ function percentMetric(label, used, resetsAt, primary) {
146
149
  }
147
150
  var dollars = (cents) => finite(cents) / 100;
148
151
 
152
+ // src/providers/storage/usage-shard-store.ts
153
+ import { chmod, mkdir, readFile, readdir, rename, rmdir, unlink, writeFile } from "fs/promises";
154
+ import { createHash, randomUUID } from "crypto";
155
+ import { join } from "path";
156
+ var DEFAULT_MAX_MEMORY_BYTES = 48 * 1024 * 1024;
157
+ var DEFAULT_MAX_MEMORY_SHARDS = 192;
158
+ var DEFAULT_STALE_AFTER_MS = 230 * 864e5;
159
+ var PRUNE_INTERVAL_MS = 60 * 6e4;
160
+ function digest(value) {
161
+ return createHash("sha256").update(value).digest("hex");
162
+ }
163
+ function sameSource(a, b) {
164
+ return a.path === b.path && a.mtimeMs === b.mtimeMs && a.size === b.size;
165
+ }
166
+ function isSource(value) {
167
+ const x = value;
168
+ return !!x && typeof x.path === "string" && typeof x.mtimeMs === "number" && Number.isFinite(x.mtimeMs) && typeof x.size === "number" && Number.isFinite(x.size);
169
+ }
170
+ function sameFingerprint(a, b) {
171
+ return a.format === b.format && a.parser === b.parser && a.pricing === b.pricing;
172
+ }
173
+ var UsageShardStore = class {
174
+ constructor(options) {
175
+ this.options = options;
176
+ this.root = join(options.dir, "usage-shards");
177
+ this.shardDir = join(this.root, `v1-${digest(JSON.stringify(options.fingerprint)).slice(0, 24)}`);
178
+ this.maxMemoryBytes = options.maxMemoryBytes ?? DEFAULT_MAX_MEMORY_BYTES;
179
+ this.maxMemoryShards = options.maxMemoryShards ?? DEFAULT_MAX_MEMORY_SHARDS;
180
+ this.staleAfterMs = options.staleAfterMs ?? DEFAULT_STALE_AFTER_MS;
181
+ this.now = options.now ?? Date.now;
182
+ }
183
+ options;
184
+ root;
185
+ shardDir;
186
+ maxMemoryBytes;
187
+ maxMemoryShards;
188
+ staleAfterMs;
189
+ now;
190
+ memory = /* @__PURE__ */ new Map();
191
+ inFlight = /* @__PURE__ */ new Map();
192
+ latestRequest = /* @__PURE__ */ new Map();
193
+ writes = /* @__PURE__ */ new Set();
194
+ memoryBytes = 0;
195
+ initialized = null;
196
+ lastPrune = 0;
197
+ /** Loads an unchanged shard or parses it exactly once among concurrent callers. */
198
+ async load(source, parse) {
199
+ const key = this.key(source.path);
200
+ const cached = this.memory.get(key);
201
+ if (cached && sameSource(cached.source, source)) {
202
+ this.touch(key, cached);
203
+ if (!cached.persisted) await this.persist(key, cached);
204
+ return cached.entries;
205
+ }
206
+ const flightKey = `${key}\0${source.mtimeMs}\0${source.size}`;
207
+ this.latestRequest.set(key, flightKey);
208
+ let pending = this.inFlight.get(flightKey);
209
+ if (!pending) {
210
+ pending = this.loadOrParse(key, flightKey, source, parse);
211
+ this.inFlight.set(flightKey, pending);
212
+ void pending.finally(() => {
213
+ this.inFlight.delete(flightKey);
214
+ if (this.latestRequest.get(key) === flightKey) this.latestRequest.delete(key);
215
+ }).catch(() => {
216
+ });
217
+ }
218
+ return (await pending).entries;
219
+ }
220
+ /** Waits for incremental writes; retained for the existing daemon shutdown hook. */
221
+ async flush() {
222
+ await Promise.allSettled([...this.writes]);
223
+ }
224
+ /** Deletes only completed shard files. Temp files are intentionally never scavenged. */
225
+ async prune(force = false) {
226
+ const now = this.now();
227
+ if (!force && now - this.lastPrune < PRUNE_INTERVAL_MS) return;
228
+ this.lastPrune = now;
229
+ await this.ensureDirectory();
230
+ let namespaces;
231
+ try {
232
+ namespaces = (await readdir(this.root, { withFileTypes: true })).filter((entry) => entry.isDirectory() && /^v1-[a-f0-9]{24}$/.test(entry.name)).map((entry) => entry.name);
233
+ } catch {
234
+ return;
235
+ }
236
+ const completed = [];
237
+ await mapLimit(namespaces, 8, async (namespace) => {
238
+ const dir = join(this.root, namespace);
239
+ try {
240
+ const names = await readdir(dir);
241
+ for (const name of names) {
242
+ if (/^[a-f0-9]{64}\.json$/.test(name)) completed.push(join(dir, name));
243
+ }
244
+ } catch {
245
+ }
246
+ });
247
+ await mapLimit(completed, 16, async (file) => {
248
+ try {
249
+ const raw = JSON.parse(await readFile(file, "utf8"));
250
+ if (!isSource(raw?.source) || now - raw.source.mtimeMs >= this.staleAfterMs) {
251
+ await unlink(file).catch(() => {
252
+ });
253
+ }
254
+ } catch {
255
+ await unlink(file).catch(() => {
256
+ });
257
+ }
258
+ });
259
+ await Promise.all(namespaces.map(async (namespace) => {
260
+ const dir = join(this.root, namespace);
261
+ if (dir !== this.shardDir) await rmdir(dir).catch(() => {
262
+ });
263
+ }));
264
+ }
265
+ memoryStats() {
266
+ return { shards: this.memory.size, bytes: this.memoryBytes, inFlight: this.inFlight.size };
267
+ }
268
+ async loadOrParse(key, requestKey, source, parse) {
269
+ await this.ensureDirectory();
270
+ const onDisk = await this.read(key, source);
271
+ if (onDisk) {
272
+ return this.latestRequest.get(key) === requestKey ? this.remember(key, onDisk) : onDisk;
273
+ }
274
+ const entries = await parse();
275
+ const cached = { source: { ...source }, entries, bytes: this.estimate(entries), persisted: false };
276
+ if (this.latestRequest.get(key) !== requestKey) return cached;
277
+ this.remember(key, cached);
278
+ await this.persist(key, cached);
279
+ void this.prune().catch(() => {
280
+ });
281
+ return cached;
282
+ }
283
+ async read(key, source) {
284
+ try {
285
+ const raw = JSON.parse(await readFile(this.file(key), "utf8"));
286
+ if (!raw || raw.format !== "tokmon.usage-shard/v1" || !sameFingerprint(raw.fingerprint, this.options.fingerprint) || !sameSource(raw.source, source)) return null;
287
+ const entries = this.options.decode(raw.entries);
288
+ if (!entries) return null;
289
+ return { source: { ...source }, entries, bytes: this.estimate(entries), persisted: true };
290
+ } catch {
291
+ return null;
292
+ }
293
+ }
294
+ async persist(key, cached) {
295
+ const write = this.write(key, cached).then(() => {
296
+ cached.persisted = true;
297
+ }).catch(() => {
298
+ cached.persisted = false;
299
+ });
300
+ this.writes.add(write);
301
+ try {
302
+ await write;
303
+ } finally {
304
+ this.writes.delete(write);
305
+ }
306
+ }
307
+ async write(key, cached) {
308
+ await this.ensureDirectory();
309
+ const file = this.file(key);
310
+ const tmp = `${file}.${process.pid}.${randomUUID()}.tmp`;
311
+ const value = {
312
+ format: "tokmon.usage-shard/v1",
313
+ fingerprint: this.options.fingerprint,
314
+ source: cached.source,
315
+ entries: this.options.encode(cached.entries)
316
+ };
317
+ try {
318
+ await writeFile(tmp, JSON.stringify(value), { encoding: "utf8", mode: 384 });
319
+ await rename(tmp, file);
320
+ } catch (error) {
321
+ await unlink(tmp).catch(() => {
322
+ });
323
+ throw error;
324
+ }
325
+ }
326
+ remember(key, cached) {
327
+ const old = this.memory.get(key);
328
+ if (old) this.memoryBytes -= old.bytes;
329
+ this.memory.delete(key);
330
+ this.memory.set(key, cached);
331
+ this.memoryBytes += cached.bytes;
332
+ this.evict();
333
+ return cached;
334
+ }
335
+ touch(key, cached) {
336
+ this.memory.delete(key);
337
+ this.memory.set(key, cached);
338
+ }
339
+ evict() {
340
+ while (this.memory.size > this.maxMemoryShards || this.memoryBytes > this.maxMemoryBytes) {
341
+ const oldest = this.memory.entries().next().value;
342
+ if (!oldest) break;
343
+ this.memory.delete(oldest[0]);
344
+ this.memoryBytes -= oldest[1].bytes;
345
+ }
346
+ }
347
+ async ensureDirectory() {
348
+ if (!this.initialized) {
349
+ this.initialized = (async () => {
350
+ await mkdir(this.options.dir, { recursive: true, mode: 448 });
351
+ await chmod(this.options.dir, 448);
352
+ await mkdir(this.root, { recursive: true, mode: 448 });
353
+ await chmod(this.root, 448);
354
+ await mkdir(this.shardDir, { recursive: true, mode: 448 });
355
+ await chmod(this.shardDir, 448);
356
+ })();
357
+ }
358
+ await this.initialized;
359
+ }
360
+ key(path) {
361
+ return digest(path);
362
+ }
363
+ file(key) {
364
+ return join(this.shardDir, `${key}.json`);
365
+ }
366
+ estimate(entries) {
367
+ try {
368
+ return Buffer.byteLength(JSON.stringify(this.options.encode(entries)));
369
+ } catch {
370
+ return 1024;
371
+ }
372
+ }
373
+ };
374
+ async function mapLimit(items, limit, fn) {
375
+ let next = 0;
376
+ const worker = async () => {
377
+ while (next < items.length) {
378
+ const item = items[next++];
379
+ await fn(item);
380
+ }
381
+ };
382
+ await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
383
+ }
384
+
149
385
  // src/providers/usage-core.ts
150
386
  var SPARK_DAYS = 14;
151
387
  var DAY_MS = 864e5;
152
- var CACHE_VERSION = 8;
153
388
  var PRUNE_AGE_MS = 230 * DAY_MS;
154
- var memCache = /* @__PURE__ */ new Map();
155
- var diskLoaded = false;
156
- var diskLoadPromise = null;
157
- var dirty = false;
158
- var flushTimer = null;
159
- function cacheFile() {
160
- return join(cacheDir(), `usage-v${CACHE_VERSION}.json`);
161
- }
162
- async function cleanupOldCacheFiles() {
389
+ var stores = /* @__PURE__ */ new Map();
390
+ var MAX_STORES = 6;
391
+ function hash(value) {
392
+ return createHash2("sha256").update(value).digest("hex").slice(0, 24);
393
+ }
394
+ function runningBuildFingerprint() {
163
395
  try {
164
- const dir = cacheDir();
165
- const names = await readdir(dir);
166
- await Promise.all(names.map(async (name) => {
167
- const version = /^usage-v(\d+)\.json$/.exec(name);
168
- const oldVersion = version ? Number(version[1]) !== CACHE_VERSION : false;
169
- const tmp = /^usage-v\d+\.json\..*\.tmp$/.test(name);
170
- if (oldVersion || tmp) await unlink(join(dir, name)).catch(() => {
171
- });
172
- }));
396
+ const modulePath = fileURLToPath(import.meta.url);
397
+ if (modulePath.endsWith(".ts")) {
398
+ const root = dirname(modulePath);
399
+ const files = [];
400
+ const stack = [root];
401
+ while (stack.length > 0) {
402
+ const dir = stack.pop();
403
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
404
+ const path = join2(dir, entry.name);
405
+ if (entry.isDirectory()) stack.push(path);
406
+ else if (entry.isFile() && entry.name.endsWith(".ts") && !entry.name.includes(".test.")) files.push(path);
407
+ }
408
+ }
409
+ const sourceHash = createHash2("sha256");
410
+ for (const path of files.sort()) {
411
+ sourceHash.update(relative(root, path)).update("\0").update(readFileSync(path)).update("\0");
412
+ }
413
+ return `source-tree-${sourceHash.digest("hex").slice(0, 24)}`;
414
+ }
415
+ const entrypoint = process.argv[1];
416
+ if (!entrypoint) return "build-unknown";
417
+ const bytes = readFileSync(entrypoint);
418
+ if (bytes.byteLength > 8 * 1024 * 1024) return "build-unknown";
419
+ return `build-${createHash2("sha256").update(bytes).digest("hex").slice(0, 24)}`;
173
420
  } catch {
421
+ return "build-unknown";
174
422
  }
175
423
  }
176
- function encode(mtimeMs, size, entries) {
424
+ var RUNNING_BUILD_FINGERPRINT = runningBuildFingerprint();
425
+ function fingerprintFor(parse, options) {
426
+ const sourceVersion = `source-${hash(Function.prototype.toString.call(parse))}`;
427
+ return {
428
+ format: options?.fingerprint?.format ?? "usage-shard-v2",
429
+ parser: options?.fingerprint?.parser ?? `${sourceVersion}.${RUNNING_BUILD_FINGERPRINT}`,
430
+ pricing: options?.fingerprint?.pricing ?? RUNNING_BUILD_FINGERPRINT
431
+ };
432
+ }
433
+ function encode(entries) {
177
434
  const mods = [];
178
435
  const idx = /* @__PURE__ */ new Map();
179
436
  const rows = entries.map((e) => {
@@ -183,11 +440,13 @@ function encode(mtimeMs, size, entries) {
183
440
  mods.push(e.model);
184
441
  idx.set(e.model, mi);
185
442
  }
186
- return [e.ts, mi, e.input, e.output, e.cacheCreate, e.cacheRead, e.cost, e.cacheSavings, e.id ?? 0];
443
+ return [e.ts, mi, e.input, e.output, e.cacheCreate, e.cacheRead, e.cost, e.cacheSavings, e.id ?? 0, e.count ?? 1];
187
444
  });
188
- return { m: mtimeMs, s: size, mods, rows };
445
+ return { mods, rows };
189
446
  }
190
- function decode(s) {
447
+ function decode(value) {
448
+ const s = value;
449
+ if (!s || !Array.isArray(s.rows) || !Array.isArray(s.mods)) return null;
191
450
  const num2 = (x) => typeof x === "number" && Number.isFinite(x) && x >= 0 ? x : 0;
192
451
  const out = [];
193
452
  for (const r of s.rows) {
@@ -204,105 +463,77 @@ function decode(s) {
204
463
  cacheRead: num2(r[5]),
205
464
  cost: num2(r[6]),
206
465
  cacheSavings: num2(r[7]),
207
- id: typeof r[8] === "string" ? r[8] : void 0
466
+ id: typeof r[8] === "string" ? r[8] : void 0,
467
+ count: num2(r[9]) || 1
208
468
  });
209
469
  }
210
470
  return out;
211
471
  }
212
- async function ensureDiskLoaded() {
213
- if (diskLoaded) return;
214
- if (!diskLoadPromise) {
215
- diskLoadPromise = (async () => {
216
- try {
217
- const obj = JSON.parse(await readFile(cacheFile(), "utf-8"));
218
- for (const [path, s] of Object.entries(obj)) {
219
- if (s && typeof s.m === "number" && Array.isArray(s.rows) && Array.isArray(s.mods)) {
220
- memCache.set(path, { mtimeMs: s.m, size: typeof s.s === "number" ? s.s : -1, entries: decode(s) });
221
- }
222
- }
223
- } catch {
224
- } finally {
225
- await cleanupOldCacheFiles();
226
- diskLoaded = true;
227
- }
228
- })().finally(() => {
229
- diskLoadPromise = null;
472
+ function storeFor(parse, options) {
473
+ const dir = options?.storageDir ?? cacheDir();
474
+ const fingerprint = fingerprintFor(parse, options);
475
+ const key = `${dir}\0${fingerprint.format}\0${fingerprint.parser}\0${fingerprint.pricing}`;
476
+ let store = stores.get(key);
477
+ if (store) {
478
+ stores.delete(key);
479
+ stores.set(key, store);
480
+ } else {
481
+ if (stores.size >= MAX_STORES) stores.delete(stores.keys().next().value);
482
+ store = new UsageShardStore({
483
+ dir,
484
+ fingerprint,
485
+ // Several providers can be active at once; each gets a bounded small LRU
486
+ // instead of retaining every decoded log in one process-global map.
487
+ maxMemoryBytes: 8 * 1024 * 1024,
488
+ maxMemoryShards: 128,
489
+ staleAfterMs: PRUNE_AGE_MS,
490
+ encode,
491
+ decode
230
492
  });
493
+ stores.set(key, store);
231
494
  }
232
- await diskLoadPromise;
495
+ return store;
233
496
  }
234
497
  async function flushDisk() {
235
- if (!dirty) return;
236
- dirty = false;
237
- const now = Date.now();
238
- const obj = {};
239
- for (const [path, v] of memCache) {
240
- if (now - v.mtimeMs < PRUNE_AGE_MS) {
241
- obj[path] = encode(v.mtimeMs, v.size, v.entries);
242
- }
243
- }
244
- try {
245
- await mkdir(cacheDir(), { recursive: true });
246
- const tmp = `${cacheFile()}.${process.pid}.tmp`;
247
- await writeFile(tmp, JSON.stringify(obj));
248
- await rename(tmp, cacheFile());
249
- } catch {
250
- dirty = true;
251
- }
252
- }
253
- function scheduleFlush() {
254
- if (flushTimer) return;
255
- flushTimer = setTimeout(() => {
256
- flushTimer = null;
257
- void flushDisk();
258
- }, 3e4);
259
- flushTimer.unref?.();
498
+ await Promise.all([...stores.values()].map((store) => store.flush()));
260
499
  }
261
500
  async function walkFiles(root) {
262
501
  const files = [];
263
502
  const stack = [""];
264
503
  while (stack.length > 0) {
265
504
  const rel = stack.pop() ?? "";
266
- const dir = rel ? join(root, rel) : root;
505
+ const dir = rel ? join2(root, rel) : root;
267
506
  let entries;
268
507
  try {
269
- entries = await readdir(dir, { withFileTypes: true });
508
+ entries = await readdir2(dir, { withFileTypes: true });
270
509
  } catch {
271
510
  continue;
272
511
  }
273
512
  for (const entry of entries) {
274
- const child = rel ? join(rel, entry.name) : entry.name;
513
+ const child = rel ? join2(rel, entry.name) : entry.name;
275
514
  if (entry.isDirectory()) stack.push(child);
276
515
  else if (entry.isFile()) files.push(child);
277
516
  }
278
517
  }
279
518
  return files;
280
519
  }
281
- async function mapLimit(items, limit, fn) {
520
+ async function mapLimit2(items, limit, fn) {
282
521
  let i = 0;
283
522
  const worker = async () => {
284
523
  while (i < items.length) await fn(items[i++]);
285
524
  };
286
525
  await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
287
526
  }
288
- async function loadCachedEntries(files, parse, since) {
289
- await ensureDiskLoaded();
527
+ async function loadCachedEntries(files, parse, since, options) {
528
+ const store = storeFor(parse, options);
290
529
  const chunks = [];
291
- await mapLimit(files, 8, async (f) => {
530
+ await mapLimit2(files, 8, async (f) => {
292
531
  try {
293
- let c = memCache.get(f.path);
294
- if (!c || c.mtimeMs !== f.mtimeMs || c.size !== f.size) {
295
- const entries = await parse(f.path);
296
- c = { mtimeMs: f.mtimeMs, size: f.size, entries };
297
- memCache.set(f.path, c);
298
- dirty = true;
299
- }
300
- chunks.push(c.entries);
532
+ chunks.push((await store.load(f, () => parse(f.path))).filter((e) => e.ts >= since));
301
533
  } catch {
302
534
  }
303
535
  });
304
- if (dirty) scheduleFlush();
305
- return dedupe(chunks.flat().filter((e) => e.ts >= since));
536
+ return dedupe(chunks.flat());
306
537
  }
307
538
  function lastDayKeys(now, tz, n) {
308
539
  const keys = [];
@@ -561,13 +792,34 @@ var resetIn = formatResetIn;
561
792
  import { stat as fsStat, access } from "fs/promises";
562
793
  import { createReadStream } from "fs";
563
794
  import { createInterface } from "readline";
564
- import { join as join2, isAbsolute } from "path";
795
+ import { join as join3, isAbsolute } from "path";
565
796
  import { homedir } from "os";
797
+
798
+ // src/providers/_shared/time.ts
799
+ function msToIso(ms) {
800
+ return Number.isFinite(ms) && Math.abs(ms) <= 864e13 ? new Date(ms).toISOString() : null;
801
+ }
802
+ function timestampMs(value) {
803
+ const numeric = typeof value === "number" ? value : typeof value === "string" && /^[-+]?\d+(?:\.\d+)?$/.test(value.trim()) ? Number(value) : NaN;
804
+ if (Number.isFinite(numeric)) {
805
+ const ms2 = Math.abs(numeric) < 1e10 ? numeric * 1e3 : numeric;
806
+ return ms2 > 0 && Math.abs(ms2) <= 864e13 ? ms2 : null;
807
+ }
808
+ if (typeof value !== "string" || !value.trim()) return null;
809
+ const ms = Date.parse(value.trim());
810
+ return Number.isFinite(ms) && ms > 0 ? ms : null;
811
+ }
812
+
813
+ // src/providers/claude/usage.ts
566
814
  var PRICING = {
815
+ "claude-opus-4-8": { i: 5e-6, o: 25e-6, cc: 625e-8, cr: 5e-7 },
816
+ "claude-opus-4-7": { i: 5e-6, o: 25e-6, cc: 625e-8, cr: 5e-7 },
817
+ "claude-opus-4-6": { i: 5e-6, o: 25e-6, cc: 625e-8, cr: 5e-7 },
818
+ "claude-opus-4-5": { i: 5e-6, o: 25e-6, cc: 625e-8, cr: 5e-7 },
567
819
  "claude-opus-4-1": { i: 15e-6, o: 75e-6, cc: 1875e-8, cr: 15e-7 },
568
820
  "claude-opus-4-0": { i: 15e-6, o: 75e-6, cc: 1875e-8, cr: 15e-7 },
569
821
  "claude-opus-4-20250514": { i: 15e-6, o: 75e-6, cc: 1875e-8, cr: 15e-7 },
570
- "claude-opus-4": { i: 5e-6, o: 25e-6, cc: 625e-8, cr: 5e-7 },
822
+ "claude-opus-4": { i: 15e-6, o: 75e-6, cc: 1875e-8, cr: 15e-7 },
571
823
  "claude-3-opus": { i: 15e-6, o: 75e-6, cc: 1875e-8, cr: 15e-7 },
572
824
  "claude-sonnet-4": { i: 3e-6, o: 15e-6, cc: 375e-8, cr: 3e-7 },
573
825
  // intro pricing through 2026-08-31 — revert to 3/15/3.75/0.3 after.
@@ -577,21 +829,23 @@ var PRICING = {
577
829
  };
578
830
  var PRICE_KEYS = Object.keys(PRICING).sort((a, b) => b.length - a.length);
579
831
  var ZERO_PRICE = { i: 0, o: 0, cc: 0, cr: 0 };
832
+ var SONNET_5_STANDARD_FROM = Date.UTC(2026, 8, 1);
833
+ var SONNET_5_STANDARD_PRICE = { i: 3e-6, o: 15e-6, cc: 375e-8, cr: 3e-7 };
580
834
  function claudeConfigDirs(homeDir) {
581
835
  if (homeDir) {
582
- return [join2(homeDir, ".claude"), join2(homeDir, ".config", "claude")];
836
+ return [join3(homeDir, ".claude"), join3(homeDir, ".config", "claude")];
583
837
  }
584
838
  const home = homedir();
585
- const dirs = [join2(home, ".claude")];
839
+ const dirs = [join3(home, ".claude")];
586
840
  const xdg = envDir("XDG_CONFIG_HOME");
587
841
  if (xdg) {
588
- dirs.push(join2(xdg, "claude"));
842
+ dirs.push(join3(xdg, "claude"));
589
843
  }
590
844
  if (process.platform !== "win32") {
591
- dirs.push(join2(home, ".config", "claude"));
845
+ dirs.push(join3(home, ".config", "claude"));
592
846
  }
593
847
  const appData = envDir("APPDATA");
594
- if (appData) dirs.push(join2(appData, "claude"));
848
+ if (appData) dirs.push(join3(appData, "claude"));
595
849
  if (process.env.CLAUDE_CONFIG_DIR) {
596
850
  for (const p of process.env.CLAUDE_CONFIG_DIR.split(process.platform === "win32" ? ";" : ",")) {
597
851
  const t = p.trim();
@@ -601,7 +855,7 @@ function claudeConfigDirs(homeDir) {
601
855
  return [...new Set(dirs)];
602
856
  }
603
857
  function getClaudeDirs(homeDir) {
604
- return claudeConfigDirs(homeDir).map((d) => join2(d, "projects"));
858
+ return claudeConfigDirs(homeDir).map((d) => join3(d, "projects"));
605
859
  }
606
860
  async function detectClaude(homeDir) {
607
861
  for (const dir of getClaudeDirs(homeDir)) {
@@ -613,28 +867,26 @@ async function detectClaude(homeDir) {
613
867
  }
614
868
  return false;
615
869
  }
616
- function priceFor(model) {
870
+ function claudePriceFor(model, timestamp = Date.now()) {
617
871
  const m = model.toLowerCase().trim();
618
872
  for (const key of PRICE_KEYS) {
619
873
  if (!m.startsWith(key)) continue;
620
874
  const rest = m.slice(key.length);
621
- if (rest === "" || rest[0] === "-") return PRICING[key];
875
+ if (rest === "" || rest[0] === "-") {
876
+ if (key === "claude-sonnet-5" && timestamp >= SONNET_5_STANDARD_FROM) return SONNET_5_STANDARD_PRICE;
877
+ return PRICING[key];
878
+ }
622
879
  }
623
880
  return ZERO_PRICE;
624
881
  }
625
- function costOf(model, u, cacheCreate5m, cacheCreate1h, hasCacheCreateSplit) {
626
- const p = priceFor(model);
882
+ function costOf(model, u, cacheCreate5m, cacheCreate1h, hasCacheCreateSplit, timestamp) {
883
+ const p = claudePriceFor(model, timestamp);
627
884
  const cacheCreateCost = hasCacheCreateSplit ? cacheCreate5m * p.cc + cacheCreate1h * (2 * p.i) : safeNum(u.cache_creation_input_tokens) * p.cc;
628
885
  return safeNum(u.input_tokens) * p.i + safeNum(u.output_tokens) * p.o + cacheCreateCost + safeNum(u.cache_read_input_tokens) * p.cr;
629
886
  }
630
887
  function shortModel(model) {
631
888
  return model.replace("claude-", "").replace(/-\d{8}$/, "");
632
889
  }
633
- function timestampMs(value) {
634
- if (typeof value === "number" && Number.isFinite(value)) return value > 1e10 ? value : value * 1e3;
635
- if (typeof value === "string" && value.trim()) return new Date(value.trim()).getTime();
636
- return NaN;
637
- }
638
890
  async function parseFile(path) {
639
891
  const entries = [];
640
892
  const input = createReadStream(path);
@@ -648,7 +900,7 @@ async function parseFile(path) {
648
900
  const obj = JSON.parse(line.charCodeAt(0) === 65279 ? line.slice(1) : line);
649
901
  if (obj.type !== "assistant" || !obj.message?.usage) continue;
650
902
  const ts = timestampMs(obj.timestamp);
651
- if (!Number.isFinite(ts)) continue;
903
+ if (ts === null) continue;
652
904
  const u = obj.message.usage;
653
905
  const model = typeof obj.message.model === "string" && obj.message.model ? obj.message.model : "unknown";
654
906
  const inputTokens = safeNum(u.input_tokens);
@@ -659,13 +911,13 @@ async function parseFile(path) {
659
911
  const cacheCreate = hasCacheCreateSplit ? cacheCreate5m + cacheCreate1h : safeNum(u.cache_creation_input_tokens);
660
912
  const cacheRead = safeNum(u.cache_read_input_tokens);
661
913
  if (inputTokens + output + cacheCreate + cacheRead === 0) continue;
662
- const p = priceFor(model);
914
+ const p = claudePriceFor(model, ts);
663
915
  const msgId = obj.message?.id;
664
916
  entries.push({
665
917
  id: msgId ? msgId + (obj.requestId ? ":" + obj.requestId : "") : void 0,
666
918
  ts,
667
919
  model: shortModel(model),
668
- cost: costOf(model, u, cacheCreate5m, cacheCreate1h, hasCacheCreateSplit),
920
+ cost: costOf(model, u, cacheCreate5m, cacheCreate1h, hasCacheCreateSplit, ts),
669
921
  input: inputTokens,
670
922
  output,
671
923
  cacheCreate,
@@ -687,7 +939,7 @@ async function loadEntries(since, homeDir) {
687
939
  const listing = await walkFiles(dir);
688
940
  for (const f of listing) {
689
941
  if (!f.endsWith(".jsonl")) continue;
690
- const path = join2(dir, f);
942
+ const path = join3(dir, f);
691
943
  if (seen.has(path)) continue;
692
944
  seen.add(path);
693
945
  try {
@@ -716,7 +968,7 @@ async function claudeTable(tz, homeDir) {
716
968
 
717
969
  // src/providers/claude/billing.ts
718
970
  import { access as access2, readFile as readFile2 } from "fs/promises";
719
- import { join as join4 } from "path";
971
+ import { join as join5 } from "path";
720
972
  import { homedir as homedir3 } from "os";
721
973
 
722
974
  // src/http.ts
@@ -738,11 +990,6 @@ function identityFields(identity) {
738
990
  };
739
991
  }
740
992
 
741
- // src/providers/_shared/time.ts
742
- function msToIso(ms) {
743
- return Number.isFinite(ms) && Math.abs(ms) <= 864e13 ? new Date(ms).toISOString() : null;
744
- }
745
-
746
993
  // src/providers/_shared/keychain.ts
747
994
  import { execFile as execFileCb } from "child_process";
748
995
  import { promisify } from "util";
@@ -786,8 +1033,8 @@ function unwrapGoKeyringBase64(raw) {
786
1033
  }
787
1034
 
788
1035
  // src/providers/claude/identity.ts
789
- import { readFileSync } from "fs";
790
- import { join as join3 } from "path";
1036
+ import { readFileSync as readFileSync2 } from "fs";
1037
+ import { join as join4 } from "path";
791
1038
  import { homedir as homedir2 } from "os";
792
1039
  function titleWords(value) {
793
1040
  return value.split(/[_\s-]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()).join(" ");
@@ -802,7 +1049,7 @@ function claudeOrgPlanLabel(orgType) {
802
1049
  function readClaudeIdentity(homeDir) {
803
1050
  const base = homeDir ? expandHome(homeDir) : homedir2();
804
1051
  try {
805
- const parsed = JSON.parse(readFileSync(join3(base, ".claude.json"), "utf-8"));
1052
+ const parsed = JSON.parse(readFileSync2(join4(base, ".claude.json"), "utf-8"));
806
1053
  const oauth = parsed?.oauthAccount;
807
1054
  const email = typeof oauth?.emailAddress === "string" && oauth.emailAddress.trim() ? oauth.emailAddress.trim() : void 0;
808
1055
  const displayName = typeof oauth?.displayName === "string" && oauth.displayName.trim() ? oauth.displayName.trim() : void 0;
@@ -819,8 +1066,8 @@ function parseAuth(raw) {
819
1066
  try {
820
1067
  const creds = JSON.parse(raw);
821
1068
  const o = creds?.claudeAiOauth ?? creds;
822
- const token = o?.accessToken;
823
- if (typeof token !== "string" || !token) return null;
1069
+ const token = typeof o?.accessToken === "string" ? o.accessToken.trim() : "";
1070
+ if (!token) return null;
824
1071
  return {
825
1072
  token,
826
1073
  subscriptionType: typeof o.subscriptionType === "string" ? o.subscriptionType : void 0,
@@ -834,7 +1081,7 @@ function parseAuth(raw) {
834
1081
  async function readCredentialsFile(homeDir) {
835
1082
  for (const dir of claudeConfigDirs(homeDir)) {
836
1083
  try {
837
- const auth = parseAuth(await readFile2(join4(dir, ".credentials.json"), "utf-8"));
1084
+ const auth = parseAuth(await readFile2(join5(dir, ".credentials.json"), "utf-8"));
838
1085
  if (auth) return auth;
839
1086
  } catch {
840
1087
  }
@@ -847,7 +1094,7 @@ async function readMacKeychain() {
847
1094
  }
848
1095
  async function readHomeKeychain(homeDir) {
849
1096
  if (process.platform !== "darwin") return null;
850
- const keychainPath = join4(homeDir, "Library", "Keychains", "login.keychain-db");
1097
+ const keychainPath = join5(homeDir, "Library", "Keychains", "login.keychain-db");
851
1098
  try {
852
1099
  await access2(keychainPath);
853
1100
  } catch {
@@ -863,7 +1110,7 @@ async function authCandidates(homeDir) {
863
1110
  const file = await readCredentialsFile(isDefault ? void 0 : expandedHomeDir);
864
1111
  const keychain = process.platform === "darwin" ? await readMacKeychain() : null;
865
1112
  const homeKeychain = !isDefault && expandedHomeDir ? await readHomeKeychain(expandedHomeDir) : null;
866
- const ordered = isDefault ? [keychain && { auth: keychain, shared: true }, file && { auth: file, shared: false }] : [
1113
+ const ordered = isDefault ? [keychain && { auth: keychain, shared: false }, file && { auth: file, shared: false }] : [
867
1114
  homeKeychain && { auth: homeKeychain, shared: false },
868
1115
  file && { auth: file, shared: false },
869
1116
  keychain && { auth: keychain, shared: true }
@@ -902,6 +1149,9 @@ async function tokenIdentity(token) {
902
1149
  return void 0;
903
1150
  }
904
1151
  }
1152
+ function sharedClaudeCredentialMatches(expectedUuid, identity) {
1153
+ return typeof expectedUuid === "string" && expectedUuid.length > 0 && identity?.accountUuid === expectedUuid;
1154
+ }
905
1155
  async function getAuth(homeDir, expectedUuid) {
906
1156
  const candidates = await authCandidates(homeDir);
907
1157
  let sharedAccountEmail;
@@ -913,11 +1163,10 @@ async function getAuth(homeDir, expectedUuid) {
913
1163
  if (!shared) sawExpiredOwn = true;
914
1164
  continue;
915
1165
  }
916
- if (!shared || !expectedUuid) return { auth };
1166
+ if (!shared) return { auth };
917
1167
  const identity = await tokenIdentity(auth.token);
918
- if (identity === void 0) return { auth };
919
- if (identity === null) continue;
920
- if (identity.accountUuid === expectedUuid) return { auth };
1168
+ if (sharedClaudeCredentialMatches(expectedUuid, identity)) return { auth };
1169
+ if (!identity) continue;
921
1170
  sharedAccountEmail = identity.email;
922
1171
  }
923
1172
  if (sawExpiredOwn) return { auth: null, expired: true };
@@ -1083,11 +1332,18 @@ var claudeProvider = {
1083
1332
  import { stat as fsStat2, access as access3, open as openFile } from "fs/promises";
1084
1333
  import { createReadStream as createReadStream2 } from "fs";
1085
1334
  import { createInterface as createInterface2 } from "readline";
1086
- import { join as join5 } from "path";
1335
+ import { join as join6 } from "path";
1087
1336
  import { homedir as homedir4 } from "os";
1088
1337
  var PRICING2 = {
1338
+ "gpt-5.6-terra": { in: 25e-7, cr: 25e-8, out: 15e-6 },
1339
+ "gpt-5.6-luna": { in: 1e-6, cr: 1e-7, out: 6e-6 },
1340
+ "gpt-5.6-sol": { in: 5e-6, cr: 5e-7, out: 3e-5 },
1341
+ "gpt-5.5-pro": { in: 3e-5, cr: 3e-5, out: 18e-5 },
1089
1342
  "gpt-5.5-codex": { in: 5e-6, cr: 5e-7, out: 3e-5 },
1090
1343
  "gpt-5.5": { in: 5e-6, cr: 5e-7, out: 3e-5 },
1344
+ "gpt-5.4-mini": { in: 75e-8, cr: 75e-9, out: 45e-7 },
1345
+ "gpt-5.4-nano": { in: 2e-7, cr: 2e-8, out: 125e-8 },
1346
+ "gpt-5.4-pro": { in: 3e-5, cr: 3e-5, out: 18e-5 },
1091
1347
  "gpt-5.4-codex": { in: 25e-7, cr: 25e-8, out: 15e-6 },
1092
1348
  "gpt-5.4": { in: 25e-7, cr: 25e-8, out: 15e-6 },
1093
1349
  "gpt-5.3-codex": { in: 175e-8, cr: 175e-9, out: 14e-6 },
@@ -1104,23 +1360,23 @@ var PRICING2 = {
1104
1360
  var FALLBACK_PRICE = PRICING2["gpt-5.5"];
1105
1361
  var PRICE_KEYS2 = Object.keys(PRICING2).sort((a, b) => b.length - a.length);
1106
1362
  function codexHomes(homeDir) {
1107
- if (homeDir) return [.../* @__PURE__ */ new Set([join5(homeDir, ".codex"), homeDir])];
1363
+ if (homeDir) return [.../* @__PURE__ */ new Set([join6(homeDir, ".codex"), homeDir])];
1108
1364
  const homes = [];
1109
1365
  const codexHome = envDir("CODEX_HOME");
1110
1366
  if (codexHome) homes.push(codexHome);
1111
- homes.push(join5(homedir4(), ".codex"));
1112
- homes.push(join5(homedir4(), ".config", "codex"));
1367
+ homes.push(join6(homedir4(), ".codex"));
1368
+ homes.push(join6(homedir4(), ".config", "codex"));
1113
1369
  return [...new Set(homes)];
1114
1370
  }
1115
1371
  async function detectCodex(homeDir) {
1116
1372
  for (const home of codexHomes(homeDir)) {
1117
1373
  try {
1118
- await access3(join5(home, "sessions"));
1374
+ await access3(join6(home, "sessions"));
1119
1375
  return true;
1120
1376
  } catch {
1121
1377
  }
1122
1378
  try {
1123
- await access3(join5(home, "archived_sessions"));
1379
+ await access3(join6(home, "archived_sessions"));
1124
1380
  return true;
1125
1381
  } catch {
1126
1382
  }
@@ -1140,7 +1396,7 @@ function modelKeyMatches(model, key) {
1140
1396
  }
1141
1397
  return false;
1142
1398
  }
1143
- function priceFor2(model) {
1399
+ function codexPriceFor(model) {
1144
1400
  const m = model.toLowerCase().trim();
1145
1401
  for (const key of PRICE_KEYS2) {
1146
1402
  if (modelKeyMatches(m, key)) return PRICING2[key];
@@ -1193,15 +1449,9 @@ function eventSig(last, total) {
1193
1449
  const f = (x) => x ? `${x.input_tokens ?? 0},${x.cached_input_tokens ?? 0},${x.output_tokens ?? 0},${x.reasoning_output_tokens ?? 0},${x.total_tokens ?? 0}` : "-";
1194
1450
  return `${f(last)}|${f(total)}`;
1195
1451
  }
1196
- function timestampMs2(value) {
1197
- if (typeof value === "number" && Number.isFinite(value)) return value > 1e10 ? value : value * 1e3;
1198
- if (typeof value === "string" && value.trim()) return new Date(value.trim()).getTime();
1199
- return NaN;
1200
- }
1201
1452
  function timestampSecond(value) {
1202
- if (typeof value === "string" && value.trim().length >= 19) return value.trim().slice(0, 19);
1203
- const ts = timestampMs2(value);
1204
- return Number.isFinite(ts) ? new Date(ts).toISOString().slice(0, 19) : null;
1453
+ const ts = timestampMs(value);
1454
+ return ts === null ? null : new Date(ts).toISOString().slice(0, 19);
1205
1455
  }
1206
1456
  async function hasThreadSpawn(path) {
1207
1457
  let handle = null;
@@ -1248,7 +1498,7 @@ function findUsage(obj) {
1248
1498
  return normalizeUsage(obj?.usage) ?? normalizeUsage(obj?.payload?.usage) ?? normalizeUsage(obj?.payload?.info?.usage) ?? normalizeUsage(obj?.result?.usage) ?? normalizeUsage(obj?.response?.usage) ?? normalizeUsage(obj?.token_usage) ?? normalizeUsage(obj);
1249
1499
  }
1250
1500
  function findTimestamp(obj) {
1251
- return timestampMs2(obj?.timestamp ?? obj?.payload?.timestamp ?? obj?.created_at ?? obj?.createdAt ?? obj?.time);
1501
+ return timestampMs(obj?.timestamp ?? obj?.payload?.timestamp ?? obj?.created_at ?? obj?.createdAt ?? obj?.time);
1252
1502
  }
1253
1503
  async function parseFile2(path) {
1254
1504
  const entries = [];
@@ -1279,13 +1529,13 @@ async function parseFile2(path) {
1279
1529
  const m2 = extractModel(obj);
1280
1530
  if (typeof m2 === "string" && m2.trim()) model = m2;
1281
1531
  const ts2 = findTimestamp(obj);
1282
- if (!Number.isFinite(ts2)) continue;
1532
+ if (ts2 === null) continue;
1283
1533
  const inputTotal2 = safeNum(usage.input_tokens);
1284
1534
  const cached2 = Math.min(safeNum(usage.cached_input_tokens), inputTotal2);
1285
1535
  const inputTokens2 = inputTotal2 - cached2;
1286
1536
  const output2 = safeNum(usage.output_tokens);
1287
1537
  if (inputTokens2 + output2 + cached2 === 0) continue;
1288
- const p2 = priceFor2(model);
1538
+ const p2 = codexPriceFor(model);
1289
1539
  entries.push({
1290
1540
  id: `${ts2}|${model}|${inputTotal2}|${cached2}|${output2}|${safeNum(usage.reasoning_output_tokens)}|${safeNum(usage.total_tokens)}`,
1291
1541
  ts: ts2,
@@ -1321,8 +1571,8 @@ async function parseFile2(path) {
1321
1571
  }
1322
1572
  if (total) prevTotal = total;
1323
1573
  if (!d) continue;
1324
- const ts = timestampMs2(tsValue);
1325
- if (!Number.isFinite(ts)) continue;
1574
+ const ts = timestampMs(tsValue);
1575
+ if (ts === null) continue;
1326
1576
  const m = extractModel(obj);
1327
1577
  if (typeof m === "string" && m.trim()) model = m;
1328
1578
  const inputTotal = safeNum(d.input_tokens);
@@ -1330,7 +1580,7 @@ async function parseFile2(path) {
1330
1580
  const inputTokens = inputTotal - cached;
1331
1581
  const output = safeNum(d.output_tokens);
1332
1582
  if (inputTokens + output + cached === 0) continue;
1333
- const p = priceFor2(model);
1583
+ const p = codexPriceFor(model);
1334
1584
  entries.push({
1335
1585
  id: `${ts}|${model}|${inputTotal}|${cached}|${output}|${safeNum(d.reasoning_output_tokens)}|${safeNum(d.total_tokens)}`,
1336
1586
  ts,
@@ -1354,11 +1604,11 @@ async function loadEntries2(since, homeDir) {
1354
1604
  const seen = /* @__PURE__ */ new Set();
1355
1605
  const seenIno = /* @__PURE__ */ new Set();
1356
1606
  for (const home of codexHomes(homeDir)) {
1357
- for (const dir of [join5(home, "sessions"), join5(home, "archived_sessions")]) {
1607
+ for (const dir of [join6(home, "sessions"), join6(home, "archived_sessions")]) {
1358
1608
  const listing = await walkFiles(dir);
1359
1609
  for (const f of listing) {
1360
1610
  if (!f.endsWith(".jsonl")) continue;
1361
- const path = join5(dir, f);
1611
+ const path = join6(dir, f);
1362
1612
  if (seen.has(path)) continue;
1363
1613
  seen.add(path);
1364
1614
  try {
@@ -1387,14 +1637,15 @@ async function codexTable(tz, homeDir) {
1387
1637
  }
1388
1638
 
1389
1639
  // src/providers/codex/billing.ts
1390
- import { readFile as readFile3, readdir as readdir2, stat as fsStat3 } from "fs/promises";
1640
+ import { readFile as readFile3, readdir as readdir3, stat as fsStat3 } from "fs/promises";
1391
1641
  import { createReadStream as createReadStream3 } from "fs";
1392
1642
  import { createInterface as createInterface3 } from "readline";
1393
- import { join as join6 } from "path";
1643
+ import { join as join7 } from "path";
1394
1644
 
1395
1645
  // src/providers/_shared/jwt.ts
1396
1646
  function decodeBase64UrlJson(segment) {
1397
1647
  try {
1648
+ if (!segment || !/^[A-Za-z0-9_-]+$/.test(segment)) return null;
1398
1649
  const normalized = segment.replace(/-/g, "+").replace(/_/g, "/");
1399
1650
  const padded = normalized + "=".repeat((4 - normalized.length % 4) % 4);
1400
1651
  return JSON.parse(Buffer.from(padded, "base64").toString("utf8"));
@@ -1403,9 +1654,11 @@ function decodeBase64UrlJson(segment) {
1403
1654
  }
1404
1655
  }
1405
1656
  function identityFromIdToken(idToken) {
1406
- if (typeof idToken !== "string" || !idToken.includes(".")) return {};
1407
- const payload = decodeBase64UrlJson(idToken.split(".")[1]);
1408
- if (!payload || typeof payload !== "object") return {};
1657
+ if (typeof idToken !== "string") return {};
1658
+ const parts = idToken.split(".");
1659
+ if (parts.length !== 3) return {};
1660
+ const payload = decodeBase64UrlJson(parts[1]);
1661
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) return {};
1409
1662
  const email = typeof payload.email === "string" && payload.email.trim() ? payload.email.trim() : void 0;
1410
1663
  const displayName = typeof payload.name === "string" && payload.name.trim() ? payload.name.trim() : typeof payload.given_name === "string" && payload.given_name.trim() ? payload.given_name.trim() : void 0;
1411
1664
  return { email, displayName, payload };
@@ -1437,11 +1690,12 @@ function codexIdentity(idToken) {
1437
1690
  }
1438
1691
  async function readAuthFile(home) {
1439
1692
  try {
1440
- const raw = await readFile3(join6(home, "auth.json"), "utf-8");
1693
+ const raw = await readFile3(join7(home, "auth.json"), "utf-8");
1441
1694
  const auth = JSON.parse(raw);
1442
- const accessToken = auth?.tokens?.access_token;
1695
+ const accessToken = typeof auth?.tokens?.access_token === "string" ? auth.tokens.access_token.trim() : "";
1443
1696
  if (!accessToken) return null;
1444
- return { accessToken, accountId: auth?.tokens?.account_id, ...codexIdentity(auth?.tokens?.id_token) };
1697
+ const accountId = typeof auth?.tokens?.account_id === "string" && auth.tokens.account_id.trim() ? auth.tokens.account_id.trim() : void 0;
1698
+ return { accessToken, accountId, ...codexIdentity(auth?.tokens?.id_token) };
1445
1699
  } catch {
1446
1700
  return null;
1447
1701
  }
@@ -1451,9 +1705,10 @@ async function readKeychainAuth() {
1451
1705
  const raw = await readMacKeychainRaw("Codex Auth");
1452
1706
  if (!raw) return null;
1453
1707
  const auth = JSON.parse(raw);
1454
- const accessToken = auth?.tokens?.access_token;
1708
+ const accessToken = typeof auth?.tokens?.access_token === "string" ? auth.tokens.access_token.trim() : "";
1455
1709
  if (!accessToken) return null;
1456
- return { accessToken, accountId: auth?.tokens?.account_id, ...codexIdentity(auth?.tokens?.id_token) };
1710
+ const accountId = typeof auth?.tokens?.account_id === "string" && auth.tokens.account_id.trim() ? auth.tokens.account_id.trim() : void 0;
1711
+ return { accessToken, accountId, ...codexIdentity(auth?.tokens?.id_token) };
1457
1712
  } catch {
1458
1713
  return null;
1459
1714
  }
@@ -1593,23 +1848,39 @@ async function liveBilling(auth) {
1593
1848
  }
1594
1849
  var SNAPSHOT_CANDIDATES = 8;
1595
1850
  var SNAPSHOT_STALE_MS = 24 * 36e5;
1851
+ var MAX_SNAPSHOT_SCAN_DIRS = 512;
1852
+ var MAX_SNAPSHOT_SCAN_FILES = 4096;
1853
+ var MAX_SNAPSHOT_TAIL_BYTES = 8 * 1024 * 1024;
1596
1854
  async function newestRolloutFiles(homeDir) {
1597
1855
  const all = [];
1598
1856
  for (const home of codexHomes(homeDir)) {
1599
- const dir = join6(home, "sessions");
1600
- let listing;
1601
- try {
1602
- listing = await readdir2(dir, { recursive: true });
1603
- } catch {
1604
- continue;
1605
- }
1606
- for (const f of listing) {
1607
- if (!f.endsWith(".jsonl") || !f.includes("rollout-")) continue;
1608
- const path = join6(dir, f);
1857
+ const stack = [join7(home, "sessions")];
1858
+ let visitedDirs = 0;
1859
+ let visitedFiles = 0;
1860
+ while (stack.length > 0 && visitedDirs < MAX_SNAPSHOT_SCAN_DIRS && visitedFiles < MAX_SNAPSHOT_SCAN_FILES) {
1861
+ const dir = stack.pop();
1862
+ let entries;
1609
1863
  try {
1610
- const s = await fsStat3(path);
1611
- all.push({ path, mtime: s.mtimeMs });
1864
+ entries = await readdir3(dir, { withFileTypes: true });
1612
1865
  } catch {
1866
+ continue;
1867
+ }
1868
+ entries.sort((a, b) => a.name.localeCompare(b.name));
1869
+ visitedDirs++;
1870
+ for (const entry of entries) {
1871
+ const path = join7(dir, entry.name);
1872
+ if (entry.isDirectory()) {
1873
+ stack.push(path);
1874
+ continue;
1875
+ }
1876
+ visitedFiles++;
1877
+ if (!entry.isFile() || !entry.name.endsWith(".jsonl") || !entry.name.includes("rollout-")) continue;
1878
+ try {
1879
+ const s = await fsStat3(path);
1880
+ all.push({ path, mtime: s.mtimeMs });
1881
+ } catch {
1882
+ }
1883
+ if (visitedFiles >= MAX_SNAPSHOT_SCAN_FILES) break;
1613
1884
  }
1614
1885
  }
1615
1886
  }
@@ -1618,7 +1889,8 @@ async function newestRolloutFiles(homeDir) {
1618
1889
  async function lastRateLimits(path) {
1619
1890
  let last = null;
1620
1891
  try {
1621
- const input = createReadStream3(path);
1892
+ const size = (await fsStat3(path)).size;
1893
+ const input = createReadStream3(path, { start: Math.max(0, size - MAX_SNAPSHOT_TAIL_BYTES) });
1622
1894
  input.on("error", () => {
1623
1895
  });
1624
1896
  const rl = createInterface3({ input, crlfDelay: Infinity });
@@ -1685,17 +1957,17 @@ var codexProvider = {
1685
1957
 
1686
1958
  // src/providers/cursor/billing.ts
1687
1959
  import { access as access4 } from "fs/promises";
1688
- import { join as join9 } from "path";
1960
+ import { join as join10 } from "path";
1689
1961
  import { homedir as homedir6 } from "os";
1690
1962
 
1691
1963
  // src/providers/cursor/activity.ts
1692
- import { join as join8 } from "path";
1964
+ import { join as join9 } from "path";
1693
1965
  import { homedir as homedir5 } from "os";
1694
1966
 
1695
1967
  // src/providers/cursor/sqlite.ts
1696
1968
  import { execFile as execFileCb2 } from "child_process";
1697
1969
  import { accessSync, constants } from "fs";
1698
- import { delimiter, join as join7 } from "path";
1970
+ import { delimiter, join as join8 } from "path";
1699
1971
  import { promisify as promisify2 } from "util";
1700
1972
  var execFile2 = promisify2(execFileCb2);
1701
1973
  var nativeDb;
@@ -1754,7 +2026,7 @@ function resolveSqliteCli() {
1754
2026
  const names = process.platform === "win32" ? ["sqlite3.exe", "sqlite3.cmd", "sqlite3.bat"] : ["sqlite3"];
1755
2027
  for (const dir of (process.env.PATH ?? "").split(delimiter).filter(Boolean)) {
1756
2028
  for (const name of names) {
1757
- const path = join7(dir, name);
2029
+ const path = join8(dir, name);
1758
2030
  if (isExec(path)) return path;
1759
2031
  }
1760
2032
  }
@@ -1806,7 +2078,7 @@ function sqliteStatusMessage(status) {
1806
2078
  var DAY_MS2 = 864e5;
1807
2079
  var HOUR_MS = 36e5;
1808
2080
  function trackingDb(homeDir) {
1809
- return join8(homeDir ?? homedir5(), ".cursor", "ai-tracking", "ai-code-tracking.db");
2081
+ return join9(homeDir ?? homedir5(), ".cursor", "ai-tracking", "ai-code-tracking.db");
1810
2082
  }
1811
2083
  async function cursorActivity(tz, homeDir) {
1812
2084
  const db = trackingDb(homeDir);
@@ -1904,14 +2176,14 @@ function cursorStateDb(homeDir) {
1904
2176
  const base = homeDir ?? homedir6();
1905
2177
  const tail = ["Cursor", "User", "globalStorage", "state.vscdb"];
1906
2178
  if (process.platform === "darwin") {
1907
- return join9(base, "Library", "Application Support", ...tail);
2179
+ return join10(base, "Library", "Application Support", ...tail);
1908
2180
  }
1909
2181
  if (process.platform === "win32") {
1910
- const roaming = homeDir ? join9(homeDir, "AppData", "Roaming") : envDir("APPDATA") ?? join9(base, "AppData", "Roaming");
1911
- return join9(roaming, ...tail);
2182
+ const roaming = homeDir ? join10(homeDir, "AppData", "Roaming") : envDir("APPDATA") ?? join10(base, "AppData", "Roaming");
2183
+ return join10(roaming, ...tail);
1912
2184
  }
1913
- const cfg = homeDir ? join9(homeDir, ".config") : envDir("XDG_CONFIG_HOME") ?? join9(base, ".config");
1914
- return join9(cfg, ...tail);
2185
+ const cfg = homeDir ? join10(homeDir, ".config") : envDir("XDG_CONFIG_HOME") ?? join10(base, ".config");
2186
+ return join10(cfg, ...tail);
1915
2187
  }
1916
2188
  async function detectCursor(homeDir) {
1917
2189
  try {
@@ -2051,9 +2323,9 @@ async function cursorBillingCore(account) {
2051
2323
  const pu = usage.planUsage ?? {};
2052
2324
  const metrics = [];
2053
2325
  const rawEnd = usage.billingCycleEnd;
2054
- const endMs = numberValue(rawEnd) ?? NaN;
2055
- const iso = msToIso(endMs);
2056
- const resets = iso && endMs > 0 ? resetIn(iso) : null;
2326
+ const endMs = timestampMs(rawEnd);
2327
+ const iso = endMs === null ? null : msToIso(endMs);
2328
+ const resets = iso ? resetIn(iso) : null;
2057
2329
  const limit = numberValue(pu.limit);
2058
2330
  const planUsedCents = numberValue(pu.totalSpend) ?? (limit !== void 0 && numberValue(pu.remaining) !== void 0 ? limit - numberValue(pu.remaining) : void 0);
2059
2331
  const computedPercent = limit !== void 0 && limit > 0 && planUsedCents !== void 0 ? planUsedCents / limit * 100 : void 0;
@@ -2153,8 +2425,8 @@ async function fetchPage(token, startMs, endMs, page) {
2153
2425
  function eventToEntry(e) {
2154
2426
  if (e.kind && SKIP_KINDS.has(e.kind)) return null;
2155
2427
  const rawTs = e.timestamp;
2156
- const ts = typeof rawTs === "number" ? rawTs : typeof rawTs === "string" && rawTs.trim() ? /^\d+$/.test(rawTs.trim()) ? Number(rawTs) : Date.parse(rawTs) : NaN;
2157
- if (!Number.isFinite(ts) || ts <= 0) return null;
2428
+ const ts = timestampMs(rawTs);
2429
+ if (ts === null) return null;
2158
2430
  const tu = e.tokenUsage ?? {};
2159
2431
  const input = safeNum(tu.inputTokens);
2160
2432
  const output = safeNum(tu.outputTokens);
@@ -2216,14 +2488,24 @@ async function fetchApiEntries(homeDir) {
2216
2488
  apiInflight.delete(cacheKey);
2217
2489
  }
2218
2490
  }
2491
+ function localDayTimestamp(label, tz) {
2492
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(label)) return null;
2493
+ const [year, month, date] = label.split("-").map(Number);
2494
+ const base = Date.UTC(year, month - 1, date);
2495
+ if (!Number.isFinite(base)) return null;
2496
+ for (let hour = -14; hour <= 26; hour += 2) {
2497
+ const ts = base + hour * 36e5;
2498
+ if (dayKey(ts, tz) === label) return ts;
2499
+ }
2500
+ return null;
2501
+ }
2219
2502
  async function fetchLocalEntries(tz, homeDir) {
2220
2503
  const table = await cursorUsageTable(tz, homeDir);
2221
2504
  if (!table) return [];
2222
2505
  const out = [];
2223
2506
  for (const day of table.daily) {
2224
- const [y, mo, d] = day.label.split("-").map(Number);
2225
- const ts = Date.UTC(y, mo - 1, d, 12);
2226
- if (!Number.isFinite(ts)) continue;
2507
+ const ts = localDayTimestamp(day.label, tz);
2508
+ if (ts === null) continue;
2227
2509
  for (const b of day.breakdown) {
2228
2510
  out.push({
2229
2511
  day: day.label,
@@ -2277,10 +2559,10 @@ var cursorProvider = {
2277
2559
  import { stat as fsStat4, access as access5 } from "fs/promises";
2278
2560
  import { createReadStream as createReadStream4 } from "fs";
2279
2561
  import { createInterface as createInterface4 } from "readline";
2280
- import { join as join10 } from "path";
2562
+ import { join as join11 } from "path";
2281
2563
  import { homedir as homedir7 } from "os";
2282
2564
  function piSessionsDir(homeDir) {
2283
- return join10(homeDir ?? homedir7(), ".pi", "agent", "sessions");
2565
+ return join11(homeDir ?? homedir7(), ".pi", "agent", "sessions");
2284
2566
  }
2285
2567
  async function detectPi(homeDir) {
2286
2568
  try {
@@ -2290,46 +2572,51 @@ async function detectPi(homeDir) {
2290
2572
  return false;
2291
2573
  }
2292
2574
  }
2293
- function timestampMs3(value) {
2294
- if (typeof value === "number" && Number.isFinite(value)) return value > 1e10 ? value : value * 1e3;
2295
- if (typeof value === "string" && value.trim()) return new Date(value.trim()).getTime();
2296
- return NaN;
2297
- }
2298
2575
  async function parseFile3(path) {
2299
2576
  const entries = [];
2300
- const rl = createInterface4({ input: createReadStream4(path), crlfDelay: Infinity });
2301
- for await (const rawLine of rl) {
2302
- if (!rawLine.includes('"usage"')) continue;
2303
- try {
2304
- const line = rawLine.charCodeAt(0) === 65279 ? rawLine.slice(1) : rawLine;
2305
- const obj = JSON.parse(line);
2306
- if (obj?.type !== "message") continue;
2307
- const msg = obj.message;
2308
- if (msg?.role !== "assistant" || !msg?.usage) continue;
2309
- const u = msg.usage;
2310
- const ts = timestampMs3(obj.timestamp ?? msg.timestamp);
2311
- if (!Number.isFinite(ts)) continue;
2312
- const input = safeNum(u.input);
2313
- const output = safeNum(u.output);
2314
- const cacheRead = safeNum(u.cacheRead);
2315
- const cacheCreate = safeNum(u.cacheWrite);
2316
- if (input + output + cacheRead + cacheCreate === 0) continue;
2317
- const c = u.cost ?? {};
2318
- const costInput = finitePositive(c.input);
2319
- const cacheSavings = input > 0 && cacheRead > 0 ? Math.max(0, cacheRead * (costInput / input) - finitePositive(c.cacheRead)) : 0;
2320
- const model = typeof msg.responseModel === "string" && msg.responseModel || typeof msg.model === "string" && msg.model || "unknown";
2321
- entries.push({
2322
- ts,
2323
- model,
2324
- cost: finitePositive(c.total),
2325
- input,
2326
- output,
2327
- cacheCreate,
2328
- cacheRead,
2329
- cacheSavings
2330
- });
2331
- } catch {
2577
+ const stream = createReadStream4(path);
2578
+ stream.on("error", () => {
2579
+ });
2580
+ const rl = createInterface4({ input: stream, crlfDelay: Infinity });
2581
+ try {
2582
+ for await (const rawLine of rl) {
2583
+ if (!rawLine.includes('"usage"')) continue;
2584
+ try {
2585
+ const line = rawLine.charCodeAt(0) === 65279 ? rawLine.slice(1) : rawLine;
2586
+ const obj = JSON.parse(line);
2587
+ if (obj?.type !== "message") continue;
2588
+ const msg = obj.message;
2589
+ if (msg?.role !== "assistant" || !msg?.usage) continue;
2590
+ const u = msg.usage;
2591
+ const ts = timestampMs(obj.timestamp ?? msg.timestamp);
2592
+ if (ts === null) continue;
2593
+ const input = safeNum(u.input);
2594
+ const output = safeNum(u.output);
2595
+ const cacheRead = safeNum(u.cacheRead);
2596
+ const cacheCreate = safeNum(u.cacheWrite);
2597
+ if (input + output + cacheRead + cacheCreate === 0) continue;
2598
+ const c = u.cost ?? {};
2599
+ const costInput = finitePositive(c.input);
2600
+ const cacheSavings = input > 0 && cacheRead > 0 ? Math.max(0, cacheRead * (costInput / input) - finitePositive(c.cacheRead)) : 0;
2601
+ const model = typeof msg.responseModel === "string" && msg.responseModel || typeof msg.model === "string" && msg.model || "unknown";
2602
+ entries.push({
2603
+ ts,
2604
+ model,
2605
+ cost: finitePositive(c.total),
2606
+ input,
2607
+ output,
2608
+ cacheCreate,
2609
+ cacheRead,
2610
+ cacheSavings
2611
+ });
2612
+ } catch {
2613
+ }
2332
2614
  }
2615
+ } catch {
2616
+ return entries;
2617
+ } finally {
2618
+ rl.close();
2619
+ stream.destroy();
2333
2620
  }
2334
2621
  return entries;
2335
2622
  }
@@ -2340,7 +2627,7 @@ async function loadEntries3(since, homeDir) {
2340
2627
  const listing = await walkFiles(dir);
2341
2628
  for (const f of listing) {
2342
2629
  if (!f.endsWith(".jsonl")) continue;
2343
- const path = join10(dir, f);
2630
+ const path = join11(dir, f);
2344
2631
  try {
2345
2632
  const s = await fsStat4(path);
2346
2633
  if (s.mtimeMs < since) continue;
@@ -2376,17 +2663,17 @@ var piProvider = {
2376
2663
 
2377
2664
  // src/providers/opencode/usage.ts
2378
2665
  import { access as access6 } from "fs/promises";
2379
- import { join as join11 } from "path";
2666
+ import { join as join12 } from "path";
2380
2667
  import { homedir as homedir8 } from "os";
2381
2668
  function opencodeDbPaths(homeDir) {
2382
2669
  const base = homeDir ?? homedir8();
2383
2670
  const paths = [];
2384
- if (!homeDir && process.env.XDG_DATA_HOME) paths.push(join11(process.env.XDG_DATA_HOME, "opencode", "opencode.db"));
2385
- paths.push(join11(base, ".local", "share", "opencode", "opencode.db"));
2386
- if (process.platform === "darwin") paths.push(join11(base, "Library", "Application Support", "opencode", "opencode.db"));
2671
+ if (!homeDir && process.env.XDG_DATA_HOME) paths.push(join12(process.env.XDG_DATA_HOME, "opencode", "opencode.db"));
2672
+ paths.push(join12(base, ".local", "share", "opencode", "opencode.db"));
2673
+ if (process.platform === "darwin") paths.push(join12(base, "Library", "Application Support", "opencode", "opencode.db"));
2387
2674
  if (process.platform === "win32") {
2388
- const lad = homeDir ? join11(homeDir, "AppData", "Local") : process.env.LOCALAPPDATA;
2389
- if (lad) paths.push(join11(lad, "opencode", "opencode.db"));
2675
+ const lad = homeDir ? join12(homeDir, "AppData", "Local") : process.env.LOCALAPPDATA;
2676
+ if (lad) paths.push(join12(lad, "opencode", "opencode.db"));
2390
2677
  }
2391
2678
  return [...new Set(paths)];
2392
2679
  }
@@ -2451,8 +2738,8 @@ var opencodeProvider = {
2451
2738
  };
2452
2739
 
2453
2740
  // src/providers/copilot/billing.ts
2454
- import { access as access7, readFile as readFile4, readdir as readdir3 } from "fs/promises";
2455
- import { join as join12 } from "path";
2741
+ import { access as access7, readFile as readFile4, readdir as readdir4 } from "fs/promises";
2742
+ import { join as join13 } from "path";
2456
2743
  import { homedir as homedir9 } from "os";
2457
2744
  var USAGE_URL3 = "https://api.github.com/copilot_internal/user";
2458
2745
  var GH_KEYCHAIN_SERVICE = "gh:github.com";
@@ -2461,26 +2748,26 @@ function ghConfigDir(homeDir) {
2461
2748
  const explicit = process.env.GH_CONFIG_DIR;
2462
2749
  if (explicit && explicit.trim()) return explicit.trim();
2463
2750
  if (process.platform === "win32") {
2464
- return join12(envDir("APPDATA") ?? join12(homedir9(), "AppData", "Roaming"), "GitHub CLI");
2751
+ return join13(envDir("APPDATA") ?? join13(homedir9(), "AppData", "Roaming"), "GitHub CLI");
2465
2752
  }
2466
2753
  const xdg = envDir("XDG_CONFIG_HOME");
2467
- return xdg ? join12(xdg, "gh") : join12(homedir9(), ".config", "gh");
2754
+ return xdg ? join13(xdg, "gh") : join13(homedir9(), ".config", "gh");
2468
2755
  }
2469
- return process.platform === "win32" ? join12(homeDir, "AppData", "Roaming", "GitHub CLI") : join12(homeDir, ".config", "gh");
2756
+ return process.platform === "win32" ? join13(homeDir, "AppData", "Roaming", "GitHub CLI") : join13(homeDir, ".config", "gh");
2470
2757
  }
2471
2758
  function ghHostsPath(homeDir) {
2472
- return join12(ghConfigDir(homeDir), "hosts.yml");
2759
+ return join13(ghConfigDir(homeDir), "hosts.yml");
2473
2760
  }
2474
2761
  async function detectCopilot(homeDir) {
2475
2762
  const home = homeDir ?? homedir9();
2476
2763
  const candidates = [
2477
- join12(home, ".config", "github-copilot"),
2478
- join12(home, ".copilot"),
2479
- join12(vscodeUserDir(homeDir), "globalStorage", "github.copilot"),
2480
- join12(vscodeUserDir(homeDir), "globalStorage", "github.copilot-chat")
2764
+ join13(home, ".config", "github-copilot"),
2765
+ join13(home, ".copilot"),
2766
+ join13(vscodeUserDir(homeDir), "globalStorage", "github.copilot"),
2767
+ join13(vscodeUserDir(homeDir), "globalStorage", "github.copilot-chat")
2481
2768
  ];
2482
2769
  if (process.platform === "win32") {
2483
- candidates.push(join12(envDir("LOCALAPPDATA") ?? join12(home, "AppData", "Local"), "github-copilot"));
2770
+ candidates.push(join13(envDir("LOCALAPPDATA") ?? join13(home, "AppData", "Local"), "github-copilot"));
2484
2771
  }
2485
2772
  for (const path of candidates) {
2486
2773
  try {
@@ -2537,9 +2824,9 @@ async function loadTokenFromGhKeychain() {
2537
2824
  }
2538
2825
  function vscodeUserDir(homeDir) {
2539
2826
  const home = homeDir ?? homedir9();
2540
- if (process.platform === "darwin") return join12(home, "Library", "Application Support", "Code", "User");
2541
- if (process.platform === "win32") return join12(home, "AppData", "Roaming", "Code", "User");
2542
- return join12(home, ".config", "Code", "User");
2827
+ if (process.platform === "darwin") return join13(home, "Library", "Application Support", "Code", "User");
2828
+ if (process.platform === "win32") return join13(home, "AppData", "Roaming", "Code", "User");
2829
+ return join13(home, ".config", "Code", "User");
2543
2830
  }
2544
2831
  function tokenFromText(raw) {
2545
2832
  const patterns = [
@@ -2556,14 +2843,14 @@ function tokenFromText(raw) {
2556
2843
  async function loadTokenFromVsCode(homeDir) {
2557
2844
  const userDir = vscodeUserDir(homeDir);
2558
2845
  const candidates = [
2559
- join12(userDir, "globalStorage", "github.copilot-chat", "auth.json"),
2560
- join12(userDir, "globalStorage", "github.copilot", "auth.json"),
2561
- join12(userDir, "globalStorage", "state.vscdb")
2846
+ join13(userDir, "globalStorage", "github.copilot-chat", "auth.json"),
2847
+ join13(userDir, "globalStorage", "github.copilot", "auth.json"),
2848
+ join13(userDir, "globalStorage", "state.vscdb")
2562
2849
  ];
2563
2850
  try {
2564
- for (const dirent of await readdir3(join12(userDir, "globalStorage"), { withFileTypes: true })) {
2851
+ for (const dirent of await readdir4(join13(userDir, "globalStorage"), { withFileTypes: true })) {
2565
2852
  if (dirent.isDirectory() && dirent.name.toLowerCase().includes("github")) {
2566
- candidates.push(join12(userDir, "globalStorage", dirent.name, "auth.json"));
2853
+ candidates.push(join13(userDir, "globalStorage", dirent.name, "auth.json"));
2567
2854
  }
2568
2855
  }
2569
2856
  } catch {
@@ -2701,15 +2988,15 @@ var copilotProvider = {
2701
2988
  };
2702
2989
 
2703
2990
  // src/providers/antigravity/billing.ts
2704
- import { access as access8, readdir as readdir5 } from "fs/promises";
2705
- import { join as join14 } from "path";
2991
+ import { access as access8, readdir as readdir6 } from "fs/promises";
2992
+ import { join as join15 } from "path";
2706
2993
  import { homedir as homedir11 } from "os";
2707
2994
 
2708
2995
  // src/providers/cloud-code/auth.ts
2709
- import { readFile as readFile5, readdir as readdir4, realpath, stat } from "fs/promises";
2996
+ import { readFile as readFile5, readdir as readdir5, realpath, stat } from "fs/promises";
2710
2997
  import { spawnSync } from "child_process";
2711
2998
  import { homedir as homedir10 } from "os";
2712
- import { dirname, join as join13 } from "path";
2999
+ import { dirname as dirname2, join as join14 } from "path";
2713
3000
  var GOOGLE_OAUTH_URL = "https://oauth2.googleapis.com/token";
2714
3001
  var GOOGLE_OAUTH_CLIENT_REGEX = /OAUTH_CLIENT_ID\s*=\s*["']([0-9]{6,}-[a-z0-9]+\.apps\.googleusercontent\.com)["']\s*;?\s*(?:var|const|let)?\s*OAUTH_CLIENT_SECRET\s*=\s*["'](GOCSPX-[A-Za-z0-9_-]+)["']/s;
2715
3002
  var MAX_BUNDLE_READ = 32 * 1024 * 1024;
@@ -2813,7 +3100,7 @@ function geminiBundleCandidates() {
2813
3100
  const candidates = [];
2814
3101
  const addBundle = (nodeModulesRoot) => {
2815
3102
  if (!nodeModulesRoot) return;
2816
- candidates.push(join13(nodeModulesRoot, "@google", "gemini-cli", "bundle"));
3103
+ candidates.push(join14(nodeModulesRoot, "@google", "gemini-cli", "bundle"));
2817
3104
  };
2818
3105
  try {
2819
3106
  const which = process.platform === "win32" ? spawnSync("where", ["gemini"], { encoding: "utf8", timeout: 5e3 }) : spawnSync("sh", ["-lc", "command -v gemini"], { encoding: "utf8", timeout: 5e3 });
@@ -2823,12 +3110,12 @@ function geminiBundleCandidates() {
2823
3110
  }
2824
3111
  const home = homedir10();
2825
3112
  if (process.platform === "win32") {
2826
- addBundle(join13(process.env.APPDATA ?? join13(home, "AppData", "Roaming"), "npm", "node_modules"));
3113
+ addBundle(join14(process.env.APPDATA ?? join14(home, "AppData", "Roaming"), "npm", "node_modules"));
2827
3114
  } else {
2828
3115
  addBundle("/opt/homebrew/lib/node_modules");
2829
3116
  addBundle("/usr/local/lib/node_modules");
2830
- addBundle(join13(home, ".local", "share", "node_modules"));
2831
- addBundle(join13(home, ".bun", "install", "global", "node_modules"));
3117
+ addBundle(join14(home, ".local", "share", "node_modules"));
3118
+ addBundle(join14(home, ".bun", "install", "global", "node_modules"));
2832
3119
  }
2833
3120
  try {
2834
3121
  const prefix = spawnSync(process.platform === "win32" ? "npm.cmd" : "npm", ["config", "get", "prefix"], {
@@ -2838,7 +3125,7 @@ function geminiBundleCandidates() {
2838
3125
  });
2839
3126
  const root = typeof prefix.stdout === "string" ? prefix.stdout.trim() : "";
2840
3127
  if (root && root !== "undefined") {
2841
- addBundle(process.platform === "win32" ? join13(root, "node_modules") : join13(root, "lib", "node_modules"));
3128
+ addBundle(process.platform === "win32" ? join14(root, "node_modules") : join14(root, "lib", "node_modules"));
2842
3129
  }
2843
3130
  } catch {
2844
3131
  }
@@ -2846,11 +3133,11 @@ function geminiBundleCandidates() {
2846
3133
  }
2847
3134
  async function resolveBundleDir(candidate) {
2848
3135
  try {
2849
- if (candidate.endsWith(`${join13("@google", "gemini-cli", "bundle")}`)) {
3136
+ if (candidate.endsWith(`${join14("@google", "gemini-cli", "bundle")}`)) {
2850
3137
  return candidate;
2851
3138
  }
2852
3139
  const real = await realpath(candidate);
2853
- return dirname(real);
3140
+ return dirname2(real);
2854
3141
  } catch {
2855
3142
  return null;
2856
3143
  }
@@ -2858,13 +3145,13 @@ async function resolveBundleDir(candidate) {
2858
3145
  async function scanBundleDir(dir) {
2859
3146
  let entries;
2860
3147
  try {
2861
- entries = await readdir4(dir);
3148
+ entries = await readdir5(dir);
2862
3149
  } catch {
2863
3150
  return null;
2864
3151
  }
2865
3152
  const targets = entries.filter((name) => name === "gemini.js" || name.startsWith("chunk-") && name.endsWith(".js"));
2866
3153
  for (const name of targets) {
2867
- const filePath = join13(dir, name);
3154
+ const filePath = join14(dir, name);
2868
3155
  try {
2869
3156
  const info = await stat(filePath);
2870
3157
  if (!info.isFile() || info.size > MAX_BUNDLE_READ) continue;
@@ -3077,7 +3364,7 @@ function sortKey(label) {
3077
3364
  function cloudCodeBucketsToMetrics(buckets, options = {}) {
3078
3365
  const pooled = /* @__PURE__ */ new Map();
3079
3366
  for (const bucket of buckets) {
3080
- if (!Number.isFinite(bucket.remainingFraction)) continue;
3367
+ if (!Number.isFinite(bucket.remainingFraction) || bucket.remainingFraction < 0 || bucket.remainingFraction > 1) continue;
3081
3368
  const label = poolLabel(bucket.modelId, options.fullGeminiLabels === true);
3082
3369
  const existing = pooled.get(label);
3083
3370
  if (!existing || bucket.remainingFraction < existing.remainingFraction) {
@@ -3091,7 +3378,7 @@ function cloudCodeBucketsToMetrics(buckets, options = {}) {
3091
3378
  used: Math.round((1 - clamped) * 100),
3092
3379
  limit: 100,
3093
3380
  format: { kind: "percent" },
3094
- resetsAt: bucket.resetTime ? resetIn(bucket.resetTime) : null,
3381
+ resetsAt: bucket.resetTime && Number.isFinite(Date.parse(bucket.resetTime)) ? resetIn(bucket.resetTime) : null,
3095
3382
  primary: i === 0
3096
3383
  };
3097
3384
  });
@@ -3119,30 +3406,30 @@ async function antigravityStateDb(homeDir) {
3119
3406
  const base = homeDir ?? homedir11();
3120
3407
  const tail = ["User", "globalStorage", "state.vscdb"];
3121
3408
  if (process.platform === "darwin") {
3122
- const support = join14(base, "Library", "Application Support");
3409
+ const support = join15(base, "Library", "Application Support");
3123
3410
  const exact = [
3124
- join14(support, "Antigravity IDE", ...tail),
3125
- join14(support, "Antigravity", ...tail)
3411
+ join15(support, "Antigravity IDE", ...tail),
3412
+ join15(support, "Antigravity", ...tail)
3126
3413
  ];
3127
3414
  try {
3128
- const entries = await readdir5(support, { withFileTypes: true });
3129
- const matches = entries.filter((e) => e.isDirectory() && e.name.includes("Antigravity")).map((e) => join14(support, e.name, ...tail));
3415
+ const entries = await readdir6(support, { withFileTypes: true });
3416
+ const matches = entries.filter((e) => e.isDirectory() && e.name.includes("Antigravity")).map((e) => join15(support, e.name, ...tail));
3130
3417
  return firstExisting([...exact, ...matches]);
3131
3418
  } catch {
3132
3419
  return firstExisting(exact);
3133
3420
  }
3134
3421
  }
3135
3422
  if (process.platform === "win32") {
3136
- const roaming = homeDir ? join14(homeDir, "AppData", "Roaming") : envDir("APPDATA") ?? join14(base, "AppData", "Roaming");
3423
+ const roaming = homeDir ? join15(homeDir, "AppData", "Roaming") : envDir("APPDATA") ?? join15(base, "AppData", "Roaming");
3137
3424
  return firstExisting([
3138
- join14(roaming, "Antigravity IDE", ...tail),
3139
- join14(roaming, "Antigravity", ...tail)
3425
+ join15(roaming, "Antigravity IDE", ...tail),
3426
+ join15(roaming, "Antigravity", ...tail)
3140
3427
  ]);
3141
3428
  }
3142
- const cfg = homeDir ? join14(homeDir, ".config") : envDir("XDG_CONFIG_HOME") ?? join14(base, ".config");
3429
+ const cfg = homeDir ? join15(homeDir, ".config") : envDir("XDG_CONFIG_HOME") ?? join15(base, ".config");
3143
3430
  return firstExisting([
3144
- join14(cfg, "Antigravity IDE", ...tail),
3145
- join14(cfg, "Antigravity", ...tail)
3431
+ join15(cfg, "Antigravity IDE", ...tail),
3432
+ join15(cfg, "Antigravity", ...tail)
3146
3433
  ]);
3147
3434
  }
3148
3435
  async function detectAntigravity(homeDir) {
@@ -3173,41 +3460,47 @@ var antigravityProvider = {
3173
3460
  };
3174
3461
 
3175
3462
  // src/providers/gemini/billing.ts
3176
- import { access as access9, readFile as readFile7, readdir as readdir6 } from "fs/promises";
3177
- import { join as join16 } from "path";
3463
+ import { access as access9, readFile as readFile7, readdir as readdir7 } from "fs/promises";
3464
+ import { join as join17 } from "path";
3178
3465
  import { homedir as homedir13 } from "os";
3179
3466
 
3180
3467
  // src/providers/gemini/usage.ts
3181
3468
  import { readFile as readFile6, stat as fsStat5 } from "fs/promises";
3182
3469
  import { createReadStream as createReadStream5 } from "fs";
3183
3470
  import { createInterface as createInterface5 } from "readline";
3184
- import { join as join15 } from "path";
3471
+ import { join as join16 } from "path";
3185
3472
  import { homedir as homedir12 } from "os";
3186
3473
  var PRICING3 = {
3187
3474
  "gemini-3.1-pro-preview": { in: 2e-6, out: 12e-6, cr: 2e-7 },
3188
3475
  "gemini-3.1-pro": { in: 2e-6, out: 12e-6, cr: 2e-7 },
3189
3476
  "gemini-3-pro-preview": { in: 2e-6, out: 12e-6, cr: 2e-7 },
3190
3477
  "gemini-3-pro": { in: 2e-6, out: 12e-6, cr: 2e-7 },
3191
- "gemini-3.5-flash": { in: 15e-7, out: 9e-6, cr: 15e-8 },
3478
+ "gemini-3.5-flash": { in: 75e-8, out: 45e-7, cr: 75e-9 },
3192
3479
  "gemini-3-flash-preview": { in: 5e-7, out: 3e-6, cr: 5e-8 },
3193
3480
  "gemini-3-flash": { in: 5e-7, out: 3e-6, cr: 5e-8 },
3194
3481
  "gemini-2.5-flash-lite": { in: 1e-7, out: 4e-7, cr: 1e-8 },
3195
- "gemini-3.1-flash-lite": { in: 1e-7, out: 4e-7, cr: 1e-8 },
3482
+ "gemini-3.1-flash-lite": { in: 25e-8, out: 15e-7, cr: 25e-9 },
3196
3483
  "gemini-2.5-flash": { in: 3e-7, out: 25e-7, cr: 3e-8 },
3197
3484
  "gemini-2.5-pro": { in: 125e-8, out: 1e-5, cr: 125e-9 },
3198
3485
  "gemini-2.0-flash": { in: 1e-7, out: 4e-7, cr: 25e-9 }
3199
3486
  };
3200
3487
  var PRICE_KEYS3 = Object.keys(PRICING3).sort((a, b) => b.length - a.length);
3201
3488
  var ZERO_PRICE2 = { in: 0, out: 0, cr: 0 };
3489
+ var GEMINI_31_PRO_LONG_PRICE = { in: 4e-6, out: 18e-6, cr: 4e-7 };
3490
+ var MAX_SESSION_FILE_BYTES = 16 * 1024 * 1024;
3491
+ var MAX_JSON_ENTRIES = 1e5;
3202
3492
  function geminiTmpDir(homeDir) {
3203
- return join15(homeDir ?? homedir12(), ".gemini", "tmp");
3493
+ return join16(homeDir ?? homedir12(), ".gemini", "tmp");
3204
3494
  }
3205
- function priceFor3(model) {
3495
+ function geminiPriceFor(model, promptTokens = 0) {
3206
3496
  const m = model.toLowerCase().trim();
3207
3497
  for (const key of PRICE_KEYS3) {
3208
3498
  if (!m.startsWith(key)) continue;
3209
3499
  const rest = m.slice(key.length);
3210
- if (rest === "" || rest[0] === "-") return PRICING3[key];
3500
+ if (rest === "" || rest[0] === "-") {
3501
+ if (key.startsWith("gemini-3.1-pro") && promptTokens > 2e5) return GEMINI_31_PRO_LONG_PRICE;
3502
+ return PRICING3[key];
3503
+ }
3211
3504
  }
3212
3505
  return ZERO_PRICE2;
3213
3506
  }
@@ -3223,12 +3516,13 @@ function entryFromObject(obj) {
3223
3516
  const ts = Date.parse(obj.timestamp ?? "");
3224
3517
  if (!Number.isFinite(ts)) return null;
3225
3518
  const t = obj.tokens;
3226
- const input = Math.max(0, safeNum(t.input) + safeNum(t.tool) - safeNum(t.cached));
3519
+ const promptTokens = safeNum(t.input) + safeNum(t.tool);
3520
+ const input = Math.max(0, promptTokens - safeNum(t.cached));
3227
3521
  const output = safeNum(t.output) + safeNum(t.thoughts);
3228
3522
  const cacheRead = safeNum(t.cached);
3229
3523
  if (input + output + cacheRead === 0) return null;
3230
3524
  const model = typeof obj.model === "string" && obj.model ? obj.model : "unknown";
3231
- const p = priceFor3(model);
3525
+ const p = geminiPriceFor(model, promptTokens);
3232
3526
  return {
3233
3527
  id: typeof obj.id === "string" ? obj.id : void 0,
3234
3528
  ts,
@@ -3244,6 +3538,7 @@ function entryFromObject(obj) {
3244
3538
  function entriesFromJson(value) {
3245
3539
  const entries = [];
3246
3540
  const visit = (v) => {
3541
+ if (entries.length >= MAX_JSON_ENTRIES) return;
3247
3542
  if (Array.isArray(v)) {
3248
3543
  for (const item of v) visit(item);
3249
3544
  return;
@@ -3285,6 +3580,7 @@ async function parseLineFile(path) {
3285
3580
  async function parseFile4(path) {
3286
3581
  if (path.endsWith(".json")) {
3287
3582
  try {
3583
+ if ((await fsStat5(path)).size > MAX_SESSION_FILE_BYTES) return [];
3288
3584
  const raw = await readFile6(path, "utf-8");
3289
3585
  return entriesFromJson(JSON.parse(raw.charCodeAt(0) === 65279 ? raw.slice(1) : raw));
3290
3586
  } catch {
@@ -3300,7 +3596,7 @@ async function loadEntries5(since, homeDir) {
3300
3596
  const listing = await walkFiles(root);
3301
3597
  for (const f of listing) {
3302
3598
  if (!isGeminiSessionFile(f)) continue;
3303
- const path = join15(root, f);
3599
+ const path = join16(root, f);
3304
3600
  if (seen.has(path)) continue;
3305
3601
  seen.add(path);
3306
3602
  try {
@@ -3328,10 +3624,10 @@ async function geminiTable(tz, homeDir) {
3328
3624
 
3329
3625
  // src/providers/gemini/billing.ts
3330
3626
  function geminiCredsPath(homeDir) {
3331
- return join16(homeDir ?? homedir13(), ".gemini", "oauth_creds.json");
3627
+ return join17(homeDir ?? homedir13(), ".gemini", "oauth_creds.json");
3332
3628
  }
3333
3629
  function geminiDir(homeDir) {
3334
- return join16(homeDir ?? homedir13(), ".gemini");
3630
+ return join17(homeDir ?? homedir13(), ".gemini");
3335
3631
  }
3336
3632
  function authTypeFromSettings(settings) {
3337
3633
  const raw = settings?.security?.auth?.selectedType ?? settings?.selectedAuthType;
@@ -3344,7 +3640,7 @@ function authTypeFromSettings(settings) {
3344
3640
  }
3345
3641
  async function authMethodFromSettings(homeDir) {
3346
3642
  try {
3347
- const raw = await readFile7(join16(geminiDir(homeDir), "settings.json"), "utf8");
3643
+ const raw = await readFile7(join17(geminiDir(homeDir), "settings.json"), "utf8");
3348
3644
  return authTypeFromSettings(JSON.parse(raw));
3349
3645
  } catch {
3350
3646
  return "none";
@@ -3352,12 +3648,12 @@ async function authMethodFromSettings(homeDir) {
3352
3648
  }
3353
3649
  async function hasGeminiApiKeyFile(homeDir) {
3354
3650
  try {
3355
- await access9(join16(geminiDir(homeDir), "api_key"));
3651
+ await access9(join17(geminiDir(homeDir), "api_key"));
3356
3652
  return true;
3357
3653
  } catch {
3358
3654
  }
3359
3655
  try {
3360
- const env = await readFile7(join16(geminiDir(homeDir), ".env"), "utf8");
3656
+ const env = await readFile7(join17(geminiDir(homeDir), ".env"), "utf8");
3361
3657
  return /^\s*GEMINI_API_KEY\s*=/m.test(env);
3362
3658
  } catch {
3363
3659
  return false;
@@ -3385,13 +3681,29 @@ async function detectGemini(homeDir) {
3385
3681
  return oauthOk || await hasGeminiChatSessions(homeDir);
3386
3682
  }
3387
3683
  async function hasGeminiChatSessions(homeDir) {
3388
- let listing;
3389
- try {
3390
- listing = await readdir6(geminiTmpDir(homeDir), { recursive: true });
3391
- } catch {
3392
- return false;
3684
+ const root = geminiTmpDir(homeDir);
3685
+ const stack = [root];
3686
+ let visitedDirs = 0;
3687
+ const maxDirs = 512;
3688
+ while (stack.length > 0 && visitedDirs < maxDirs) {
3689
+ const dir = stack.pop();
3690
+ let entries;
3691
+ try {
3692
+ entries = await readdir7(dir, { withFileTypes: true });
3693
+ } catch {
3694
+ continue;
3695
+ }
3696
+ visitedDirs++;
3697
+ for (const entry of entries) {
3698
+ const child = join17(dir, entry.name);
3699
+ if (entry.isDirectory()) {
3700
+ stack.push(child);
3701
+ } else if (entry.isFile() && /(^|[\\/])chats[\\/]session-.*\.jsonl$/.test(child)) {
3702
+ return true;
3703
+ }
3704
+ }
3393
3705
  }
3394
- return listing.some((path) => /(^|[\\/])chats[\\/]session-.*\.jsonl$/.test(path));
3706
+ return false;
3395
3707
  }
3396
3708
  function geminiIdentity(creds) {
3397
3709
  const tokenPayload = typeof creds?.id_token === "string" && creds.id_token.includes(".") ? decodeBase64UrlJson(creds.id_token.split(".")[1]) : null;
@@ -3446,30 +3758,30 @@ var geminiProvider = {
3446
3758
  };
3447
3759
 
3448
3760
  // src/providers/grok/identity.ts
3449
- import { readFileSync as readFileSync2 } from "fs";
3450
- import { join as join17 } from "path";
3761
+ import { readFileSync as readFileSync3 } from "fs";
3762
+ import { join as join18 } from "path";
3451
3763
  import { homedir as homedir14 } from "os";
3452
3764
  function grokHomes(homeDir) {
3453
3765
  if (homeDir) {
3454
3766
  const base = expandHome(homeDir);
3455
- return [.../* @__PURE__ */ new Set([join17(base, ".grok"), base])];
3767
+ return [.../* @__PURE__ */ new Set([join18(base, ".grok"), base])];
3456
3768
  }
3457
3769
  const homes = [];
3458
3770
  const env = envDir("GROK_HOME");
3459
3771
  if (env) homes.push(env);
3460
- homes.push(join17(homedir14(), ".grok"));
3772
+ homes.push(join18(homedir14(), ".grok"));
3461
3773
  return [...new Set(homes)];
3462
3774
  }
3463
3775
  function grokAuthPaths(homeDir) {
3464
- const explicit = process.env.GROK_AUTH_PATH;
3465
- const paths = grokHomes(homeDir).map((h) => join17(h, "auth.json"));
3776
+ const explicit = homeDir ? void 0 : process.env.GROK_AUTH_PATH;
3777
+ const paths = grokHomes(homeDir).map((h) => join18(h, "auth.json"));
3466
3778
  return explicit ? [explicit, ...paths] : paths;
3467
3779
  }
3468
3780
  function isAuthEntry(v) {
3469
3781
  return !!v && typeof v === "object" && typeof v.key === "string" && !!v.key;
3470
3782
  }
3471
3783
  function readGrokAuth(homeDir) {
3472
- const inline = process.env.GROK_AUTH?.trim();
3784
+ const inline = homeDir ? void 0 : process.env.GROK_AUTH?.trim();
3473
3785
  if (inline) {
3474
3786
  try {
3475
3787
  const parsed = JSON.parse(inline);
@@ -3480,7 +3792,7 @@ function readGrokAuth(homeDir) {
3480
3792
  }
3481
3793
  for (const path of grokAuthPaths(homeDir)) {
3482
3794
  try {
3483
- const parsed = JSON.parse(readFileSync2(path, "utf-8"));
3795
+ const parsed = JSON.parse(readFileSync3(path, "utf-8"));
3484
3796
  const entry = pickAuthEntry(parsed);
3485
3797
  if (entry) return entry;
3486
3798
  } catch {
@@ -3503,9 +3815,11 @@ function pickAuthEntry(parsed) {
3503
3815
  if (entries.length === 0) return null;
3504
3816
  const session = entries.filter((e) => e.key !== "xai::api_key" && !e.key.startsWith("xai::"));
3505
3817
  const pool = session.length ? session : entries;
3506
- return pool.sort(
3507
- (a, b) => Date.parse(b.entry.create_time ?? b.entry.expires_at ?? "") - Date.parse(a.entry.create_time ?? a.entry.expires_at ?? "")
3508
- )[0]?.entry ?? null;
3818
+ const authTime = (entry) => {
3819
+ const value = Date.parse(entry.create_time ?? entry.expires_at ?? "");
3820
+ return Number.isFinite(value) ? value : 0;
3821
+ };
3822
+ return pool.sort((a, b) => authTime(b.entry) - authTime(a.entry))[0]?.entry ?? null;
3509
3823
  }
3510
3824
  function readGrokIdentity(homeDir) {
3511
3825
  const entry = readGrokAuth(homeDir);
@@ -3525,7 +3839,7 @@ function readGrokIdentity(homeDir) {
3525
3839
  function grokClientVersion(homeDir) {
3526
3840
  for (const home of grokHomes(homeDir)) {
3527
3841
  try {
3528
- const v = readFileSync2(join17(home, ".metadata_version"), "utf-8").trim();
3842
+ const v = readFileSync3(join18(home, ".metadata_version"), "utf-8").trim();
3529
3843
  if (v) return v;
3530
3844
  } catch {
3531
3845
  }
@@ -3542,14 +3856,6 @@ function billingBase() {
3542
3856
  function num(v) {
3543
3857
  return numberValue(v) ?? (typeof v === "number" && Number.isFinite(v) ? v : void 0);
3544
3858
  }
3545
- function periodMs(v) {
3546
- if (typeof v === "number" && Number.isFinite(v)) return v > 1e10 ? v : v * 1e3;
3547
- if (typeof v === "string" && v.trim()) {
3548
- const t = Date.parse(v);
3549
- return Number.isFinite(t) ? t : null;
3550
- }
3551
- return null;
3552
- }
3553
3859
  async function grokBilling(account) {
3554
3860
  const identity = readGrokIdentity(account.homeDir);
3555
3861
  const auth = readGrokAuth(account.homeDir);
@@ -3596,7 +3902,7 @@ async function grokBilling(account) {
3596
3902
  const metrics = [];
3597
3903
  const usagePct = num(cfg.creditUsagePercent);
3598
3904
  if (usagePct !== void 0) {
3599
- const end = periodMs(cfg.billingPeriodEnd ?? cfg.currentPeriod?.end ?? cfg.currentPeriod?.billingPeriodEnd);
3905
+ const end = timestampMs(cfg.billingPeriodEnd ?? cfg.currentPeriod?.end ?? cfg.currentPeriod?.billingPeriodEnd);
3600
3906
  metrics.push(percentMetric("Credits", finite(usagePct), end ? msToIso(end) : null, true));
3601
3907
  }
3602
3908
  const monthlyLimit = num(cfg.monthlyLimit);
@@ -3654,10 +3960,11 @@ async function grokBilling(account) {
3654
3960
  }
3655
3961
 
3656
3962
  // src/providers/grok/usage.ts
3657
- import { access as access10, readdir as readdir7, readFile as readFile8, stat as fsStat6 } from "fs/promises";
3963
+ import { access as access10, readdir as readdir8, readFile as readFile8, stat as fsStat6 } from "fs/promises";
3658
3964
  import { createReadStream as createReadStream6 } from "fs";
3965
+ import { createHash as createHash3 } from "crypto";
3659
3966
  import { createInterface as createInterface6 } from "readline";
3660
- import { join as join18 } from "path";
3967
+ import { join as join19 } from "path";
3661
3968
  var PRICING4 = {
3662
3969
  "grok-build-latest": { in: 2e-6, cr: 5e-7, out: 6e-6 },
3663
3970
  "grok-4.5": { in: 2e-6, cr: 5e-7, out: 6e-6 },
@@ -3671,6 +3978,9 @@ var PRICING4 = {
3671
3978
  };
3672
3979
  var FALLBACK_PRICE2 = PRICING4["grok-4.5"];
3673
3980
  var PRICE_KEYS4 = Object.keys(PRICING4).sort((a, b) => b.length - a.length);
3981
+ var MAX_SESSION_GROUPS = 128;
3982
+ var MAX_SESSIONS_PER_GROUP = 512;
3983
+ var MAX_MODEL_FINGERPRINT_ENTRIES = MAX_SESSION_GROUPS * MAX_SESSIONS_PER_GROUP * 2;
3674
3984
  function modelKeyMatches2(model, key) {
3675
3985
  let idx = model.indexOf(key);
3676
3986
  while (idx >= 0) {
@@ -3684,7 +3994,7 @@ function modelKeyMatches2(model, key) {
3684
3994
  }
3685
3995
  return false;
3686
3996
  }
3687
- function priceFor4(model) {
3997
+ function priceFor(model) {
3688
3998
  const m = model.toLowerCase().trim();
3689
3999
  for (const key of PRICE_KEYS4) {
3690
4000
  if (modelKeyMatches2(m, key)) return PRICING4[key];
@@ -3693,7 +4003,7 @@ function priceFor4(model) {
3693
4003
  }
3694
4004
  async function detectGrok(homeDir) {
3695
4005
  for (const home of grokHomes(homeDir)) {
3696
- for (const p of [join18(home, "logs", "unified.jsonl"), join18(home, "sessions"), join18(home, "auth.json"), join18(home, "bin", "grok")]) {
4006
+ for (const p of [join19(home, "logs", "unified.jsonl"), join19(home, "sessions"), join19(home, "auth.json"), join19(home, "bin", "grok")]) {
3697
4007
  try {
3698
4008
  await access10(p);
3699
4009
  return true;
@@ -3705,25 +4015,25 @@ async function detectGrok(homeDir) {
3705
4015
  }
3706
4016
  async function loadSessionModels(home) {
3707
4017
  const out = /* @__PURE__ */ new Map();
3708
- const sessionsRoot = join18(home, "sessions");
4018
+ const sessionsRoot = join19(home, "sessions");
3709
4019
  let groups;
3710
4020
  try {
3711
- groups = await readdir7(sessionsRoot);
4021
+ groups = await readdir8(sessionsRoot);
3712
4022
  } catch {
3713
4023
  return out;
3714
4024
  }
3715
- for (const group of groups) {
4025
+ for (const group of groups.sort().reverse().slice(0, MAX_SESSION_GROUPS)) {
3716
4026
  if (group === "session_search.sqlite" || group.startsWith(".")) continue;
3717
- const groupDir = join18(sessionsRoot, group);
4027
+ const groupDir = join19(sessionsRoot, group);
3718
4028
  let sessions;
3719
4029
  try {
3720
- sessions = await readdir7(groupDir);
4030
+ sessions = await readdir8(groupDir);
3721
4031
  } catch {
3722
4032
  continue;
3723
4033
  }
3724
- for (const sid of sessions) {
4034
+ for (const sid of sessions.sort().reverse().slice(0, MAX_SESSIONS_PER_GROUP)) {
3725
4035
  try {
3726
- const raw = JSON.parse(await readFile8(join18(groupDir, sid, "summary.json"), "utf-8"));
4036
+ const raw = JSON.parse(await readFile8(join19(groupDir, sid, "summary.json"), "utf-8"));
3727
4037
  const model = typeof raw?.current_model_id === "string" && raw.current_model_id ? raw.current_model_id : null;
3728
4038
  if (model) out.set(sid, model);
3729
4039
  } catch {
@@ -3732,8 +4042,26 @@ async function loadSessionModels(home) {
3732
4042
  }
3733
4043
  return out;
3734
4044
  }
4045
+ function grokModelMapFingerprint(modelByLog) {
4046
+ const hash2 = createHash3("sha256");
4047
+ let remaining = MAX_MODEL_FINGERPRINT_ENTRIES;
4048
+ const field = (value) => {
4049
+ hash2.update(String(Buffer.byteLength(value))).update(":").update(value).update("\0");
4050
+ };
4051
+ for (const [logPath, models] of [...modelByLog].sort(([left], [right]) => left.localeCompare(right))) {
4052
+ field(logPath);
4053
+ const entries = [...models].sort(([left], [right]) => left.localeCompare(right));
4054
+ for (const [sessionId, model] of entries.slice(0, remaining)) {
4055
+ field(sessionId);
4056
+ field(model);
4057
+ }
4058
+ remaining -= Math.min(entries.length, remaining);
4059
+ if (remaining === 0) break;
4060
+ }
4061
+ return hash2.digest("hex").slice(0, 24);
4062
+ }
3735
4063
  function costOf2(model, input, cacheRead, output) {
3736
- const p = priceFor4(model);
4064
+ const p = priceFor(model);
3737
4065
  const cost = input * p.in + cacheRead * p.cr + output * p.out;
3738
4066
  const cacheSavings = cacheRead * Math.max(0, p.in - p.cr);
3739
4067
  return { cost, cacheSavings };
@@ -3788,7 +4116,7 @@ async function loadEntries6(since, homeDir) {
3788
4116
  const files = [];
3789
4117
  const modelByHome = /* @__PURE__ */ new Map();
3790
4118
  for (const home of grokHomes(homeDir)) {
3791
- const logPath = join18(home, "logs", "unified.jsonl");
4119
+ const logPath = join19(home, "logs", "unified.jsonl");
3792
4120
  try {
3793
4121
  await access10(logPath);
3794
4122
  } catch {
@@ -3803,7 +4131,8 @@ async function loadEntries6(since, homeDir) {
3803
4131
  return loadCachedEntries(
3804
4132
  files,
3805
4133
  async (path) => parseUnifiedLog(path, modelByHome.get(path) ?? /* @__PURE__ */ new Map()),
3806
- since
4134
+ since,
4135
+ { fingerprint: { parser: `grok-unified-v2.${grokModelMapFingerprint(modelByHome)}` } }
3807
4136
  );
3808
4137
  }
3809
4138
  async function grokDashboard(tz, homeDir) {
@@ -3828,26 +4157,26 @@ var grokProvider = {
3828
4157
 
3829
4158
  // src/providers/detect.ts
3830
4159
  import { accessSync as accessSync2, constants as constants2, existsSync } from "fs";
3831
- import { join as join19, delimiter as delimiter2, isAbsolute as isAbsolute2 } from "path";
4160
+ import { join as join20, delimiter as delimiter2, isAbsolute as isAbsolute2 } from "path";
3832
4161
  import { homedir as homedir15 } from "os";
3833
4162
  function searchDirs() {
3834
4163
  const home = homedir15();
3835
4164
  const fromEnv = (process.env.PATH ?? "").split(delimiter2).filter(Boolean);
3836
4165
  const extra = process.platform === "win32" ? [
3837
- process.env.APPDATA && join19(process.env.APPDATA, "npm"),
3838
- process.env.LOCALAPPDATA && join19(process.env.LOCALAPPDATA, "pnpm"),
3839
- join19(home, "scoop", "shims")
4166
+ process.env.APPDATA && join20(process.env.APPDATA, "npm"),
4167
+ process.env.LOCALAPPDATA && join20(process.env.LOCALAPPDATA, "pnpm"),
4168
+ join20(home, "scoop", "shims")
3840
4169
  ] : [
3841
4170
  "/opt/homebrew/bin",
3842
4171
  "/usr/local/bin",
3843
4172
  "/usr/bin",
3844
4173
  "/bin",
3845
4174
  "/opt/local/bin",
3846
- join19(home, ".local", "bin"),
3847
- join19(home, "bin"),
3848
- join19(home, ".npm-global", "bin"),
3849
- join19(home, ".bun", "bin"),
3850
- join19(home, ".local", "share", "pnpm")
4175
+ join20(home, ".local", "bin"),
4176
+ join20(home, "bin"),
4177
+ join20(home, ".npm-global", "bin"),
4178
+ join20(home, ".bun", "bin"),
4179
+ join20(home, ".local", "share", "pnpm")
3851
4180
  ];
3852
4181
  return [.../* @__PURE__ */ new Set([...fromEnv, ...extra.filter((d) => !!d)])];
3853
4182
  }
@@ -3864,7 +4193,7 @@ function onPath(names) {
3864
4193
  for (const dir of searchDirs()) {
3865
4194
  for (const n of names) {
3866
4195
  for (const e of exts) {
3867
- if (isExec2(join19(dir, n + e))) return true;
4196
+ if (isExec2(join20(dir, n + e))) return true;
3868
4197
  }
3869
4198
  }
3870
4199
  }
@@ -3882,26 +4211,26 @@ function installSignals(id) {
3882
4211
  case "claude":
3883
4212
  return onPath(["claude"]) || anyExists([
3884
4213
  "/Applications/Claude.app",
3885
- join19(home, "Applications", "Claude.app"),
3886
- lad && join19(lad, "Programs", "claude", "Claude.exe")
4214
+ join20(home, "Applications", "Claude.app"),
4215
+ lad && join20(lad, "Programs", "claude", "Claude.exe")
3887
4216
  ]);
3888
4217
  case "codex": {
3889
4218
  const bin = process.env.CODEX_BIN;
3890
4219
  if (bin && isAbsolute2(bin) && isExec2(bin)) return true;
3891
4220
  return onPath(["codex"]) || anyExists([
3892
- lad && join19(lad, "Programs", "OpenAI", "Codex", "bin", "codex.exe"),
3893
- lad && join19(lad, "Programs", "OpenAI", "Codex", "codex.exe"),
3894
- lad && join19(lad, "Programs", "codex", "codex.exe"),
3895
- pf && join19(pf, "OpenAI", "Codex", "bin", "codex.exe")
3896
- ]) || existsSync(join19(home, ".codex", "sessions")) || existsSync(join19(home, ".codex", "auth.json"));
4221
+ lad && join20(lad, "Programs", "OpenAI", "Codex", "bin", "codex.exe"),
4222
+ lad && join20(lad, "Programs", "OpenAI", "Codex", "codex.exe"),
4223
+ lad && join20(lad, "Programs", "codex", "codex.exe"),
4224
+ pf && join20(pf, "OpenAI", "Codex", "bin", "codex.exe")
4225
+ ]) || existsSync(join20(home, ".codex", "sessions")) || existsSync(join20(home, ".codex", "auth.json"));
3897
4226
  }
3898
4227
  case "cursor":
3899
4228
  return onPath(["cursor", "cursor-agent"]) || anyExists([
3900
4229
  "/Applications/Cursor.app",
3901
- join19(home, "Applications", "Cursor.app"),
3902
- lad && join19(lad, "Programs", "cursor", "Cursor.exe"),
3903
- pf && join19(pf, "Cursor", "Cursor.exe"),
3904
- pf86 && join19(pf86, "Cursor", "Cursor.exe"),
4230
+ join20(home, "Applications", "Cursor.app"),
4231
+ lad && join20(lad, "Programs", "cursor", "Cursor.exe"),
4232
+ pf && join20(pf, "Cursor", "Cursor.exe"),
4233
+ pf86 && join20(pf86, "Cursor", "Cursor.exe"),
3905
4234
  "/opt/Cursor/cursor",
3906
4235
  "/usr/share/cursor/cursor",
3907
4236
  "/usr/bin/cursor"
@@ -3913,26 +4242,26 @@ function installSignals(id) {
3913
4242
  case "copilot": {
3914
4243
  const appData = process.env.APPDATA;
3915
4244
  return [
3916
- join19(home, ".config", "github-copilot"),
3917
- join19(home, ".copilot"),
3918
- lad && join19(lad, "github-copilot"),
3919
- appData && join19(appData, "GitHub Copilot"),
3920
- join19(home, ".local", "share", "gh", "extensions", "gh-copilot")
4245
+ join20(home, ".config", "github-copilot"),
4246
+ join20(home, ".copilot"),
4247
+ lad && join20(lad, "github-copilot"),
4248
+ appData && join20(appData, "GitHub Copilot"),
4249
+ join20(home, ".local", "share", "gh", "extensions", "gh-copilot")
3921
4250
  ].some((p) => !!p && existsSync(p));
3922
4251
  }
3923
4252
  case "antigravity":
3924
4253
  return onPath(["antigravity"]) || anyExists([
3925
4254
  "/Applications/Antigravity.app",
3926
- join19(home, "Applications", "Antigravity.app"),
3927
- lad && join19(lad, "Programs", "Antigravity", "Antigravity.exe")
4255
+ join20(home, "Applications", "Antigravity.app"),
4256
+ lad && join20(lad, "Programs", "Antigravity", "Antigravity.exe")
3928
4257
  ]);
3929
4258
  case "gemini":
3930
4259
  return onPath(["gemini"]);
3931
4260
  case "grok":
3932
4261
  return onPath(["grok"]) || anyExists([
3933
- join19(home, ".grok", "bin", "grok"),
3934
- join19(home, ".grok", "bin", "grok.exe")
3935
- ]) || existsSync(join19(home, ".grok", "auth.json")) || existsSync(join19(home, ".grok", "sessions")) || existsSync(join19(home, ".grok", "logs", "unified.jsonl"));
4262
+ join20(home, ".grok", "bin", "grok"),
4263
+ join20(home, ".grok", "bin", "grok.exe")
4264
+ ]) || existsSync(join20(home, ".grok", "auth.json")) || existsSync(join20(home, ".grok", "sessions")) || existsSync(join20(home, ".grok", "logs", "unified.jsonl"));
3936
4265
  default:
3937
4266
  return false;
3938
4267
  }
@@ -3966,20 +4295,20 @@ async function detectProviders() {
3966
4295
  }
3967
4296
 
3968
4297
  // src/accounts.ts
3969
- import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync4, statSync } from "fs";
4298
+ import { existsSync as existsSync2, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync } from "fs";
3970
4299
  import { homedir as homedir16 } from "os";
3971
- import { basename, join as join21, resolve } from "path";
4300
+ import { basename, join as join22, resolve } from "path";
3972
4301
 
3973
4302
  // src/providers/codex/identity.ts
3974
- import { readFileSync as readFileSync3 } from "fs";
3975
- import { join as join20 } from "path";
4303
+ import { readFileSync as readFileSync4 } from "fs";
4304
+ import { join as join21 } from "path";
3976
4305
  function codexAuthPaths(homeDir) {
3977
- return [join20(homeDir, ".codex", "auth.json"), join20(homeDir, "auth.json")];
4306
+ return [join21(homeDir, ".codex", "auth.json"), join21(homeDir, "auth.json")];
3978
4307
  }
3979
4308
  function readCodexIdentity(homeDir) {
3980
4309
  for (const path of codexAuthPaths(homeDir)) {
3981
4310
  try {
3982
- const parsed = JSON.parse(readFileSync3(path, "utf-8"));
4311
+ const parsed = JSON.parse(readFileSync4(path, "utf-8"));
3983
4312
  const { email, displayName, payload } = identityFromIdToken(parsed?.tokens?.id_token);
3984
4313
  if (!payload) continue;
3985
4314
  return { email, displayName };
@@ -4011,13 +4340,13 @@ function uniqueId(base, used) {
4011
4340
  return id;
4012
4341
  }
4013
4342
  function hasClaudeState(homeDir) {
4014
- return existsSync2(join21(homeDir, ".claude.json")) || existsSync2(join21(homeDir, ".claude", ".credentials.json")) || existsSync2(join21(homeDir, ".claude", "projects")) || existsSync2(join21(homeDir, ".config", "claude", ".credentials.json")) || existsSync2(join21(homeDir, ".config", "claude", "projects"));
4343
+ return existsSync2(join22(homeDir, ".claude.json")) || existsSync2(join22(homeDir, ".claude", ".credentials.json")) || existsSync2(join22(homeDir, ".claude", "projects")) || existsSync2(join22(homeDir, ".config", "claude", ".credentials.json")) || existsSync2(join22(homeDir, ".config", "claude", "projects"));
4015
4344
  }
4016
4345
  function candidateAlternateHomes(prefix) {
4017
4346
  const home = homedir16();
4018
4347
  let entries;
4019
4348
  try {
4020
- entries = readdirSync(home);
4349
+ entries = readdirSync2(home);
4021
4350
  } catch {
4022
4351
  return [];
4023
4352
  }
@@ -4025,7 +4354,7 @@ function candidateAlternateHomes(prefix) {
4025
4354
  const pattern = new RegExp(`^\\.${prefix}[_-]`);
4026
4355
  for (const name of entries) {
4027
4356
  if (!pattern.test(name)) continue;
4028
- const path = join21(home, name);
4357
+ const path = join22(home, name);
4029
4358
  try {
4030
4359
  if (!statSync(path).isDirectory()) continue;
4031
4360
  out.push(path);
@@ -4044,7 +4373,7 @@ function labelForClaudeHome(homeDir) {
4044
4373
  function hasCodexAuth(homeDir) {
4045
4374
  for (const path of codexAuthPaths(homeDir)) {
4046
4375
  try {
4047
- const parsed = JSON.parse(readFileSync4(path, "utf-8"));
4376
+ const parsed = JSON.parse(readFileSync5(path, "utf-8"));
4048
4377
  const accessToken = parsed?.tokens?.access_token;
4049
4378
  if (typeof accessToken === "string" && accessToken.trim()) return true;
4050
4379
  } catch {
@@ -4203,9 +4532,9 @@ var modelColorCache = /* @__PURE__ */ new Map();
4203
4532
  function modelColor(name) {
4204
4533
  const cached = modelColorCache.get(name);
4205
4534
  if (cached) return cached;
4206
- let hash = 0;
4207
- for (let i = 0; i < name.length; i++) hash = hash * 31 + name.charCodeAt(i) >>> 0;
4208
- const color = MODEL_PALETTE[hash % MODEL_PALETTE.length];
4535
+ let hash2 = 0;
4536
+ for (let i = 0; i < name.length; i++) hash2 = hash2 * 31 + name.charCodeAt(i) >>> 0;
4537
+ const color = MODEL_PALETTE[hash2 % MODEL_PALETTE.length];
4209
4538
  modelColorCache.set(name, color);
4210
4539
  return color;
4211
4540
  }
@@ -4260,6 +4589,8 @@ import { Schema } from "effect";
4260
4589
  import * as Rpc from "effect/unstable/rpc/Rpc";
4261
4590
  import * as RpcGroup from "effect/unstable/rpc/RpcGroup";
4262
4591
  var TOKMON_WS_PATH = "/ws";
4592
+ var TOKMON_PROTOCOL_VERSION = 2;
4593
+ var TOKMON_CAPABILITIES = ["config-cas", "config-revision"];
4263
4594
  var TOKMON_WS_METHODS = {
4264
4595
  getConfig: "tokmon.getConfig",
4265
4596
  setConfig: "tokmon.setConfig",
@@ -4275,9 +4606,56 @@ var RefreshScopeSchema = Schema.Literals([
4275
4606
  "billing",
4276
4607
  "peak"
4277
4608
  ]);
4278
- var jsonSafePassthrough = () => Schema.Unknown;
4279
- var ConfigSchema = jsonSafePassthrough();
4280
- var ConfigResultSchema = jsonSafePassthrough();
4609
+ var ProviderIdSchema = Schema.Literals(PROVIDER_IDS);
4610
+ var NonNegativeIntegerSchema = Schema.Int.check(Schema.isGreaterThanOrEqualTo(0));
4611
+ var PositiveFiniteSchema = Schema.Finite.check(Schema.isGreaterThanOrEqualTo(1));
4612
+ var AccountSchema = Schema.Struct({
4613
+ id: Schema.String,
4614
+ providerId: ProviderIdSchema,
4615
+ name: Schema.String,
4616
+ homeDir: Schema.String,
4617
+ color: Schema.optionalKey(Schema.String)
4618
+ });
4619
+ var ConfigSchema = Schema.Struct({
4620
+ revision: NonNegativeIntegerSchema,
4621
+ interval: PositiveFiniteSchema,
4622
+ billingInterval: PositiveFiniteSchema,
4623
+ clearScreen: Schema.Boolean,
4624
+ privacyMode: Schema.Boolean,
4625
+ privacyToggleKey: Schema.String,
4626
+ timezone: Schema.NullOr(Schema.String),
4627
+ accounts: Schema.Array(AccountSchema),
4628
+ activeAccountId: Schema.NullOr(Schema.String),
4629
+ disabledProviders: Schema.Array(ProviderIdSchema),
4630
+ onboarded: Schema.Boolean,
4631
+ dashboardLayout: Schema.Literals(["grid", "single"]),
4632
+ defaultFocus: Schema.Literals(["all", "last"]),
4633
+ ascii: Schema.Literals(["auto", "on", "off"]),
4634
+ knownProviders: Schema.Array(ProviderIdSchema)
4635
+ });
4636
+ var ProtocolInfoSchema = Schema.Struct({
4637
+ version: Schema.Literal(TOKMON_PROTOCOL_VERSION),
4638
+ // Capabilities are additive within a protocol version. Older clients must
4639
+ // preserve forward compatibility when a newer daemon advertises one they do
4640
+ // not understand yet.
4641
+ capabilities: Schema.Array(Schema.String)
4642
+ });
4643
+ var ConfigStateSchema = Schema.Struct({
4644
+ protocol: ProtocolInfoSchema,
4645
+ config: ConfigSchema
4646
+ });
4647
+ var ConfigUpdateRequestSchema = Schema.Struct({
4648
+ expectedRevision: NonNegativeIntegerSchema,
4649
+ config: ConfigSchema
4650
+ });
4651
+ var ConfigUpdateConflictSchema = Schema.Struct({
4652
+ kind: Schema.Literal("conflict"),
4653
+ state: ConfigStateSchema
4654
+ });
4655
+ var ConfigPersistenceFailureSchema = Schema.Struct({
4656
+ kind: Schema.Literal("persistence"),
4657
+ message: Schema.String
4658
+ });
4281
4659
  var FsEntrySchema = Schema.Struct({
4282
4660
  name: Schema.String,
4283
4661
  path: Schema.String,
@@ -4288,15 +4666,123 @@ var FsListingSchema = Schema.Struct({
4288
4666
  parent: Schema.NullOr(Schema.String),
4289
4667
  entries: Schema.Array(FsEntrySchema)
4290
4668
  });
4291
- var WebSnapshotSchema = jsonSafePassthrough();
4669
+ var UsageSummarySchema = Schema.Struct({
4670
+ cost: Schema.Finite,
4671
+ tokens: Schema.Finite,
4672
+ cacheRead: Schema.Finite,
4673
+ cacheSavings: Schema.Finite
4674
+ });
4675
+ var ModelDetailSchema = Schema.Struct({
4676
+ name: Schema.String,
4677
+ input: Schema.Finite,
4678
+ output: Schema.Finite,
4679
+ cacheCreate: Schema.Finite,
4680
+ cacheRead: Schema.Finite,
4681
+ cacheSavings: Schema.Finite,
4682
+ cost: Schema.Finite,
4683
+ count: Schema.Finite
4684
+ });
4685
+ var TableRowSchema = Schema.Struct({
4686
+ label: Schema.String,
4687
+ models: Schema.Array(Schema.String),
4688
+ input: Schema.Finite,
4689
+ output: Schema.Finite,
4690
+ cacheCreate: Schema.Finite,
4691
+ cacheRead: Schema.Finite,
4692
+ cacheSavings: Schema.Finite,
4693
+ total: Schema.Finite,
4694
+ cost: Schema.Finite,
4695
+ count: Schema.Finite,
4696
+ breakdown: Schema.Array(ModelDetailSchema)
4697
+ });
4698
+ var DashboardDataSchema = Schema.Struct({
4699
+ today: UsageSummarySchema,
4700
+ week: UsageSummarySchema,
4701
+ month: UsageSummarySchema,
4702
+ burnRate: Schema.Finite,
4703
+ series: Schema.Array(Schema.Finite)
4704
+ });
4705
+ var TableDataSchema = Schema.Struct({
4706
+ daily: Schema.Array(TableRowSchema),
4707
+ weekly: Schema.Array(TableRowSchema),
4708
+ monthly: Schema.Array(TableRowSchema)
4709
+ });
4710
+ var MetricFormatSchema = Schema.Union([
4711
+ Schema.Struct({ kind: Schema.Literal("percent") }),
4712
+ Schema.Struct({ kind: Schema.Literal("dollars"), currency: Schema.optionalKey(Schema.String) }),
4713
+ Schema.Struct({ kind: Schema.Literal("count"), suffix: Schema.optionalKey(Schema.String) })
4714
+ ]);
4715
+ var MetricSchema = Schema.Struct({
4716
+ label: Schema.String,
4717
+ used: Schema.Finite,
4718
+ limit: Schema.NullOr(Schema.Finite),
4719
+ format: MetricFormatSchema,
4720
+ resetsAt: Schema.optionalKey(Schema.NullOr(Schema.String)),
4721
+ primary: Schema.optionalKey(Schema.Boolean)
4722
+ });
4723
+ var BillingResultSchema = Schema.Struct({
4724
+ plan: Schema.NullOr(Schema.String),
4725
+ metrics: Schema.Array(MetricSchema),
4726
+ error: Schema.NullOr(Schema.String),
4727
+ email: Schema.optionalKey(Schema.NullOr(Schema.String)),
4728
+ displayName: Schema.optionalKey(Schema.NullOr(Schema.String)),
4729
+ activity: Schema.optionalKey(Schema.NullOr(Schema.Struct({
4730
+ series: Schema.Array(Schema.Finite),
4731
+ summary: Schema.String
4732
+ }))),
4733
+ modelSpend: Schema.optionalKey(Schema.NullOr(Schema.Array(Schema.Struct({
4734
+ name: Schema.String,
4735
+ usd: Schema.Finite,
4736
+ requests: Schema.Finite
4737
+ }))))
4738
+ });
4739
+ var WebAccountSchema = Schema.Struct({
4740
+ id: Schema.String,
4741
+ providerId: ProviderIdSchema,
4742
+ name: Schema.String,
4743
+ color: Schema.String,
4744
+ homeDir: Schema.NullOr(Schema.String),
4745
+ hasUsage: Schema.Boolean,
4746
+ hasBilling: Schema.Boolean,
4747
+ email: Schema.optionalKey(Schema.NullOr(Schema.String)),
4748
+ displayName: Schema.optionalKey(Schema.NullOr(Schema.String)),
4749
+ plan: Schema.optionalKey(Schema.NullOr(Schema.String)),
4750
+ dashboard: Schema.NullOr(DashboardDataSchema),
4751
+ table: Schema.NullOr(TableDataSchema),
4752
+ billing: Schema.NullOr(BillingResultSchema),
4753
+ summaryState: Schema.Literals(["pending", "ready", "error"]),
4754
+ billingState: Schema.Literals(["pending", "ready", "error"]),
4755
+ tableState: Schema.Literals(["pending", "ready", "error"])
4756
+ });
4757
+ var WebSnapshotSchema = Schema.Struct({
4758
+ version: Schema.String,
4759
+ generatedAt: NonNegativeIntegerSchema,
4760
+ tz: Schema.String,
4761
+ intervalMs: PositiveFiniteSchema,
4762
+ providers: Schema.Array(Schema.Struct({
4763
+ id: ProviderIdSchema,
4764
+ name: Schema.String,
4765
+ color: Schema.String
4766
+ })),
4767
+ accounts: Schema.Array(WebAccountSchema),
4768
+ seeded: Schema.Boolean,
4769
+ peak: Schema.NullOr(Schema.Struct({
4770
+ state: Schema.Literals(["peak", "off-peak", "weekend"]),
4771
+ label: Schema.String,
4772
+ // The upstream clock is only normalized to a finite number; do not make
4773
+ // the wire contract narrower than the producer's declared type.
4774
+ minutesUntilChange: Schema.NullOr(Schema.Finite)
4775
+ }))
4776
+ });
4292
4777
  var EmptyPayloadSchema = Schema.Struct({});
4293
4778
  var GetConfigRpc = Rpc.make(TOKMON_WS_METHODS.getConfig, {
4294
4779
  payload: EmptyPayloadSchema,
4295
- success: ConfigResultSchema
4780
+ success: ConfigStateSchema
4296
4781
  });
4297
4782
  var SetConfigRpc = Rpc.make(TOKMON_WS_METHODS.setConfig, {
4298
- payload: ConfigSchema,
4299
- success: ConfigResultSchema
4783
+ payload: ConfigUpdateRequestSchema,
4784
+ success: ConfigStateSchema,
4785
+ error: Schema.Union([ConfigUpdateConflictSchema, ConfigPersistenceFailureSchema])
4300
4786
  });
4301
4787
  var RefreshRpc = Rpc.make(TOKMON_WS_METHODS.refresh, {
4302
4788
  payload: Schema.Struct({ scope: RefreshScopeSchema }),
@@ -4313,7 +4799,7 @@ var SnapshotRpc = Rpc.make(TOKMON_WS_METHODS.snapshot, {
4313
4799
  });
4314
4800
  var ConfigRpc = Rpc.make(TOKMON_WS_METHODS.config, {
4315
4801
  payload: EmptyPayloadSchema,
4316
- success: ConfigResultSchema,
4802
+ success: ConfigStateSchema,
4317
4803
  stream: true
4318
4804
  });
4319
4805
  var TokmonRpcGroup = RpcGroup.make(
@@ -4346,6 +4832,8 @@ export {
4346
4832
  withTimeout,
4347
4833
  fetchPeak,
4348
4834
  TOKMON_WS_PATH,
4835
+ TOKMON_PROTOCOL_VERSION,
4836
+ TOKMON_CAPABILITIES,
4349
4837
  TOKMON_WS_METHODS,
4350
4838
  TokmonRpcGroup
4351
4839
  };