tokmon 0.21.0 → 0.22.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{bootstrap-ink-QHJOOWIW.js → bootstrap-ink-CCESIGED.js} +121 -39
- package/dist/{chunk-7MFZMI5C.js → chunk-E25RUTXB.js} +12 -0
- package/dist/{chunk-YCIUMAKK.js → chunk-FLLNU6BN.js} +380 -123
- package/dist/{chunk-CPN67OK6.js → chunk-RA3SCIG6.js} +4 -52
- package/dist/cli.js +4 -4
- package/dist/{config-UPNBGIJO.js → config-F7TY5ZFA.js} +5 -1
- package/dist/{daemon-LZVP4SWF.js → daemon-A3NSDSPZ.js} +3 -3
- package/dist/server-P7T4ZMC6.js +9 -0
- package/dist/web/assets/breakdown-C3Zqnewe.js +4 -0
- package/dist/web/assets/{index-CZOCHnad.css → index-BCaPGYQA.css} +1 -1
- package/dist/web/assets/index-BkdjevbO.js +173 -0
- package/dist/web/assets/timeline-C2vHjZJ3.js +1 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/server-F7CUK4ZM.js +0 -9
- package/dist/web/assets/breakdown-ClwCJce8.js +0 -4
- package/dist/web/assets/chart-Dcuh_e3J.js +0 -69
- package/dist/web/assets/index-nAr5lqix.js +0 -105
- package/dist/web/assets/timeline-gDyrCQ0O.js +0 -2
|
@@ -6,10 +6,10 @@ import {
|
|
|
6
6
|
expandHome,
|
|
7
7
|
isValidTimezone,
|
|
8
8
|
slugify
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-E25RUTXB.js";
|
|
10
10
|
|
|
11
11
|
// src/providers/usage-core.ts
|
|
12
|
-
import { readFile, writeFile, rename, mkdir } from "fs/promises";
|
|
12
|
+
import { readFile, writeFile, rename, mkdir, readdir, unlink } from "fs/promises";
|
|
13
13
|
import { join } from "path";
|
|
14
14
|
|
|
15
15
|
// src/tz.ts
|
|
@@ -149,16 +149,30 @@ var dollars = (cents) => finite(cents) / 100;
|
|
|
149
149
|
// src/providers/usage-core.ts
|
|
150
150
|
var SPARK_DAYS = 14;
|
|
151
151
|
var DAY_MS = 864e5;
|
|
152
|
-
var CACHE_VERSION =
|
|
153
|
-
var
|
|
154
|
-
var PRUNE_AGE_MS = 200 * DAY_MS;
|
|
152
|
+
var CACHE_VERSION = 8;
|
|
153
|
+
var PRUNE_AGE_MS = 230 * DAY_MS;
|
|
155
154
|
var memCache = /* @__PURE__ */ new Map();
|
|
156
155
|
var diskLoaded = false;
|
|
156
|
+
var diskLoadPromise = null;
|
|
157
157
|
var dirty = false;
|
|
158
158
|
var flushTimer = null;
|
|
159
159
|
function cacheFile() {
|
|
160
160
|
return join(cacheDir(), `usage-v${CACHE_VERSION}.json`);
|
|
161
161
|
}
|
|
162
|
+
async function cleanupOldCacheFiles() {
|
|
163
|
+
try {
|
|
164
|
+
const dir = cacheDir();
|
|
165
|
+
const names = await readdir(dir);
|
|
166
|
+
await Promise.all(names.map(async (name) => {
|
|
167
|
+
const version = /^usage-v(\d+)\.json$/.exec(name);
|
|
168
|
+
const oldVersion = version ? Number(version[1]) !== CACHE_VERSION : false;
|
|
169
|
+
const tmp = /^usage-v\d+\.json\..*\.tmp$/.test(name);
|
|
170
|
+
if (oldVersion || tmp) await unlink(join(dir, name)).catch(() => {
|
|
171
|
+
});
|
|
172
|
+
}));
|
|
173
|
+
} catch {
|
|
174
|
+
}
|
|
175
|
+
}
|
|
162
176
|
function encode(mtimeMs, size, entries) {
|
|
163
177
|
const mods = [];
|
|
164
178
|
const idx = /* @__PURE__ */ new Map();
|
|
@@ -197,23 +211,33 @@ function decode(s) {
|
|
|
197
211
|
}
|
|
198
212
|
async function ensureDiskLoaded() {
|
|
199
213
|
if (diskLoaded) return;
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
214
|
+
if (!diskLoadPromise) {
|
|
215
|
+
diskLoadPromise = (async () => {
|
|
216
|
+
try {
|
|
217
|
+
const obj = JSON.parse(await readFile(cacheFile(), "utf-8"));
|
|
218
|
+
for (const [path, s] of Object.entries(obj)) {
|
|
219
|
+
if (s && typeof s.m === "number" && Array.isArray(s.rows) && Array.isArray(s.mods)) {
|
|
220
|
+
memCache.set(path, { mtimeMs: s.m, size: typeof s.s === "number" ? s.s : -1, entries: decode(s) });
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
} catch {
|
|
224
|
+
} finally {
|
|
225
|
+
await cleanupOldCacheFiles();
|
|
226
|
+
diskLoaded = true;
|
|
206
227
|
}
|
|
207
|
-
}
|
|
208
|
-
|
|
228
|
+
})().finally(() => {
|
|
229
|
+
diskLoadPromise = null;
|
|
230
|
+
});
|
|
209
231
|
}
|
|
232
|
+
await diskLoadPromise;
|
|
210
233
|
}
|
|
211
234
|
async function flushDisk() {
|
|
212
235
|
if (!dirty) return;
|
|
236
|
+
dirty = false;
|
|
213
237
|
const now = Date.now();
|
|
214
238
|
const obj = {};
|
|
215
239
|
for (const [path, v] of memCache) {
|
|
216
|
-
if (now - v.mtimeMs
|
|
240
|
+
if (now - v.mtimeMs < PRUNE_AGE_MS) {
|
|
217
241
|
obj[path] = encode(v.mtimeMs, v.size, v.entries);
|
|
218
242
|
}
|
|
219
243
|
}
|
|
@@ -222,8 +246,8 @@ async function flushDisk() {
|
|
|
222
246
|
const tmp = `${cacheFile()}.${process.pid}.tmp`;
|
|
223
247
|
await writeFile(tmp, JSON.stringify(obj));
|
|
224
248
|
await rename(tmp, cacheFile());
|
|
225
|
-
dirty = false;
|
|
226
249
|
} catch {
|
|
250
|
+
dirty = true;
|
|
227
251
|
}
|
|
228
252
|
}
|
|
229
253
|
function scheduleFlush() {
|
|
@@ -231,9 +255,29 @@ function scheduleFlush() {
|
|
|
231
255
|
flushTimer = setTimeout(() => {
|
|
232
256
|
flushTimer = null;
|
|
233
257
|
void flushDisk();
|
|
234
|
-
},
|
|
258
|
+
}, 3e4);
|
|
235
259
|
flushTimer.unref?.();
|
|
236
260
|
}
|
|
261
|
+
async function walkFiles(root) {
|
|
262
|
+
const files = [];
|
|
263
|
+
const stack = [""];
|
|
264
|
+
while (stack.length > 0) {
|
|
265
|
+
const rel = stack.pop() ?? "";
|
|
266
|
+
const dir = rel ? join(root, rel) : root;
|
|
267
|
+
let entries;
|
|
268
|
+
try {
|
|
269
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
270
|
+
} catch {
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
for (const entry of entries) {
|
|
274
|
+
const child = rel ? join(rel, entry.name) : entry.name;
|
|
275
|
+
if (entry.isDirectory()) stack.push(child);
|
|
276
|
+
else if (entry.isFile()) files.push(child);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return files;
|
|
280
|
+
}
|
|
237
281
|
async function mapLimit(items, limit, fn) {
|
|
238
282
|
let i = 0;
|
|
239
283
|
const worker = async () => {
|
|
@@ -251,7 +295,7 @@ async function loadCachedEntries(files, parse, since) {
|
|
|
251
295
|
const entries = await parse(f.path);
|
|
252
296
|
c = { mtimeMs: f.mtimeMs, size: f.size, entries };
|
|
253
297
|
memCache.set(f.path, c);
|
|
254
|
-
|
|
298
|
+
dirty = true;
|
|
255
299
|
}
|
|
256
300
|
chunks.push(c.entries);
|
|
257
301
|
} catch {
|
|
@@ -289,17 +333,14 @@ function cleanEntry(e) {
|
|
|
289
333
|
};
|
|
290
334
|
}
|
|
291
335
|
function dedupe(entries) {
|
|
292
|
-
const
|
|
293
|
-
const out = [];
|
|
336
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
294
337
|
for (const raw of entries) {
|
|
295
338
|
const e = cleanEntry(raw);
|
|
296
339
|
if (e.ts <= 0) continue;
|
|
297
340
|
const k = e.id ?? `${e.ts} ${e.model} ${e.input} ${e.output} ${e.cacheCreate} ${e.cacheRead} ${e.cost}`;
|
|
298
|
-
|
|
299
|
-
seen.add(k);
|
|
300
|
-
out.push(e);
|
|
341
|
+
byKey.set(k, e);
|
|
301
342
|
}
|
|
302
|
-
return
|
|
343
|
+
return [...byKey.values()];
|
|
303
344
|
}
|
|
304
345
|
function summarize(entries, tz) {
|
|
305
346
|
const now = Date.now();
|
|
@@ -992,7 +1033,7 @@ async function cursorUsageTable(tz, homeDir) {
|
|
|
992
1033
|
}
|
|
993
1034
|
|
|
994
1035
|
// src/providers/claude/usage.ts
|
|
995
|
-
import {
|
|
1036
|
+
import { stat as fsStat, access as access2 } from "fs/promises";
|
|
996
1037
|
import { createReadStream } from "fs";
|
|
997
1038
|
import { createInterface } from "readline";
|
|
998
1039
|
import { join as join5, isAbsolute } from "path";
|
|
@@ -1004,7 +1045,8 @@ var PRICING = {
|
|
|
1004
1045
|
"claude-opus-4": { i: 5e-6, o: 25e-6, cc: 625e-8, cr: 5e-7 },
|
|
1005
1046
|
"claude-3-opus": { i: 15e-6, o: 75e-6, cc: 1875e-8, cr: 15e-7 },
|
|
1006
1047
|
"claude-sonnet-4": { i: 3e-6, o: 15e-6, cc: 375e-8, cr: 3e-7 },
|
|
1007
|
-
|
|
1048
|
+
// intro pricing through 2026-08-31 — revert to 3/15/3.75/0.3 after.
|
|
1049
|
+
"claude-sonnet-5": { i: 2e-6, o: 1e-5, cc: 25e-7, cr: 2e-7 },
|
|
1008
1050
|
"claude-haiku-4": { i: 1e-6, o: 5e-6, cc: 125e-8, cr: 1e-7 },
|
|
1009
1051
|
"claude-fable-5": { i: 1e-5, o: 5e-5, cc: 125e-7, cr: 1e-6 }
|
|
1010
1052
|
};
|
|
@@ -1019,7 +1061,8 @@ function claudeConfigDirs(homeDir) {
|
|
|
1019
1061
|
const xdg = envDir("XDG_CONFIG_HOME");
|
|
1020
1062
|
if (xdg) {
|
|
1021
1063
|
dirs.push(join5(xdg, "claude"));
|
|
1022
|
-
}
|
|
1064
|
+
}
|
|
1065
|
+
if (process.platform !== "win32") {
|
|
1023
1066
|
dirs.push(join5(home, ".config", "claude"));
|
|
1024
1067
|
}
|
|
1025
1068
|
const appData = envDir("APPDATA");
|
|
@@ -1054,9 +1097,10 @@ function priceFor(model) {
|
|
|
1054
1097
|
}
|
|
1055
1098
|
return ZERO_PRICE;
|
|
1056
1099
|
}
|
|
1057
|
-
function costOf(model, u) {
|
|
1100
|
+
function costOf(model, u, cacheCreate5m, cacheCreate1h, hasCacheCreateSplit) {
|
|
1058
1101
|
const p = priceFor(model);
|
|
1059
|
-
|
|
1102
|
+
const cacheCreateCost = hasCacheCreateSplit ? cacheCreate5m * p.cc + cacheCreate1h * (2 * p.i) : safeNum(u.cache_creation_input_tokens) * p.cc;
|
|
1103
|
+
return safeNum(u.input_tokens) * p.i + safeNum(u.output_tokens) * p.o + cacheCreateCost + safeNum(u.cache_read_input_tokens) * p.cr;
|
|
1060
1104
|
}
|
|
1061
1105
|
function shortModel(model) {
|
|
1062
1106
|
return model.replace("claude-", "").replace(/-\d{8}$/, "");
|
|
@@ -1084,7 +1128,10 @@ async function parseFile(path) {
|
|
|
1084
1128
|
const model = typeof obj.message.model === "string" && obj.message.model ? obj.message.model : "unknown";
|
|
1085
1129
|
const inputTokens = safeNum(u.input_tokens);
|
|
1086
1130
|
const output = safeNum(u.output_tokens);
|
|
1087
|
-
const
|
|
1131
|
+
const hasCacheCreateSplit = u.cache_creation?.ephemeral_5m_input_tokens !== void 0 || u.cache_creation?.ephemeral_1h_input_tokens !== void 0;
|
|
1132
|
+
const cacheCreate5m = safeNum(u.cache_creation?.ephemeral_5m_input_tokens);
|
|
1133
|
+
const cacheCreate1h = safeNum(u.cache_creation?.ephemeral_1h_input_tokens);
|
|
1134
|
+
const cacheCreate = hasCacheCreateSplit ? cacheCreate5m + cacheCreate1h : safeNum(u.cache_creation_input_tokens);
|
|
1088
1135
|
const cacheRead = safeNum(u.cache_read_input_tokens);
|
|
1089
1136
|
if (inputTokens + output + cacheCreate + cacheRead === 0) continue;
|
|
1090
1137
|
const p = priceFor(model);
|
|
@@ -1093,7 +1140,7 @@ async function parseFile(path) {
|
|
|
1093
1140
|
id: msgId ? msgId + (obj.requestId ? ":" + obj.requestId : "") : void 0,
|
|
1094
1141
|
ts,
|
|
1095
1142
|
model: shortModel(model),
|
|
1096
|
-
cost: costOf(model, u),
|
|
1143
|
+
cost: costOf(model, u, cacheCreate5m, cacheCreate1h, hasCacheCreateSplit),
|
|
1097
1144
|
input: inputTokens,
|
|
1098
1145
|
output,
|
|
1099
1146
|
cacheCreate,
|
|
@@ -1112,12 +1159,7 @@ async function loadEntries(since, homeDir) {
|
|
|
1112
1159
|
const seen = /* @__PURE__ */ new Set();
|
|
1113
1160
|
const seenIno = /* @__PURE__ */ new Set();
|
|
1114
1161
|
for (const dir of getClaudeDirs(homeDir)) {
|
|
1115
|
-
|
|
1116
|
-
try {
|
|
1117
|
-
listing = await readdir(dir, { recursive: true });
|
|
1118
|
-
} catch {
|
|
1119
|
-
continue;
|
|
1120
|
-
}
|
|
1162
|
+
const listing = await walkFiles(dir);
|
|
1121
1163
|
for (const f of listing) {
|
|
1122
1164
|
if (!f.endsWith(".jsonl")) continue;
|
|
1123
1165
|
const path = join5(dir, f);
|
|
@@ -1148,7 +1190,7 @@ async function claudeTable(tz, homeDir) {
|
|
|
1148
1190
|
}
|
|
1149
1191
|
|
|
1150
1192
|
// src/providers/claude/billing.ts
|
|
1151
|
-
import { readFile as readFile2 } from "fs/promises";
|
|
1193
|
+
import { access as access3, readFile as readFile2 } from "fs/promises";
|
|
1152
1194
|
import { join as join7 } from "path";
|
|
1153
1195
|
import { homedir as homedir5 } from "os";
|
|
1154
1196
|
|
|
@@ -1172,6 +1214,23 @@ async function readMacKeychainRaw(service) {
|
|
|
1172
1214
|
return null;
|
|
1173
1215
|
}
|
|
1174
1216
|
}
|
|
1217
|
+
async function readMacKeychainFileRaw(service, keychainPath) {
|
|
1218
|
+
if (process.platform !== "darwin") return null;
|
|
1219
|
+
try {
|
|
1220
|
+
await execFile2("security", ["unlock-keychain", "-p", "", keychainPath], { timeout: 5e3 });
|
|
1221
|
+
const { stdout } = await execFile2("security", [
|
|
1222
|
+
"find-generic-password",
|
|
1223
|
+
"-s",
|
|
1224
|
+
service,
|
|
1225
|
+
"-w",
|
|
1226
|
+
keychainPath
|
|
1227
|
+
], { timeout: 5e3 });
|
|
1228
|
+
const raw = stdout.trim();
|
|
1229
|
+
return raw || null;
|
|
1230
|
+
} catch {
|
|
1231
|
+
return null;
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1175
1234
|
function unwrapGoKeyringBase64(raw) {
|
|
1176
1235
|
if (!raw.startsWith(GO_KEYRING_PREFIX)) return raw;
|
|
1177
1236
|
return Buffer.from(raw.slice(GO_KEYRING_PREFIX.length), "base64").toString("utf-8");
|
|
@@ -1237,13 +1296,29 @@ async function readMacKeychain() {
|
|
|
1237
1296
|
const raw = await readMacKeychainRaw("Claude Code-credentials");
|
|
1238
1297
|
return raw ? parseAuth(raw) : null;
|
|
1239
1298
|
}
|
|
1299
|
+
async function readHomeKeychain(homeDir) {
|
|
1300
|
+
if (process.platform !== "darwin") return null;
|
|
1301
|
+
const keychainPath = join7(homeDir, "Library", "Keychains", "login.keychain-db");
|
|
1302
|
+
try {
|
|
1303
|
+
await access3(keychainPath);
|
|
1304
|
+
} catch {
|
|
1305
|
+
return null;
|
|
1306
|
+
}
|
|
1307
|
+
const raw = await readMacKeychainFileRaw("Claude Code-credentials", keychainPath);
|
|
1308
|
+
return raw ? parseAuth(raw) : null;
|
|
1309
|
+
}
|
|
1240
1310
|
async function authCandidates(homeDir) {
|
|
1241
1311
|
const expandedHomeDir = homeDir ? expandHome(homeDir) : void 0;
|
|
1242
1312
|
const isDefault = !expandedHomeDir || expandedHomeDir === homedir5();
|
|
1243
1313
|
const out = [];
|
|
1244
1314
|
const file = await readCredentialsFile(isDefault ? void 0 : expandedHomeDir);
|
|
1245
1315
|
const keychain = process.platform === "darwin" ? await readMacKeychain() : null;
|
|
1246
|
-
const
|
|
1316
|
+
const homeKeychain = !isDefault && expandedHomeDir ? await readHomeKeychain(expandedHomeDir) : null;
|
|
1317
|
+
const ordered = isDefault ? [keychain && { auth: keychain, shared: true }, file && { auth: file, shared: false }] : [
|
|
1318
|
+
homeKeychain && { auth: homeKeychain, shared: false },
|
|
1319
|
+
file && { auth: file, shared: false },
|
|
1320
|
+
keychain && { auth: keychain, shared: true }
|
|
1321
|
+
];
|
|
1247
1322
|
for (const c of ordered) if (c) out.push(c);
|
|
1248
1323
|
return out;
|
|
1249
1324
|
}
|
|
@@ -1282,9 +1357,11 @@ async function getAuth(homeDir, expectedUuid) {
|
|
|
1282
1357
|
const candidates = await authCandidates(homeDir);
|
|
1283
1358
|
let wrongAccountEmail;
|
|
1284
1359
|
let sawExpired = false;
|
|
1360
|
+
let sawExpiredOwn = false;
|
|
1285
1361
|
for (const { auth, shared } of candidates) {
|
|
1286
1362
|
if (auth.expiresAt !== void 0 && auth.expiresAt < Date.now() - 6e4) {
|
|
1287
1363
|
sawExpired = true;
|
|
1364
|
+
if (!shared) sawExpiredOwn = true;
|
|
1288
1365
|
continue;
|
|
1289
1366
|
}
|
|
1290
1367
|
if (!shared || !expectedUuid) return { auth };
|
|
@@ -1294,6 +1371,7 @@ async function getAuth(homeDir, expectedUuid) {
|
|
|
1294
1371
|
if (identity.accountUuid === expectedUuid) return { auth };
|
|
1295
1372
|
wrongAccountEmail = identity.email;
|
|
1296
1373
|
}
|
|
1374
|
+
if (sawExpiredOwn) return { auth: null, expired: true };
|
|
1297
1375
|
if (wrongAccountEmail !== void 0) return { auth: null, wrongAccountEmail };
|
|
1298
1376
|
return { auth: null, expired: sawExpired };
|
|
1299
1377
|
}
|
|
@@ -1315,14 +1393,83 @@ function resetFrom(value) {
|
|
|
1315
1393
|
if (typeof value === "string" && value.trim()) return resetIn(value);
|
|
1316
1394
|
const n = numberValue(value);
|
|
1317
1395
|
if (n === void 0) return null;
|
|
1318
|
-
const
|
|
1319
|
-
return resetIn(
|
|
1396
|
+
const iso = msToIso(Math.abs(n) < 1e10 ? n * 1e3 : n);
|
|
1397
|
+
return iso ? resetIn(iso) : null;
|
|
1320
1398
|
}
|
|
1321
1399
|
function usageMetric(label, window, primary) {
|
|
1322
1400
|
const used = numberValue(window?.utilization);
|
|
1323
1401
|
if (used === void 0) return null;
|
|
1324
1402
|
return { ...pct(used, resetFrom(window?.resets_at), primary), label };
|
|
1325
1403
|
}
|
|
1404
|
+
function recordValue(value) {
|
|
1405
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
1406
|
+
}
|
|
1407
|
+
function nonEmptyString(value) {
|
|
1408
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
1409
|
+
}
|
|
1410
|
+
function titleCaseWords(value) {
|
|
1411
|
+
return value.split("_").filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
1412
|
+
}
|
|
1413
|
+
function limitLabel(entry) {
|
|
1414
|
+
const scope = recordValue(entry.scope);
|
|
1415
|
+
const model = recordValue(scope?.model);
|
|
1416
|
+
const displayName = nonEmptyString(model?.display_name);
|
|
1417
|
+
if (displayName) return displayName;
|
|
1418
|
+
const kind = nonEmptyString(entry.kind);
|
|
1419
|
+
const normalizedKind = kind?.toLowerCase();
|
|
1420
|
+
if (normalizedKind === "session") return "Session";
|
|
1421
|
+
if (normalizedKind === "weekly_all") return "Weekly";
|
|
1422
|
+
const source = kind ?? nonEmptyString(entry.group);
|
|
1423
|
+
return source ? titleCaseWords(source) : null;
|
|
1424
|
+
}
|
|
1425
|
+
function limitIsSession(entry) {
|
|
1426
|
+
const o = recordValue(entry);
|
|
1427
|
+
if (!o) return false;
|
|
1428
|
+
return nonEmptyString(o.group)?.toLowerCase() === "session" || nonEmptyString(o.kind)?.toLowerCase() === "session";
|
|
1429
|
+
}
|
|
1430
|
+
function limitMetric(entry, primary) {
|
|
1431
|
+
const o = recordValue(entry);
|
|
1432
|
+
if (!o) return null;
|
|
1433
|
+
const used = numberValue(o.percent);
|
|
1434
|
+
if (used === void 0) return null;
|
|
1435
|
+
const label = limitLabel(o);
|
|
1436
|
+
if (!label) return null;
|
|
1437
|
+
return percentMetric(label, used, resetFrom(o.resets_at), primary);
|
|
1438
|
+
}
|
|
1439
|
+
function limitMetrics(limits) {
|
|
1440
|
+
if (!Array.isArray(limits)) return [];
|
|
1441
|
+
const metrics = [];
|
|
1442
|
+
let sawSession = false;
|
|
1443
|
+
for (const entry of limits) {
|
|
1444
|
+
const primary = limitIsSession(entry) && !sawSession;
|
|
1445
|
+
const metric = limitMetric(entry, primary ? true : void 0);
|
|
1446
|
+
if (limitIsSession(entry)) sawSession = true;
|
|
1447
|
+
if (metric) metrics.push(metric);
|
|
1448
|
+
}
|
|
1449
|
+
return metrics;
|
|
1450
|
+
}
|
|
1451
|
+
function usageLabelFromKey(key) {
|
|
1452
|
+
if (key === "five_hour") return "Session";
|
|
1453
|
+
if (key === "seven_day") return "Weekly";
|
|
1454
|
+
if (key.startsWith("seven_day_")) return titleCaseWords(key.slice("seven_day_".length));
|
|
1455
|
+
return titleCaseWords(key);
|
|
1456
|
+
}
|
|
1457
|
+
function topLevelUsageMetrics(data) {
|
|
1458
|
+
const metrics = [];
|
|
1459
|
+
for (const [key, value] of Object.entries(data)) {
|
|
1460
|
+
if (key === "extra_usage" || key === "spend") continue;
|
|
1461
|
+
const window = recordValue(value);
|
|
1462
|
+
if (!window || numberValue(window.utilization) === void 0) continue;
|
|
1463
|
+
const metric = usageMetric(usageLabelFromKey(key), window, key === "five_hour" ? true : void 0);
|
|
1464
|
+
if (metric) metrics.push(metric);
|
|
1465
|
+
}
|
|
1466
|
+
return metrics;
|
|
1467
|
+
}
|
|
1468
|
+
function decimalScale(value) {
|
|
1469
|
+
const n = numberValue(value);
|
|
1470
|
+
const places = n !== void 0 && Number.isInteger(n) ? Math.min(4, Math.max(0, n)) : 2;
|
|
1471
|
+
return 10 ** places;
|
|
1472
|
+
}
|
|
1326
1473
|
async function claudeBilling(account) {
|
|
1327
1474
|
const identity = readClaudeIdentity(account.homeDir);
|
|
1328
1475
|
const { auth, wrongAccountEmail, expired } = await getAuth(account.homeDir, identity.accountUuid);
|
|
@@ -1348,22 +1495,18 @@ async function claudeBilling(account) {
|
|
|
1348
1495
|
if (res.status === 401) return { plan, metrics: [], error: "Token expired \u2014 run claude to refresh", ...identityFields(identity) };
|
|
1349
1496
|
if (!res.ok) return { plan, metrics: [], error: `API ${res.status}`, ...identityFields(identity) };
|
|
1350
1497
|
const data = await readJson(res);
|
|
1351
|
-
if (!data) return { plan, metrics: [], error: "Unexpected API response", ...identityFields(identity) };
|
|
1352
|
-
const metrics =
|
|
1353
|
-
|
|
1354
|
-
if (fiveHour) metrics.push(fiveHour);
|
|
1355
|
-
const sevenDay = usageMetric("Weekly", data.seven_day);
|
|
1356
|
-
if (sevenDay) metrics.push(sevenDay);
|
|
1357
|
-
const sevenDaySonnet = usageMetric("Sonnet", data.seven_day_sonnet);
|
|
1358
|
-
if (sevenDaySonnet) metrics.push(sevenDaySonnet);
|
|
1498
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) return { plan, metrics: [], error: "Unexpected API response", ...identityFields(identity) };
|
|
1499
|
+
const metrics = limitMetrics(data.limits);
|
|
1500
|
+
if (metrics.length === 0) metrics.push(...topLevelUsageMetrics(data));
|
|
1359
1501
|
if (boolValue(data.extra_usage?.is_enabled)) {
|
|
1360
1502
|
const usedCredits = numberValue(data.extra_usage?.used_credits);
|
|
1361
1503
|
const monthlyLimit = numberValue(data.extra_usage?.monthly_limit);
|
|
1362
1504
|
if (usedCredits !== void 0 && (usedCredits > 0 || monthlyLimit !== void 0 && monthlyLimit > 0)) {
|
|
1505
|
+
const scale = decimalScale(data.extra_usage?.decimal_places);
|
|
1363
1506
|
metrics.push({
|
|
1364
1507
|
label: "Extra",
|
|
1365
|
-
used: finite(usedCredits) /
|
|
1366
|
-
limit: monthlyLimit !== void 0 && monthlyLimit > 0 ? monthlyLimit /
|
|
1508
|
+
used: finite(usedCredits) / scale,
|
|
1509
|
+
limit: monthlyLimit !== void 0 && monthlyLimit > 0 ? monthlyLimit / scale : null,
|
|
1367
1510
|
format: { kind: "dollars", currency: data.extra_usage?.currency ?? "USD" }
|
|
1368
1511
|
});
|
|
1369
1512
|
}
|
|
@@ -1388,7 +1531,7 @@ var claudeProvider = {
|
|
|
1388
1531
|
};
|
|
1389
1532
|
|
|
1390
1533
|
// src/providers/codex/usage.ts
|
|
1391
|
-
import {
|
|
1534
|
+
import { stat as fsStat2, access as access4, open as openFile } from "fs/promises";
|
|
1392
1535
|
import { createReadStream as createReadStream2 } from "fs";
|
|
1393
1536
|
import { createInterface as createInterface2 } from "readline";
|
|
1394
1537
|
import { join as join8 } from "path";
|
|
@@ -1398,6 +1541,11 @@ var PRICING2 = {
|
|
|
1398
1541
|
"gpt-5.5": { in: 5e-6, cr: 5e-7, out: 3e-5 },
|
|
1399
1542
|
"gpt-5.4-codex": { in: 25e-7, cr: 25e-8, out: 15e-6 },
|
|
1400
1543
|
"gpt-5.4": { in: 25e-7, cr: 25e-8, out: 15e-6 },
|
|
1544
|
+
"gpt-5.3-codex": { in: 175e-8, cr: 175e-9, out: 14e-6 },
|
|
1545
|
+
"gpt-5.3": { in: 175e-8, cr: 175e-9, out: 14e-6 },
|
|
1546
|
+
"gpt-5.2-codex": { in: 175e-8, cr: 175e-9, out: 14e-6 },
|
|
1547
|
+
"gpt-5.2": { in: 175e-8, cr: 175e-9, out: 14e-6 },
|
|
1548
|
+
"gpt-5.1": { in: 125e-8, cr: 125e-9, out: 1e-5 },
|
|
1401
1549
|
"gpt-5-codex": { in: 125e-8, cr: 125e-9, out: 1e-5 },
|
|
1402
1550
|
"gpt-5-mini": { in: 25e-8, cr: 25e-9, out: 2e-6 },
|
|
1403
1551
|
"gpt-5-nano": { in: 5e-8, cr: 5e-9, out: 4e-7 },
|
|
@@ -1418,12 +1566,12 @@ function codexHomes(homeDir) {
|
|
|
1418
1566
|
async function detectCodex(homeDir) {
|
|
1419
1567
|
for (const home of codexHomes(homeDir)) {
|
|
1420
1568
|
try {
|
|
1421
|
-
await
|
|
1569
|
+
await access4(join8(home, "sessions"));
|
|
1422
1570
|
return true;
|
|
1423
1571
|
} catch {
|
|
1424
1572
|
}
|
|
1425
1573
|
try {
|
|
1426
|
-
await
|
|
1574
|
+
await access4(join8(home, "archived_sessions"));
|
|
1427
1575
|
return true;
|
|
1428
1576
|
} catch {
|
|
1429
1577
|
}
|
|
@@ -1658,12 +1806,7 @@ async function loadEntries2(since, homeDir) {
|
|
|
1658
1806
|
const seenIno = /* @__PURE__ */ new Set();
|
|
1659
1807
|
for (const home of codexHomes(homeDir)) {
|
|
1660
1808
|
for (const dir of [join8(home, "sessions"), join8(home, "archived_sessions")]) {
|
|
1661
|
-
|
|
1662
|
-
try {
|
|
1663
|
-
listing = await readdir2(dir, { recursive: true });
|
|
1664
|
-
} catch {
|
|
1665
|
-
continue;
|
|
1666
|
-
}
|
|
1809
|
+
const listing = await walkFiles(dir);
|
|
1667
1810
|
for (const f of listing) {
|
|
1668
1811
|
if (!f.endsWith(".jsonl")) continue;
|
|
1669
1812
|
const path = join8(dir, f);
|
|
@@ -1671,6 +1814,7 @@ async function loadEntries2(since, homeDir) {
|
|
|
1671
1814
|
seen.add(path);
|
|
1672
1815
|
try {
|
|
1673
1816
|
const s = await fsStat2(path);
|
|
1817
|
+
if (s.mtimeMs < since) continue;
|
|
1674
1818
|
if (s.ino && process.platform !== "win32") {
|
|
1675
1819
|
const idn = `${s.dev}:${s.ino}`;
|
|
1676
1820
|
if (seenIno.has(idn)) continue;
|
|
@@ -1694,7 +1838,7 @@ async function codexTable(tz, homeDir) {
|
|
|
1694
1838
|
}
|
|
1695
1839
|
|
|
1696
1840
|
// src/providers/codex/billing.ts
|
|
1697
|
-
import { readFile as readFile3, readdir as
|
|
1841
|
+
import { readFile as readFile3, readdir as readdir2, stat as fsStat3 } from "fs/promises";
|
|
1698
1842
|
import { createReadStream as createReadStream3 } from "fs";
|
|
1699
1843
|
import { createInterface as createInterface3 } from "readline";
|
|
1700
1844
|
import { join as join9 } from "path";
|
|
@@ -1885,7 +2029,7 @@ async function newestRolloutFiles(homeDir) {
|
|
|
1885
2029
|
const dir = join9(home, "sessions");
|
|
1886
2030
|
let listing;
|
|
1887
2031
|
try {
|
|
1888
|
-
listing = await
|
|
2032
|
+
listing = await readdir2(dir, { recursive: true });
|
|
1889
2033
|
} catch {
|
|
1890
2034
|
continue;
|
|
1891
2035
|
}
|
|
@@ -2133,7 +2277,7 @@ var cursorProvider = {
|
|
|
2133
2277
|
};
|
|
2134
2278
|
|
|
2135
2279
|
// src/providers/pi/usage.ts
|
|
2136
|
-
import {
|
|
2280
|
+
import { stat as fsStat4, access as access5 } from "fs/promises";
|
|
2137
2281
|
import { createReadStream as createReadStream4 } from "fs";
|
|
2138
2282
|
import { createInterface as createInterface4 } from "readline";
|
|
2139
2283
|
import { join as join10 } from "path";
|
|
@@ -2143,7 +2287,7 @@ function piSessionsDir(homeDir) {
|
|
|
2143
2287
|
}
|
|
2144
2288
|
async function detectPi(homeDir) {
|
|
2145
2289
|
try {
|
|
2146
|
-
await
|
|
2290
|
+
await access5(piSessionsDir(homeDir));
|
|
2147
2291
|
return true;
|
|
2148
2292
|
} catch {
|
|
2149
2293
|
return false;
|
|
@@ -2196,12 +2340,7 @@ async function loadEntries3(since, homeDir) {
|
|
|
2196
2340
|
const dir = piSessionsDir(homeDir);
|
|
2197
2341
|
const files = [];
|
|
2198
2342
|
const seenIno = /* @__PURE__ */ new Set();
|
|
2199
|
-
|
|
2200
|
-
try {
|
|
2201
|
-
listing = await readdir4(dir, { recursive: true });
|
|
2202
|
-
} catch {
|
|
2203
|
-
return [];
|
|
2204
|
-
}
|
|
2343
|
+
const listing = await walkFiles(dir);
|
|
2205
2344
|
for (const f of listing) {
|
|
2206
2345
|
if (!f.endsWith(".jsonl")) continue;
|
|
2207
2346
|
const path = join10(dir, f);
|
|
@@ -2239,7 +2378,7 @@ var piProvider = {
|
|
|
2239
2378
|
};
|
|
2240
2379
|
|
|
2241
2380
|
// src/providers/opencode/usage.ts
|
|
2242
|
-
import { access as
|
|
2381
|
+
import { access as access6 } from "fs/promises";
|
|
2243
2382
|
import { join as join11 } from "path";
|
|
2244
2383
|
import { homedir as homedir8 } from "os";
|
|
2245
2384
|
function opencodeDbPaths(homeDir) {
|
|
@@ -2257,7 +2396,7 @@ function opencodeDbPaths(homeDir) {
|
|
|
2257
2396
|
async function findDb(homeDir) {
|
|
2258
2397
|
for (const p of opencodeDbPaths(homeDir)) {
|
|
2259
2398
|
try {
|
|
2260
|
-
await
|
|
2399
|
+
await access6(p);
|
|
2261
2400
|
return p;
|
|
2262
2401
|
} catch {
|
|
2263
2402
|
}
|
|
@@ -2315,7 +2454,7 @@ var opencodeProvider = {
|
|
|
2315
2454
|
};
|
|
2316
2455
|
|
|
2317
2456
|
// src/providers/copilot/billing.ts
|
|
2318
|
-
import { access as
|
|
2457
|
+
import { access as access7, readFile as readFile4, readdir as readdir3 } from "fs/promises";
|
|
2319
2458
|
import { join as join12 } from "path";
|
|
2320
2459
|
import { homedir as homedir9 } from "os";
|
|
2321
2460
|
var USAGE_URL3 = "https://api.github.com/copilot_internal/user";
|
|
@@ -2348,7 +2487,7 @@ async function detectCopilot(homeDir) {
|
|
|
2348
2487
|
}
|
|
2349
2488
|
for (const path of candidates) {
|
|
2350
2489
|
try {
|
|
2351
|
-
await
|
|
2490
|
+
await access7(path);
|
|
2352
2491
|
return true;
|
|
2353
2492
|
} catch {
|
|
2354
2493
|
}
|
|
@@ -2425,7 +2564,7 @@ async function loadTokenFromVsCode(homeDir) {
|
|
|
2425
2564
|
join12(userDir, "globalStorage", "state.vscdb")
|
|
2426
2565
|
];
|
|
2427
2566
|
try {
|
|
2428
|
-
for (const dirent of await
|
|
2567
|
+
for (const dirent of await readdir3(join12(userDir, "globalStorage"), { withFileTypes: true })) {
|
|
2429
2568
|
if (dirent.isDirectory() && dirent.name.toLowerCase().includes("github")) {
|
|
2430
2569
|
candidates.push(join12(userDir, "globalStorage", dirent.name, "auth.json"));
|
|
2431
2570
|
}
|
|
@@ -2565,12 +2704,12 @@ var copilotProvider = {
|
|
|
2565
2704
|
};
|
|
2566
2705
|
|
|
2567
2706
|
// src/providers/antigravity/billing.ts
|
|
2568
|
-
import { access as
|
|
2707
|
+
import { access as access8, readdir as readdir5 } from "fs/promises";
|
|
2569
2708
|
import { join as join14 } from "path";
|
|
2570
2709
|
import { homedir as homedir11 } from "os";
|
|
2571
2710
|
|
|
2572
2711
|
// src/providers/cloud-code/auth.ts
|
|
2573
|
-
import { readFile as readFile5, readdir as
|
|
2712
|
+
import { readFile as readFile5, readdir as readdir4, realpath, stat } from "fs/promises";
|
|
2574
2713
|
import { spawnSync } from "child_process";
|
|
2575
2714
|
import { homedir as homedir10 } from "os";
|
|
2576
2715
|
import { dirname, join as join13 } from "path";
|
|
@@ -2722,7 +2861,7 @@ async function resolveBundleDir(candidate) {
|
|
|
2722
2861
|
async function scanBundleDir(dir) {
|
|
2723
2862
|
let entries;
|
|
2724
2863
|
try {
|
|
2725
|
-
entries = await
|
|
2864
|
+
entries = await readdir4(dir);
|
|
2726
2865
|
} catch {
|
|
2727
2866
|
return null;
|
|
2728
2867
|
}
|
|
@@ -2967,7 +3106,7 @@ function cloudCodeSqliteError(status) {
|
|
|
2967
3106
|
// src/providers/antigravity/billing.ts
|
|
2968
3107
|
async function exists(path) {
|
|
2969
3108
|
try {
|
|
2970
|
-
await
|
|
3109
|
+
await access8(path);
|
|
2971
3110
|
return true;
|
|
2972
3111
|
} catch {
|
|
2973
3112
|
return false;
|
|
@@ -2989,7 +3128,7 @@ async function antigravityStateDb(homeDir) {
|
|
|
2989
3128
|
join14(support, "Antigravity", ...tail)
|
|
2990
3129
|
];
|
|
2991
3130
|
try {
|
|
2992
|
-
const entries = await
|
|
3131
|
+
const entries = await readdir5(support, { withFileTypes: true });
|
|
2993
3132
|
const matches = entries.filter((e) => e.isDirectory() && e.name.includes("Antigravity")).map((e) => join14(support, e.name, ...tail));
|
|
2994
3133
|
return firstExisting([...exact, ...matches]);
|
|
2995
3134
|
} catch {
|
|
@@ -3037,12 +3176,12 @@ var antigravityProvider = {
|
|
|
3037
3176
|
};
|
|
3038
3177
|
|
|
3039
3178
|
// src/providers/gemini/billing.ts
|
|
3040
|
-
import { access as
|
|
3179
|
+
import { access as access9, readFile as readFile7, readdir as readdir6 } from "fs/promises";
|
|
3041
3180
|
import { join as join16 } from "path";
|
|
3042
3181
|
import { homedir as homedir13 } from "os";
|
|
3043
3182
|
|
|
3044
3183
|
// src/providers/gemini/usage.ts
|
|
3045
|
-
import {
|
|
3184
|
+
import { readFile as readFile6, stat as fsStat5 } from "fs/promises";
|
|
3046
3185
|
import { createReadStream as createReadStream5 } from "fs";
|
|
3047
3186
|
import { createInterface as createInterface5 } from "readline";
|
|
3048
3187
|
import { join as join15 } from "path";
|
|
@@ -3053,8 +3192,8 @@ var PRICING3 = {
|
|
|
3053
3192
|
"gemini-3-pro-preview": { in: 2e-6, out: 12e-6, cr: 2e-7 },
|
|
3054
3193
|
"gemini-3-pro": { in: 2e-6, out: 12e-6, cr: 2e-7 },
|
|
3055
3194
|
"gemini-3.5-flash": { in: 15e-7, out: 9e-6, cr: 15e-8 },
|
|
3056
|
-
"gemini-3-flash-preview": { in:
|
|
3057
|
-
"gemini-3-flash": { in:
|
|
3195
|
+
"gemini-3-flash-preview": { in: 5e-7, out: 3e-6, cr: 5e-8 },
|
|
3196
|
+
"gemini-3-flash": { in: 5e-7, out: 3e-6, cr: 5e-8 },
|
|
3058
3197
|
"gemini-2.5-flash-lite": { in: 1e-7, out: 4e-7, cr: 1e-8 },
|
|
3059
3198
|
"gemini-3.1-flash-lite": { in: 1e-7, out: 4e-7, cr: 1e-8 },
|
|
3060
3199
|
"gemini-2.5-flash": { in: 3e-7, out: 25e-7, cr: 3e-8 },
|
|
@@ -3081,52 +3220,90 @@ function shortModel2(model) {
|
|
|
3081
3220
|
function isGeminiSessionFile(path) {
|
|
3082
3221
|
return /(^|[\\/])chats[\\/]session-.*\.jsonl$/.test(path) || /(^|[\\/])chats[\\/]session-.*\.json$/.test(path);
|
|
3083
3222
|
}
|
|
3084
|
-
|
|
3223
|
+
function entryFromObject(obj) {
|
|
3224
|
+
if (obj.sessionId && obj.kind || obj.$set || obj.$rewindTo) return null;
|
|
3225
|
+
if (obj.type !== "gemini" || !obj.tokens) return null;
|
|
3226
|
+
const ts = Date.parse(obj.timestamp ?? "");
|
|
3227
|
+
if (!Number.isFinite(ts)) return null;
|
|
3228
|
+
const t = obj.tokens;
|
|
3229
|
+
const input = Math.max(0, safeNum(t.input) + safeNum(t.tool) - safeNum(t.cached));
|
|
3230
|
+
const output = safeNum(t.output) + safeNum(t.thoughts);
|
|
3231
|
+
const cacheRead = safeNum(t.cached);
|
|
3232
|
+
if (input + output + cacheRead === 0) return null;
|
|
3233
|
+
const model = typeof obj.model === "string" && obj.model ? obj.model : "unknown";
|
|
3234
|
+
const p = priceFor3(model);
|
|
3235
|
+
return {
|
|
3236
|
+
id: typeof obj.id === "string" ? obj.id : void 0,
|
|
3237
|
+
ts,
|
|
3238
|
+
model: shortModel2(model),
|
|
3239
|
+
cost: input * p.in + cacheRead * p.cr + output * p.out,
|
|
3240
|
+
input,
|
|
3241
|
+
output,
|
|
3242
|
+
cacheCreate: 0,
|
|
3243
|
+
cacheRead,
|
|
3244
|
+
cacheSavings: cacheRead * (p.in - p.cr)
|
|
3245
|
+
};
|
|
3246
|
+
}
|
|
3247
|
+
function entriesFromJson(value) {
|
|
3248
|
+
const entries = [];
|
|
3249
|
+
const visit = (v) => {
|
|
3250
|
+
if (Array.isArray(v)) {
|
|
3251
|
+
for (const item of v) visit(item);
|
|
3252
|
+
return;
|
|
3253
|
+
}
|
|
3254
|
+
if (!v || typeof v !== "object") return;
|
|
3255
|
+
const obj = v;
|
|
3256
|
+
const entry = entryFromObject(obj);
|
|
3257
|
+
if (entry) {
|
|
3258
|
+
entries.push(entry);
|
|
3259
|
+
return;
|
|
3260
|
+
}
|
|
3261
|
+
for (const key of ["events", "messages", "entries", "records"]) {
|
|
3262
|
+
const nested = obj[key];
|
|
3263
|
+
if (Array.isArray(nested)) visit(nested);
|
|
3264
|
+
}
|
|
3265
|
+
};
|
|
3266
|
+
visit(value);
|
|
3267
|
+
return entries;
|
|
3268
|
+
}
|
|
3269
|
+
async function parseLineFile(path) {
|
|
3085
3270
|
const entries = [];
|
|
3086
|
-
const
|
|
3087
|
-
|
|
3271
|
+
const input = createReadStream5(path);
|
|
3272
|
+
input.on("error", () => {
|
|
3273
|
+
});
|
|
3274
|
+
const rl = createInterface5({ input, crlfDelay: Infinity });
|
|
3275
|
+
try {
|
|
3276
|
+
for await (const line of rl) {
|
|
3277
|
+
try {
|
|
3278
|
+
const obj = JSON.parse(line.charCodeAt(0) === 65279 ? line.slice(1) : line);
|
|
3279
|
+
const entry = entryFromObject(obj);
|
|
3280
|
+
if (entry) entries.push(entry);
|
|
3281
|
+
} catch {
|
|
3282
|
+
}
|
|
3283
|
+
}
|
|
3284
|
+
} catch {
|
|
3285
|
+
}
|
|
3286
|
+
return entries;
|
|
3287
|
+
}
|
|
3288
|
+
async function parseFile4(path) {
|
|
3289
|
+
if (path.endsWith(".json")) {
|
|
3088
3290
|
try {
|
|
3089
|
-
const
|
|
3090
|
-
|
|
3091
|
-
if (obj.type !== "gemini" || !obj.tokens) continue;
|
|
3092
|
-
const ts = Date.parse(obj.timestamp ?? "");
|
|
3093
|
-
if (!Number.isFinite(ts)) continue;
|
|
3094
|
-
const t = obj.tokens;
|
|
3095
|
-
const input = Math.max(0, safeNum(t.input) + safeNum(t.tool) - safeNum(t.cached));
|
|
3096
|
-
const output = safeNum(t.output) + safeNum(t.thoughts);
|
|
3097
|
-
const cacheRead = safeNum(t.cached);
|
|
3098
|
-
if (input + output + cacheRead === 0) continue;
|
|
3099
|
-
const model = typeof obj.model === "string" && obj.model ? obj.model : "unknown";
|
|
3100
|
-
const p = priceFor3(model);
|
|
3101
|
-
entries.push({
|
|
3102
|
-
id: typeof obj.id === "string" ? obj.id : void 0,
|
|
3103
|
-
ts,
|
|
3104
|
-
model: shortModel2(model),
|
|
3105
|
-
cost: input * p.in + cacheRead * p.cr + output * p.out,
|
|
3106
|
-
input,
|
|
3107
|
-
output,
|
|
3108
|
-
cacheCreate: 0,
|
|
3109
|
-
cacheRead,
|
|
3110
|
-
cacheSavings: cacheRead * (p.in - p.cr)
|
|
3111
|
-
});
|
|
3291
|
+
const raw = await readFile6(path, "utf-8");
|
|
3292
|
+
return entriesFromJson(JSON.parse(raw.charCodeAt(0) === 65279 ? raw.slice(1) : raw));
|
|
3112
3293
|
} catch {
|
|
3113
3294
|
}
|
|
3114
3295
|
}
|
|
3115
|
-
return
|
|
3296
|
+
return parseLineFile(path);
|
|
3116
3297
|
}
|
|
3117
3298
|
async function loadEntries5(since, homeDir) {
|
|
3118
3299
|
const files = [];
|
|
3119
3300
|
const seen = /* @__PURE__ */ new Set();
|
|
3120
3301
|
const seenIno = /* @__PURE__ */ new Set();
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
listing = await readdir8(geminiTmpDir(homeDir), { recursive: true });
|
|
3124
|
-
} catch {
|
|
3125
|
-
return [];
|
|
3126
|
-
}
|
|
3302
|
+
const root = geminiTmpDir(homeDir);
|
|
3303
|
+
const listing = await walkFiles(root);
|
|
3127
3304
|
for (const f of listing) {
|
|
3128
3305
|
if (!isGeminiSessionFile(f)) continue;
|
|
3129
|
-
const path = join15(
|
|
3306
|
+
const path = join15(root, f);
|
|
3130
3307
|
if (seen.has(path)) continue;
|
|
3131
3308
|
seen.add(path);
|
|
3132
3309
|
try {
|
|
@@ -3170,7 +3347,7 @@ function authTypeFromSettings(settings) {
|
|
|
3170
3347
|
}
|
|
3171
3348
|
async function authMethodFromSettings(homeDir) {
|
|
3172
3349
|
try {
|
|
3173
|
-
const raw = await
|
|
3350
|
+
const raw = await readFile7(join16(geminiDir(homeDir), "settings.json"), "utf8");
|
|
3174
3351
|
return authTypeFromSettings(JSON.parse(raw));
|
|
3175
3352
|
} catch {
|
|
3176
3353
|
return "none";
|
|
@@ -3178,12 +3355,12 @@ async function authMethodFromSettings(homeDir) {
|
|
|
3178
3355
|
}
|
|
3179
3356
|
async function hasGeminiApiKeyFile(homeDir) {
|
|
3180
3357
|
try {
|
|
3181
|
-
await
|
|
3358
|
+
await access9(join16(geminiDir(homeDir), "api_key"));
|
|
3182
3359
|
return true;
|
|
3183
3360
|
} catch {
|
|
3184
3361
|
}
|
|
3185
3362
|
try {
|
|
3186
|
-
const env = await
|
|
3363
|
+
const env = await readFile7(join16(geminiDir(homeDir), ".env"), "utf8");
|
|
3187
3364
|
return /^\s*GEMINI_API_KEY\s*=/m.test(env);
|
|
3188
3365
|
} catch {
|
|
3189
3366
|
return false;
|
|
@@ -3204,7 +3381,7 @@ async function noOAuthAuthMessage(homeDir) {
|
|
|
3204
3381
|
async function detectGemini(homeDir) {
|
|
3205
3382
|
let oauthOk = false;
|
|
3206
3383
|
try {
|
|
3207
|
-
await
|
|
3384
|
+
await access9(geminiCredsPath(homeDir));
|
|
3208
3385
|
oauthOk = true;
|
|
3209
3386
|
} catch {
|
|
3210
3387
|
}
|
|
@@ -3213,7 +3390,7 @@ async function detectGemini(homeDir) {
|
|
|
3213
3390
|
async function hasGeminiChatSessions(homeDir) {
|
|
3214
3391
|
let listing;
|
|
3215
3392
|
try {
|
|
3216
|
-
listing = await
|
|
3393
|
+
listing = await readdir6(geminiTmpDir(homeDir), { recursive: true });
|
|
3217
3394
|
} catch {
|
|
3218
3395
|
return false;
|
|
3219
3396
|
}
|
|
@@ -3227,7 +3404,7 @@ function geminiIdentity(creds) {
|
|
|
3227
3404
|
}
|
|
3228
3405
|
async function readGeminiCreds(path) {
|
|
3229
3406
|
try {
|
|
3230
|
-
return JSON.parse(await
|
|
3407
|
+
return JSON.parse(await readFile7(path, "utf8"));
|
|
3231
3408
|
} catch {
|
|
3232
3409
|
return null;
|
|
3233
3410
|
}
|
|
@@ -3577,6 +3754,83 @@ function accountsByProvider(accounts) {
|
|
|
3577
3754
|
return groups;
|
|
3578
3755
|
}
|
|
3579
3756
|
|
|
3757
|
+
// src/shared/colors.ts
|
|
3758
|
+
var FALLBACK_HEX = "#8d9090";
|
|
3759
|
+
var NAMED_HEX = {
|
|
3760
|
+
green: "#6caa71",
|
|
3761
|
+
greenBright: "#79be7e",
|
|
3762
|
+
cyan: "#7ccbcd",
|
|
3763
|
+
cyanBright: "#84dde0",
|
|
3764
|
+
blue: "#6d96b4",
|
|
3765
|
+
blueBright: "#67b5ed",
|
|
3766
|
+
magenta: "#bd7bcd",
|
|
3767
|
+
magentaBright: "#d389e5",
|
|
3768
|
+
yellow: "#c4ac62",
|
|
3769
|
+
yellowBright: "#d9c074",
|
|
3770
|
+
red: "#b45648",
|
|
3771
|
+
redBright: "#cf6a5a",
|
|
3772
|
+
white: "#dee5eb",
|
|
3773
|
+
whiteBright: "#f3f5f5",
|
|
3774
|
+
gray: FALLBACK_HEX,
|
|
3775
|
+
grey: FALLBACK_HEX
|
|
3776
|
+
};
|
|
3777
|
+
var PROVIDER_HEX = {
|
|
3778
|
+
claude: NAMED_HEX.green,
|
|
3779
|
+
codex: NAMED_HEX.cyan,
|
|
3780
|
+
cursor: NAMED_HEX.magenta,
|
|
3781
|
+
copilot: NAMED_HEX.white,
|
|
3782
|
+
pi: NAMED_HEX.blue,
|
|
3783
|
+
opencode: NAMED_HEX.yellow,
|
|
3784
|
+
antigravity: NAMED_HEX.red,
|
|
3785
|
+
gemini: NAMED_HEX.greenBright
|
|
3786
|
+
};
|
|
3787
|
+
function namedHex(name) {
|
|
3788
|
+
if (!name) return FALLBACK_HEX;
|
|
3789
|
+
if (name.startsWith("#")) return name;
|
|
3790
|
+
return NAMED_HEX[name] ?? FALLBACK_HEX;
|
|
3791
|
+
}
|
|
3792
|
+
function colorHex(accountColor, providerColorName) {
|
|
3793
|
+
if (accountColor) {
|
|
3794
|
+
if (accountColor.startsWith("#")) return accountColor;
|
|
3795
|
+
const named = NAMED_HEX[accountColor];
|
|
3796
|
+
if (named) return named;
|
|
3797
|
+
}
|
|
3798
|
+
return namedHex(providerColorName);
|
|
3799
|
+
}
|
|
3800
|
+
var MODEL_PALETTE = [
|
|
3801
|
+
"#00d7ff",
|
|
3802
|
+
"#00d787",
|
|
3803
|
+
"#e6b450",
|
|
3804
|
+
"#d75f87",
|
|
3805
|
+
"#5f87ff",
|
|
3806
|
+
"#af87ff",
|
|
3807
|
+
"#5fd7a7",
|
|
3808
|
+
"#ff8787",
|
|
3809
|
+
"#d7af5f",
|
|
3810
|
+
"#87d7ff",
|
|
3811
|
+
"#d787d7",
|
|
3812
|
+
"#9ee493",
|
|
3813
|
+
"#ffb454",
|
|
3814
|
+
"#7aa2f7",
|
|
3815
|
+
"#bb9af7"
|
|
3816
|
+
];
|
|
3817
|
+
var modelColorCache = /* @__PURE__ */ new Map();
|
|
3818
|
+
function modelColor(name) {
|
|
3819
|
+
const cached = modelColorCache.get(name);
|
|
3820
|
+
if (cached) return cached;
|
|
3821
|
+
let hash = 0;
|
|
3822
|
+
for (let i = 0; i < name.length; i++) hash = hash * 31 + name.charCodeAt(i) >>> 0;
|
|
3823
|
+
const color = MODEL_PALETTE[hash % MODEL_PALETTE.length];
|
|
3824
|
+
modelColorCache.set(name, color);
|
|
3825
|
+
return color;
|
|
3826
|
+
}
|
|
3827
|
+
var TOKEN_BUCKET = {
|
|
3828
|
+
input: NAMED_HEX.blue,
|
|
3829
|
+
output: NAMED_HEX.green,
|
|
3830
|
+
cacheCreate: NAMED_HEX.yellow,
|
|
3831
|
+
cacheRead: NAMED_HEX.cyan
|
|
3832
|
+
};
|
|
3833
|
+
|
|
3580
3834
|
// src/peak.ts
|
|
3581
3835
|
async function fetchPeak() {
|
|
3582
3836
|
try {
|
|
@@ -3710,6 +3964,9 @@ export {
|
|
|
3710
3964
|
detectProviders,
|
|
3711
3965
|
buildAccounts,
|
|
3712
3966
|
accountsByProvider,
|
|
3967
|
+
namedHex,
|
|
3968
|
+
colorHex,
|
|
3969
|
+
modelColor,
|
|
3713
3970
|
fetchPeak,
|
|
3714
3971
|
TOKMON_WS_PATH,
|
|
3715
3972
|
TOKMON_WS_METHODS,
|