tokmon 0.22.7 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/{bootstrap-ink-OHPQZKGB.js → bootstrap-ink-UD3KDTOB.js} +1113 -818
- package/dist/{chunk-EMQL5P5J.js → chunk-AON6GE7D.js} +368 -280
- package/dist/{chunk-TNIADJ3W.js → chunk-DJPUYMZM.js} +49 -16
- package/dist/chunk-HV6AYQDB.js +192 -0
- package/dist/chunk-HXDAOOSI.js +154 -0
- package/dist/{chunk-I4USANOF.js → chunk-PGQK7MOE.js} +879 -391
- package/dist/cli.js +93 -54
- package/dist/{config-WLRQHLVJ.js → config-5CNJN3P7.js} +1 -1
- package/dist/daemon-AW3YHFSJ.js +193 -0
- package/dist/daemon-handle-EYAXNNKZ.js +126 -0
- package/dist/server-4CDZMLH6.js +10 -0
- package/dist/web/assets/Area-DGo9K-U3.js +2 -0
- package/dist/web/assets/analytics-BOhbhV5Z.js +2 -0
- package/dist/web/assets/breakdown-CO04nSN-.js +4 -0
- package/dist/web/assets/chart-bNxdfXxA.js +69 -0
- package/dist/web/assets/explore-sZnmjzAj.js +22 -0
- package/dist/web/assets/index-DSj-Bxov.css +1 -0
- package/dist/web/assets/index-DnDr1VUr.js +83 -0
- package/dist/web/assets/models-Cl6D8VVF.js +2 -0
- package/dist/web/assets/overview-C18nvaAK.js +2 -0
- package/dist/web/assets/panel-DTjMnHaz.js +1 -0
- package/dist/web/assets/primitives-Dj_9YxTF.js +1 -0
- package/dist/web/assets/settings-sheet-0BGb5Ahv.js +1 -0
- package/dist/web/assets/share-sheet-BHtt-OmT.js +2 -0
- package/dist/web/assets/timeline-DmzB8Z3W.js +1 -0
- package/dist/web/assets/use-dialog-trap-DeXFtxx8.js +1 -0
- package/dist/web/index.html +3 -2
- package/package.json +9 -5
- package/dist/daemon-IP3UCTPB.js +0 -213
- package/dist/daemon-handle-HLSKLMWU.js +0 -142
- package/dist/server-GA2FKYDQ.js +0 -9
- package/dist/web/assets/breakdown-DU2j1sQ8.js +0 -4
- package/dist/web/assets/index-BCaPGYQA.css +0 -1
- package/dist/web/assets/index-ygZyXDLT.js +0 -173
- package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-500-normal-DJqRU3vO.woff +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-500-normal-DmUKJPL_.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-700-normal-BWTpRfYl.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-700-normal-CEoEElIJ.woff +0 -0
- package/dist/web/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
- package/dist/web/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-greek-500-normal-D7SFKleX.woff +0 -0
- package/dist/web/assets/jetbrains-mono-greek-500-normal-JpySY46c.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-greek-700-normal-C6CZE3T8.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-greek-700-normal-DEigVDxa.woff +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-500-normal-Cut-4mMH.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-500-normal-ckzbgY84.woff +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-700-normal-CZipNAKV.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-700-normal-CxPITLHs.woff +0 -0
- package/dist/web/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
- package/dist/web/assets/jetbrains-mono-vietnamese-500-normal-DNRqzVM1.woff +0 -0
- package/dist/web/assets/jetbrains-mono-vietnamese-700-normal-BDLVIk2r.woff +0 -0
- 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-
|
|
9
|
+
} from "./chunk-DJPUYMZM.js";
|
|
10
10
|
|
|
11
11
|
// src/providers/usage-core.ts
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
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
|
|
155
|
-
var
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
function
|
|
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
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
const
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
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
|
-
|
|
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 {
|
|
445
|
+
return { mods, rows };
|
|
189
446
|
}
|
|
190
|
-
function decode(
|
|
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
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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
|
-
|
|
495
|
+
return store;
|
|
233
496
|
}
|
|
234
497
|
async function flushDisk() {
|
|
235
|
-
|
|
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 ?
|
|
505
|
+
const dir = rel ? join2(root, rel) : root;
|
|
267
506
|
let entries;
|
|
268
507
|
try {
|
|
269
|
-
entries = await
|
|
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 ?
|
|
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
|
|
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
|
-
|
|
527
|
+
async function loadCachedEntries(files, parse, since, options) {
|
|
528
|
+
const store = storeFor(parse, options);
|
|
290
529
|
const chunks = [];
|
|
291
|
-
await
|
|
530
|
+
await mapLimit2(files, 8, async (f) => {
|
|
292
531
|
try {
|
|
293
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
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 [
|
|
836
|
+
return [join3(homeDir, ".claude"), join3(homeDir, ".config", "claude")];
|
|
583
837
|
}
|
|
584
838
|
const home = homedir();
|
|
585
|
-
const dirs = [
|
|
839
|
+
const dirs = [join3(home, ".claude")];
|
|
586
840
|
const xdg = envDir("XDG_CONFIG_HOME");
|
|
587
841
|
if (xdg) {
|
|
588
|
-
dirs.push(
|
|
842
|
+
dirs.push(join3(xdg, "claude"));
|
|
589
843
|
}
|
|
590
844
|
if (process.platform !== "win32") {
|
|
591
|
-
dirs.push(
|
|
845
|
+
dirs.push(join3(home, ".config", "claude"));
|
|
592
846
|
}
|
|
593
847
|
const appData = envDir("APPDATA");
|
|
594
|
-
if (appData) dirs.push(
|
|
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) =>
|
|
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
|
|
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] === "-")
|
|
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 =
|
|
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 (
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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(
|
|
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 (
|
|
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(
|
|
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 =
|
|
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:
|
|
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
|
|
1166
|
+
if (!shared) return { auth };
|
|
917
1167
|
const identity = await tokenIdentity(auth.token);
|
|
918
|
-
if (identity
|
|
919
|
-
if (identity
|
|
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
|
|
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([
|
|
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(
|
|
1112
|
-
homes.push(
|
|
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(
|
|
1374
|
+
await access3(join6(home, "sessions"));
|
|
1119
1375
|
return true;
|
|
1120
1376
|
} catch {
|
|
1121
1377
|
}
|
|
1122
1378
|
try {
|
|
1123
|
-
await access3(
|
|
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
|
|
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
|
-
|
|
1203
|
-
|
|
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
|
|
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 (
|
|
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 =
|
|
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 =
|
|
1325
|
-
if (
|
|
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 =
|
|
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 [
|
|
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 =
|
|
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
|
|
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
|
|
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"
|
|
1407
|
-
const
|
|
1408
|
-
if (
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1600
|
-
let
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
2179
|
+
return join10(base, "Library", "Application Support", ...tail);
|
|
1908
2180
|
}
|
|
1909
2181
|
if (process.platform === "win32") {
|
|
1910
|
-
const roaming = homeDir ?
|
|
1911
|
-
return
|
|
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 ?
|
|
1914
|
-
return
|
|
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 =
|
|
2055
|
-
const iso = msToIso(endMs);
|
|
2056
|
-
const resets = iso
|
|
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 =
|
|
2157
|
-
if (
|
|
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
|
|
2225
|
-
|
|
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
|
|
2562
|
+
import { join as join11 } from "path";
|
|
2281
2563
|
import { homedir as homedir7 } from "os";
|
|
2282
2564
|
function piSessionsDir(homeDir) {
|
|
2283
|
-
return
|
|
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
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
if (
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
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 =
|
|
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
|
|
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(
|
|
2385
|
-
paths.push(
|
|
2386
|
-
if (process.platform === "darwin") paths.push(
|
|
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 ?
|
|
2389
|
-
if (lad) paths.push(
|
|
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
|
|
2455
|
-
import { join as
|
|
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
|
|
2751
|
+
return join13(envDir("APPDATA") ?? join13(homedir9(), "AppData", "Roaming"), "GitHub CLI");
|
|
2465
2752
|
}
|
|
2466
2753
|
const xdg = envDir("XDG_CONFIG_HOME");
|
|
2467
|
-
return xdg ?
|
|
2754
|
+
return xdg ? join13(xdg, "gh") : join13(homedir9(), ".config", "gh");
|
|
2468
2755
|
}
|
|
2469
|
-
return process.platform === "win32" ?
|
|
2756
|
+
return process.platform === "win32" ? join13(homeDir, "AppData", "Roaming", "GitHub CLI") : join13(homeDir, ".config", "gh");
|
|
2470
2757
|
}
|
|
2471
2758
|
function ghHostsPath(homeDir) {
|
|
2472
|
-
return
|
|
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
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
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(
|
|
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
|
|
2541
|
-
if (process.platform === "win32") return
|
|
2542
|
-
return
|
|
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
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
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
|
|
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(
|
|
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
|
|
2705
|
-
import { join as
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
2831
|
-
addBundle(
|
|
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" ?
|
|
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(`${
|
|
3136
|
+
if (candidate.endsWith(`${join14("@google", "gemini-cli", "bundle")}`)) {
|
|
2850
3137
|
return candidate;
|
|
2851
3138
|
}
|
|
2852
3139
|
const real = await realpath(candidate);
|
|
2853
|
-
return
|
|
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
|
|
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 =
|
|
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 =
|
|
3409
|
+
const support = join15(base, "Library", "Application Support");
|
|
3123
3410
|
const exact = [
|
|
3124
|
-
|
|
3125
|
-
|
|
3411
|
+
join15(support, "Antigravity IDE", ...tail),
|
|
3412
|
+
join15(support, "Antigravity", ...tail)
|
|
3126
3413
|
];
|
|
3127
3414
|
try {
|
|
3128
|
-
const entries = await
|
|
3129
|
-
const matches = entries.filter((e) => e.isDirectory() && e.name.includes("Antigravity")).map((e) =>
|
|
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 ?
|
|
3423
|
+
const roaming = homeDir ? join15(homeDir, "AppData", "Roaming") : envDir("APPDATA") ?? join15(base, "AppData", "Roaming");
|
|
3137
3424
|
return firstExisting([
|
|
3138
|
-
|
|
3139
|
-
|
|
3425
|
+
join15(roaming, "Antigravity IDE", ...tail),
|
|
3426
|
+
join15(roaming, "Antigravity", ...tail)
|
|
3140
3427
|
]);
|
|
3141
3428
|
}
|
|
3142
|
-
const cfg = homeDir ?
|
|
3429
|
+
const cfg = homeDir ? join15(homeDir, ".config") : envDir("XDG_CONFIG_HOME") ?? join15(base, ".config");
|
|
3143
3430
|
return firstExisting([
|
|
3144
|
-
|
|
3145
|
-
|
|
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
|
|
3177
|
-
import { join as
|
|
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
|
|
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:
|
|
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:
|
|
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
|
|
3493
|
+
return join16(homeDir ?? homedir12(), ".gemini", "tmp");
|
|
3204
3494
|
}
|
|
3205
|
-
function
|
|
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] === "-")
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
3627
|
+
return join17(homeDir ?? homedir13(), ".gemini", "oauth_creds.json");
|
|
3332
3628
|
}
|
|
3333
3629
|
function geminiDir(homeDir) {
|
|
3334
|
-
return
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
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
|
|
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
|
|
3450
|
-
import { join as
|
|
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([
|
|
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(
|
|
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) =>
|
|
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(
|
|
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
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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 [
|
|
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 =
|
|
4018
|
+
const sessionsRoot = join19(home, "sessions");
|
|
3709
4019
|
let groups;
|
|
3710
4020
|
try {
|
|
3711
|
-
groups = await
|
|
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 =
|
|
4027
|
+
const groupDir = join19(sessionsRoot, group);
|
|
3718
4028
|
let sessions;
|
|
3719
4029
|
try {
|
|
3720
|
-
sessions = await
|
|
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(
|
|
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 =
|
|
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 =
|
|
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
|
|
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 &&
|
|
3838
|
-
process.env.LOCALAPPDATA &&
|
|
3839
|
-
|
|
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
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
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(
|
|
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
|
-
|
|
3886
|
-
lad &&
|
|
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 &&
|
|
3893
|
-
lad &&
|
|
3894
|
-
lad &&
|
|
3895
|
-
pf &&
|
|
3896
|
-
]) || existsSync(
|
|
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
|
-
|
|
3902
|
-
lad &&
|
|
3903
|
-
pf &&
|
|
3904
|
-
pf86 &&
|
|
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
|
-
|
|
3917
|
-
|
|
3918
|
-
lad &&
|
|
3919
|
-
appData &&
|
|
3920
|
-
|
|
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
|
-
|
|
3927
|
-
lad &&
|
|
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
|
-
|
|
3934
|
-
|
|
3935
|
-
]) || existsSync(
|
|
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
|
|
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
|
|
4300
|
+
import { basename, join as join22, resolve } from "path";
|
|
3972
4301
|
|
|
3973
4302
|
// src/providers/codex/identity.ts
|
|
3974
|
-
import { readFileSync as
|
|
3975
|
-
import { join as
|
|
4303
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
4304
|
+
import { join as join21 } from "path";
|
|
3976
4305
|
function codexAuthPaths(homeDir) {
|
|
3977
|
-
return [
|
|
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(
|
|
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(
|
|
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 =
|
|
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 =
|
|
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(
|
|
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
|
|
4207
|
-
for (let i = 0; i < name.length; i++)
|
|
4208
|
-
const color = MODEL_PALETTE[
|
|
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
|
|
4279
|
-
var
|
|
4280
|
-
var
|
|
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
|
|
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:
|
|
4780
|
+
success: ConfigStateSchema
|
|
4296
4781
|
});
|
|
4297
4782
|
var SetConfigRpc = Rpc.make(TOKMON_WS_METHODS.setConfig, {
|
|
4298
|
-
payload:
|
|
4299
|
-
success:
|
|
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:
|
|
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
|
};
|