tokmon 0.20.6 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{bootstrap-ink-WWSVQO6D.js → bootstrap-ink-QHJOOWIW.js} +552 -439
- package/dist/{chunk-XQEJ4WQ5.js → chunk-7MFZMI5C.js} +2 -2
- package/dist/{chunk-E6YT66ST.js → chunk-CPN67OK6.js} +70 -111
- package/dist/{chunk-YWJT3OG4.js → chunk-YCIUMAKK.js} +425 -390
- package/dist/cli.js +4 -4
- package/dist/{config-C6Z65JUP.js → config-UPNBGIJO.js} +1 -1
- package/dist/{daemon-ZUWJQEKA.js → daemon-LZVP4SWF.js} +3 -3
- package/dist/server-F7CUK4ZM.js +9 -0
- package/dist/web/assets/{breakdown-DFnPYZtA.js → breakdown-ClwCJce8.js} +1 -1
- package/dist/web/assets/{chart-C8J22kR3.js → chart-Dcuh_e3J.js} +1 -1
- package/dist/web/assets/index-nAr5lqix.js +105 -0
- package/dist/web/assets/{timeline-BjoaOdbh.js → timeline-gDyrCQ0O.js} +1 -1
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
- package/dist/server-TSTBMVTJ.js +0 -9
- package/dist/web/assets/index-B9eW55YB.js +0 -105
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
expandHome,
|
|
7
7
|
isValidTimezone,
|
|
8
8
|
slugify
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-7MFZMI5C.js";
|
|
10
10
|
|
|
11
11
|
// src/providers/usage-core.ts
|
|
12
12
|
import { readFile, writeFile, rename, mkdir } from "fs/promises";
|
|
@@ -126,6 +126,14 @@ function finitePositiveCoerced(value) {
|
|
|
126
126
|
const n = Number(value);
|
|
127
127
|
return Number.isFinite(n) && n > 0 ? n : 0;
|
|
128
128
|
}
|
|
129
|
+
function numberValue(value) {
|
|
130
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
131
|
+
if (typeof value === "string" && value.trim()) {
|
|
132
|
+
const n = Number(value);
|
|
133
|
+
if (Number.isFinite(n)) return n;
|
|
134
|
+
}
|
|
135
|
+
return void 0;
|
|
136
|
+
}
|
|
129
137
|
function percentMetric(label, used, resetsAt, primary) {
|
|
130
138
|
return {
|
|
131
139
|
label,
|
|
@@ -141,7 +149,7 @@ var dollars = (cents) => finite(cents) / 100;
|
|
|
141
149
|
// src/providers/usage-core.ts
|
|
142
150
|
var SPARK_DAYS = 14;
|
|
143
151
|
var DAY_MS = 864e5;
|
|
144
|
-
var CACHE_VERSION =
|
|
152
|
+
var CACHE_VERSION = 7;
|
|
145
153
|
var STABLE_AGE_MS = 5 * 6e4;
|
|
146
154
|
var PRUNE_AGE_MS = 200 * DAY_MS;
|
|
147
155
|
var memCache = /* @__PURE__ */ new Map();
|
|
@@ -252,6 +260,15 @@ async function loadCachedEntries(files, parse, since) {
|
|
|
252
260
|
if (dirty) scheduleFlush();
|
|
253
261
|
return dedupe(chunks.flat().filter((e) => e.ts >= since));
|
|
254
262
|
}
|
|
263
|
+
function lastDayKeys(now, tz, n) {
|
|
264
|
+
const keys = [];
|
|
265
|
+
let cursor = now;
|
|
266
|
+
for (let i = 0; i < n; i++) {
|
|
267
|
+
keys.unshift(dayKey(cursor, tz));
|
|
268
|
+
cursor = startOfDay(cursor, tz) - DAY_MS / 2;
|
|
269
|
+
}
|
|
270
|
+
return keys;
|
|
271
|
+
}
|
|
255
272
|
function dashboardSince(tz) {
|
|
256
273
|
const now = Date.now();
|
|
257
274
|
return Math.min(startOfMonth(now, tz), startOfWeek(now, tz), now - SPARK_DAYS * DAY_MS);
|
|
@@ -277,7 +294,7 @@ function dedupe(entries) {
|
|
|
277
294
|
for (const raw of entries) {
|
|
278
295
|
const e = cleanEntry(raw);
|
|
279
296
|
if (e.ts <= 0) continue;
|
|
280
|
-
const k = e.id ?? `${e.ts} ${e.model} ${e.input} ${e.output} ${e.cacheCreate} ${e.cacheRead}`;
|
|
297
|
+
const k = e.id ?? `${e.ts} ${e.model} ${e.input} ${e.output} ${e.cacheCreate} ${e.cacheRead} ${e.cost}`;
|
|
281
298
|
if (seen.has(k)) continue;
|
|
282
299
|
seen.add(k);
|
|
283
300
|
out.push(e);
|
|
@@ -316,8 +333,7 @@ function summarize(entries, tz) {
|
|
|
316
333
|
const hrs = Math.max((now - oldestToday) / 36e5, 1 / 60);
|
|
317
334
|
const rawBurnRate = hadToday ? today.cost / hrs : 0;
|
|
318
335
|
const burnRate = Number.isFinite(rawBurnRate) ? rawBurnRate : 0;
|
|
319
|
-
const series =
|
|
320
|
-
for (let i = SPARK_DAYS - 1; i >= 0; i--) series.push(byDay.get(dayKey(now - i * DAY_MS, tz)) ?? 0);
|
|
336
|
+
const series = lastDayKeys(now, tz, SPARK_DAYS).map((k) => byDay.get(k) ?? 0);
|
|
321
337
|
return { today, week, month, burnRate, series };
|
|
322
338
|
}
|
|
323
339
|
function groupBy(entries, keyFn) {
|
|
@@ -515,6 +531,33 @@ async function readJson(res) {
|
|
|
515
531
|
}
|
|
516
532
|
}
|
|
517
533
|
|
|
534
|
+
// src/providers/_shared/identity.ts
|
|
535
|
+
function identityFields(identity) {
|
|
536
|
+
return {
|
|
537
|
+
email: identity?.email ?? null,
|
|
538
|
+
displayName: identity?.displayName ?? null
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// src/providers/_shared/jwt.ts
|
|
543
|
+
function decodeBase64UrlJson(segment) {
|
|
544
|
+
try {
|
|
545
|
+
const normalized = segment.replace(/-/g, "+").replace(/_/g, "/");
|
|
546
|
+
const padded = normalized + "=".repeat((4 - normalized.length % 4) % 4);
|
|
547
|
+
return JSON.parse(Buffer.from(padded, "base64").toString("utf8"));
|
|
548
|
+
} catch {
|
|
549
|
+
return null;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
function identityFromIdToken(idToken) {
|
|
553
|
+
if (typeof idToken !== "string" || !idToken.includes(".")) return {};
|
|
554
|
+
const payload = decodeBase64UrlJson(idToken.split(".")[1]);
|
|
555
|
+
if (!payload || typeof payload !== "object") return {};
|
|
556
|
+
const email = typeof payload.email === "string" && payload.email.trim() ? payload.email.trim() : void 0;
|
|
557
|
+
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;
|
|
558
|
+
return { email, displayName, payload };
|
|
559
|
+
}
|
|
560
|
+
|
|
518
561
|
// src/providers/_shared/time.ts
|
|
519
562
|
function msToIso(ms) {
|
|
520
563
|
return Number.isFinite(ms) && Math.abs(ms) <= 864e13 ? new Date(ms).toISOString() : null;
|
|
@@ -636,20 +679,17 @@ function sqliteStatusMessage(status) {
|
|
|
636
679
|
|
|
637
680
|
// src/providers/cursor/activity.ts
|
|
638
681
|
var DAY_MS2 = 864e5;
|
|
682
|
+
var HOUR_MS = 36e5;
|
|
639
683
|
function trackingDb(homeDir) {
|
|
640
684
|
return join3(homeDir ?? homedir(), ".cursor", "ai-tracking", "ai-code-tracking.db");
|
|
641
685
|
}
|
|
642
|
-
function
|
|
643
|
-
const d = new Date(ms);
|
|
644
|
-
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
645
|
-
}
|
|
646
|
-
async function cursorActivity(homeDir) {
|
|
686
|
+
async function cursorActivity(tz, homeDir) {
|
|
647
687
|
const db = trackingDb(homeDir);
|
|
648
688
|
try {
|
|
649
689
|
const now = Date.now();
|
|
650
690
|
const res = await runSqlite(
|
|
651
691
|
db,
|
|
652
|
-
`SELECT
|
|
692
|
+
`SELECT createdAt/${HOUR_MS} AS h, count(*) AS c FROM ai_code_hashes WHERE source!='human' AND createdAt >= ${Math.floor(now - 30 * DAY_MS2)} GROUP BY h;`
|
|
653
693
|
);
|
|
654
694
|
if (res.status !== "ok") return null;
|
|
655
695
|
const byDay = /* @__PURE__ */ new Map();
|
|
@@ -657,11 +697,13 @@ async function cursorActivity(homeDir) {
|
|
|
657
697
|
for (const row of res.rows) {
|
|
658
698
|
const raw = Number(row.c);
|
|
659
699
|
const n = Number.isFinite(raw) && raw > 0 ? raw : 0;
|
|
660
|
-
|
|
700
|
+
const hour = Number(row.h);
|
|
701
|
+
if (!Number.isFinite(hour)) continue;
|
|
702
|
+
const key = dayKey(hour * HOUR_MS + HOUR_MS / 2, tz);
|
|
703
|
+
byDay.set(key, (byDay.get(key) ?? 0) + n);
|
|
661
704
|
month += n;
|
|
662
705
|
}
|
|
663
|
-
const series =
|
|
664
|
-
for (let i = SPARK_DAYS - 1; i >= 0; i--) series.push(byDay.get(localDayKey(now - i * DAY_MS2)) ?? 0);
|
|
706
|
+
const series = lastDayKeys(now, tz, SPARK_DAYS).map((k) => byDay.get(k) ?? 0);
|
|
665
707
|
if (month === 0 && series.every((v) => v === 0)) return null;
|
|
666
708
|
return { series, summary: `${tokens(month)} lines` };
|
|
667
709
|
} catch {
|
|
@@ -711,23 +753,6 @@ function cleanStoredString(value) {
|
|
|
711
753
|
const trimmed = value.trim().replace(/^"|"$/g, "");
|
|
712
754
|
return trimmed || void 0;
|
|
713
755
|
}
|
|
714
|
-
function numberValue(value) {
|
|
715
|
-
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
716
|
-
if (typeof value === "string" && value.trim()) {
|
|
717
|
-
const n = Number(value);
|
|
718
|
-
if (Number.isFinite(n)) return n;
|
|
719
|
-
}
|
|
720
|
-
return void 0;
|
|
721
|
-
}
|
|
722
|
-
function decodeBase64UrlJson(segment) {
|
|
723
|
-
try {
|
|
724
|
-
const normalized = segment.replace(/-/g, "+").replace(/_/g, "/");
|
|
725
|
-
const padded = normalized + "=".repeat((4 - normalized.length % 4) % 4);
|
|
726
|
-
return JSON.parse(Buffer.from(padded, "base64").toString("utf8"));
|
|
727
|
-
} catch {
|
|
728
|
-
return null;
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
756
|
function cursorSessionToken(accessToken) {
|
|
732
757
|
const payload = accessToken.includes(".") ? decodeBase64UrlJson(accessToken.split(".")[1]) : null;
|
|
733
758
|
const subject = typeof payload?.sub === "string" ? payload.sub : null;
|
|
@@ -736,12 +761,6 @@ function cursorSessionToken(accessToken) {
|
|
|
736
761
|
const userId = (parts.length > 1 ? parts[1] : parts[0]).trim();
|
|
737
762
|
return userId ? `${userId}%3A%3A${accessToken}` : null;
|
|
738
763
|
}
|
|
739
|
-
function identityFields(email, displayName) {
|
|
740
|
-
return {
|
|
741
|
-
email: email ?? null,
|
|
742
|
-
displayName: displayName ?? null
|
|
743
|
-
};
|
|
744
|
-
}
|
|
745
764
|
async function connectPost(url, token) {
|
|
746
765
|
try {
|
|
747
766
|
const res = await fetch(url, {
|
|
@@ -798,10 +817,10 @@ function appendCredits(metrics, creditGrants, stripe) {
|
|
|
798
817
|
if (total <= 0) return;
|
|
799
818
|
metrics.push({ label: "Credits", used: dollars(Math.max(0, total - (hasValidGrants ? grantUsed : 0))), limit: null, format: { kind: "dollars" } });
|
|
800
819
|
}
|
|
801
|
-
async function cursorBilling(account) {
|
|
820
|
+
async function cursorBilling(account, tz) {
|
|
802
821
|
const [core, activity, spend] = await Promise.all([
|
|
803
822
|
cursorBillingCore(account),
|
|
804
|
-
cursorActivity(account.homeDir),
|
|
823
|
+
cursorActivity(tz, account.homeDir),
|
|
805
824
|
cursorModelSpend(account.homeDir)
|
|
806
825
|
]);
|
|
807
826
|
let merged = activity;
|
|
@@ -831,7 +850,7 @@ async function cursorBillingCore(account) {
|
|
|
831
850
|
const planFallback = membership ? membership.charAt(0).toUpperCase() + membership.slice(1) : null;
|
|
832
851
|
if (!token) {
|
|
833
852
|
const error = tokenRes.status === "ok" ? "Not signed in \u2014 open Cursor" : sqliteStatusMessage(tokenRes.status);
|
|
834
|
-
return { plan: planFallback, metrics: [], error, ...identityFields(email, displayName) };
|
|
853
|
+
return { plan: planFallback, metrics: [], error, ...identityFields({ email, displayName }) };
|
|
835
854
|
}
|
|
836
855
|
const [usage, planInfo, creditGrants, stripe] = await Promise.all([
|
|
837
856
|
connectPost(USAGE_URL, token),
|
|
@@ -841,7 +860,7 @@ async function cursorBillingCore(account) {
|
|
|
841
860
|
]);
|
|
842
861
|
if (!usage || usage.__status) {
|
|
843
862
|
const expired = usage?.__status === 401 || usage?.__status === 403;
|
|
844
|
-
return { plan: planFallback, metrics: [], error: expired ? "Token expired \u2014 re-open Cursor" : "Cursor API error", ...identityFields(email, displayName) };
|
|
863
|
+
return { plan: planFallback, metrics: [], error: expired ? "Token expired \u2014 re-open Cursor" : "Cursor API error", ...identityFields({ email, displayName }) };
|
|
845
864
|
}
|
|
846
865
|
const planName = planInfo?.planInfo?.planName ?? planFallback;
|
|
847
866
|
const price = planInfo?.planInfo?.price;
|
|
@@ -852,7 +871,6 @@ async function cursorBillingCore(account) {
|
|
|
852
871
|
const endMs = numberValue(rawEnd) ?? NaN;
|
|
853
872
|
const iso = msToIso(endMs);
|
|
854
873
|
const resets = iso && endMs > 0 ? resetIn(iso) : null;
|
|
855
|
-
appendCredits(metrics, creditGrants, stripe);
|
|
856
874
|
const limit = numberValue(pu.limit);
|
|
857
875
|
const planUsedCents = numberValue(pu.totalSpend) ?? (limit !== void 0 && numberValue(pu.remaining) !== void 0 ? limit - numberValue(pu.remaining) : void 0);
|
|
858
876
|
const computedPercent = limit !== void 0 && limit > 0 && planUsedCents !== void 0 ? planUsedCents / limit * 100 : void 0;
|
|
@@ -908,10 +926,11 @@ async function cursorBillingCore(account) {
|
|
|
908
926
|
});
|
|
909
927
|
}
|
|
910
928
|
}
|
|
929
|
+
appendCredits(metrics, creditGrants, stripe);
|
|
911
930
|
if (metrics.length === 0) {
|
|
912
|
-
return { plan, metrics: [], error: usage.enabled === false ? "No active subscription" : "No usage data", ...identityFields(email, displayName) };
|
|
931
|
+
return { plan, metrics: [], error: usage.enabled === false ? "No active subscription" : "No usage data", ...identityFields({ email, displayName }) };
|
|
913
932
|
}
|
|
914
|
-
return { plan, metrics, error: null, ...identityFields(email, displayName) };
|
|
933
|
+
return { plan, metrics, error: null, ...identityFields({ email, displayName }) };
|
|
915
934
|
}
|
|
916
935
|
|
|
917
936
|
// src/providers/cursor/composer.ts
|
|
@@ -985,6 +1004,7 @@ var PRICING = {
|
|
|
985
1004
|
"claude-opus-4": { i: 5e-6, o: 25e-6, cc: 625e-8, cr: 5e-7 },
|
|
986
1005
|
"claude-3-opus": { i: 15e-6, o: 75e-6, cc: 1875e-8, cr: 15e-7 },
|
|
987
1006
|
"claude-sonnet-4": { i: 3e-6, o: 15e-6, cc: 375e-8, cr: 3e-7 },
|
|
1007
|
+
"claude-sonnet-5": { i: 3e-6, o: 15e-6, cc: 375e-8, cr: 3e-7 },
|
|
988
1008
|
"claude-haiku-4": { i: 1e-6, o: 5e-6, cc: 125e-8, cr: 1e-7 },
|
|
989
1009
|
"claude-fable-5": { i: 1e-5, o: 5e-5, cc: 125e-7, cr: 1e-6 }
|
|
990
1010
|
};
|
|
@@ -1129,8 +1149,8 @@ async function claudeTable(tz, homeDir) {
|
|
|
1129
1149
|
|
|
1130
1150
|
// src/providers/claude/billing.ts
|
|
1131
1151
|
import { readFile as readFile2 } from "fs/promises";
|
|
1132
|
-
import { join as
|
|
1133
|
-
import { homedir as
|
|
1152
|
+
import { join as join7 } from "path";
|
|
1153
|
+
import { homedir as homedir5 } from "os";
|
|
1134
1154
|
|
|
1135
1155
|
// src/providers/_shared/keychain.ts
|
|
1136
1156
|
import { execFile as execFileCb2 } from "child_process";
|
|
@@ -1157,7 +1177,10 @@ function unwrapGoKeyringBase64(raw) {
|
|
|
1157
1177
|
return Buffer.from(raw.slice(GO_KEYRING_PREFIX.length), "base64").toString("utf-8");
|
|
1158
1178
|
}
|
|
1159
1179
|
|
|
1160
|
-
// src/providers/claude/
|
|
1180
|
+
// src/providers/claude/identity.ts
|
|
1181
|
+
import { readFileSync } from "fs";
|
|
1182
|
+
import { join as join6 } from "path";
|
|
1183
|
+
import { homedir as homedir4 } from "os";
|
|
1161
1184
|
function titleWords(value) {
|
|
1162
1185
|
return value.split(/[_\s-]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()).join(" ");
|
|
1163
1186
|
}
|
|
@@ -1168,25 +1191,22 @@ function claudeOrgPlanLabel(orgType) {
|
|
|
1168
1191
|
const label = titleWords(stripped);
|
|
1169
1192
|
return label ? `Claude ${label}` : null;
|
|
1170
1193
|
}
|
|
1171
|
-
|
|
1194
|
+
function readClaudeIdentity(homeDir) {
|
|
1172
1195
|
const base = homeDir ? expandHome(homeDir) : homedir4();
|
|
1173
1196
|
try {
|
|
1174
|
-
const parsed = JSON.parse(
|
|
1197
|
+
const parsed = JSON.parse(readFileSync(join6(base, ".claude.json"), "utf-8"));
|
|
1175
1198
|
const oauth = parsed?.oauthAccount;
|
|
1176
1199
|
const email = typeof oauth?.emailAddress === "string" && oauth.emailAddress.trim() ? oauth.emailAddress.trim() : void 0;
|
|
1177
1200
|
const displayName = typeof oauth?.displayName === "string" && oauth.displayName.trim() ? oauth.displayName.trim() : void 0;
|
|
1178
1201
|
const plan = claudeOrgPlanLabel(parsed?.organizationType);
|
|
1179
|
-
|
|
1202
|
+
const accountUuid = typeof oauth?.accountUuid === "string" && oauth.accountUuid.trim() ? oauth.accountUuid.trim() : void 0;
|
|
1203
|
+
return { email, displayName, plan: plan ?? void 0, accountUuid };
|
|
1180
1204
|
} catch {
|
|
1181
1205
|
return {};
|
|
1182
1206
|
}
|
|
1183
1207
|
}
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
email: identity.email ?? null,
|
|
1187
|
-
displayName: identity.displayName ?? null
|
|
1188
|
-
};
|
|
1189
|
-
}
|
|
1208
|
+
|
|
1209
|
+
// src/providers/claude/billing.ts
|
|
1190
1210
|
function parseAuth(raw) {
|
|
1191
1211
|
try {
|
|
1192
1212
|
const creds = JSON.parse(raw);
|
|
@@ -1196,7 +1216,8 @@ function parseAuth(raw) {
|
|
|
1196
1216
|
return {
|
|
1197
1217
|
token,
|
|
1198
1218
|
subscriptionType: typeof o.subscriptionType === "string" ? o.subscriptionType : void 0,
|
|
1199
|
-
rateLimitTier: typeof o.rateLimitTier === "string" ? o.rateLimitTier : void 0
|
|
1219
|
+
rateLimitTier: typeof o.rateLimitTier === "string" ? o.rateLimitTier : void 0,
|
|
1220
|
+
expiresAt: typeof o.expiresAt === "number" && Number.isFinite(o.expiresAt) ? o.expiresAt : void 0
|
|
1200
1221
|
};
|
|
1201
1222
|
} catch {
|
|
1202
1223
|
return null;
|
|
@@ -1205,7 +1226,7 @@ function parseAuth(raw) {
|
|
|
1205
1226
|
async function readCredentialsFile(homeDir) {
|
|
1206
1227
|
for (const dir of claudeConfigDirs(homeDir)) {
|
|
1207
1228
|
try {
|
|
1208
|
-
const auth = parseAuth(await readFile2(
|
|
1229
|
+
const auth = parseAuth(await readFile2(join7(dir, ".credentials.json"), "utf-8"));
|
|
1209
1230
|
if (auth) return auth;
|
|
1210
1231
|
} catch {
|
|
1211
1232
|
}
|
|
@@ -1216,20 +1237,65 @@ async function readMacKeychain() {
|
|
|
1216
1237
|
const raw = await readMacKeychainRaw("Claude Code-credentials");
|
|
1217
1238
|
return raw ? parseAuth(raw) : null;
|
|
1218
1239
|
}
|
|
1219
|
-
async function
|
|
1240
|
+
async function authCandidates(homeDir) {
|
|
1220
1241
|
const expandedHomeDir = homeDir ? expandHome(homeDir) : void 0;
|
|
1221
|
-
const isDefault = !expandedHomeDir || expandedHomeDir ===
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1242
|
+
const isDefault = !expandedHomeDir || expandedHomeDir === homedir5();
|
|
1243
|
+
const out = [];
|
|
1244
|
+
const file = await readCredentialsFile(isDefault ? void 0 : expandedHomeDir);
|
|
1245
|
+
const keychain = process.platform === "darwin" ? await readMacKeychain() : null;
|
|
1246
|
+
const ordered = isDefault ? [keychain && { auth: keychain, shared: true }, file && { auth: file, shared: false }] : [file && { auth: file, shared: false }, keychain && { auth: keychain, shared: true }];
|
|
1247
|
+
for (const c of ordered) if (c) out.push(c);
|
|
1248
|
+
return out;
|
|
1249
|
+
}
|
|
1250
|
+
var tokenIdentityCache = /* @__PURE__ */ new Map();
|
|
1251
|
+
async function tokenIdentity(token) {
|
|
1252
|
+
if (tokenIdentityCache.has(token)) return tokenIdentityCache.get(token);
|
|
1253
|
+
try {
|
|
1254
|
+
const res = await fetch("https://api.anthropic.com/api/oauth/profile", {
|
|
1255
|
+
headers: {
|
|
1256
|
+
"Authorization": `Bearer ${token}`,
|
|
1257
|
+
"anthropic-beta": "oauth-2025-04-20",
|
|
1258
|
+
"User-Agent": "tokmon"
|
|
1259
|
+
},
|
|
1260
|
+
signal: AbortSignal.timeout(1e4)
|
|
1261
|
+
});
|
|
1262
|
+
if (res.status === 401 || res.status === 403) {
|
|
1263
|
+
tokenIdentityCache.set(token, null);
|
|
1264
|
+
return null;
|
|
1226
1265
|
}
|
|
1227
|
-
return
|
|
1266
|
+
if (!res.ok) return void 0;
|
|
1267
|
+
const data = await readJson(res);
|
|
1268
|
+
const uuid = data?.account?.uuid;
|
|
1269
|
+
if (typeof uuid !== "string" || !uuid) return void 0;
|
|
1270
|
+
const identity = {
|
|
1271
|
+
accountUuid: uuid,
|
|
1272
|
+
email: typeof data?.account?.email === "string" ? data.account.email : null
|
|
1273
|
+
};
|
|
1274
|
+
if (tokenIdentityCache.size > 64) tokenIdentityCache.clear();
|
|
1275
|
+
tokenIdentityCache.set(token, identity);
|
|
1276
|
+
return identity;
|
|
1277
|
+
} catch {
|
|
1278
|
+
return void 0;
|
|
1228
1279
|
}
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1280
|
+
}
|
|
1281
|
+
async function getAuth(homeDir, expectedUuid) {
|
|
1282
|
+
const candidates = await authCandidates(homeDir);
|
|
1283
|
+
let wrongAccountEmail;
|
|
1284
|
+
let sawExpired = false;
|
|
1285
|
+
for (const { auth, shared } of candidates) {
|
|
1286
|
+
if (auth.expiresAt !== void 0 && auth.expiresAt < Date.now() - 6e4) {
|
|
1287
|
+
sawExpired = true;
|
|
1288
|
+
continue;
|
|
1289
|
+
}
|
|
1290
|
+
if (!shared || !expectedUuid) return { auth };
|
|
1291
|
+
const identity = await tokenIdentity(auth.token);
|
|
1292
|
+
if (identity === void 0) return { auth };
|
|
1293
|
+
if (identity === null) continue;
|
|
1294
|
+
if (identity.accountUuid === expectedUuid) return { auth };
|
|
1295
|
+
wrongAccountEmail = identity.email;
|
|
1296
|
+
}
|
|
1297
|
+
if (wrongAccountEmail !== void 0) return { auth: null, wrongAccountEmail };
|
|
1298
|
+
return { auth: null, expired: sawExpired };
|
|
1233
1299
|
}
|
|
1234
1300
|
function planLabel(auth) {
|
|
1235
1301
|
const sub = auth.subscriptionType;
|
|
@@ -1239,14 +1305,6 @@ function planLabel(auth) {
|
|
|
1239
1305
|
return tier ? `${base} ${tier[1]}x` : base;
|
|
1240
1306
|
}
|
|
1241
1307
|
var pct = (used, resets, primary) => percentMetric("", used, resets ?? null, primary);
|
|
1242
|
-
function numberValue2(value) {
|
|
1243
|
-
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
1244
|
-
if (typeof value === "string" && value.trim()) {
|
|
1245
|
-
const n = Number(value);
|
|
1246
|
-
if (Number.isFinite(n)) return n;
|
|
1247
|
-
}
|
|
1248
|
-
return void 0;
|
|
1249
|
-
}
|
|
1250
1308
|
function boolValue(value) {
|
|
1251
1309
|
if (typeof value === "boolean") return value;
|
|
1252
1310
|
if (typeof value === "number") return value !== 0;
|
|
@@ -1255,22 +1313,23 @@ function boolValue(value) {
|
|
|
1255
1313
|
}
|
|
1256
1314
|
function resetFrom(value) {
|
|
1257
1315
|
if (typeof value === "string" && value.trim()) return resetIn(value);
|
|
1258
|
-
const n =
|
|
1316
|
+
const n = numberValue(value);
|
|
1259
1317
|
if (n === void 0) return null;
|
|
1260
1318
|
const ms = Math.abs(n) < 1e10 ? n * 1e3 : n;
|
|
1261
1319
|
return resetIn(new Date(ms).toISOString());
|
|
1262
1320
|
}
|
|
1263
1321
|
function usageMetric(label, window, primary) {
|
|
1264
|
-
const used =
|
|
1322
|
+
const used = numberValue(window?.utilization);
|
|
1265
1323
|
if (used === void 0) return null;
|
|
1266
1324
|
return { ...pct(used, resetFrom(window?.resets_at), primary), label };
|
|
1267
1325
|
}
|
|
1268
1326
|
async function claudeBilling(account) {
|
|
1269
|
-
const
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1327
|
+
const identity = readClaudeIdentity(account.homeDir);
|
|
1328
|
+
const { auth, wrongAccountEmail, expired } = await getAuth(account.homeDir, identity.accountUuid);
|
|
1329
|
+
if (!auth) {
|
|
1330
|
+
const error = wrongAccountEmail !== void 0 ? `Signed in as ${wrongAccountEmail ?? "another account"} \u2014 run claude in this home to refresh` : expired ? "Token expired \u2014 run claude to refresh" : "No OAuth token \u2014 run claude and log in";
|
|
1331
|
+
return { plan: identity.plan ?? null, metrics: [], error, ...identityFields(identity) };
|
|
1332
|
+
}
|
|
1274
1333
|
const plan = identity.plan ?? planLabel(auth);
|
|
1275
1334
|
try {
|
|
1276
1335
|
const res = await fetch("https://api.anthropic.com/api/oauth/usage", {
|
|
@@ -1282,14 +1341,14 @@ async function claudeBilling(account) {
|
|
|
1282
1341
|
signal: AbortSignal.timeout(1e4)
|
|
1283
1342
|
});
|
|
1284
1343
|
if (res.status === 429) {
|
|
1285
|
-
const retryAfter =
|
|
1344
|
+
const retryAfter = numberValue(res.headers.get("retry-after"));
|
|
1286
1345
|
const retryText = retryAfter !== void 0 ? ` \u2014 retry in ~${Math.ceil(retryAfter / 60)}m` : " \u2014 retrying next poll";
|
|
1287
|
-
return { plan, metrics: [], error: `Rate limited${retryText}`, ...
|
|
1346
|
+
return { plan, metrics: [], error: `Rate limited${retryText}`, ...identityFields(identity) };
|
|
1288
1347
|
}
|
|
1289
|
-
if (res.status === 401) return { plan, metrics: [], error: "Token expired \u2014
|
|
1290
|
-
if (!res.ok) return { plan, metrics: [], error: `API ${res.status}`, ...
|
|
1348
|
+
if (res.status === 401) return { plan, metrics: [], error: "Token expired \u2014 run claude to refresh", ...identityFields(identity) };
|
|
1349
|
+
if (!res.ok) return { plan, metrics: [], error: `API ${res.status}`, ...identityFields(identity) };
|
|
1291
1350
|
const data = await readJson(res);
|
|
1292
|
-
if (!data) return { plan, metrics: [], error: "Unexpected API response", ...
|
|
1351
|
+
if (!data) return { plan, metrics: [], error: "Unexpected API response", ...identityFields(identity) };
|
|
1293
1352
|
const metrics = [];
|
|
1294
1353
|
const fiveHour = usageMetric("Session", data.five_hour, true);
|
|
1295
1354
|
if (fiveHour) metrics.push(fiveHour);
|
|
@@ -1298,8 +1357,8 @@ async function claudeBilling(account) {
|
|
|
1298
1357
|
const sevenDaySonnet = usageMetric("Sonnet", data.seven_day_sonnet);
|
|
1299
1358
|
if (sevenDaySonnet) metrics.push(sevenDaySonnet);
|
|
1300
1359
|
if (boolValue(data.extra_usage?.is_enabled)) {
|
|
1301
|
-
const usedCredits =
|
|
1302
|
-
const monthlyLimit =
|
|
1360
|
+
const usedCredits = numberValue(data.extra_usage?.used_credits);
|
|
1361
|
+
const monthlyLimit = numberValue(data.extra_usage?.monthly_limit);
|
|
1303
1362
|
if (usedCredits !== void 0 && (usedCredits > 0 || monthlyLimit !== void 0 && monthlyLimit > 0)) {
|
|
1304
1363
|
metrics.push({
|
|
1305
1364
|
label: "Extra",
|
|
@@ -1309,9 +1368,9 @@ async function claudeBilling(account) {
|
|
|
1309
1368
|
});
|
|
1310
1369
|
}
|
|
1311
1370
|
}
|
|
1312
|
-
return { plan, metrics, error: null, ...
|
|
1371
|
+
return { plan, metrics, error: null, ...identityFields(identity) };
|
|
1313
1372
|
} catch {
|
|
1314
|
-
return { plan, metrics: [], error: "Network error", ...
|
|
1373
|
+
return { plan, metrics: [], error: "Network error", ...identityFields(identity) };
|
|
1315
1374
|
}
|
|
1316
1375
|
}
|
|
1317
1376
|
|
|
@@ -1332,8 +1391,8 @@ var claudeProvider = {
|
|
|
1332
1391
|
import { readdir as readdir2, stat as fsStat2, access as access3, open as openFile } from "fs/promises";
|
|
1333
1392
|
import { createReadStream as createReadStream2 } from "fs";
|
|
1334
1393
|
import { createInterface as createInterface2 } from "readline";
|
|
1335
|
-
import { join as
|
|
1336
|
-
import { homedir as
|
|
1394
|
+
import { join as join8 } from "path";
|
|
1395
|
+
import { homedir as homedir6 } from "os";
|
|
1337
1396
|
var PRICING2 = {
|
|
1338
1397
|
"gpt-5.5-codex": { in: 5e-6, cr: 5e-7, out: 3e-5 },
|
|
1339
1398
|
"gpt-5.5": { in: 5e-6, cr: 5e-7, out: 3e-5 },
|
|
@@ -1345,26 +1404,26 @@ var PRICING2 = {
|
|
|
1345
1404
|
"gpt-5": { in: 125e-8, cr: 125e-9, out: 1e-5 },
|
|
1346
1405
|
"o4-mini": { in: 11e-7, cr: 275e-9, out: 44e-7 }
|
|
1347
1406
|
};
|
|
1348
|
-
var
|
|
1407
|
+
var FALLBACK_PRICE = PRICING2["gpt-5.5"];
|
|
1349
1408
|
var PRICE_KEYS2 = Object.keys(PRICING2).sort((a, b) => b.length - a.length);
|
|
1350
1409
|
function codexHomes(homeDir) {
|
|
1351
|
-
if (homeDir) return [.../* @__PURE__ */ new Set([
|
|
1410
|
+
if (homeDir) return [.../* @__PURE__ */ new Set([join8(homeDir, ".codex"), homeDir])];
|
|
1352
1411
|
const homes = [];
|
|
1353
1412
|
const codexHome = envDir("CODEX_HOME");
|
|
1354
1413
|
if (codexHome) homes.push(codexHome);
|
|
1355
|
-
homes.push(
|
|
1356
|
-
homes.push(
|
|
1414
|
+
homes.push(join8(homedir6(), ".codex"));
|
|
1415
|
+
homes.push(join8(homedir6(), ".config", "codex"));
|
|
1357
1416
|
return [...new Set(homes)];
|
|
1358
1417
|
}
|
|
1359
1418
|
async function detectCodex(homeDir) {
|
|
1360
1419
|
for (const home of codexHomes(homeDir)) {
|
|
1361
1420
|
try {
|
|
1362
|
-
await access3(
|
|
1421
|
+
await access3(join8(home, "sessions"));
|
|
1363
1422
|
return true;
|
|
1364
1423
|
} catch {
|
|
1365
1424
|
}
|
|
1366
1425
|
try {
|
|
1367
|
-
await access3(
|
|
1426
|
+
await access3(join8(home, "archived_sessions"));
|
|
1368
1427
|
return true;
|
|
1369
1428
|
} catch {
|
|
1370
1429
|
}
|
|
@@ -1376,7 +1435,8 @@ function modelKeyMatches(model, key) {
|
|
|
1376
1435
|
while (idx >= 0) {
|
|
1377
1436
|
const before = idx === 0 ? "" : model[idx - 1];
|
|
1378
1437
|
const rest = model.slice(idx + key.length);
|
|
1379
|
-
|
|
1438
|
+
const versionContinues = rest[0] === "." && /\d/.test(rest[1] ?? "");
|
|
1439
|
+
if ((!before || !/[a-z0-9-]/.test(before)) && !versionContinues && (rest === "" || rest[0] === "-" || !/[a-z0-9]/.test(rest[0]))) {
|
|
1380
1440
|
return true;
|
|
1381
1441
|
}
|
|
1382
1442
|
idx = model.indexOf(key, idx + key.length);
|
|
@@ -1388,7 +1448,7 @@ function priceFor2(model) {
|
|
|
1388
1448
|
for (const key of PRICE_KEYS2) {
|
|
1389
1449
|
if (modelKeyMatches(m, key)) return PRICING2[key];
|
|
1390
1450
|
}
|
|
1391
|
-
return
|
|
1451
|
+
return FALLBACK_PRICE;
|
|
1392
1452
|
}
|
|
1393
1453
|
function extractModel(obj) {
|
|
1394
1454
|
const p = obj?.payload ?? obj;
|
|
@@ -1597,7 +1657,7 @@ async function loadEntries2(since, homeDir) {
|
|
|
1597
1657
|
const seen = /* @__PURE__ */ new Set();
|
|
1598
1658
|
const seenIno = /* @__PURE__ */ new Set();
|
|
1599
1659
|
for (const home of codexHomes(homeDir)) {
|
|
1600
|
-
for (const dir of [
|
|
1660
|
+
for (const dir of [join8(home, "sessions"), join8(home, "archived_sessions")]) {
|
|
1601
1661
|
let listing;
|
|
1602
1662
|
try {
|
|
1603
1663
|
listing = await readdir2(dir, { recursive: true });
|
|
@@ -1606,7 +1666,7 @@ async function loadEntries2(since, homeDir) {
|
|
|
1606
1666
|
}
|
|
1607
1667
|
for (const f of listing) {
|
|
1608
1668
|
if (!f.endsWith(".jsonl")) continue;
|
|
1609
|
-
const path =
|
|
1669
|
+
const path = join8(dir, f);
|
|
1610
1670
|
if (seen.has(path)) continue;
|
|
1611
1671
|
seen.add(path);
|
|
1612
1672
|
try {
|
|
@@ -1637,19 +1697,10 @@ async function codexTable(tz, homeDir) {
|
|
|
1637
1697
|
import { readFile as readFile3, readdir as readdir3, stat as fsStat3 } from "fs/promises";
|
|
1638
1698
|
import { createReadStream as createReadStream3 } from "fs";
|
|
1639
1699
|
import { createInterface as createInterface3 } from "readline";
|
|
1640
|
-
import { join as
|
|
1700
|
+
import { join as join9 } from "path";
|
|
1641
1701
|
var USAGE_URL2 = "https://chatgpt.com/backend-api/wham/usage";
|
|
1642
1702
|
var RESET_CREDITS_URL = "https://chatgpt.com/backend-api/wham/rate-limit-reset-credits";
|
|
1643
1703
|
var CREDIT_USD_RATE = 0.04;
|
|
1644
|
-
function decodeBase64UrlJson2(segment) {
|
|
1645
|
-
try {
|
|
1646
|
-
const normalized = segment.replace(/-/g, "+").replace(/_/g, "/");
|
|
1647
|
-
const padded = normalized + "=".repeat((4 - normalized.length % 4) % 4);
|
|
1648
|
-
return JSON.parse(Buffer.from(padded, "base64").toString("utf8"));
|
|
1649
|
-
} catch {
|
|
1650
|
-
return null;
|
|
1651
|
-
}
|
|
1652
|
-
}
|
|
1653
1704
|
function chatGptPlanLabel(planType) {
|
|
1654
1705
|
if (typeof planType !== "string" || !planType.trim()) return null;
|
|
1655
1706
|
const p = planType.trim().toLowerCase();
|
|
@@ -1664,28 +1715,19 @@ function chatGptPlanLabel(planType) {
|
|
|
1664
1715
|
if (labels[p]) return labels[p];
|
|
1665
1716
|
return `ChatGPT ${p.charAt(0).toUpperCase()}${p.slice(1)}`;
|
|
1666
1717
|
}
|
|
1667
|
-
function
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
if (!payload || typeof payload !== "object") return {};
|
|
1671
|
-
const email = typeof payload.email === "string" && payload.email.trim() ? payload.email.trim() : void 0;
|
|
1672
|
-
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;
|
|
1718
|
+
function codexIdentity(idToken) {
|
|
1719
|
+
const { email, displayName, payload } = identityFromIdToken(idToken);
|
|
1720
|
+
if (!payload) return {};
|
|
1673
1721
|
const plan = chatGptPlanLabel(payload["https://api.openai.com/auth"]?.chatgpt_plan_type);
|
|
1674
1722
|
return { email, displayName, plan: plan ?? void 0 };
|
|
1675
1723
|
}
|
|
1676
|
-
function identityFields3(auth) {
|
|
1677
|
-
return {
|
|
1678
|
-
email: auth?.email ?? null,
|
|
1679
|
-
displayName: auth?.displayName ?? null
|
|
1680
|
-
};
|
|
1681
|
-
}
|
|
1682
1724
|
async function readAuthFile(home) {
|
|
1683
1725
|
try {
|
|
1684
|
-
const raw = await readFile3(
|
|
1726
|
+
const raw = await readFile3(join9(home, "auth.json"), "utf-8");
|
|
1685
1727
|
const auth = JSON.parse(raw);
|
|
1686
1728
|
const accessToken = auth?.tokens?.access_token;
|
|
1687
1729
|
if (!accessToken) return null;
|
|
1688
|
-
return { accessToken, accountId: auth?.tokens?.account_id, ...
|
|
1730
|
+
return { accessToken, accountId: auth?.tokens?.account_id, ...codexIdentity(auth?.tokens?.id_token) };
|
|
1689
1731
|
} catch {
|
|
1690
1732
|
return null;
|
|
1691
1733
|
}
|
|
@@ -1697,7 +1739,7 @@ async function readKeychainAuth() {
|
|
|
1697
1739
|
const auth = JSON.parse(raw);
|
|
1698
1740
|
const accessToken = auth?.tokens?.access_token;
|
|
1699
1741
|
if (!accessToken) return null;
|
|
1700
|
-
return { accessToken, accountId: auth?.tokens?.account_id, ...
|
|
1742
|
+
return { accessToken, accountId: auth?.tokens?.account_id, ...codexIdentity(auth?.tokens?.id_token) };
|
|
1701
1743
|
} catch {
|
|
1702
1744
|
return null;
|
|
1703
1745
|
}
|
|
@@ -1707,7 +1749,7 @@ async function getAuth2(homeDir) {
|
|
|
1707
1749
|
const auth = await readAuthFile(home);
|
|
1708
1750
|
if (auth) return auth;
|
|
1709
1751
|
}
|
|
1710
|
-
if (process.platform === "darwin") return readKeychainAuth();
|
|
1752
|
+
if (!homeDir && process.platform === "darwin") return readKeychainAuth();
|
|
1711
1753
|
return null;
|
|
1712
1754
|
}
|
|
1713
1755
|
function planLabel2(planType) {
|
|
@@ -1717,19 +1759,11 @@ function planLabel2(planType) {
|
|
|
1717
1759
|
if (p === "pro") return "Pro 20x";
|
|
1718
1760
|
return planType.charAt(0).toUpperCase() + planType.slice(1);
|
|
1719
1761
|
}
|
|
1720
|
-
function numberValue3(value) {
|
|
1721
|
-
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
1722
|
-
if (typeof value === "string" && value.trim()) {
|
|
1723
|
-
const n = Number(value);
|
|
1724
|
-
if (Number.isFinite(n)) return n;
|
|
1725
|
-
}
|
|
1726
|
-
return void 0;
|
|
1727
|
-
}
|
|
1728
1762
|
function resetDateMs(window) {
|
|
1729
1763
|
if (!window) return null;
|
|
1730
|
-
const absolute =
|
|
1764
|
+
const absolute = numberValue(window.reset_at ?? window.resets_at);
|
|
1731
1765
|
if (absolute !== void 0) return absolute > 1e10 ? absolute : absolute * 1e3;
|
|
1732
|
-
const after =
|
|
1766
|
+
const after = numberValue(window.reset_after_seconds);
|
|
1733
1767
|
if (after !== void 0) return Date.now() + after * 1e3;
|
|
1734
1768
|
for (const key of ["reset_at", "resets_at"]) {
|
|
1735
1769
|
const raw = window[key];
|
|
@@ -1745,10 +1779,16 @@ function resetFrom2(window) {
|
|
|
1745
1779
|
const iso = ms === null ? null : msToIso(ms);
|
|
1746
1780
|
return iso ? resetIn(iso) : null;
|
|
1747
1781
|
}
|
|
1782
|
+
function windowSeconds(window) {
|
|
1783
|
+
const seconds = numberValue(window?.limit_window_seconds ?? window?.period_seconds ?? window?.window_seconds);
|
|
1784
|
+
if (seconds !== void 0) return seconds;
|
|
1785
|
+
const minutes = numberValue(window?.limit_window_minutes ?? window?.window_minutes ?? window?.period_minutes);
|
|
1786
|
+
return minutes === void 0 ? void 0 : minutes * 60;
|
|
1787
|
+
}
|
|
1748
1788
|
function normalizedUsedPercent(window, percent) {
|
|
1749
|
-
const used =
|
|
1789
|
+
const used = numberValue(percent);
|
|
1750
1790
|
if (used === void 0) return void 0;
|
|
1751
|
-
const periodSeconds =
|
|
1791
|
+
const periodSeconds = windowSeconds(window);
|
|
1752
1792
|
const resetMs = resetDateMs(window);
|
|
1753
1793
|
if (periodSeconds && resetMs) {
|
|
1754
1794
|
const elapsedMs = periodSeconds * 1e3 - Math.max(0, resetMs - Date.now());
|
|
@@ -1759,7 +1799,7 @@ function normalizedUsedPercent(window, percent) {
|
|
|
1759
1799
|
function metricLabelForAdditional(window) {
|
|
1760
1800
|
const name = String(window?.limit_name ?? window?.name ?? window?.metered_feature ?? window?.feature ?? "").toLowerCase();
|
|
1761
1801
|
if (!name.includes("spark")) return null;
|
|
1762
|
-
const seconds =
|
|
1802
|
+
const seconds = windowSeconds(window);
|
|
1763
1803
|
return name.includes("week") || seconds !== void 0 && seconds >= 6 * 24 * 60 * 60 ? "Spark Weekly" : "Spark";
|
|
1764
1804
|
}
|
|
1765
1805
|
function additionalRateLimits(rl) {
|
|
@@ -1784,9 +1824,9 @@ function appendWindowMetrics(metrics, rl, headerPct) {
|
|
|
1784
1824
|
}
|
|
1785
1825
|
}
|
|
1786
1826
|
function appendCredits2(metrics, source) {
|
|
1787
|
-
const balance =
|
|
1827
|
+
const balance = numberValue(source?.credits?.balance ?? source?.credit_balance);
|
|
1788
1828
|
if (balance !== void 0 && balance >= 0) {
|
|
1789
|
-
metrics.push({ label: "Credits", used:
|
|
1829
|
+
metrics.push({ label: "Credits", used: balance * CREDIT_USD_RATE, limit: null, format: { kind: "dollars" } });
|
|
1790
1830
|
}
|
|
1791
1831
|
}
|
|
1792
1832
|
async function fetchResetCredits(headers) {
|
|
@@ -1801,7 +1841,7 @@ async function fetchResetCredits(headers) {
|
|
|
1801
1841
|
});
|
|
1802
1842
|
if (!res.ok) return void 0;
|
|
1803
1843
|
const data = await readJson(res);
|
|
1804
|
-
return
|
|
1844
|
+
return numberValue(data?.available_count ?? data?.available ?? data?.remaining);
|
|
1805
1845
|
} catch {
|
|
1806
1846
|
return void 0;
|
|
1807
1847
|
}
|
|
@@ -1827,20 +1867,22 @@ async function liveBilling(auth) {
|
|
|
1827
1867
|
const metrics = [];
|
|
1828
1868
|
appendWindowMetrics(metrics, data.rate_limit ?? data, headerPct);
|
|
1829
1869
|
appendCredits2(metrics, data);
|
|
1830
|
-
const resetCredits =
|
|
1870
|
+
const resetCredits = numberValue(data?.rate_limit_reset_credits?.available_count ?? data?.rate_limit_reset_credits?.available) ?? await fetchResetCredits(headers);
|
|
1831
1871
|
if (resetCredits !== void 0 && resetCredits >= 0) {
|
|
1832
|
-
metrics.push({ label: "
|
|
1872
|
+
metrics.push({ label: "Resets", used: resetCredits, limit: null, format: { kind: "count", suffix: "available" } });
|
|
1833
1873
|
}
|
|
1834
1874
|
if (metrics.length === 0) return null;
|
|
1835
|
-
return { plan: auth.plan ?? planLabel2(data.plan_type), metrics, error: null, ...
|
|
1875
|
+
return { plan: auth.plan ?? planLabel2(data.plan_type), metrics, error: null, ...identityFields(auth) };
|
|
1836
1876
|
} catch {
|
|
1837
1877
|
return null;
|
|
1838
1878
|
}
|
|
1839
1879
|
}
|
|
1840
|
-
|
|
1841
|
-
|
|
1880
|
+
var SNAPSHOT_CANDIDATES = 8;
|
|
1881
|
+
var SNAPSHOT_STALE_MS = 24 * 36e5;
|
|
1882
|
+
async function newestRolloutFiles(homeDir) {
|
|
1883
|
+
const all = [];
|
|
1842
1884
|
for (const home of codexHomes(homeDir)) {
|
|
1843
|
-
const dir =
|
|
1885
|
+
const dir = join9(home, "sessions");
|
|
1844
1886
|
let listing;
|
|
1845
1887
|
try {
|
|
1846
1888
|
listing = await readdir3(dir, { recursive: true });
|
|
@@ -1849,19 +1891,17 @@ async function newestRolloutFile(homeDir) {
|
|
|
1849
1891
|
}
|
|
1850
1892
|
for (const f of listing) {
|
|
1851
1893
|
if (!f.endsWith(".jsonl") || !f.includes("rollout-")) continue;
|
|
1852
|
-
const path =
|
|
1894
|
+
const path = join9(dir, f);
|
|
1853
1895
|
try {
|
|
1854
1896
|
const s = await fsStat3(path);
|
|
1855
|
-
|
|
1897
|
+
all.push({ path, mtime: s.mtimeMs });
|
|
1856
1898
|
} catch {
|
|
1857
1899
|
}
|
|
1858
1900
|
}
|
|
1859
1901
|
}
|
|
1860
|
-
return
|
|
1902
|
+
return all.sort((a, b) => b.mtime - a.mtime).slice(0, SNAPSHOT_CANDIDATES);
|
|
1861
1903
|
}
|
|
1862
|
-
async function
|
|
1863
|
-
const path = await newestRolloutFile(homeDir);
|
|
1864
|
-
if (!path) return null;
|
|
1904
|
+
async function lastRateLimits(path) {
|
|
1865
1905
|
let last = null;
|
|
1866
1906
|
try {
|
|
1867
1907
|
const input = createReadStream3(path);
|
|
@@ -1879,16 +1919,23 @@ async function snapshotBilling(homeDir, auth = null) {
|
|
|
1879
1919
|
} catch {
|
|
1880
1920
|
return null;
|
|
1881
1921
|
}
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
metrics
|
|
1922
|
+
return last;
|
|
1923
|
+
}
|
|
1924
|
+
async function snapshotBilling(homeDir, auth = null) {
|
|
1925
|
+
for (const file of await newestRolloutFiles(homeDir)) {
|
|
1926
|
+
const last = await lastRateLimits(file.path);
|
|
1927
|
+
if (!last) continue;
|
|
1928
|
+
const metrics = [];
|
|
1929
|
+
appendWindowMetrics(metrics, last.rate_limit ?? last);
|
|
1930
|
+
appendCredits2(metrics, last);
|
|
1931
|
+
const resetCredits = numberValue(last?.rate_limit_reset_credits?.available_count ?? last?.rate_limit_reset_credits?.available);
|
|
1932
|
+
if (resetCredits !== void 0 && resetCredits >= 0) {
|
|
1933
|
+
metrics.push({ label: "Resets", used: resetCredits, limit: null, format: { kind: "count", suffix: "available" } });
|
|
1934
|
+
}
|
|
1935
|
+
if (metrics.length === 0) continue;
|
|
1936
|
+
return { plan: auth?.plan ?? planLabel2(last.plan_type), metrics, error: null, asOfMs: file.mtime, ...identityFields(auth) };
|
|
1889
1937
|
}
|
|
1890
|
-
|
|
1891
|
-
return { plan: auth?.plan ?? planLabel2(last.plan_type), metrics, error: null, ...identityFields3(auth) };
|
|
1938
|
+
return null;
|
|
1892
1939
|
}
|
|
1893
1940
|
async function codexBilling(account) {
|
|
1894
1941
|
const auth = await getAuth2(account.homeDir);
|
|
@@ -1897,12 +1944,15 @@ async function codexBilling(account) {
|
|
|
1897
1944
|
if (live) return live;
|
|
1898
1945
|
}
|
|
1899
1946
|
const snap = await snapshotBilling(account.homeDir, auth);
|
|
1900
|
-
if (snap)
|
|
1947
|
+
if (snap && Date.now() - snap.asOfMs < SNAPSHOT_STALE_MS) {
|
|
1948
|
+
const { asOfMs: _asOfMs, ...result } = snap;
|
|
1949
|
+
return result;
|
|
1950
|
+
}
|
|
1901
1951
|
return {
|
|
1902
|
-
plan: auth?.plan ?? null,
|
|
1952
|
+
plan: auth?.plan ?? snap?.plan ?? null,
|
|
1903
1953
|
metrics: [],
|
|
1904
1954
|
error: auth ? "Usage API failed \u2014 run codex to refresh" : "Not logged in \u2014 run codex",
|
|
1905
|
-
...
|
|
1955
|
+
...identityFields(auth)
|
|
1906
1956
|
};
|
|
1907
1957
|
}
|
|
1908
1958
|
|
|
@@ -2079,17 +2129,17 @@ var cursorProvider = {
|
|
|
2079
2129
|
hasBilling: true,
|
|
2080
2130
|
detect: (homeDir) => detectCursor(homeDir),
|
|
2081
2131
|
fetchTable: (account, tz) => cursorTable(tz, account.homeDir),
|
|
2082
|
-
fetchBilling: (account) => cursorBilling(account)
|
|
2132
|
+
fetchBilling: (account, tz) => cursorBilling(account, tz)
|
|
2083
2133
|
};
|
|
2084
2134
|
|
|
2085
2135
|
// src/providers/pi/usage.ts
|
|
2086
2136
|
import { readdir as readdir4, stat as fsStat4, access as access4 } from "fs/promises";
|
|
2087
2137
|
import { createReadStream as createReadStream4 } from "fs";
|
|
2088
2138
|
import { createInterface as createInterface4 } from "readline";
|
|
2089
|
-
import { join as
|
|
2090
|
-
import { homedir as
|
|
2139
|
+
import { join as join10 } from "path";
|
|
2140
|
+
import { homedir as homedir7 } from "os";
|
|
2091
2141
|
function piSessionsDir(homeDir) {
|
|
2092
|
-
return
|
|
2142
|
+
return join10(homeDir ?? homedir7(), ".pi", "agent", "sessions");
|
|
2093
2143
|
}
|
|
2094
2144
|
async function detectPi(homeDir) {
|
|
2095
2145
|
try {
|
|
@@ -2154,7 +2204,7 @@ async function loadEntries3(since, homeDir) {
|
|
|
2154
2204
|
}
|
|
2155
2205
|
for (const f of listing) {
|
|
2156
2206
|
if (!f.endsWith(".jsonl")) continue;
|
|
2157
|
-
const path =
|
|
2207
|
+
const path = join10(dir, f);
|
|
2158
2208
|
try {
|
|
2159
2209
|
const s = await fsStat4(path);
|
|
2160
2210
|
if (s.mtimeMs < since) continue;
|
|
@@ -2190,17 +2240,17 @@ var piProvider = {
|
|
|
2190
2240
|
|
|
2191
2241
|
// src/providers/opencode/usage.ts
|
|
2192
2242
|
import { access as access5 } from "fs/promises";
|
|
2193
|
-
import { join as
|
|
2194
|
-
import { homedir as
|
|
2243
|
+
import { join as join11 } from "path";
|
|
2244
|
+
import { homedir as homedir8 } from "os";
|
|
2195
2245
|
function opencodeDbPaths(homeDir) {
|
|
2196
|
-
const base = homeDir ??
|
|
2246
|
+
const base = homeDir ?? homedir8();
|
|
2197
2247
|
const paths = [];
|
|
2198
|
-
if (!homeDir && process.env.XDG_DATA_HOME) paths.push(
|
|
2199
|
-
paths.push(
|
|
2200
|
-
if (process.platform === "darwin") paths.push(
|
|
2248
|
+
if (!homeDir && process.env.XDG_DATA_HOME) paths.push(join11(process.env.XDG_DATA_HOME, "opencode", "opencode.db"));
|
|
2249
|
+
paths.push(join11(base, ".local", "share", "opencode", "opencode.db"));
|
|
2250
|
+
if (process.platform === "darwin") paths.push(join11(base, "Library", "Application Support", "opencode", "opencode.db"));
|
|
2201
2251
|
if (process.platform === "win32") {
|
|
2202
|
-
const lad = homeDir ?
|
|
2203
|
-
if (lad) paths.push(
|
|
2252
|
+
const lad = homeDir ? join11(homeDir, "AppData", "Local") : process.env.LOCALAPPDATA;
|
|
2253
|
+
if (lad) paths.push(join11(lad, "opencode", "opencode.db"));
|
|
2204
2254
|
}
|
|
2205
2255
|
return [...new Set(paths)];
|
|
2206
2256
|
}
|
|
@@ -2268,107 +2318,6 @@ var opencodeProvider = {
|
|
|
2268
2318
|
import { access as access6, readFile as readFile4, readdir as readdir5 } from "fs/promises";
|
|
2269
2319
|
import { join as join12 } from "path";
|
|
2270
2320
|
import { homedir as homedir9 } from "os";
|
|
2271
|
-
|
|
2272
|
-
// src/providers/detect.ts
|
|
2273
|
-
import { accessSync as accessSync2, constants as constants2, existsSync } from "fs";
|
|
2274
|
-
import { join as join11, delimiter as delimiter2, isAbsolute as isAbsolute2 } from "path";
|
|
2275
|
-
import { homedir as homedir8 } from "os";
|
|
2276
|
-
function searchDirs() {
|
|
2277
|
-
const home = homedir8();
|
|
2278
|
-
const fromEnv = (process.env.PATH ?? "").split(delimiter2).filter(Boolean);
|
|
2279
|
-
const extra = process.platform === "win32" ? [
|
|
2280
|
-
process.env.APPDATA && join11(process.env.APPDATA, "npm"),
|
|
2281
|
-
process.env.LOCALAPPDATA && join11(process.env.LOCALAPPDATA, "pnpm"),
|
|
2282
|
-
join11(home, "scoop", "shims")
|
|
2283
|
-
] : [
|
|
2284
|
-
"/opt/homebrew/bin",
|
|
2285
|
-
"/usr/local/bin",
|
|
2286
|
-
"/usr/bin",
|
|
2287
|
-
"/bin",
|
|
2288
|
-
"/opt/local/bin",
|
|
2289
|
-
join11(home, ".local", "bin"),
|
|
2290
|
-
join11(home, "bin"),
|
|
2291
|
-
join11(home, ".npm-global", "bin"),
|
|
2292
|
-
join11(home, ".bun", "bin"),
|
|
2293
|
-
join11(home, ".local", "share", "pnpm")
|
|
2294
|
-
];
|
|
2295
|
-
return [.../* @__PURE__ */ new Set([...fromEnv, ...extra.filter((d) => !!d)])];
|
|
2296
|
-
}
|
|
2297
|
-
function isExec2(p) {
|
|
2298
|
-
try {
|
|
2299
|
-
accessSync2(p, process.platform === "win32" ? constants2.F_OK : constants2.X_OK);
|
|
2300
|
-
return true;
|
|
2301
|
-
} catch {
|
|
2302
|
-
return false;
|
|
2303
|
-
}
|
|
2304
|
-
}
|
|
2305
|
-
function onPath(names) {
|
|
2306
|
-
const exts = process.platform === "win32" ? (process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM").split(";").map((e) => e.toLowerCase()).concat("") : [""];
|
|
2307
|
-
for (const dir of searchDirs()) {
|
|
2308
|
-
for (const n of names) {
|
|
2309
|
-
for (const e of exts) {
|
|
2310
|
-
if (isExec2(join11(dir, n + e))) return true;
|
|
2311
|
-
}
|
|
2312
|
-
}
|
|
2313
|
-
}
|
|
2314
|
-
return false;
|
|
2315
|
-
}
|
|
2316
|
-
function anyExists(paths) {
|
|
2317
|
-
return paths.some((p) => !!p && isExec2(p));
|
|
2318
|
-
}
|
|
2319
|
-
function installSignals(id) {
|
|
2320
|
-
const home = homedir8();
|
|
2321
|
-
const pf = process.env.ProgramFiles;
|
|
2322
|
-
const pf86 = process.env["ProgramFiles(x86)"];
|
|
2323
|
-
const lad = process.env.LOCALAPPDATA;
|
|
2324
|
-
switch (id) {
|
|
2325
|
-
case "claude":
|
|
2326
|
-
return onPath(["claude"]) || anyExists([
|
|
2327
|
-
"/Applications/Claude.app",
|
|
2328
|
-
join11(home, "Applications", "Claude.app"),
|
|
2329
|
-
lad && join11(lad, "Programs", "claude", "Claude.exe")
|
|
2330
|
-
]);
|
|
2331
|
-
case "codex": {
|
|
2332
|
-
const bin = process.env.CODEX_BIN;
|
|
2333
|
-
if (bin && isAbsolute2(bin) && isExec2(bin)) return true;
|
|
2334
|
-
return onPath(["codex"]) || anyExists([
|
|
2335
|
-
lad && join11(lad, "Programs", "OpenAI", "Codex", "bin", "codex.exe"),
|
|
2336
|
-
lad && join11(lad, "Programs", "OpenAI", "Codex", "codex.exe"),
|
|
2337
|
-
lad && join11(lad, "Programs", "codex", "codex.exe"),
|
|
2338
|
-
pf && join11(pf, "OpenAI", "Codex", "bin", "codex.exe")
|
|
2339
|
-
]) || existsSync(join11(home, ".codex", "sessions")) || existsSync(join11(home, ".codex", "auth.json"));
|
|
2340
|
-
}
|
|
2341
|
-
case "cursor":
|
|
2342
|
-
return onPath(["cursor", "cursor-agent"]) || anyExists([
|
|
2343
|
-
"/Applications/Cursor.app",
|
|
2344
|
-
join11(home, "Applications", "Cursor.app"),
|
|
2345
|
-
lad && join11(lad, "Programs", "cursor", "Cursor.exe"),
|
|
2346
|
-
pf && join11(pf, "Cursor", "Cursor.exe"),
|
|
2347
|
-
pf86 && join11(pf86, "Cursor", "Cursor.exe"),
|
|
2348
|
-
"/opt/Cursor/cursor",
|
|
2349
|
-
"/usr/share/cursor/cursor",
|
|
2350
|
-
"/usr/bin/cursor"
|
|
2351
|
-
]);
|
|
2352
|
-
case "pi":
|
|
2353
|
-
return onPath(["pi"]);
|
|
2354
|
-
case "opencode":
|
|
2355
|
-
return onPath(["opencode"]);
|
|
2356
|
-
case "copilot":
|
|
2357
|
-
return onPath(["gh"]);
|
|
2358
|
-
case "antigravity":
|
|
2359
|
-
return onPath(["antigravity"]) || anyExists([
|
|
2360
|
-
"/Applications/Antigravity.app",
|
|
2361
|
-
join11(home, "Applications", "Antigravity.app"),
|
|
2362
|
-
lad && join11(lad, "Programs", "Antigravity", "Antigravity.exe")
|
|
2363
|
-
]);
|
|
2364
|
-
case "gemini":
|
|
2365
|
-
return onPath(["gemini"]);
|
|
2366
|
-
default:
|
|
2367
|
-
return false;
|
|
2368
|
-
}
|
|
2369
|
-
}
|
|
2370
|
-
|
|
2371
|
-
// src/providers/copilot/billing.ts
|
|
2372
2321
|
var USAGE_URL3 = "https://api.github.com/copilot_internal/user";
|
|
2373
2322
|
var GH_KEYCHAIN_SERVICE = "gh:github.com";
|
|
2374
2323
|
function ghConfigDir(homeDir) {
|
|
@@ -2387,12 +2336,24 @@ function ghHostsPath(homeDir) {
|
|
|
2387
2336
|
return join12(ghConfigDir(homeDir), "hosts.yml");
|
|
2388
2337
|
}
|
|
2389
2338
|
async function detectCopilot(homeDir) {
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2339
|
+
const home = homeDir ?? homedir9();
|
|
2340
|
+
const candidates = [
|
|
2341
|
+
join12(home, ".config", "github-copilot"),
|
|
2342
|
+
join12(home, ".copilot"),
|
|
2343
|
+
join12(vscodeUserDir(homeDir), "globalStorage", "github.copilot"),
|
|
2344
|
+
join12(vscodeUserDir(homeDir), "globalStorage", "github.copilot-chat")
|
|
2345
|
+
];
|
|
2346
|
+
if (process.platform === "win32") {
|
|
2347
|
+
candidates.push(join12(envDir("LOCALAPPDATA") ?? join12(home, "AppData", "Local"), "github-copilot"));
|
|
2394
2348
|
}
|
|
2395
|
-
|
|
2349
|
+
for (const path of candidates) {
|
|
2350
|
+
try {
|
|
2351
|
+
await access6(path);
|
|
2352
|
+
return true;
|
|
2353
|
+
} catch {
|
|
2354
|
+
}
|
|
2355
|
+
}
|
|
2356
|
+
return false;
|
|
2396
2357
|
}
|
|
2397
2358
|
function unquoteYamlValue(value) {
|
|
2398
2359
|
const trimmed = value.trim();
|
|
@@ -2481,7 +2442,7 @@ async function loadTokenFromVsCode(homeDir) {
|
|
|
2481
2442
|
return null;
|
|
2482
2443
|
}
|
|
2483
2444
|
async function loadToken(homeDir) {
|
|
2484
|
-
return await loadTokenFromHosts(homeDir) || await loadTokenFromGhKeychain() || await loadTokenFromVsCode(homeDir);
|
|
2445
|
+
return await loadTokenFromHosts(homeDir) || (homeDir ? null : await loadTokenFromGhKeychain()) || await loadTokenFromVsCode(homeDir);
|
|
2485
2446
|
}
|
|
2486
2447
|
function redactToken(token) {
|
|
2487
2448
|
return token.length <= 4 ? "****" : `****${token.slice(-4)}`;
|
|
@@ -2489,14 +2450,6 @@ function redactToken(token) {
|
|
|
2489
2450
|
function resetDate(value) {
|
|
2490
2451
|
return typeof value === "string" && value.trim() ? resetIn(value) : null;
|
|
2491
2452
|
}
|
|
2492
|
-
function numberValue4(value) {
|
|
2493
|
-
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
2494
|
-
if (typeof value === "string" && value.trim()) {
|
|
2495
|
-
const n = Number(value);
|
|
2496
|
-
if (Number.isFinite(n)) return n;
|
|
2497
|
-
}
|
|
2498
|
-
return void 0;
|
|
2499
|
-
}
|
|
2500
2453
|
function boolValue2(value) {
|
|
2501
2454
|
if (typeof value === "boolean") return value;
|
|
2502
2455
|
if (typeof value === "number") return value !== 0;
|
|
@@ -2509,10 +2462,10 @@ function boolValue2(value) {
|
|
|
2509
2462
|
}
|
|
2510
2463
|
function percentMetric2(label, snapshot, reset, primary) {
|
|
2511
2464
|
if (!snapshot) return null;
|
|
2512
|
-
const entitlement =
|
|
2513
|
-
const remaining =
|
|
2465
|
+
const entitlement = numberValue(snapshot.entitlement);
|
|
2466
|
+
const remaining = numberValue(snapshot.remaining);
|
|
2514
2467
|
if (boolValue2(snapshot.unlimited) === true || entitlement === -1 || remaining === -1 || entitlement === 0) return null;
|
|
2515
|
-
const percentRemaining =
|
|
2468
|
+
const percentRemaining = numberValue(snapshot.percent_remaining);
|
|
2516
2469
|
const used = percentRemaining !== void 0 ? 100 - percentRemaining : entitlement !== void 0 && entitlement > 0 && remaining !== void 0 ? 100 - remaining / entitlement * 100 : void 0;
|
|
2517
2470
|
if (used === void 0) return null;
|
|
2518
2471
|
return {
|
|
@@ -2525,8 +2478,8 @@ function percentMetric2(label, snapshot, reset, primary) {
|
|
|
2525
2478
|
};
|
|
2526
2479
|
}
|
|
2527
2480
|
function countMetric(label, remaining, total, reset) {
|
|
2528
|
-
const remainingCount =
|
|
2529
|
-
const totalCount =
|
|
2481
|
+
const remainingCount = numberValue(remaining);
|
|
2482
|
+
const totalCount = numberValue(total);
|
|
2530
2483
|
if (remainingCount === void 0 || totalCount === void 0 || totalCount <= 0) return null;
|
|
2531
2484
|
return {
|
|
2532
2485
|
label,
|
|
@@ -2540,7 +2493,7 @@ function overageMetric(snapshot) {
|
|
|
2540
2493
|
if (!snapshot || boolValue2(snapshot.overage_permitted) !== true) return null;
|
|
2541
2494
|
return {
|
|
2542
2495
|
label: "Extra",
|
|
2543
|
-
used: Math.max(0,
|
|
2496
|
+
used: Math.max(0, numberValue(snapshot.overage_count) ?? 0),
|
|
2544
2497
|
limit: null,
|
|
2545
2498
|
format: { kind: "count" }
|
|
2546
2499
|
};
|
|
@@ -2616,35 +2569,17 @@ import { access as access7, readdir as readdir7 } from "fs/promises";
|
|
|
2616
2569
|
import { join as join14 } from "path";
|
|
2617
2570
|
import { homedir as homedir11 } from "os";
|
|
2618
2571
|
|
|
2619
|
-
// src/providers/cloud-code.ts
|
|
2572
|
+
// src/providers/cloud-code/auth.ts
|
|
2620
2573
|
import { readFile as readFile5, readdir as readdir6, realpath, stat } from "fs/promises";
|
|
2621
2574
|
import { spawnSync } from "child_process";
|
|
2622
2575
|
import { homedir as homedir10 } from "os";
|
|
2623
2576
|
import { dirname, join as join13 } from "path";
|
|
2624
|
-
var CLOUD_CODE_URLS = [
|
|
2625
|
-
"https://daily-cloudcode-pa.googleapis.com",
|
|
2626
|
-
"https://cloudcode-pa.googleapis.com"
|
|
2627
|
-
];
|
|
2628
|
-
var LOAD_CODE_ASSIST_PATH = "/v1internal:loadCodeAssist";
|
|
2629
|
-
var FETCH_MODELS_PATH = "/v1internal:fetchAvailableModels";
|
|
2630
|
-
var RETRIEVE_QUOTA_PATH = "/v1internal:retrieveUserQuota";
|
|
2631
2577
|
var GOOGLE_OAUTH_URL = "https://oauth2.googleapis.com/token";
|
|
2632
2578
|
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;
|
|
2633
2579
|
var MAX_BUNDLE_READ = 32 * 1024 * 1024;
|
|
2634
2580
|
var cachedClient;
|
|
2635
2581
|
var OAUTH_TOKEN_KEY = "antigravityUnifiedStateSync.oauthToken";
|
|
2636
2582
|
var OAUTH_TOKEN_SENTINEL = "oauthTokenInfoSentinelKey";
|
|
2637
|
-
var CC_MODEL_BLACKLIST = {
|
|
2638
|
-
MODEL_CHAT_20706: true,
|
|
2639
|
-
MODEL_CHAT_23310: true,
|
|
2640
|
-
MODEL_GOOGLE_GEMINI_2_5_FLASH: true,
|
|
2641
|
-
MODEL_GOOGLE_GEMINI_2_5_FLASH_THINKING: true,
|
|
2642
|
-
MODEL_GOOGLE_GEMINI_2_5_FLASH_LITE: true,
|
|
2643
|
-
MODEL_GOOGLE_GEMINI_2_5_PRO: true,
|
|
2644
|
-
MODEL_PLACEHOLDER_M19: true,
|
|
2645
|
-
MODEL_PLACEHOLDER_M9: true,
|
|
2646
|
-
MODEL_PLACEHOLDER_M12: true
|
|
2647
|
-
};
|
|
2648
2583
|
function readVarint(bytes, start) {
|
|
2649
2584
|
let value = 0;
|
|
2650
2585
|
let shift = 0;
|
|
@@ -2738,10 +2673,6 @@ async function readAntigravityOAuthToken(db) {
|
|
|
2738
2673
|
if (!accessToken && !refreshToken) return { token: null, status: "ok" };
|
|
2739
2674
|
return { token: { accessToken, refreshToken, expirySeconds }, status: "ok" };
|
|
2740
2675
|
}
|
|
2741
|
-
function redact(token) {
|
|
2742
|
-
if (!token) return "none";
|
|
2743
|
-
return `...${token.slice(-4)}`;
|
|
2744
|
-
}
|
|
2745
2676
|
function geminiBundleCandidates() {
|
|
2746
2677
|
const candidates = [];
|
|
2747
2678
|
const addBundle = (nodeModulesRoot) => {
|
|
@@ -2853,6 +2784,30 @@ async function refreshAccessToken(refreshToken) {
|
|
|
2853
2784
|
return null;
|
|
2854
2785
|
}
|
|
2855
2786
|
}
|
|
2787
|
+
|
|
2788
|
+
// src/providers/cloud-code/api.ts
|
|
2789
|
+
var CLOUD_CODE_URLS = [
|
|
2790
|
+
"https://daily-cloudcode-pa.googleapis.com",
|
|
2791
|
+
"https://cloudcode-pa.googleapis.com"
|
|
2792
|
+
];
|
|
2793
|
+
var LOAD_CODE_ASSIST_PATH = "/v1internal:loadCodeAssist";
|
|
2794
|
+
var FETCH_MODELS_PATH = "/v1internal:fetchAvailableModels";
|
|
2795
|
+
var RETRIEVE_QUOTA_PATH = "/v1internal:retrieveUserQuota";
|
|
2796
|
+
var CC_MODEL_BLACKLIST = {
|
|
2797
|
+
MODEL_CHAT_20706: true,
|
|
2798
|
+
MODEL_CHAT_23310: true,
|
|
2799
|
+
MODEL_GOOGLE_GEMINI_2_5_FLASH: true,
|
|
2800
|
+
MODEL_GOOGLE_GEMINI_2_5_FLASH_THINKING: true,
|
|
2801
|
+
MODEL_GOOGLE_GEMINI_2_5_FLASH_LITE: true,
|
|
2802
|
+
MODEL_GOOGLE_GEMINI_2_5_PRO: true,
|
|
2803
|
+
MODEL_PLACEHOLDER_M19: true,
|
|
2804
|
+
MODEL_PLACEHOLDER_M9: true,
|
|
2805
|
+
MODEL_PLACEHOLDER_M12: true
|
|
2806
|
+
};
|
|
2807
|
+
function redact(token) {
|
|
2808
|
+
if (!token) return "none";
|
|
2809
|
+
return `...${token.slice(-4)}`;
|
|
2810
|
+
}
|
|
2856
2811
|
async function requestCloudCodeJson(path, token, body) {
|
|
2857
2812
|
for (const base of CLOUD_CODE_URLS) {
|
|
2858
2813
|
try {
|
|
@@ -2958,6 +2913,8 @@ async function fetchCloudCodeQuota(token, expiredMessage = "Token expired") {
|
|
|
2958
2913
|
if (!refreshed) return { ok: false, plan: result.plan, error: expiredMessage };
|
|
2959
2914
|
return fetchWithAccessToken(refreshed);
|
|
2960
2915
|
}
|
|
2916
|
+
|
|
2917
|
+
// src/providers/cloud-code/index.ts
|
|
2961
2918
|
function normalizeLabel(label) {
|
|
2962
2919
|
return label.replace(/\s*\([^)]*\)\s*$/, "").trim();
|
|
2963
2920
|
}
|
|
@@ -3105,7 +3062,7 @@ var PRICING3 = {
|
|
|
3105
3062
|
"gemini-2.0-flash": { in: 1e-7, out: 4e-7, cr: 25e-9 }
|
|
3106
3063
|
};
|
|
3107
3064
|
var PRICE_KEYS3 = Object.keys(PRICING3).sort((a, b) => b.length - a.length);
|
|
3108
|
-
var
|
|
3065
|
+
var ZERO_PRICE2 = { in: 0, out: 0, cr: 0 };
|
|
3109
3066
|
function geminiTmpDir(homeDir) {
|
|
3110
3067
|
return join15(homeDir ?? homedir12(), ".gemini", "tmp");
|
|
3111
3068
|
}
|
|
@@ -3116,7 +3073,7 @@ function priceFor3(model) {
|
|
|
3116
3073
|
const rest = m.slice(key.length);
|
|
3117
3074
|
if (rest === "" || rest[0] === "-") return PRICING3[key];
|
|
3118
3075
|
}
|
|
3119
|
-
return
|
|
3076
|
+
return ZERO_PRICE2;
|
|
3120
3077
|
}
|
|
3121
3078
|
function shortModel2(model) {
|
|
3122
3079
|
return model.replace(/(-preview|-customtools)+$/, "");
|
|
@@ -3262,17 +3219,8 @@ async function hasGeminiChatSessions(homeDir) {
|
|
|
3262
3219
|
}
|
|
3263
3220
|
return listing.some((path) => /(^|[\\/])chats[\\/]session-.*\.jsonl$/.test(path));
|
|
3264
3221
|
}
|
|
3265
|
-
function decodeBase64UrlJson3(segment) {
|
|
3266
|
-
try {
|
|
3267
|
-
const normalized = segment.replace(/-/g, "+").replace(/_/g, "/");
|
|
3268
|
-
const padded = normalized + "=".repeat((4 - normalized.length % 4) % 4);
|
|
3269
|
-
return JSON.parse(Buffer.from(padded, "base64").toString("utf8"));
|
|
3270
|
-
} catch {
|
|
3271
|
-
return null;
|
|
3272
|
-
}
|
|
3273
|
-
}
|
|
3274
3222
|
function geminiIdentity(creds) {
|
|
3275
|
-
const tokenPayload = typeof creds?.id_token === "string" && creds.id_token.includes(".") ?
|
|
3223
|
+
const tokenPayload = typeof creds?.id_token === "string" && creds.id_token.includes(".") ? decodeBase64UrlJson(creds.id_token.split(".")[1]) : null;
|
|
3276
3224
|
const email = typeof creds?.email === "string" && creds.email.trim() || typeof tokenPayload?.email === "string" && tokenPayload.email.trim() || null;
|
|
3277
3225
|
const displayName = typeof creds?.displayName === "string" && creds.displayName.trim() || typeof creds?.name === "string" && creds.name.trim() || typeof tokenPayload?.name === "string" && tokenPayload.name.trim() || null;
|
|
3278
3226
|
return { email, displayName };
|
|
@@ -3323,6 +3271,113 @@ var geminiProvider = {
|
|
|
3323
3271
|
fetchBilling: (account) => geminiBilling(account)
|
|
3324
3272
|
};
|
|
3325
3273
|
|
|
3274
|
+
// src/providers/detect.ts
|
|
3275
|
+
import { accessSync as accessSync2, constants as constants2, existsSync } from "fs";
|
|
3276
|
+
import { join as join17, delimiter as delimiter2, isAbsolute as isAbsolute2 } from "path";
|
|
3277
|
+
import { homedir as homedir14 } from "os";
|
|
3278
|
+
function searchDirs() {
|
|
3279
|
+
const home = homedir14();
|
|
3280
|
+
const fromEnv = (process.env.PATH ?? "").split(delimiter2).filter(Boolean);
|
|
3281
|
+
const extra = process.platform === "win32" ? [
|
|
3282
|
+
process.env.APPDATA && join17(process.env.APPDATA, "npm"),
|
|
3283
|
+
process.env.LOCALAPPDATA && join17(process.env.LOCALAPPDATA, "pnpm"),
|
|
3284
|
+
join17(home, "scoop", "shims")
|
|
3285
|
+
] : [
|
|
3286
|
+
"/opt/homebrew/bin",
|
|
3287
|
+
"/usr/local/bin",
|
|
3288
|
+
"/usr/bin",
|
|
3289
|
+
"/bin",
|
|
3290
|
+
"/opt/local/bin",
|
|
3291
|
+
join17(home, ".local", "bin"),
|
|
3292
|
+
join17(home, "bin"),
|
|
3293
|
+
join17(home, ".npm-global", "bin"),
|
|
3294
|
+
join17(home, ".bun", "bin"),
|
|
3295
|
+
join17(home, ".local", "share", "pnpm")
|
|
3296
|
+
];
|
|
3297
|
+
return [.../* @__PURE__ */ new Set([...fromEnv, ...extra.filter((d) => !!d)])];
|
|
3298
|
+
}
|
|
3299
|
+
function isExec2(p) {
|
|
3300
|
+
try {
|
|
3301
|
+
accessSync2(p, process.platform === "win32" ? constants2.F_OK : constants2.X_OK);
|
|
3302
|
+
return true;
|
|
3303
|
+
} catch {
|
|
3304
|
+
return false;
|
|
3305
|
+
}
|
|
3306
|
+
}
|
|
3307
|
+
function onPath(names) {
|
|
3308
|
+
const exts = process.platform === "win32" ? (process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM").split(";").map((e) => e.toLowerCase()).concat("") : [""];
|
|
3309
|
+
for (const dir of searchDirs()) {
|
|
3310
|
+
for (const n of names) {
|
|
3311
|
+
for (const e of exts) {
|
|
3312
|
+
if (isExec2(join17(dir, n + e))) return true;
|
|
3313
|
+
}
|
|
3314
|
+
}
|
|
3315
|
+
}
|
|
3316
|
+
return false;
|
|
3317
|
+
}
|
|
3318
|
+
function anyExists(paths) {
|
|
3319
|
+
return paths.some((p) => !!p && isExec2(p));
|
|
3320
|
+
}
|
|
3321
|
+
function installSignals(id) {
|
|
3322
|
+
const home = homedir14();
|
|
3323
|
+
const pf = process.env.ProgramFiles;
|
|
3324
|
+
const pf86 = process.env["ProgramFiles(x86)"];
|
|
3325
|
+
const lad = process.env.LOCALAPPDATA;
|
|
3326
|
+
switch (id) {
|
|
3327
|
+
case "claude":
|
|
3328
|
+
return onPath(["claude"]) || anyExists([
|
|
3329
|
+
"/Applications/Claude.app",
|
|
3330
|
+
join17(home, "Applications", "Claude.app"),
|
|
3331
|
+
lad && join17(lad, "Programs", "claude", "Claude.exe")
|
|
3332
|
+
]);
|
|
3333
|
+
case "codex": {
|
|
3334
|
+
const bin = process.env.CODEX_BIN;
|
|
3335
|
+
if (bin && isAbsolute2(bin) && isExec2(bin)) return true;
|
|
3336
|
+
return onPath(["codex"]) || anyExists([
|
|
3337
|
+
lad && join17(lad, "Programs", "OpenAI", "Codex", "bin", "codex.exe"),
|
|
3338
|
+
lad && join17(lad, "Programs", "OpenAI", "Codex", "codex.exe"),
|
|
3339
|
+
lad && join17(lad, "Programs", "codex", "codex.exe"),
|
|
3340
|
+
pf && join17(pf, "OpenAI", "Codex", "bin", "codex.exe")
|
|
3341
|
+
]) || existsSync(join17(home, ".codex", "sessions")) || existsSync(join17(home, ".codex", "auth.json"));
|
|
3342
|
+
}
|
|
3343
|
+
case "cursor":
|
|
3344
|
+
return onPath(["cursor", "cursor-agent"]) || anyExists([
|
|
3345
|
+
"/Applications/Cursor.app",
|
|
3346
|
+
join17(home, "Applications", "Cursor.app"),
|
|
3347
|
+
lad && join17(lad, "Programs", "cursor", "Cursor.exe"),
|
|
3348
|
+
pf && join17(pf, "Cursor", "Cursor.exe"),
|
|
3349
|
+
pf86 && join17(pf86, "Cursor", "Cursor.exe"),
|
|
3350
|
+
"/opt/Cursor/cursor",
|
|
3351
|
+
"/usr/share/cursor/cursor",
|
|
3352
|
+
"/usr/bin/cursor"
|
|
3353
|
+
]);
|
|
3354
|
+
case "pi":
|
|
3355
|
+
return onPath(["pi"]);
|
|
3356
|
+
case "opencode":
|
|
3357
|
+
return onPath(["opencode"]);
|
|
3358
|
+
case "copilot": {
|
|
3359
|
+
const appData = process.env.APPDATA;
|
|
3360
|
+
return [
|
|
3361
|
+
join17(home, ".config", "github-copilot"),
|
|
3362
|
+
join17(home, ".copilot"),
|
|
3363
|
+
lad && join17(lad, "github-copilot"),
|
|
3364
|
+
appData && join17(appData, "GitHub Copilot"),
|
|
3365
|
+
join17(home, ".local", "share", "gh", "extensions", "gh-copilot")
|
|
3366
|
+
].some((p) => !!p && existsSync(p));
|
|
3367
|
+
}
|
|
3368
|
+
case "antigravity":
|
|
3369
|
+
return onPath(["antigravity"]) || anyExists([
|
|
3370
|
+
"/Applications/Antigravity.app",
|
|
3371
|
+
join17(home, "Applications", "Antigravity.app"),
|
|
3372
|
+
lad && join17(lad, "Programs", "Antigravity", "Antigravity.exe")
|
|
3373
|
+
]);
|
|
3374
|
+
case "gemini":
|
|
3375
|
+
return onPath(["gemini"]);
|
|
3376
|
+
default:
|
|
3377
|
+
return false;
|
|
3378
|
+
}
|
|
3379
|
+
}
|
|
3380
|
+
|
|
3326
3381
|
// src/providers/index.ts
|
|
3327
3382
|
var PROVIDER_ORDER = [...PROVIDER_IDS];
|
|
3328
3383
|
var PROVIDERS = {
|
|
@@ -3350,11 +3405,32 @@ async function detectProviders() {
|
|
|
3350
3405
|
}
|
|
3351
3406
|
|
|
3352
3407
|
// src/accounts.ts
|
|
3353
|
-
import { existsSync as existsSync2, readdirSync, readFileSync, statSync } from "fs";
|
|
3354
|
-
import { homedir as
|
|
3355
|
-
import { basename, join as
|
|
3408
|
+
import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync3, statSync } from "fs";
|
|
3409
|
+
import { homedir as homedir15 } from "os";
|
|
3410
|
+
import { basename, join as join19, resolve } from "path";
|
|
3411
|
+
|
|
3412
|
+
// src/providers/codex/identity.ts
|
|
3413
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
3414
|
+
import { join as join18 } from "path";
|
|
3415
|
+
function codexAuthPaths(homeDir) {
|
|
3416
|
+
return [join18(homeDir, ".codex", "auth.json"), join18(homeDir, "auth.json")];
|
|
3417
|
+
}
|
|
3418
|
+
function readCodexIdentity(homeDir) {
|
|
3419
|
+
for (const path of codexAuthPaths(homeDir)) {
|
|
3420
|
+
try {
|
|
3421
|
+
const parsed = JSON.parse(readFileSync2(path, "utf-8"));
|
|
3422
|
+
const { email, displayName, payload } = identityFromIdToken(parsed?.tokens?.id_token);
|
|
3423
|
+
if (!payload) continue;
|
|
3424
|
+
return { email, displayName };
|
|
3425
|
+
} catch {
|
|
3426
|
+
}
|
|
3427
|
+
}
|
|
3428
|
+
return {};
|
|
3429
|
+
}
|
|
3430
|
+
|
|
3431
|
+
// src/accounts.ts
|
|
3356
3432
|
function accountKey(providerId, homeDir) {
|
|
3357
|
-
return `${providerId}:${homeDir ? resolve(expandHome(homeDir)) :
|
|
3433
|
+
return `${providerId}:${homeDir ? resolve(expandHome(homeDir)) : homedir15()}`;
|
|
3358
3434
|
}
|
|
3359
3435
|
function uniqueId(base, used) {
|
|
3360
3436
|
let id = slugify(base) || "account";
|
|
@@ -3373,23 +3449,11 @@ function uniqueId(base, used) {
|
|
|
3373
3449
|
used.add(id);
|
|
3374
3450
|
return id;
|
|
3375
3451
|
}
|
|
3376
|
-
function readClaudeIdentity2(homeDir) {
|
|
3377
|
-
try {
|
|
3378
|
-
const parsed = JSON.parse(readFileSync(join17(homeDir, ".claude.json"), "utf-8"));
|
|
3379
|
-
const oauth = parsed?.oauthAccount;
|
|
3380
|
-
return {
|
|
3381
|
-
email: typeof oauth?.emailAddress === "string" && oauth.emailAddress.trim() ? oauth.emailAddress.trim() : void 0,
|
|
3382
|
-
displayName: typeof oauth?.displayName === "string" && oauth.displayName.trim() ? oauth.displayName.trim() : void 0
|
|
3383
|
-
};
|
|
3384
|
-
} catch {
|
|
3385
|
-
return {};
|
|
3386
|
-
}
|
|
3387
|
-
}
|
|
3388
3452
|
function hasClaudeState(homeDir) {
|
|
3389
|
-
return existsSync2(
|
|
3453
|
+
return existsSync2(join19(homeDir, ".claude.json")) || existsSync2(join19(homeDir, ".claude", ".credentials.json")) || existsSync2(join19(homeDir, ".claude", "projects")) || existsSync2(join19(homeDir, ".config", "claude", ".credentials.json")) || existsSync2(join19(homeDir, ".config", "claude", "projects"));
|
|
3390
3454
|
}
|
|
3391
3455
|
function candidateAlternateHomes(prefix) {
|
|
3392
|
-
const home =
|
|
3456
|
+
const home = homedir15();
|
|
3393
3457
|
let entries;
|
|
3394
3458
|
try {
|
|
3395
3459
|
entries = readdirSync(home);
|
|
@@ -3400,7 +3464,7 @@ function candidateAlternateHomes(prefix) {
|
|
|
3400
3464
|
const pattern = new RegExp(`^\\.${prefix}[_-]`);
|
|
3401
3465
|
for (const name of entries) {
|
|
3402
3466
|
if (!pattern.test(name)) continue;
|
|
3403
|
-
const path =
|
|
3467
|
+
const path = join19(home, name);
|
|
3404
3468
|
try {
|
|
3405
3469
|
if (!statSync(path).isDirectory()) continue;
|
|
3406
3470
|
out.push(path);
|
|
@@ -3410,45 +3474,16 @@ function candidateAlternateHomes(prefix) {
|
|
|
3410
3474
|
return out.sort();
|
|
3411
3475
|
}
|
|
3412
3476
|
function labelForClaudeHome(homeDir) {
|
|
3413
|
-
const identity =
|
|
3477
|
+
const identity = readClaudeIdentity(homeDir);
|
|
3414
3478
|
if (identity.email) return `Claude ${identity.email}`;
|
|
3415
3479
|
if (identity.displayName) return `Claude ${identity.displayName}`;
|
|
3416
3480
|
const raw = basename(homeDir).replace(/^\.claude[_-]?/, "").replace(/[_-]+/g, " ").trim();
|
|
3417
3481
|
return raw ? `Claude ${raw}` : "Claude";
|
|
3418
3482
|
}
|
|
3419
|
-
function decodeBase64UrlJson4(segment) {
|
|
3420
|
-
try {
|
|
3421
|
-
const normalized = segment.replace(/-/g, "+").replace(/_/g, "/");
|
|
3422
|
-
const padded = normalized + "=".repeat((4 - normalized.length % 4) % 4);
|
|
3423
|
-
return JSON.parse(Buffer.from(padded, "base64").toString("utf8"));
|
|
3424
|
-
} catch {
|
|
3425
|
-
return null;
|
|
3426
|
-
}
|
|
3427
|
-
}
|
|
3428
|
-
function codexAuthPaths(homeDir) {
|
|
3429
|
-
return [join17(homeDir, ".codex", "auth.json"), join17(homeDir, "auth.json")];
|
|
3430
|
-
}
|
|
3431
|
-
function readCodexIdentity(homeDir) {
|
|
3432
|
-
for (const path of codexAuthPaths(homeDir)) {
|
|
3433
|
-
try {
|
|
3434
|
-
const parsed = JSON.parse(readFileSync(path, "utf-8"));
|
|
3435
|
-
const idToken = parsed?.tokens?.id_token;
|
|
3436
|
-
if (typeof idToken !== "string" || !idToken.includes(".")) continue;
|
|
3437
|
-
const payload = decodeBase64UrlJson4(idToken.split(".")[1]);
|
|
3438
|
-
if (!payload || typeof payload !== "object") continue;
|
|
3439
|
-
return {
|
|
3440
|
-
email: typeof payload?.email === "string" && payload.email.trim() ? payload.email.trim() : void 0,
|
|
3441
|
-
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
|
|
3442
|
-
};
|
|
3443
|
-
} catch {
|
|
3444
|
-
}
|
|
3445
|
-
}
|
|
3446
|
-
return {};
|
|
3447
|
-
}
|
|
3448
3483
|
function hasCodexAuth(homeDir) {
|
|
3449
3484
|
for (const path of codexAuthPaths(homeDir)) {
|
|
3450
3485
|
try {
|
|
3451
|
-
const parsed = JSON.parse(
|
|
3486
|
+
const parsed = JSON.parse(readFileSync3(path, "utf-8"));
|
|
3452
3487
|
const accessToken = parsed?.tokens?.access_token;
|
|
3453
3488
|
if (typeof accessToken === "string" && accessToken.trim()) return true;
|
|
3454
3489
|
} catch {
|