@ipv9/tokentracker-cli 0.39.35 → 0.39.36
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-rUm9bSZ9.js → ActivityHeatmap-BZbe6OVf.js} +1 -1
- package/dashboard/dist/assets/{Card-YWr91b5b.js → Card-DZDffviN.js} +1 -1
- package/dashboard/dist/assets/{DashboardPage-DdJOVJuw.js → DashboardPage-DvrBOnfG.js} +1 -1
- package/dashboard/dist/assets/{DevicePage-CQMG1bh8.js → DevicePage-DNrsGqbS.js} +1 -1
- package/dashboard/dist/assets/{DialogTitle-BM0Hz4Pq.js → DialogTitle-DMgnAEO9.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-D8xysY3i.js → FadeIn-DpHBH3x-.js} +1 -1
- package/dashboard/dist/assets/{InsforgeUserHeaderControls-CsAafXyj.js → InsforgeUserHeaderControls-DkKxxuF4.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-Bbg9erkT.js → IpCheckPage-CjcJwf3_.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-BCMsWOwJ.js → LandingPage-BoVGYS64.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardAvatar-CkQa4EX2.js → LeaderboardAvatar-DQ8ynVhp.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardPage-CyDX3tDL.js → LeaderboardPage-JYAYCp0V.js} +3 -3
- package/dashboard/dist/assets/{LeaderboardProfileModal-CMctMA-p.js → LeaderboardProfileModal-Dy-QGydj.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardProfilePage-DTZ_6dBt.js → LeaderboardProfilePage-DM5JCs97.js} +1 -1
- package/dashboard/dist/assets/{LimitsPage-V7939U8b.js → LimitsPage-hRivQP36.js} +2 -2
- package/dashboard/dist/assets/{LocalOnlyNotice-dODOM3SB.js → LocalOnlyNotice-jYtTsZPC.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-CE5GBEI2.js → LoginPage-CamCWjrS.js} +1 -1
- package/dashboard/dist/assets/{PopoverPopup-CRzMMFsC.js → PopoverPopup-OsVVgo9j.js} +1 -1
- package/dashboard/dist/assets/{Select-m6edgfye.js → Select-CPShxNj8.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-DIoTPvZM.js → SelectItemText-Euh3FNnr.js} +1 -1
- package/dashboard/dist/assets/{SettingsPage-DObaSleW.js → SettingsPage-GgHfE5Ux.js} +1 -1
- package/dashboard/dist/assets/{SkillsPage-BSi26dZo.js → SkillsPage-B5VnJZGi.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-CTl7OtvF.js → WidgetsPage-DLgvjves.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-BXwhD9aq.js → WrappedPage-Cn9hW775.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-DxdoYoxg.js → arrow-up-right-DbC_nfsr.js} +1 -1
- package/dashboard/dist/assets/{download-BZkwGDg8.js → download-CcPm5jr5.js} +1 -1
- package/dashboard/dist/assets/{info-DRaQWOpM.js → info-DCynm2Zt.js} +1 -1
- package/dashboard/dist/assets/{main-CuaPRwHu.js → main-BRziFxTy.js} +3 -2
- package/dashboard/dist/assets/{use-limits-display-prefs-5cj2WfP-.js → use-limits-display-prefs-Z0jXcttr.js} +1 -1
- package/dashboard/dist/assets/{use-native-settings-BfydbPtC.js → use-native-settings-C2hcqfEA.js} +1 -1
- package/dashboard/dist/assets/{use-spring-BxDSGjR_.js → use-spring-CvCkOHy_.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-D_5PMCyF.js → use-usage-limits-ClV5RR82.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-DQhzQX2k.js → useCurrency-DV0oi8od.js} +1 -1
- package/dashboard/dist/assets/{useOpenInteractionType--grUNSll.js → useOpenInteractionType-C6kbIbxP.js} +1 -1
- package/dashboard/dist/index.html +1 -1
- package/dashboard/dist/share.html +1 -1
- package/package.json +1 -1
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/usage-limits.js +101 -17
package/src/lib/usage-limits.js
CHANGED
|
@@ -29,6 +29,8 @@ const DEFAULT_PROVIDER_TIMEOUT_MS = 15_000;
|
|
|
29
29
|
const ANTIGRAVITY_LIMITS_CACHE_FILE = "usage-limits-cache.json";
|
|
30
30
|
const ANTIGRAVITY_LIMITS_CACHE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
31
31
|
const ANTIGRAVITY_LIMITS_CACHE_UNKNOWN_RESET_TTL_MS = 12 * 60 * 60 * 1000;
|
|
32
|
+
const CLAUDE_LIMITS_CACHE_MAX_AGE_MS = 24 * 60 * 60 * 1000;
|
|
33
|
+
const CLAUDE_USAGE_RATE_LIMITED = "CLAUDE_USAGE_RATE_LIMITED";
|
|
32
34
|
|
|
33
35
|
function clampPercent(value) {
|
|
34
36
|
if (value === null || value === undefined || value === "") return null;
|
|
@@ -119,9 +121,12 @@ async function fetchClaudeUsageLimits(accessToken, { fetchImpl = fetch, maxAttem
|
|
|
119
121
|
}
|
|
120
122
|
if (!res.ok) {
|
|
121
123
|
if (res.status === 429) {
|
|
122
|
-
|
|
124
|
+
const error = new Error(
|
|
123
125
|
"Claude API rate limited (429) — wait ~1 minute and refresh.",
|
|
124
126
|
);
|
|
127
|
+
error.code = CLAUDE_USAGE_RATE_LIMITED;
|
|
128
|
+
error.transient = true;
|
|
129
|
+
throw error;
|
|
125
130
|
}
|
|
126
131
|
throw new Error(`Claude API returned ${res.status}`);
|
|
127
132
|
}
|
|
@@ -135,6 +140,13 @@ async function fetchClaudeUsageLimits(accessToken, { fetchImpl = fetch, maxAttem
|
|
|
135
140
|
}
|
|
136
141
|
}
|
|
137
142
|
|
|
143
|
+
function isClaudeTransientUsageError(error) {
|
|
144
|
+
if (!error) return false;
|
|
145
|
+
if (error.code === CLAUDE_USAGE_RATE_LIMITED || error.transient === true) return true;
|
|
146
|
+
const message = String(error.message || "");
|
|
147
|
+
return /rate limited|timed out/i.test(message);
|
|
148
|
+
}
|
|
149
|
+
|
|
138
150
|
// Classify a wham window by `limit_window_seconds` rather than its slot name.
|
|
139
151
|
// 18000s = 5h session window. 604800s = 7d weekly window. Free-tier accounts only get a
|
|
140
152
|
// weekly window, often delivered in the `primary_window` slot — naive position-based
|
|
@@ -1324,11 +1336,34 @@ function detectAntigravityProcess({ commandRunner } = {}) {
|
|
|
1324
1336
|
return { configured: false };
|
|
1325
1337
|
}
|
|
1326
1338
|
|
|
1327
|
-
function
|
|
1339
|
+
function resolveUsageLimitsCachePath({ home } = {}) {
|
|
1328
1340
|
return path.join(home || os.homedir(), ".tokentracker", "tracker", ANTIGRAVITY_LIMITS_CACHE_FILE);
|
|
1329
1341
|
}
|
|
1330
1342
|
|
|
1343
|
+
function readUsageLimitsCacheFile({ home } = {}) {
|
|
1344
|
+
try {
|
|
1345
|
+
return JSON.parse(fs.readFileSync(resolveUsageLimitsCachePath({ home }), "utf8"));
|
|
1346
|
+
} catch (_error) {
|
|
1347
|
+
return {};
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
function writeUsageLimitsCacheFile(update, { home } = {}) {
|
|
1352
|
+
const cachePath = resolveUsageLimitsCachePath({ home });
|
|
1353
|
+
try {
|
|
1354
|
+
const current = readUsageLimitsCacheFile({ home });
|
|
1355
|
+
fs.mkdirSync(path.dirname(cachePath), { recursive: true });
|
|
1356
|
+
const tmpPath = `${cachePath}.${process.pid}.tmp`;
|
|
1357
|
+
fs.writeFileSync(tmpPath, JSON.stringify({ ...current, ...update }, null, 2), { encoding: "utf8", mode: 0o600 });
|
|
1358
|
+
fs.renameSync(tmpPath, cachePath);
|
|
1359
|
+
} catch (_error) {}
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1331
1362
|
function parseTimeMs(value) {
|
|
1363
|
+
if (typeof value === "number") {
|
|
1364
|
+
if (!Number.isFinite(value) || value <= 0) return null;
|
|
1365
|
+
return value < 1_000_000_000_000 ? value * 1000 : value;
|
|
1366
|
+
}
|
|
1332
1367
|
if (typeof value !== "string" || !value) return null;
|
|
1333
1368
|
const ts = Date.parse(value);
|
|
1334
1369
|
return Number.isFinite(ts) && ts > 0 ? ts : null;
|
|
@@ -1367,18 +1402,12 @@ function normalizeAntigravityCachedLimits(raw, { nowMs = Date.now() } = {}) {
|
|
|
1367
1402
|
}
|
|
1368
1403
|
|
|
1369
1404
|
function readAntigravityLimitsCache({ home, nowMs = Date.now() } = {}) {
|
|
1370
|
-
const
|
|
1371
|
-
|
|
1372
|
-
const parsed = JSON.parse(fs.readFileSync(cachePath, "utf8"));
|
|
1373
|
-
return normalizeAntigravityCachedLimits(parsed?.antigravity, { nowMs });
|
|
1374
|
-
} catch (_error) {
|
|
1375
|
-
return null;
|
|
1376
|
-
}
|
|
1405
|
+
const parsed = readUsageLimitsCacheFile({ home });
|
|
1406
|
+
return normalizeAntigravityCachedLimits(parsed?.antigravity, { nowMs });
|
|
1377
1407
|
}
|
|
1378
1408
|
|
|
1379
1409
|
function writeAntigravityLimitsCache(limits, { home, nowMs = Date.now() } = {}) {
|
|
1380
1410
|
if (!limits?.configured || limits.error || !hasAntigravityWindow(limits)) return;
|
|
1381
|
-
const cachePath = resolveAntigravityLimitsCachePath({ home });
|
|
1382
1411
|
const payload = {
|
|
1383
1412
|
antigravity: {
|
|
1384
1413
|
account_email: limits.account_email || null,
|
|
@@ -1389,12 +1418,62 @@ function writeAntigravityLimitsCache(limits, { home, nowMs = Date.now() } = {})
|
|
|
1389
1418
|
cached_at: new Date(nowMs).toISOString(),
|
|
1390
1419
|
},
|
|
1391
1420
|
};
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1421
|
+
writeUsageLimitsCacheFile(payload, { home });
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
function hasClaudeWindow(limits) {
|
|
1425
|
+
return Boolean(limits?.five_hour || limits?.seven_day || limits?.seven_day_opus);
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
function isClaudeCacheWindowUsable(window, { cachedAtMs, nowMs } = {}) {
|
|
1429
|
+
if (!window || typeof window !== "object") return false;
|
|
1430
|
+
const resetAtMs = parseTimeMs(window.resets_at);
|
|
1431
|
+
if (resetAtMs !== null) return resetAtMs > nowMs;
|
|
1432
|
+
return Number.isFinite(cachedAtMs)
|
|
1433
|
+
&& nowMs - cachedAtMs <= CLAUDE_LIMITS_CACHE_MAX_AGE_MS;
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
function normalizeClaudeCachedLimits(raw, { nowMs = Date.now() } = {}) {
|
|
1437
|
+
const cachedAtMs = parseTimeMs(raw?.cached_at);
|
|
1438
|
+
if (!Number.isFinite(cachedAtMs)) return null;
|
|
1439
|
+
if (cachedAtMs > nowMs + 60_000) return null;
|
|
1440
|
+
if (nowMs - cachedAtMs > CLAUDE_LIMITS_CACHE_MAX_AGE_MS) return null;
|
|
1441
|
+
|
|
1442
|
+
const cached = {
|
|
1443
|
+
configured: true,
|
|
1444
|
+
error: null,
|
|
1445
|
+
five_hour: isClaudeCacheWindowUsable(raw?.five_hour, { cachedAtMs, nowMs }) ? raw.five_hour : null,
|
|
1446
|
+
seven_day: isClaudeCacheWindowUsable(raw?.seven_day, { cachedAtMs, nowMs }) ? raw.seven_day : null,
|
|
1447
|
+
seven_day_opus: isClaudeCacheWindowUsable(raw?.seven_day_opus, { cachedAtMs, nowMs }) ? raw.seven_day_opus : null,
|
|
1448
|
+
extra_usage: raw?.extra_usage || null,
|
|
1449
|
+
cached: true,
|
|
1450
|
+
cached_at: raw.cached_at,
|
|
1451
|
+
stale_reason: typeof raw?.stale_reason === "string" ? raw.stale_reason : null,
|
|
1452
|
+
};
|
|
1453
|
+
return hasClaudeWindow(cached) ? cached : null;
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
function readClaudeLimitsCache({ home, nowMs = Date.now(), staleReason = null } = {}) {
|
|
1457
|
+
const parsed = readUsageLimitsCacheFile({ home });
|
|
1458
|
+
const cached = normalizeClaudeCachedLimits(parsed?.claude, { nowMs });
|
|
1459
|
+
if (!cached) return null;
|
|
1460
|
+
return {
|
|
1461
|
+
...cached,
|
|
1462
|
+
stale_reason: typeof staleReason === "string" && staleReason ? staleReason : cached.stale_reason,
|
|
1463
|
+
};
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
function writeClaudeLimitsCache(limits, { home, nowMs = Date.now() } = {}) {
|
|
1467
|
+
if (!limits?.configured || limits.error || !hasClaudeWindow(limits)) return;
|
|
1468
|
+
writeUsageLimitsCacheFile({
|
|
1469
|
+
claude: {
|
|
1470
|
+
five_hour: limits.five_hour || null,
|
|
1471
|
+
seven_day: limits.seven_day || null,
|
|
1472
|
+
seven_day_opus: limits.seven_day_opus || null,
|
|
1473
|
+
extra_usage: limits.extra_usage || null,
|
|
1474
|
+
cached_at: new Date(nowMs).toISOString(),
|
|
1475
|
+
},
|
|
1476
|
+
}, { home });
|
|
1398
1477
|
}
|
|
1399
1478
|
|
|
1400
1479
|
function resolveLsofBinary({ commandRunner } = {}) {
|
|
@@ -1860,7 +1939,11 @@ async function getUsageLimits({
|
|
|
1860
1939
|
if (!claudeToken) {
|
|
1861
1940
|
claude = { configured: false };
|
|
1862
1941
|
} else if (!claudeResult || claudeResult.status === "rejected") {
|
|
1863
|
-
|
|
1942
|
+
const reason = claudeResult?.reason;
|
|
1943
|
+
const cached = isClaudeTransientUsageError(reason)
|
|
1944
|
+
? readClaudeLimitsCache({ home, nowMs, staleReason: reason?.message })
|
|
1945
|
+
: null;
|
|
1946
|
+
claude = cached || { configured: true, error: reason?.message || "Unknown error" };
|
|
1864
1947
|
} else {
|
|
1865
1948
|
claude = {
|
|
1866
1949
|
configured: true,
|
|
@@ -1870,6 +1953,7 @@ async function getUsageLimits({
|
|
|
1870
1953
|
seven_day_opus: claudeResult.value.seven_day_opus,
|
|
1871
1954
|
extra_usage: claudeResult.value.extra_usage,
|
|
1872
1955
|
};
|
|
1956
|
+
writeClaudeLimitsCache(claude, { home, nowMs });
|
|
1873
1957
|
}
|
|
1874
1958
|
|
|
1875
1959
|
let codex;
|