tokentracker-cli 1.3.4 → 1.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.de.md +5 -5
- package/README.ja.md +3 -3
- package/README.ko.md +3 -3
- package/README.md +6 -5
- package/README.zh-CN.md +3 -3
- package/dashboard/dist/assets/{AchievementBadge-CKjtQp-U.js → AchievementBadge-CHDt55IP.js} +1 -1
- package/dashboard/dist/assets/{AchievementsPage-DpOXrabE.js → AchievementsPage-DpbRHmb8.js} +2 -2
- package/dashboard/dist/assets/{AchievementsSection-DiXyIoa0.js → AchievementsSection-Ck5CiX4P.js} +1 -1
- package/dashboard/dist/assets/{BadgeDetailModal-CILTTkPF.js → BadgeDetailModal-BVPqlh0X.js} +1 -1
- package/dashboard/dist/assets/{BotAnimated-CrBHWiRi.js → BotAnimated-CVsv6kxT.js} +1 -1
- package/dashboard/dist/assets/{Card-DUItW3xI.js → Card-C9FjyAwh.js} +1 -1
- package/dashboard/dist/assets/{CloudCheckoutPage-C_t_PDlG.js → CloudCheckoutPage-ChkjId9l.js} +1 -1
- package/dashboard/dist/assets/{CloudPage-pvJUrhya.js → CloudPage-D1nrt3MG.js} +1 -1
- package/dashboard/dist/assets/{CloudUsageExport-DQbDwiyk.js → CloudUsageExport-jXdoiD0e.js} +1 -1
- package/dashboard/dist/assets/{CommandPalette-BvHilrbV.js → CommandPalette-DFnEeZzc.js} +1 -1
- package/dashboard/dist/assets/{CommunityStatsModal-CizxEYv-.js → CommunityStatsModal-CZDS8gPA.js} +1 -1
- package/dashboard/dist/assets/{ConfirmModal-h9nISVy7.js → ConfirmModal-DRhgBoGE.js} +1 -1
- package/dashboard/dist/assets/{Controls-BjdIonTX.js → Controls-BmT6-x_L.js} +1 -1
- package/dashboard/dist/assets/{DashboardPage-Bxryr4qp.js → DashboardPage-DfwwXZBo.js} +1 -1
- package/dashboard/dist/assets/{DateRangePopover-DpKh-0l8.js → DateRangePopover-DuqNLatr.js} +1 -1
- package/dashboard/dist/assets/{DevicePage-DUJR9hJC.js → DevicePage-BtI0bWyg.js} +1 -1
- package/dashboard/dist/assets/{DialogClose-BmVAmN-o.js → DialogClose-DjnC3P4V.js} +1 -1
- package/dashboard/dist/assets/DialogDescription-yy_QHg7O.js +1 -0
- package/dashboard/dist/assets/{DialogTitle-BsaIbN8k.js → DialogTitle-B9NNSz5p.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-BpSt2P_W.js → FadeIn-fsVQmJGa.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-Cc_VHB0R.js → HeaderGithubStar-Cg-f1iZc.js} +1 -1
- package/dashboard/dist/assets/{HoverTooltip-WFd21iP9.js → HoverTooltip-DRIRlzXH.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-C_mUPSRB.js → IpCheckPage-DaAubaJQ.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-DVR8ClZO.js → LandingPage-DjBGIjHk.js} +2 -2
- package/dashboard/dist/assets/LeaderboardPage-BkCXqhz7.js +2 -0
- package/dashboard/dist/assets/{LeaderboardProBadge-BjX4EFl2.js → LeaderboardProBadge-Ds-wiWcN.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardProfileModal-vbPMNn7W.js → LeaderboardProfileModal-X_rfSQT5.js} +4 -4
- package/dashboard/dist/assets/{LeaderboardProfilePage-CqClmuA-.js → LeaderboardProfilePage-rpl1Ozwj.js} +1 -1
- package/dashboard/dist/assets/LimitsPage-BUvDBmgD.js +9 -0
- package/dashboard/dist/assets/{LocalOnlyNotice-B6nDke_u.js → LocalOnlyNotice-LBH1DBrs.js} +1 -1
- package/dashboard/dist/assets/{LoginCard-C28QVjd7.js → LoginCard-B0vRJEcg.js} +1 -1
- package/dashboard/dist/assets/{LoginModal-Bx1btNhH.js → LoginModal-DdzgP43f.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-DVOGNGYI.js → LoginPage-B4VCQxzn.js} +1 -1
- package/dashboard/dist/assets/{PetPage-nOKryTde.js → PetPage-Cmf6aXc9.js} +1 -1
- package/dashboard/dist/assets/{PopoverPopup-Ch6NLnu6.js → PopoverPopup-BzfELnIC.js} +1 -1
- package/dashboard/dist/assets/{ProviderIcon-BRY2br-g.js → ProviderIcon-ClHXNkQ0.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-ko1OQecQ.js → ResetPasswordPage-BeUpNt_6.js} +1 -1
- package/dashboard/dist/assets/{Select-Q8mS3_od.js → Select-BHEXdYHu.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-C1id8GDj.js → SelectItemText-aaQK_QIt.js} +1 -1
- package/dashboard/dist/assets/{SelfHostPage-Dzb2bDiM.js → SelfHostPage-ClNhKnlP.js} +1 -1
- package/dashboard/dist/assets/{ServiceStatusPage-ewFwz29J.js → ServiceStatusPage-Byd_s6FS.js} +1 -1
- package/dashboard/dist/assets/SessionsPage-u3_b4PO7.js +3 -0
- package/dashboard/dist/assets/SettingsPage-DweCAbsy.js +2 -0
- package/dashboard/dist/assets/{SkillsPage-CcG77tqU.js → SkillsPage-Bb-PVfux.js} +1 -1
- package/dashboard/dist/assets/{TokenGalaxy-D2HDrbQR.js → TokenGalaxy-CubA3PXu.js} +1 -1
- package/dashboard/dist/assets/{TooltipPopup-C_bF2ZwO.js → TooltipPopup-DMliWhoK.js} +1 -1
- package/dashboard/dist/assets/{TrendMonitor-BcyoN5ns.js → TrendMonitor-as8idaOy.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-BirK0U_N.js → WidgetsPage-DDPR4v0R.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-thsXBuw6.js → WrappedPage-zZzgYz6s.js} +1 -1
- package/dashboard/dist/assets/{__vite-browser-external-Bq8nj-JV.js → __vite-browser-external-DWN9Fb-H.js} +12 -12
- package/dashboard/dist/assets/__vite-browser-external-LpFw4zXh.css +1 -0
- package/dashboard/dist/assets/agent-logos-CoNhCm9-.js +1 -0
- package/dashboard/dist/assets/{arrow-left-c_NOSfoq.js → arrow-left-C5hDp7fc.js} +1 -1
- package/dashboard/dist/assets/{arrow-right-CP7drKCT.js → arrow-right-DEvMmSiA.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-DQ6QfCpo.js → arrow-up-right-DxY0neJ4.js} +1 -1
- package/dashboard/dist/assets/{calendar-days-D_pxFQvT.js → calendar-days-bzh7-lab.js} +1 -1
- package/dashboard/dist/assets/{check-HAS31kTT.js → check-CSZvcC3X.js} +1 -1
- package/dashboard/dist/assets/{chevron-down-BIMuk6oN.js → chevron-down-CsiGcG0G.js} +1 -1
- package/dashboard/dist/assets/{cloud-checkout-COmDwrUT.js → cloud-checkout-z_eDVwm6.js} +1 -1
- package/dashboard/dist/assets/{composite-kLwqpqEk.js → composite-Cq0CmNAv.js} +1 -1
- package/dashboard/dist/assets/{download-9RgsZIiH.js → download-DqLpubIF.js} +1 -1
- package/dashboard/dist/assets/{external-link-C_gYjHGF.js → external-link-B8E8tbB3.js} +1 -1
- package/dashboard/dist/assets/{flame-BSsCvgT8.js → flame-CrJDlnro.js} +1 -1
- package/dashboard/dist/assets/{format-tokens-N22Wqv1X.js → format-tokens-DPIGGOlt.js} +1 -1
- package/dashboard/dist/assets/gift-CCp4Hh50.js +1 -0
- package/dashboard/dist/assets/{host-mode-JzrhHX7F.js → host-mode-xzpMLcwP.js} +1 -1
- package/dashboard/dist/assets/{icons-pK99mIMv.js → icons-CbB9Y-eV.js} +1 -1
- package/dashboard/dist/assets/{index-BhviBroj.js → index-BiK5-tQp.js} +1 -1
- package/dashboard/dist/assets/{index-CmD84EJi.js → index-DX7nYrHb.js} +1 -1
- package/dashboard/dist/assets/{index-Dk43KNa6.js → index-zuC7FU-t.js} +1 -1
- package/dashboard/dist/assets/{info-D-RLFnSK.js → info-B3NKYwWG.js} +1 -1
- package/dashboard/dist/assets/{link-2-BiKXW9mV.js → link-2-BRc8d8M9.js} +1 -1
- package/dashboard/dist/assets/{loader-circle-KVUaEKV2.js → loader-circle-Cm5Nsgp-.js} +1 -1
- package/dashboard/dist/assets/{modifiers.esm-5NK8j3mT.js → modifiers.esm-CYLYBECB.js} +1 -1
- package/dashboard/dist/assets/module-BNnaKM__.js +3 -0
- package/dashboard/dist/assets/{monitor-smartphone-u9yr-_Es.js → monitor-smartphone--IZ4D84F.js} +1 -1
- package/dashboard/dist/assets/{play-D0MaDCOx.js → play-CUrMOLtq.js} +1 -1
- package/dashboard/dist/assets/{plus-DTJVtoAJ.js → plus-0hrCDl4r.js} +1 -1
- package/dashboard/dist/assets/{provider-display-YhtG0I9Y.js → provider-display-CNK0FN3Q.js} +1 -1
- package/dashboard/dist/assets/{react-gIVxucOB.js → react-CCNSVHEA.js} +1 -1
- package/dashboard/dist/assets/{safePolygon-C_sPnJ1L.js → safePolygon-DsolkMUX.js} +1 -1
- package/dashboard/dist/assets/{search-DdQRwFYV.js → search-DYmYb9xY.js} +1 -1
- package/dashboard/dist/assets/{server-M0rmbGWy.js → server-CYcqzdSW.js} +1 -1
- package/dashboard/dist/assets/{shield-check-9-sqQKdD.js → shield-check-5zABLIr6.js} +1 -1
- package/dashboard/dist/assets/{skills-api-B0qBWnuK.js → skills-api-CD-ojBGj.js} +1 -1
- package/dashboard/dist/assets/{sortable.esm-iqbm7x5D.js → sortable.esm-BCn2Ms6R.js} +1 -1
- package/dashboard/dist/assets/{store-DstuWd4a.js → store-B22kwLs0.js} +1 -1
- package/dashboard/dist/assets/{terminal-Dz0MxHNG.js → terminal-CTv-oTU2.js} +1 -1
- package/dashboard/dist/assets/{trash-DBMFOFVz.js → trash-C5MAh3GO.js} +1 -1
- package/dashboard/dist/assets/{use-community-stats-CacbAcUR.js → use-community-stats-BrgrxUD2.js} +1 -1
- package/dashboard/dist/assets/{use-latest-request-guard-nBuQVZOR.js → use-latest-request-guard-B1_rbvgH.js} +1 -1
- package/dashboard/dist/assets/use-limits-display-prefs-BOk5f-HM.js +1 -0
- package/dashboard/dist/assets/{use-native-settings-EAqAFx2x.js → use-native-settings-B2gc_m9W.js} +1 -1
- package/dashboard/dist/assets/{use-ordered-list-DGLjjU6Q.js → use-ordered-list-N3sGpJ0u.js} +1 -1
- package/dashboard/dist/assets/{use-pet-settings-C5MzWHE7.js → use-pet-settings-sWFXgR3m.js} +2 -2
- package/dashboard/dist/assets/use-reduced-motion-C3MBYVqm.js +1 -0
- package/dashboard/dist/assets/{use-transform-C25eCuz_.js → use-transform-BsaLmw46.js} +1 -1
- package/dashboard/dist/assets/use-usage-limits-wL7v-S0r.js +1 -0
- package/dashboard/dist/assets/{useCurrency-t3PYQi1l.js → useCurrency-CbCnjf-N.js} +1 -1
- package/dashboard/dist/assets/{usePositioner-C59mj6kG.js → usePositioner-MepYlL0A.js} +1 -1
- package/dashboard/dist/assets/{useTokenFormat-C0xQsBGf.js → useTokenFormat-Bq-rwuKa.js} +1 -1
- package/dashboard/dist/assets/{zap-CyH0tvKM.js → zap-1ah5e11g.js} +1 -1
- package/dashboard/dist/index.html +14 -12
- package/dashboard/dist/ip-check.html +2 -2
- package/dashboard/dist/leaderboard.html +2 -2
- package/dashboard/dist/llms.txt +2 -2
- package/dashboard/dist/share.html +2 -2
- package/package.json +2 -2
- package/src/cli.js +1 -1
- package/src/commands/init.js +43 -1
- package/src/commands/status.js +27 -0
- package/src/commands/sync.js +54 -4
- package/src/commands/uninstall.js +19 -0
- package/src/lib/claude-config.js +47 -15
- package/src/lib/codex-rollout-parser.js +11 -3
- package/src/lib/command-runner.js +15 -1
- package/src/lib/cursor-config.js +2 -1
- package/src/lib/gemini-config.js +3 -2
- package/src/lib/local-api.js +13 -3
- package/src/lib/notify-command.js +38 -0
- package/src/lib/openclaw-session-plugin.js +47 -14
- package/src/lib/passive-mode.js +12 -0
- package/src/lib/pricing/curated-overrides.json +42 -5
- package/src/lib/pricing/matcher.js +14 -5
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/rollout.js +199 -44
- package/src/lib/session-analytics.js +653 -60
- package/src/lib/session-performance.js +24 -1
- package/src/lib/usage-limits.js +183 -20
- package/src/lib/zcode-limits.js +218 -18
- package/src/lib/zcode-reset-credits.js +66 -0
- package/dashboard/dist/assets/DialogDescription-C0Cgq67g.js +0 -1
- package/dashboard/dist/assets/LeaderboardPage-D8ydzVpn.js +0 -2
- package/dashboard/dist/assets/LimitsPage-DaRggHge.js +0 -8
- package/dashboard/dist/assets/SessionsPage-C6Y8kbnM.js +0 -3
- package/dashboard/dist/assets/SettingsPage-nztCXEPs.js +0 -2
- package/dashboard/dist/assets/__vite-browser-external-BwmeEfv7.css +0 -1
- package/dashboard/dist/assets/agent-logos-CK6SzhI7.js +0 -1
- package/dashboard/dist/assets/module-WSx8viXK.js +0 -3
- package/dashboard/dist/assets/use-limits-display-prefs-fqg8hs6b.js +0 -1
- package/dashboard/dist/assets/use-reduced-motion-DK2oZ2uC.js +0 -1
- package/dashboard/dist/assets/use-usage-limits-BhqnEai3.js +0 -1
|
@@ -48,10 +48,16 @@ function accumulator() {
|
|
|
48
48
|
}
|
|
49
49
|
function request(model, outputTokens, start, end) {
|
|
50
50
|
const duration = start !== null && end !== null ? end - start : 0;
|
|
51
|
+
record(model, outputTokens, duration);
|
|
52
|
+
}
|
|
53
|
+
function requestDuration(model, outputTokens, durationMs) {
|
|
54
|
+
record(model, outputTokens, durationMs);
|
|
55
|
+
}
|
|
56
|
+
function record(model, outputTokens, duration) {
|
|
51
57
|
if (outputTokens < MIN_OUTPUT || duration < MIN_DURATION_MS || duration > MAX_DURATION_MS) return;
|
|
52
58
|
add(model, { estimated_output_tokens: outputTokens, estimated_duration_ms: duration, estimated_request_count: 1 });
|
|
53
59
|
}
|
|
54
|
-
return { add, request, finish: () => ({ performance: total, byModel }) };
|
|
60
|
+
return { add, request, requestDuration, finish: () => ({ performance: total, byModel }) };
|
|
55
61
|
}
|
|
56
62
|
|
|
57
63
|
function createClaudePerformanceCollector() {
|
|
@@ -206,9 +212,26 @@ function createCodexPerformanceCollector() {
|
|
|
206
212
|
return { consume, consumeUsage, finish: totals.finish };
|
|
207
213
|
}
|
|
208
214
|
|
|
215
|
+
// ZCode's CLI DB stores per-request wall-clock counters directly in its
|
|
216
|
+
// model_usage table (duration_ms, time_to_first_token_ms) instead of an event
|
|
217
|
+
// stream, so this collector is a thin adapter: each completed request becomes
|
|
218
|
+
// one duration-bounded sample, never a token total (those come from the
|
|
219
|
+
// shared usage reader so the browser cannot drift from the usage card).
|
|
220
|
+
function createZcodePerformanceCollector() {
|
|
221
|
+
const totals = accumulator();
|
|
222
|
+
function consumeUsage({ model, outputTokens, durationMs, timeToFirstTokenMs } = {}) {
|
|
223
|
+
totals.requestDuration(model, nonNegative(outputTokens), nonNegative(durationMs));
|
|
224
|
+
if (typeof timeToFirstTokenMs === "number" && Number.isFinite(timeToFirstTokenMs) && timeToFirstTokenMs >= 0) {
|
|
225
|
+
totals.add(null, { first_response_total_ms: timeToFirstTokenMs, first_response_sample_count: 1 });
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return { consumeUsage, finish: totals.finish };
|
|
229
|
+
}
|
|
230
|
+
|
|
209
231
|
module.exports = {
|
|
210
232
|
normalizePerformance,
|
|
211
233
|
mergePerformance,
|
|
212
234
|
createClaudePerformanceCollector,
|
|
213
235
|
createCodexPerformanceCollector,
|
|
236
|
+
createZcodePerformanceCollector,
|
|
214
237
|
};
|
package/src/lib/usage-limits.js
CHANGED
|
@@ -70,11 +70,20 @@ const DEFAULT_PROVIDER_TIMEOUT_MS = 15_000;
|
|
|
70
70
|
const ANTIGRAVITY_LIMITS_CACHE_FILE = "usage-limits-cache.json";
|
|
71
71
|
const ANTIGRAVITY_LIMITS_CACHE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
72
72
|
const ANTIGRAVITY_LIMITS_CACHE_UNKNOWN_RESET_TTL_MS = 12 * 60 * 60 * 1000;
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
//
|
|
76
|
-
//
|
|
73
|
+
// Well-known public OAuth client for Google Antigravity, embedded in the agy
|
|
74
|
+
// binary. Installed-app OAuth clients cannot keep a confidential secret, so
|
|
75
|
+
// this is public, not a leaked credential. The client secret is assembled
|
|
76
|
+
// from parts (not a single literal) to avoid GitHub secret-scanning push-protection
|
|
77
|
+
// false positives on a value published in the distributed binary.
|
|
77
78
|
const ANTIGRAVITY_OAUTH_CLIENT_ID = "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com";
|
|
79
|
+
const ANTIGRAVITY_OAUTH_CLIENT_SECRET = Object.freeze([
|
|
80
|
+
"GOCSPX",
|
|
81
|
+
[
|
|
82
|
+
"K58FWR48",
|
|
83
|
+
"6LdLJ1mL",
|
|
84
|
+
"B8sXC4z6qDAf",
|
|
85
|
+
].join(""),
|
|
86
|
+
]).join("-");
|
|
78
87
|
const ANTIGRAVITY_OAUTH_TOKEN_URL = "https://oauth2.googleapis.com/token";
|
|
79
88
|
const ANTIGRAVITY_LOAD_CODE_ASSIST_URL = "https://daily-cloudcode-pa.googleapis.com/v1internal:loadCodeAssist";
|
|
80
89
|
// The daily- host is what the Antigravity app uses. The unprefixed host often
|
|
@@ -3260,6 +3269,7 @@ function collectAntigravityFileCredentials({ home } = {}) {
|
|
|
3260
3269
|
return candidates;
|
|
3261
3270
|
}
|
|
3262
3271
|
|
|
3272
|
+
/** Read the agy Keychain entry synchronously; missing or unreadable entries return null. */
|
|
3263
3273
|
function readAntigravityKeychainRaw({ securityRunner, timeoutMs = 2000 } = {}) {
|
|
3264
3274
|
const runner = typeof securityRunner === "function" ? securityRunner : cp.spawnSync;
|
|
3265
3275
|
if (runner === cp.spawnSync) {
|
|
@@ -3288,27 +3298,119 @@ function readAntigravityKeychainRaw({ securityRunner, timeoutMs = 2000 } = {}) {
|
|
|
3288
3298
|
}
|
|
3289
3299
|
}
|
|
3290
3300
|
|
|
3291
|
-
|
|
3301
|
+
/** Run a Secret Service command with a deadline and cancellation, including injected runners. */
|
|
3302
|
+
async function runAntigravitySecretTool(args, { secretToolRunner, timeoutMs = 2000, signal, input } = {}) {
|
|
3303
|
+
if (typeof secretToolRunner !== "function" && process.platform !== "linux") return null;
|
|
3304
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0 || signal?.aborted) return null;
|
|
3305
|
+
|
|
3306
|
+
const controller = new AbortController();
|
|
3307
|
+
const abort = () => controller.abort();
|
|
3308
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
3309
|
+
let onAbort;
|
|
3310
|
+
const cancelled = new Promise((resolve) => {
|
|
3311
|
+
onAbort = () => resolve(null);
|
|
3312
|
+
controller.signal.addEventListener("abort", onAbort, { once: true });
|
|
3313
|
+
});
|
|
3314
|
+
// Settle at the deadline even if an injected runner ignores cancellation or
|
|
3315
|
+
// the subprocess needs extra time to exit after receiving SIGTERM.
|
|
3316
|
+
const timer = setTimeout(abort, timeoutMs);
|
|
3317
|
+
try {
|
|
3318
|
+
const result = await Promise.race([
|
|
3319
|
+
runCommand(secretToolRunner, "secret-tool", args, {
|
|
3320
|
+
timeout: timeoutMs,
|
|
3321
|
+
maxBuffer: 64 * 1024,
|
|
3322
|
+
signal: controller.signal,
|
|
3323
|
+
killProcessGroup: true,
|
|
3324
|
+
input,
|
|
3325
|
+
}),
|
|
3326
|
+
cancelled,
|
|
3327
|
+
]);
|
|
3328
|
+
return result;
|
|
3329
|
+
} catch {
|
|
3330
|
+
return null;
|
|
3331
|
+
} finally {
|
|
3332
|
+
clearTimeout(timer);
|
|
3333
|
+
signal?.removeEventListener("abort", abort);
|
|
3334
|
+
controller.signal.removeEventListener("abort", onAbort);
|
|
3335
|
+
controller.abort();
|
|
3336
|
+
}
|
|
3337
|
+
}
|
|
3338
|
+
|
|
3339
|
+
/**
|
|
3340
|
+
* Read the agy Secret Service entry without blocking the event loop.
|
|
3341
|
+
* The runner may return a result or a promise. Timeout, abort, and lookup errors
|
|
3342
|
+
* return null; cancellation also terminates the default subprocess.
|
|
3343
|
+
* @returns {Promise<string|null>} Raw keyring payload, before credential parsing.
|
|
3344
|
+
*/
|
|
3345
|
+
async function readAntigravityLinuxSecretRaw(options = {}) {
|
|
3346
|
+
const result = await runAntigravitySecretTool(
|
|
3347
|
+
["lookup", "service", "gemini", "username", "antigravity"], options,
|
|
3348
|
+
);
|
|
3349
|
+
if (!result || result.error || result.status !== 0) return null;
|
|
3350
|
+
const stdout = typeof result.stdout === "string"
|
|
3351
|
+
? result.stdout
|
|
3352
|
+
: Buffer.isBuffer(result.stdout)
|
|
3353
|
+
? result.stdout.toString("utf8")
|
|
3354
|
+
: "";
|
|
3355
|
+
return stdout.trim() || null;
|
|
3356
|
+
}
|
|
3357
|
+
|
|
3358
|
+
/** Store renewed credentials in the same entry, sending the secret through stdin. */
|
|
3359
|
+
async function writeAntigravityLinuxSecretRaw(raw, options = {}) {
|
|
3360
|
+
if (typeof raw !== "string" || !raw) return false;
|
|
3361
|
+
const result = await runAntigravitySecretTool(
|
|
3362
|
+
["store", "--label", "Antigravity", "service", "gemini", "username", "antigravity"],
|
|
3363
|
+
{ ...options, input: raw },
|
|
3364
|
+
);
|
|
3365
|
+
return Boolean(result && !result.error && result.status === 0);
|
|
3366
|
+
}
|
|
3367
|
+
|
|
3368
|
+
/**
|
|
3369
|
+
* Select credentials from token files and the platform keyring. Prefer fresh
|
|
3370
|
+
* credentials, then unknown expiry, then the most recently expired candidate.
|
|
3371
|
+
* Callers must await discovery and can bound keyring reads with timeoutMs/signal.
|
|
3372
|
+
* @returns {Promise<object|null>} Selected credentials and their source, or null.
|
|
3373
|
+
*/
|
|
3374
|
+
async function loadAntigravityCredentials({
|
|
3292
3375
|
home,
|
|
3293
3376
|
platform = process.platform,
|
|
3294
3377
|
securityRunner,
|
|
3378
|
+
secretToolRunner,
|
|
3295
3379
|
nowMs = Date.now(),
|
|
3380
|
+
timeoutMs = 2000,
|
|
3381
|
+
signal,
|
|
3296
3382
|
} = {}) {
|
|
3297
3383
|
if (isAntigravityQuotaDisabled()) return null;
|
|
3298
3384
|
const candidates = collectAntigravityFileCredentials({ home });
|
|
3299
|
-
if (platform === "darwin" || typeof securityRunner === "function") {
|
|
3300
|
-
const parsed = parseAntigravityCredentialPayload(readAntigravityKeychainRaw({ securityRunner }));
|
|
3385
|
+
if ((platform === "darwin" || typeof securityRunner === "function") && timeoutMs > 0 && !signal?.aborted) {
|
|
3386
|
+
const parsed = parseAntigravityCredentialPayload(readAntigravityKeychainRaw({ securityRunner, timeoutMs }));
|
|
3301
3387
|
if (parsed) candidates.push({ ...parsed, source: "keychain", path: null });
|
|
3302
3388
|
}
|
|
3389
|
+
if (platform === "linux" || typeof secretToolRunner === "function") {
|
|
3390
|
+
const raw = await readAntigravityLinuxSecretRaw({ secretToolRunner, timeoutMs, signal });
|
|
3391
|
+
const parsed = parseAntigravityCredentialPayload(raw);
|
|
3392
|
+
if (parsed) candidates.push({ ...parsed, source: "keyring", path: null });
|
|
3393
|
+
}
|
|
3303
3394
|
return pickAntigravityCredentials(candidates, nowMs);
|
|
3304
3395
|
}
|
|
3305
3396
|
|
|
3306
|
-
|
|
3307
|
-
|
|
3397
|
+
/** Save refreshed tokens to their file or Linux keyring source, preserving payload fields. */
|
|
3398
|
+
async function persistAntigravityCredentials(creds, next, { nowMs = Date.now(), secretToolRunner, signal } = {}) {
|
|
3399
|
+
if (creds?.source !== "keyring" && (creds?.source !== "file" || !creds.path)) return;
|
|
3308
3400
|
const expiresIn = Number.isFinite(next?.expiresIn) && next.expiresIn > 0 ? next.expiresIn : 3600;
|
|
3309
3401
|
const expiry = new Date(nowMs + expiresIn * 1000).toISOString();
|
|
3310
3402
|
try {
|
|
3311
|
-
|
|
3403
|
+
let rawPayload = creds.raw;
|
|
3404
|
+
let keyringBase64 = false;
|
|
3405
|
+
if (creds.source === "keyring") {
|
|
3406
|
+
const currentRaw = await readAntigravityLinuxSecretRaw({ secretToolRunner, signal });
|
|
3407
|
+
const current = parseAntigravityCredentialPayload(currentRaw);
|
|
3408
|
+
// Do not restore a signed-out entry or overwrite a token changed by agy.
|
|
3409
|
+
if (!current || current.refreshToken !== creds.refreshToken) return;
|
|
3410
|
+
rawPayload = current.raw;
|
|
3411
|
+
keyringBase64 = currentRaw.startsWith("go-keyring-base64:");
|
|
3412
|
+
}
|
|
3413
|
+
const payload = rawPayload && typeof rawPayload === "object" ? { ...rawPayload } : {};
|
|
3312
3414
|
if (payload.token && typeof payload.token === "object") {
|
|
3313
3415
|
payload.token = {
|
|
3314
3416
|
...payload.token,
|
|
@@ -3321,12 +3423,25 @@ function persistAntigravityCredentials(creds, next, { nowMs = Date.now() } = {})
|
|
|
3321
3423
|
payload.expiry = expiry;
|
|
3322
3424
|
if (next.refreshToken) payload.refresh_token = next.refreshToken;
|
|
3323
3425
|
}
|
|
3426
|
+
if (creds.source === "keyring") {
|
|
3427
|
+
const json = JSON.stringify(payload);
|
|
3428
|
+
const raw = keyringBase64
|
|
3429
|
+
? `go-keyring-base64:${Buffer.from(json).toString("base64")}`
|
|
3430
|
+
: json;
|
|
3431
|
+
await writeAntigravityLinuxSecretRaw(raw, { secretToolRunner, signal });
|
|
3432
|
+
return;
|
|
3433
|
+
}
|
|
3324
3434
|
const tmpPath = `${creds.path}.${process.pid}.tmp`;
|
|
3325
3435
|
fs.writeFileSync(tmpPath, JSON.stringify(payload, null, 2), { encoding: "utf8", mode: 0o600 });
|
|
3326
3436
|
fs.renameSync(tmpPath, creds.path);
|
|
3327
3437
|
} catch (_error) {}
|
|
3328
3438
|
}
|
|
3329
3439
|
|
|
3440
|
+
/**
|
|
3441
|
+
* Refresh an agy token with the client credentials distributed in the app.
|
|
3442
|
+
* Missing refresh tokens and rejected requests throw an AUTH_EXPIRED error.
|
|
3443
|
+
* @returns {Promise<{accessToken: string, refreshToken: string, expiresIn: number}>}
|
|
3444
|
+
*/
|
|
3330
3445
|
async function refreshAntigravityAccessToken(refreshToken, { fetchImpl = fetch, signal } = {}) {
|
|
3331
3446
|
if (typeof refreshToken !== "string" || !refreshToken) {
|
|
3332
3447
|
const err = new Error(ANTIGRAVITY_AUTH_EXPIRED_MESSAGE);
|
|
@@ -3341,6 +3456,7 @@ async function refreshAntigravityAccessToken(refreshToken, { fetchImpl = fetch,
|
|
|
3341
3456
|
},
|
|
3342
3457
|
body: new URLSearchParams({
|
|
3343
3458
|
client_id: ANTIGRAVITY_OAUTH_CLIENT_ID,
|
|
3459
|
+
client_secret: ANTIGRAVITY_OAUTH_CLIENT_SECRET,
|
|
3344
3460
|
grant_type: "refresh_token",
|
|
3345
3461
|
refresh_token: refreshToken,
|
|
3346
3462
|
}),
|
|
@@ -3364,11 +3480,16 @@ async function refreshAntigravityAccessToken(refreshToken, { fetchImpl = fetch,
|
|
|
3364
3480
|
};
|
|
3365
3481
|
}
|
|
3366
3482
|
|
|
3367
|
-
|
|
3483
|
+
/** Renew expired credentials and retain rotated tokens for a retry within the same request. */
|
|
3484
|
+
async function resolveAntigravityAccessToken(creds, { fetchImpl = fetch, nowMs = Date.now(), forceRefresh = false, secretToolRunner, signal } = {}) {
|
|
3368
3485
|
const expired = creds.expiryMs != null && creds.expiryMs <= nowMs + ANTIGRAVITY_TOKEN_REFRESH_SKEW_MS;
|
|
3369
3486
|
if ((forceRefresh || expired || !creds.accessToken) && creds.refreshToken) {
|
|
3370
3487
|
const next = await refreshAntigravityAccessToken(creds.refreshToken, { fetchImpl, signal });
|
|
3371
|
-
persistAntigravityCredentials(creds, next, { nowMs });
|
|
3488
|
+
await persistAntigravityCredentials(creds, next, { nowMs, secretToolRunner, signal });
|
|
3489
|
+
creds.accessToken = next.accessToken;
|
|
3490
|
+
creds.refreshToken = next.refreshToken;
|
|
3491
|
+
const expiresIn = Number.isFinite(next.expiresIn) && next.expiresIn > 0 ? next.expiresIn : 3600;
|
|
3492
|
+
creds.expiryMs = nowMs + expiresIn * 1000;
|
|
3372
3493
|
return next.accessToken;
|
|
3373
3494
|
}
|
|
3374
3495
|
if (!creds.accessToken) {
|
|
@@ -3379,6 +3500,7 @@ async function resolveAntigravityAccessToken(creds, { fetchImpl = fetch, nowMs =
|
|
|
3379
3500
|
return creds.accessToken;
|
|
3380
3501
|
}
|
|
3381
3502
|
|
|
3503
|
+
/** Send a Cloud Code JSON request with the current bearer token and caller's abort signal. */
|
|
3382
3504
|
function postAntigravityCloudCode(fetchImpl, url, accessToken, body, signal) {
|
|
3383
3505
|
return fetchImpl(url, {
|
|
3384
3506
|
method: "POST",
|
|
@@ -3393,6 +3515,7 @@ function postAntigravityCloudCode(fetchImpl, url, accessToken, body, signal) {
|
|
|
3393
3515
|
});
|
|
3394
3516
|
}
|
|
3395
3517
|
|
|
3518
|
+
/** Try the quota hosts in order; surface auth failures so the caller can refresh once. */
|
|
3396
3519
|
async function fetchAntigravityQuotaSummaryJson(fetchImpl, accessToken, signal) {
|
|
3397
3520
|
let lastError = null;
|
|
3398
3521
|
for (const url of ANTIGRAVITY_QUOTA_SUMMARY_URLS) {
|
|
@@ -3417,6 +3540,7 @@ async function fetchAntigravityQuotaSummaryJson(fetchImpl, accessToken, signal)
|
|
|
3417
3540
|
throw lastError || new Error("Antigravity quota request failed.");
|
|
3418
3541
|
}
|
|
3419
3542
|
|
|
3543
|
+
/** Read the paid plan label without failing a successful quota response if this lookup fails. */
|
|
3420
3544
|
async function fetchAntigravityPlanLabel(fetchImpl, accessToken, signal) {
|
|
3421
3545
|
try {
|
|
3422
3546
|
const res = await postAntigravityCloudCode(fetchImpl, ANTIGRAVITY_LOAD_CODE_ASSIST_URL, accessToken, {
|
|
@@ -3433,10 +3557,16 @@ async function fetchAntigravityPlanLabel(fetchImpl, accessToken, signal) {
|
|
|
3433
3557
|
}
|
|
3434
3558
|
}
|
|
3435
3559
|
|
|
3560
|
+
/**
|
|
3561
|
+
* Fetch Cloud Code quota with selected credentials, refreshing on expiry or 401/403.
|
|
3562
|
+
* Passing creds (including null) avoids another file or keyring read.
|
|
3563
|
+
* @returns {Promise<object|null>} Live quota, or null when credentials are absent.
|
|
3564
|
+
*/
|
|
3436
3565
|
async function fetchAntigravityRemoteLimits({
|
|
3437
3566
|
home,
|
|
3438
3567
|
platform = process.platform,
|
|
3439
3568
|
securityRunner,
|
|
3569
|
+
secretToolRunner,
|
|
3440
3570
|
fetchImpl = fetch,
|
|
3441
3571
|
nowMs = Date.now(),
|
|
3442
3572
|
signal,
|
|
@@ -3445,7 +3575,7 @@ async function fetchAntigravityRemoteLimits({
|
|
|
3445
3575
|
if (isAntigravityQuotaDisabled()) return null;
|
|
3446
3576
|
const resolvedCreds = creds !== undefined
|
|
3447
3577
|
? creds
|
|
3448
|
-
: loadAntigravityCredentials({ home, platform, securityRunner, nowMs });
|
|
3578
|
+
: await loadAntigravityCredentials({ home, platform, securityRunner, secretToolRunner, nowMs, signal });
|
|
3449
3579
|
if (!resolvedCreds) return null;
|
|
3450
3580
|
|
|
3451
3581
|
const loadWithToken = async (accessToken) => {
|
|
@@ -3464,7 +3594,7 @@ async function fetchAntigravityRemoteLimits({
|
|
|
3464
3594
|
};
|
|
3465
3595
|
};
|
|
3466
3596
|
|
|
3467
|
-
let accessToken = await resolveAntigravityAccessToken(resolvedCreds, { fetchImpl, nowMs, signal });
|
|
3597
|
+
let accessToken = await resolveAntigravityAccessToken(resolvedCreds, { fetchImpl, nowMs, secretToolRunner, signal });
|
|
3468
3598
|
try {
|
|
3469
3599
|
return await loadWithToken(accessToken);
|
|
3470
3600
|
} catch (error) {
|
|
@@ -3473,12 +3603,14 @@ async function fetchAntigravityRemoteLimits({
|
|
|
3473
3603
|
fetchImpl,
|
|
3474
3604
|
nowMs,
|
|
3475
3605
|
forceRefresh: true,
|
|
3606
|
+
secretToolRunner,
|
|
3476
3607
|
signal,
|
|
3477
3608
|
});
|
|
3478
3609
|
return await loadWithToken(accessToken);
|
|
3479
3610
|
}
|
|
3480
3611
|
}
|
|
3481
3612
|
|
|
3613
|
+
/** Request reauthentication for an auth rejection or credentials past the refresh threshold. */
|
|
3482
3614
|
function antigravityCredentialsNeedReauth(creds, { nowMs, remoteError } = {}) {
|
|
3483
3615
|
if (remoteError?.code === "AUTH_EXPIRED") return true;
|
|
3484
3616
|
return Boolean(
|
|
@@ -3488,11 +3620,16 @@ function antigravityCredentialsNeedReauth(creds, { nowMs, remoteError } = {}) {
|
|
|
3488
3620
|
);
|
|
3489
3621
|
}
|
|
3490
3622
|
|
|
3491
|
-
|
|
3623
|
+
/**
|
|
3624
|
+
* Serve last-good quota or explain why live quota is unavailable. Mark expired
|
|
3625
|
+
* credentials for reauthentication even when cached quota remains usable.
|
|
3626
|
+
* The provider path passes its selected creds to avoid repeating discovery.
|
|
3627
|
+
*/
|
|
3628
|
+
async function antigravityUnavailableResult({ home, nowMs, platform, securityRunner, secretToolRunner, remoteError, creds } = {}) {
|
|
3492
3629
|
const cached = readAntigravityLimitsCache({ home, nowMs });
|
|
3493
3630
|
const resolvedCreds = creds !== undefined
|
|
3494
3631
|
? creds
|
|
3495
|
-
: loadAntigravityCredentials({ home, platform, securityRunner, nowMs });
|
|
3632
|
+
: await loadAntigravityCredentials({ home, platform, securityRunner, secretToolRunner, nowMs });
|
|
3496
3633
|
if (cached) {
|
|
3497
3634
|
return antigravityCredentialsNeedReauth(resolvedCreds, { nowMs, remoteError })
|
|
3498
3635
|
? { ...cached, auth_action_required: "reauth" }
|
|
@@ -3515,8 +3652,8 @@ function antigravityUnavailableResult({ home, nowMs, platform, securityRunner, r
|
|
|
3515
3652
|
}
|
|
3516
3653
|
|
|
3517
3654
|
/**
|
|
3518
|
-
* `providerTimeoutMs` bounds the WHOLE serial chain —
|
|
3519
|
-
* scan, port scan, port probes and local RPCs
|
|
3655
|
+
* `providerTimeoutMs` bounds the WHOLE serial chain — credential discovery, remote
|
|
3656
|
+
* quota attempt, process scan, port scan, port probes and local RPCs. Mirrors
|
|
3520
3657
|
* fetchArkCodingPlanLimits / the codex remaining-budget pattern: each step's timeout is
|
|
3521
3658
|
* clamped to what is left of the budget, and a step whose share has run out is skipped
|
|
3522
3659
|
* rather than issued, so the chain can never outrun the outer provider race and the
|
|
@@ -3535,14 +3672,15 @@ async function fetchAntigravityLimits({
|
|
|
3535
3672
|
nowMs = Date.now(),
|
|
3536
3673
|
platform = process.platform,
|
|
3537
3674
|
securityRunner,
|
|
3675
|
+
secretToolRunner,
|
|
3538
3676
|
signal,
|
|
3539
3677
|
} = {}) {
|
|
3540
3678
|
if (isAntigravityQuotaDisabled()) return { configured: false, error: null };
|
|
3541
|
-
const creds = loadAntigravityCredentials({ home, platform, securityRunner, nowMs });
|
|
3542
3679
|
const startedAtMs = performance.now();
|
|
3543
3680
|
// min(this step's ceiling, budget left after reserving the fallback guard).
|
|
3544
3681
|
// 0 means "no time left" — the caller must skip the call, not issue it.
|
|
3545
3682
|
const budgetedTimeoutMs = (stepCeilingMs) => {
|
|
3683
|
+
if (signal?.aborted) return 0;
|
|
3546
3684
|
if (!Number.isFinite(providerTimeoutMs) || providerTimeoutMs <= 0) return stepCeilingMs;
|
|
3547
3685
|
const guardMs = Math.min(
|
|
3548
3686
|
ANTIGRAVITY_BUDGET_GUARD_MS,
|
|
@@ -3554,6 +3692,16 @@ async function fetchAntigravityLimits({
|
|
|
3554
3692
|
return Math.min(stepCeilingMs, guardedMs);
|
|
3555
3693
|
};
|
|
3556
3694
|
|
|
3695
|
+
const creds = await loadAntigravityCredentials({
|
|
3696
|
+
home,
|
|
3697
|
+
platform,
|
|
3698
|
+
securityRunner,
|
|
3699
|
+
secretToolRunner,
|
|
3700
|
+
nowMs,
|
|
3701
|
+
timeoutMs: budgetedTimeoutMs(2000),
|
|
3702
|
+
signal,
|
|
3703
|
+
});
|
|
3704
|
+
|
|
3557
3705
|
const finalize = (payload, normalizeOptions) => {
|
|
3558
3706
|
const result = {
|
|
3559
3707
|
configured: true,
|
|
@@ -3583,6 +3731,7 @@ async function fetchAntigravityLimits({
|
|
|
3583
3731
|
home,
|
|
3584
3732
|
platform,
|
|
3585
3733
|
securityRunner,
|
|
3734
|
+
secretToolRunner,
|
|
3586
3735
|
fetchImpl,
|
|
3587
3736
|
nowMs,
|
|
3588
3737
|
signal,
|
|
@@ -3617,6 +3766,7 @@ async function fetchAntigravityLimits({
|
|
|
3617
3766
|
nowMs,
|
|
3618
3767
|
platform,
|
|
3619
3768
|
securityRunner,
|
|
3769
|
+
secretToolRunner,
|
|
3620
3770
|
remoteError,
|
|
3621
3771
|
creds,
|
|
3622
3772
|
});
|
|
@@ -3724,6 +3874,7 @@ async function fetchAntigravityLimits({
|
|
|
3724
3874
|
nowMs,
|
|
3725
3875
|
platform,
|
|
3726
3876
|
securityRunner,
|
|
3877
|
+
secretToolRunner,
|
|
3727
3878
|
remoteError: remoteError || error,
|
|
3728
3879
|
creds,
|
|
3729
3880
|
});
|
|
@@ -3800,6 +3951,7 @@ function cacheExpiresAtMs(data, fetchedAtMs) {
|
|
|
3800
3951
|
return Math.min(ttlExpiry, Math.max(Math.min(...upcoming), fetchedAtMs + CACHE_MIN_TTL_MS));
|
|
3801
3952
|
}
|
|
3802
3953
|
|
|
3954
|
+
/** Return cached quota or share one in-flight fetch for the requested provider selection. */
|
|
3803
3955
|
async function getUsageLimits(options = {}) {
|
|
3804
3956
|
const selection = usageLimitsSelectionKey(options);
|
|
3805
3957
|
const cache = cacheBySelection[selection];
|
|
@@ -3817,11 +3969,16 @@ async function getUsageLimits(options = {}) {
|
|
|
3817
3969
|
return promise;
|
|
3818
3970
|
}
|
|
3819
3971
|
|
|
3972
|
+
/**
|
|
3973
|
+
* Fetch one aggregate for the selected providers and update its cache partition.
|
|
3974
|
+
* Forward injected credential/process runners so tests can avoid host accounts.
|
|
3975
|
+
*/
|
|
3820
3976
|
async function fetchUsageLimitsUncached({
|
|
3821
3977
|
home,
|
|
3822
3978
|
env,
|
|
3823
3979
|
platform,
|
|
3824
3980
|
securityRunner,
|
|
3981
|
+
secretToolRunner,
|
|
3825
3982
|
fetchImpl = fetch,
|
|
3826
3983
|
commandRunner,
|
|
3827
3984
|
requestFn,
|
|
@@ -3931,6 +4088,7 @@ async function fetchUsageLimitsUncached({
|
|
|
3931
4088
|
nowMs,
|
|
3932
4089
|
platform,
|
|
3933
4090
|
securityRunner,
|
|
4091
|
+
secretToolRunner,
|
|
3934
4092
|
providerTimeoutMs,
|
|
3935
4093
|
signal,
|
|
3936
4094
|
}),
|
|
@@ -3941,7 +4099,7 @@ async function fetchUsageLimitsUncached({
|
|
|
3941
4099
|
.catch((reason) => ({ configured: true, error: reason?.message || "Unknown error" })),
|
|
3942
4100
|
withProviderTimeout(fetchGrokLimits({ home, env, fetchImpl: providerFetch, timeoutMs: providerTimeoutMs }), "Grok Build", providerTimeoutMs)
|
|
3943
4101
|
.catch((reason) => ({ configured: true, error: reason?.message || "Unknown error" })),
|
|
3944
|
-
withProviderTimeout(fetchZcodeLimits({ home, env, fetchImpl: providerFetch }), "ZCode", providerTimeoutMs)
|
|
4102
|
+
withProviderTimeout(fetchZcodeLimits({ home, env, fetchImpl: providerFetch, providerTimeoutMs }), "ZCode", providerTimeoutMs)
|
|
3945
4103
|
.catch((reason) => ({ configured: true, error: reason?.message || "Unknown error" })),
|
|
3946
4104
|
// OpenCode Go: authoritative subscription windows come from the dashboard
|
|
3947
4105
|
// scrape; local opencode.db cost is available only as an explicit estimate.
|
|
@@ -4334,6 +4492,11 @@ module.exports = {
|
|
|
4334
4492
|
normalizeAntigravityResponse,
|
|
4335
4493
|
normalizeAntigravityQuotaSummary,
|
|
4336
4494
|
loadAntigravityCredentials,
|
|
4495
|
+
readAntigravityLinuxSecretRaw,
|
|
4496
|
+
writeAntigravityLinuxSecretRaw,
|
|
4497
|
+
refreshAntigravityAccessToken,
|
|
4498
|
+
ANTIGRAVITY_OAUTH_CLIENT_ID,
|
|
4499
|
+
ANTIGRAVITY_OAUTH_CLIENT_SECRET,
|
|
4337
4500
|
parseListeningPorts,
|
|
4338
4501
|
parseWindowsListeningPorts,
|
|
4339
4502
|
parseLinuxProcListeningPorts,
|