tokentracker-cli 0.49.3 → 0.50.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dashboard/dist/assets/{ActivityHeatmap-DByCSENs.js → ActivityHeatmap-Csg0JY68.js} +1 -1
- package/dashboard/dist/assets/{Card-B71GwGxO.js → Card-BDI8Fbh9.js} +1 -1
- package/dashboard/dist/assets/DashboardPage-woYtbruG.js +19 -0
- package/dashboard/dist/assets/{DevicePage-Cfj34qew.js → DevicePage-EjJbP8Br.js} +1 -1
- package/dashboard/dist/assets/{DialogTitle-BdNBXNZX.js → DialogTitle-D66QJAs3.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-BuCfpXMc.js → FadeIn-D3DCx8y6.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-DpZhDW9t.js → HeaderGithubStar-DpuKyZW3.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-DJtvaczo.js → IpCheckPage-Dz3YjhM0.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-DVR_EDlv.js → LandingPage-B5UjGCVn.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardAvatar-C6KucnuS.js → LeaderboardAvatar-C1HvZXs0.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardPage-DKHvUmGb.js → LeaderboardPage-CIp04vrj.js} +3 -3
- package/dashboard/dist/assets/{LeaderboardProfileModal-sb4nmMCj.js → LeaderboardProfileModal-BH31dBaK.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardProfilePage-BK-1QWfg.js → LeaderboardProfilePage-DpXj5L5i.js} +1 -1
- package/dashboard/dist/assets/{LimitsPage-D7m20XVf.js → LimitsPage-DVg3GfTr.js} +1 -1
- package/dashboard/dist/assets/{LocalOnlyNotice-DZslFoj5.js → LocalOnlyNotice-BADyYIuM.js} +1 -1
- package/dashboard/dist/assets/LoginPage-BId7DDhf.js +1 -0
- package/dashboard/dist/assets/{PopoverPopup-BjP_x_N0.js → PopoverPopup-BQhCMi7D.js} +1 -1
- package/dashboard/dist/assets/ResetPasswordPage-Bgj2woMZ.js +1 -0
- package/dashboard/dist/assets/{Select-B5C3SgjS.js → Select-BRQHUMnI.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-XBZTRfTk.js → SelectItemText-DKPqC7Id.js} +1 -1
- package/dashboard/dist/assets/{SettingsPage-C3pgKsph.js → SettingsPage-D7LxfBPd.js} +1 -1
- package/dashboard/dist/assets/{SkillsPage-C884cEOL.js → SkillsPage-cFXqx_rg.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-Wsg4rX9h.js → WidgetsPage-D6wEB36p.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-BSWIQwdE.js → WrappedPage-C6tq3AaH.js} +1 -1
- package/dashboard/dist/assets/{agent-logos-umK0ojkk.js → agent-logos-CjZOhWL1.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-BGMGSKrn.js → arrow-up-right-BVQI0Szz.js} +1 -1
- package/dashboard/dist/assets/{download-DBdm8vu1.js → download-45UoPdKK.js} +1 -1
- package/dashboard/dist/assets/{info-Dc1md1UE.js → info-ClM58qOx.js} +1 -1
- package/dashboard/dist/assets/{main-Br5SsufY.css → main-B1lf1bLt.css} +1 -1
- package/dashboard/dist/assets/main-CR3s46Km.js +1009 -0
- package/dashboard/dist/assets/{use-limits-display-prefs-yZId6XkL.js → use-limits-display-prefs-Boy1oEqT.js} +1 -1
- package/dashboard/dist/assets/{use-native-settings-CquG9vh_.js → use-native-settings-CGKAtYGs.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-BvhFrG8a.js → use-usage-limits-kGnhUHc-.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-DCzLyxzN.js → useCurrency-xorCxnW3.js} +1 -1
- package/dashboard/dist/assets/{useScrollLock-C5gi8Y07.js → useScrollLock-B0eyFEUR.js} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/dashboard/dist/share.html +2 -2
- package/package.json +1 -1
- package/src/lib/cloud-account.js +58 -7
- package/src/lib/local-api.js +6 -1
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/rollout.js +11 -3
- package/src/lib/usage-limits.js +35 -6
- package/dashboard/dist/assets/DashboardPage-BjOx4W82.js +0 -19
- package/dashboard/dist/assets/LoginPage-HQ8ks6KL.js +0 -1
- package/dashboard/dist/assets/main-BTU7nBLK.js +0 -999
package/src/lib/rollout.js
CHANGED
|
@@ -7590,15 +7590,23 @@ async function parseCopilotIncremental({ otelPaths, cursors, queuePath, onProgre
|
|
|
7590
7590
|
|
|
7591
7591
|
const inputRaw = toNonNegativeInt(attrs["gen_ai.usage.input_tokens"]);
|
|
7592
7592
|
const output = toNonNegativeInt(attrs["gen_ai.usage.output_tokens"]);
|
|
7593
|
-
const cacheRead = toNonNegativeInt(
|
|
7593
|
+
const cacheRead = toNonNegativeInt(
|
|
7594
|
+
attrs["gen_ai.usage.cache_read.input_tokens"] ??
|
|
7595
|
+
attrs["gen_ai.usage.cache_read_input_tokens"] ??
|
|
7596
|
+
attrs["gen_ai.usage.cached_input_tokens"],
|
|
7597
|
+
);
|
|
7594
7598
|
// Copilot CLI: cache_write.input_tokens; Copilot Chat extension: cache_creation.input_tokens
|
|
7595
7599
|
const cacheWrite = toNonNegativeInt(
|
|
7596
7600
|
attrs["gen_ai.usage.cache_write.input_tokens"] ??
|
|
7597
|
-
attrs["gen_ai.usage.cache_creation.input_tokens"]
|
|
7601
|
+
attrs["gen_ai.usage.cache_creation.input_tokens"] ??
|
|
7602
|
+
attrs["gen_ai.usage.cache_write_input_tokens"] ??
|
|
7603
|
+
attrs["gen_ai.usage.cache_creation_input_tokens"],
|
|
7598
7604
|
);
|
|
7599
7605
|
// Copilot CLI: reasoning.output_tokens; Copilot Chat extension: reasoning_tokens
|
|
7600
7606
|
const reasoning = toNonNegativeInt(
|
|
7601
|
-
attrs["gen_ai.usage.reasoning.output_tokens"] ??
|
|
7607
|
+
attrs["gen_ai.usage.reasoning.output_tokens"] ??
|
|
7608
|
+
attrs["gen_ai.usage.reasoning_tokens"] ??
|
|
7609
|
+
attrs["gen_ai.usage.reasoning_output_tokens"],
|
|
7602
7610
|
);
|
|
7603
7611
|
// OTEL input_tokens INCLUDES cache_read — subtract per project convention
|
|
7604
7612
|
const cacheReadClamped = Math.min(cacheRead, inputRaw);
|
package/src/lib/usage-limits.js
CHANGED
|
@@ -36,6 +36,7 @@ const ANTIGRAVITY_LIMITS_CACHE_UNKNOWN_RESET_TTL_MS = 12 * 60 * 60 * 1000;
|
|
|
36
36
|
// file with only its own key, so a shared file would clobber).
|
|
37
37
|
const CLAUDE_LIMITS_CACHE_FILE = "claude-usage-limits-cache.json";
|
|
38
38
|
const CLAUDE_LIMITS_CACHE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
39
|
+
const CLAUDE_LIMITS_CACHE_FRESH_TTL_MS = 10 * 60 * 1000;
|
|
39
40
|
// A 429 from the usage endpoint carries a long `retry-after` (often 20+ minutes). Persist
|
|
40
41
|
// the cooldown so every surface — this process, the menu bar app's embedded server, a later
|
|
41
42
|
// restart — stops calling until it expires. Hammering during the cooldown just renews the
|
|
@@ -1484,11 +1485,18 @@ function hasClaudeWindow(limits) {
|
|
|
1484
1485
|
return Boolean(limits?.five_hour || limits?.seven_day || limits?.seven_day_opus);
|
|
1485
1486
|
}
|
|
1486
1487
|
|
|
1487
|
-
function normalizeClaudeCachedLimits(
|
|
1488
|
+
function normalizeClaudeCachedLimits(
|
|
1489
|
+
raw,
|
|
1490
|
+
{
|
|
1491
|
+
nowMs = Date.now(),
|
|
1492
|
+
maxAgeMs = CLAUDE_LIMITS_CACHE_MAX_AGE_MS,
|
|
1493
|
+
stale = true,
|
|
1494
|
+
} = {},
|
|
1495
|
+
) {
|
|
1488
1496
|
const cachedAtMs = parseTimeMs(raw?.cached_at);
|
|
1489
1497
|
if (!Number.isFinite(cachedAtMs)) return null;
|
|
1490
1498
|
if (cachedAtMs > nowMs + 60_000) return null;
|
|
1491
|
-
if (nowMs - cachedAtMs >
|
|
1499
|
+
if (nowMs - cachedAtMs > maxAgeMs) return null;
|
|
1492
1500
|
|
|
1493
1501
|
const cached = {
|
|
1494
1502
|
configured: true,
|
|
@@ -1497,22 +1505,36 @@ function normalizeClaudeCachedLimits(raw, { nowMs = Date.now() } = {}) {
|
|
|
1497
1505
|
seven_day: isClaudeCacheWindowUsable(raw?.seven_day, { nowMs }) ? raw.seven_day : null,
|
|
1498
1506
|
seven_day_opus: isClaudeCacheWindowUsable(raw?.seven_day_opus, { nowMs }) ? raw.seven_day_opus : null,
|
|
1499
1507
|
extra_usage: raw?.extra_usage ?? null,
|
|
1500
|
-
stale
|
|
1508
|
+
stale,
|
|
1501
1509
|
cached_at: raw.cached_at,
|
|
1502
1510
|
};
|
|
1503
1511
|
return hasClaudeWindow(cached) ? cached : null;
|
|
1504
1512
|
}
|
|
1505
1513
|
|
|
1506
|
-
function readClaudeLimitsCache({
|
|
1514
|
+
function readClaudeLimitsCache({
|
|
1515
|
+
home,
|
|
1516
|
+
nowMs = Date.now(),
|
|
1517
|
+
maxAgeMs = CLAUDE_LIMITS_CACHE_MAX_AGE_MS,
|
|
1518
|
+
stale = true,
|
|
1519
|
+
} = {}) {
|
|
1507
1520
|
const cachePath = resolveClaudeLimitsCachePath({ home });
|
|
1508
1521
|
try {
|
|
1509
1522
|
const parsed = JSON.parse(fs.readFileSync(cachePath, "utf8"));
|
|
1510
|
-
return normalizeClaudeCachedLimits(parsed?.claude, { nowMs });
|
|
1523
|
+
return normalizeClaudeCachedLimits(parsed?.claude, { nowMs, maxAgeMs, stale });
|
|
1511
1524
|
} catch (_error) {
|
|
1512
1525
|
return null;
|
|
1513
1526
|
}
|
|
1514
1527
|
}
|
|
1515
1528
|
|
|
1529
|
+
function readFreshClaudeLimitsCache({ home, nowMs = Date.now() } = {}) {
|
|
1530
|
+
return readClaudeLimitsCache({
|
|
1531
|
+
home,
|
|
1532
|
+
nowMs,
|
|
1533
|
+
maxAgeMs: CLAUDE_LIMITS_CACHE_FRESH_TTL_MS,
|
|
1534
|
+
stale: false,
|
|
1535
|
+
});
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1516
1538
|
function writeClaudeLimitsCache(limits, { home, nowMs = Date.now() } = {}) {
|
|
1517
1539
|
if (!limits?.configured || limits.error || !hasClaudeWindow(limits)) return;
|
|
1518
1540
|
const cachePath = resolveClaudeLimitsCachePath({ home });
|
|
@@ -2016,10 +2038,15 @@ async function fetchUsageLimitsUncached({
|
|
|
2016
2038
|
// Skip the upstream Claude call entirely while a 429 cooldown is active — calling again
|
|
2017
2039
|
// just renews the penalty. The result handling below serves cache or a cooldown message.
|
|
2018
2040
|
const claudeRetryAtMs = claudeToken ? readClaudeRateLimitRetryAtMs({ home, nowMs }) : null;
|
|
2041
|
+
// Also avoid cross-process hammering after a recent successful read. The macOS app can
|
|
2042
|
+
// restart its embedded Node server or force-refresh the limits page, both of which clear
|
|
2043
|
+
// the in-memory cache; the disk cache keeps those paths from immediately spending
|
|
2044
|
+
// another Claude OAuth usage request.
|
|
2045
|
+
const freshClaudeCache = claudeToken ? readFreshClaudeLimitsCache({ home, nowMs }) : null;
|
|
2019
2046
|
|
|
2020
2047
|
const providerFetch = withFetchTimeout(fetchImpl, providerTimeoutMs);
|
|
2021
2048
|
const [claudeResult, codexResult, cursor, kimi, gemini, kiro, antigravity, copilot, grok] = await Promise.all([
|
|
2022
|
-
claudeToken && !claudeRetryAtMs
|
|
2049
|
+
claudeToken && !freshClaudeCache && !claudeRetryAtMs
|
|
2023
2050
|
? withProviderTimeout(fetchClaudeUsageLimits(claudeToken, { fetchImpl: providerFetch, maxAttempts: 1 }), "Claude", providerTimeoutMs).then(
|
|
2024
2051
|
(value) => ({ status: "fulfilled", value }),
|
|
2025
2052
|
(reason) => ({ status: "rejected", reason }),
|
|
@@ -2052,6 +2079,8 @@ async function fetchUsageLimitsUncached({
|
|
|
2052
2079
|
let claude;
|
|
2053
2080
|
if (!claudeToken) {
|
|
2054
2081
|
claude = { configured: false };
|
|
2082
|
+
} else if (freshClaudeCache) {
|
|
2083
|
+
claude = freshClaudeCache;
|
|
2055
2084
|
} else if (claudeResult && claudeResult.status === "fulfilled") {
|
|
2056
2085
|
claude = {
|
|
2057
2086
|
configured: true,
|