tokentracker-cli 0.70.0 → 0.71.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/dashboard/dist/assets/{ActivityHeatmap-HkjamKhQ.js → ActivityHeatmap-Dk9JY1ug.js} +1 -1
- package/dashboard/dist/assets/{Card-BAu_q1P8.js → Card-CNWjr8mp.js} +1 -1
- package/dashboard/dist/assets/{CommunityStatsModal-Cvb5lxum.js → CommunityStatsModal-C2qyGffN.js} +1 -1
- package/dashboard/dist/assets/{ConfirmModal-Ckc0m2pF.js → ConfirmModal-DSu8Tu1r.js} +1 -1
- package/dashboard/dist/assets/{DashboardPage-CdTpYDO0.js → DashboardPage-DIf7YTQ9.js} +1 -1
- package/dashboard/dist/assets/{DevicePage-BIQ25ul5.js → DevicePage-CXQPPyMG.js} +1 -1
- package/dashboard/dist/assets/{DialogClose-DiOWMlOM.js → DialogClose-Cl6zG-Oy.js} +1 -1
- package/dashboard/dist/assets/DialogDescription-Bg5Mv-UG.js +1 -0
- package/dashboard/dist/assets/{DialogTitle-BrR7t4i0.js → DialogTitle-BRCsGR2a.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-CkFXh9qu.js → FadeIn-B3oaxBze.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-BqKaM3ph.js → HeaderGithubStar-Dl3VFKq_.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-k4RZKPiL.js → IpCheckPage-BJGOP6DZ.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-Duj0yl_C.js → LandingPage-CM30tqFJ.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardAvatar-dILRiEW5.js → LeaderboardAvatar-P3H4JJH7.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardPage-yWly0uWz.js → LeaderboardPage-CMgku-JV.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardProfileModal-DyBDRAKE.js → LeaderboardProfileModal-1pDzkqlc.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardProfilePage-CpXppYzc.js → LeaderboardProfilePage-CXv6v78X.js} +1 -1
- package/dashboard/dist/assets/{LimitsPage-BG86Jtvl.js → LimitsPage-DPmTlaMP.js} +1 -1
- package/dashboard/dist/assets/{LocalOnlyNotice-CYCe0N_S.js → LocalOnlyNotice-DZ2JWpUi.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-D9XhznZH.js → LoginPage-yZfMo0Bn.js} +1 -1
- package/dashboard/dist/assets/{PopoverPopup-C8iZhys6.js → PopoverPopup-C7ClU-a4.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-B8mXGzFe.js → ResetPasswordPage-DRxzMEet.js} +1 -1
- package/dashboard/dist/assets/{Select-Wgv6XKdi.js → Select-ShDjR08e.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-rxW6xT66.js → SelectItemText-Cdb3AE2d.js} +1 -1
- package/dashboard/dist/assets/{SettingsPage-DRpIQZJx.js → SettingsPage-KhtHkiXb.js} +1 -1
- package/dashboard/dist/assets/{SkillsPage-Bl_FJwQu.js → SkillsPage-BfvTyaan.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-B0AlwiRQ.js → WidgetsPage-B9WHyvqX.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-C-ojLydF.js → WrappedPage-BVY8Z7e8.js} +1 -1
- package/dashboard/dist/assets/{agent-logos-wKEmyIg8.js → agent-logos-w8DphLG5.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-BwT8ykRA.js → arrow-up-right-CCe_6-J8.js} +1 -1
- package/dashboard/dist/assets/check-BuuQsfIt.js +1 -0
- package/dashboard/dist/assets/{copy-DvlBby5m.js → copy-B6Uad-NU.js} +1 -1
- package/dashboard/dist/assets/{download-C7MsK41l.js → download-BYcyu1Bg.js} +1 -1
- package/dashboard/dist/assets/{format-tokens-CtXNYfM2.js → format-tokens-BFn66jct.js} +1 -1
- package/dashboard/dist/assets/{info-DXSmyEHB.js → info-CTKpV2vu.js} +1 -1
- package/dashboard/dist/assets/{main-DPyNBrFb.js → main-DbLbrnIE.js} +2 -2
- package/dashboard/dist/assets/{use-community-stats-BC4Va1tk.js → use-community-stats-Bumdx05F.js} +1 -1
- package/dashboard/dist/assets/{use-limits-display-prefs-BL-NCgDn.js → use-limits-display-prefs-DeSmHt-l.js} +1 -1
- package/dashboard/dist/assets/{use-native-settings-D0x_4uCT.js → use-native-settings-CdTOidJ4.js} +1 -1
- package/dashboard/dist/assets/{use-ordered-list-DEi9FKn9.js → use-ordered-list-DQoW_9df.js} +1 -1
- package/dashboard/dist/assets/{use-quality-per-dollar-pref-BIf1mWJK.js → use-quality-per-dollar-pref-CwFs7F06.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-CosJF8AC.js → use-usage-limits-CKuCunJc.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-4cyeApuY.js → useCurrency-CG5fqlMR.js} +1 -1
- package/dashboard/dist/assets/{useScrollLock-D0Td7eQX.js → useScrollLock-DsnK0M2M.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 +80 -14
- package/dashboard/dist/assets/DialogDescription-DNyMczI_.js +0 -1
- package/dashboard/dist/assets/check-gQkfPY06.js +0 -1
package/src/lib/usage-limits.js
CHANGED
|
@@ -28,9 +28,15 @@ const { fetchZcodeLimits } = require("./zcode-limits");
|
|
|
28
28
|
const { fetchOpencodeGoLimits } = require("./opencode-go-limits");
|
|
29
29
|
const { readSqliteJsonRows } = require("./sqlite-reader");
|
|
30
30
|
|
|
31
|
-
// 2-minute in-memory cache
|
|
32
|
-
|
|
31
|
+
// 2-minute in-memory cache. It also expires early at the earliest upcoming window
|
|
32
|
+
// reset in the cached data (see cacheExpiresAtMs), floored so a provider reporting
|
|
33
|
+
// a reset "right now" can't turn every poll into a full upstream round.
|
|
34
|
+
let cache = { data: null, expiresAtMs: 0 };
|
|
33
35
|
const CACHE_TTL_MS = 2 * 60 * 1000;
|
|
36
|
+
// Must stay below the macOS app's post-reset re-fetch grace (10s in
|
|
37
|
+
// DashboardViewModel.resetBoundaryGrace), or that targeted refresh would be
|
|
38
|
+
// served this cache's pre-reset snapshot and miss the rollover.
|
|
39
|
+
const CACHE_MIN_TTL_MS = 5 * 1000;
|
|
34
40
|
const DEFAULT_PROVIDER_TIMEOUT_MS = 15_000;
|
|
35
41
|
const ANTIGRAVITY_LIMITS_CACHE_FILE = "usage-limits-cache.json";
|
|
36
42
|
const ANTIGRAVITY_LIMITS_CACHE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
@@ -1921,24 +1927,48 @@ function normalizeClaudeCachedLimits(
|
|
|
1921
1927
|
return hasClaudeWindow(cached) ? cached : null;
|
|
1922
1928
|
}
|
|
1923
1929
|
|
|
1930
|
+
function readClaudeLimitsCacheRaw({ home } = {}) {
|
|
1931
|
+
try {
|
|
1932
|
+
const parsed = JSON.parse(fs.readFileSync(resolveClaudeLimitsCachePath({ home }), "utf8"));
|
|
1933
|
+
return parsed?.claude ?? null;
|
|
1934
|
+
} catch (_error) {
|
|
1935
|
+
return null;
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1924
1939
|
function readClaudeLimitsCache({
|
|
1925
1940
|
home,
|
|
1926
1941
|
nowMs = Date.now(),
|
|
1927
1942
|
maxAgeMs = CLAUDE_LIMITS_CACHE_MAX_AGE_MS,
|
|
1928
1943
|
stale = true,
|
|
1929
1944
|
} = {}) {
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1945
|
+
return normalizeClaudeCachedLimits(readClaudeLimitsCacheRaw({ home }), { nowMs, maxAgeMs, stale });
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
// A cached snapshot stops being "fresh" the moment any of its windows crosses the
|
|
1949
|
+
// reset boundary it was written with: serving it past that point hides the rollover
|
|
1950
|
+
// from reset detection (the menu bar confetti) for up to the fresh TTL. Only resets
|
|
1951
|
+
// that were still ahead at write time count — a provider stamping already-past reset
|
|
1952
|
+
// times must not force a live call on every poll.
|
|
1953
|
+
function claudeCacheCrossedReset(raw, { nowMs } = {}) {
|
|
1954
|
+
const cachedAtMs = parseTimeMs(raw?.cached_at);
|
|
1955
|
+
if (!Number.isFinite(cachedAtMs)) return false;
|
|
1956
|
+
const windows = [
|
|
1957
|
+
raw?.five_hour,
|
|
1958
|
+
raw?.seven_day,
|
|
1959
|
+
raw?.seven_day_opus,
|
|
1960
|
+
...(Array.isArray(raw?.weekly_scoped) ? raw.weekly_scoped : []),
|
|
1961
|
+
];
|
|
1962
|
+
return windows.some((window) => {
|
|
1963
|
+
const resetMs = parseTimeMs(window?.resets_at);
|
|
1964
|
+
return resetMs !== null && resetMs > cachedAtMs && resetMs <= nowMs;
|
|
1965
|
+
});
|
|
1937
1966
|
}
|
|
1938
1967
|
|
|
1939
1968
|
function readFreshClaudeLimitsCache({ home, nowMs = Date.now() } = {}) {
|
|
1940
|
-
|
|
1941
|
-
|
|
1969
|
+
const raw = readClaudeLimitsCacheRaw({ home });
|
|
1970
|
+
if (!raw || claudeCacheCrossedReset(raw, { nowMs })) return null;
|
|
1971
|
+
return normalizeClaudeCachedLimits(raw, {
|
|
1942
1972
|
nowMs,
|
|
1943
1973
|
maxAgeMs: CLAUDE_LIMITS_CACHE_FRESH_TTL_MS,
|
|
1944
1974
|
stale: false,
|
|
@@ -2564,9 +2594,44 @@ function withPlanLabel(obj, raw, brand) {
|
|
|
2564
2594
|
// in-flight fetch and returns its result.
|
|
2565
2595
|
let inFlightFetch = null;
|
|
2566
2596
|
|
|
2597
|
+
// Codex stamps reset_at as unix seconds; every other provider (and Claude's
|
|
2598
|
+
// resets_at) uses ISO strings. Numbers that look like epoch milliseconds are
|
|
2599
|
+
// accepted as-is so a future provider can't silently regress this.
|
|
2600
|
+
function resetValueToMs(value) {
|
|
2601
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
2602
|
+
return value < 1e12 ? value * 1000 : value;
|
|
2603
|
+
}
|
|
2604
|
+
return parseTimeMs(value);
|
|
2605
|
+
}
|
|
2606
|
+
|
|
2607
|
+
// Walk the aggregated response and collect every window reset timestamp,
|
|
2608
|
+
// whatever the provider-specific nesting looks like.
|
|
2609
|
+
function collectResetTimesMs(node, out) {
|
|
2610
|
+
if (!node || typeof node !== "object") return;
|
|
2611
|
+
for (const [key, value] of Object.entries(node)) {
|
|
2612
|
+
if (key === "reset_at" || key === "resets_at") {
|
|
2613
|
+
const ms = resetValueToMs(value);
|
|
2614
|
+
if (ms !== null) out.push(ms);
|
|
2615
|
+
} else if (value && typeof value === "object") {
|
|
2616
|
+
collectResetTimesMs(value, out);
|
|
2617
|
+
}
|
|
2618
|
+
}
|
|
2619
|
+
}
|
|
2620
|
+
|
|
2621
|
+
// The moment any window rolls over, cached data stops being current — reset
|
|
2622
|
+
// detection (the menu bar confetti) would otherwise lag by up to the full TTL.
|
|
2623
|
+
function cacheExpiresAtMs(data, fetchedAtMs) {
|
|
2624
|
+
const times = [];
|
|
2625
|
+
collectResetTimesMs(data, times);
|
|
2626
|
+
const upcoming = times.filter((t) => t > fetchedAtMs);
|
|
2627
|
+
const ttlExpiry = fetchedAtMs + CACHE_TTL_MS;
|
|
2628
|
+
if (upcoming.length === 0) return ttlExpiry;
|
|
2629
|
+
return Math.min(ttlExpiry, Math.max(Math.min(...upcoming), fetchedAtMs + CACHE_MIN_TTL_MS));
|
|
2630
|
+
}
|
|
2631
|
+
|
|
2567
2632
|
async function getUsageLimits(options = {}) {
|
|
2568
2633
|
const nowMs = Date.now();
|
|
2569
|
-
if (cache.data && nowMs
|
|
2634
|
+
if (cache.data && nowMs < cache.expiresAtMs) {
|
|
2570
2635
|
return cache.data;
|
|
2571
2636
|
}
|
|
2572
2637
|
if (inFlightFetch) {
|
|
@@ -2775,18 +2840,19 @@ async function fetchUsageLimitsUncached({
|
|
|
2775
2840
|
opencodeGo: withPlanLabel(opencodeGo, opencodeGo?.plan_label, "OpenCode Go"),
|
|
2776
2841
|
};
|
|
2777
2842
|
|
|
2778
|
-
cache = { data,
|
|
2843
|
+
cache = { data, expiresAtMs: cacheExpiresAtMs(data, nowMs) };
|
|
2779
2844
|
return data;
|
|
2780
2845
|
}
|
|
2781
2846
|
|
|
2782
2847
|
function resetUsageLimitsCache() {
|
|
2783
|
-
cache = { data: null,
|
|
2848
|
+
cache = { data: null, expiresAtMs: 0 };
|
|
2784
2849
|
}
|
|
2785
2850
|
|
|
2786
2851
|
module.exports = {
|
|
2787
2852
|
getUsageLimits,
|
|
2788
2853
|
normalizePlanLabel,
|
|
2789
2854
|
resetUsageLimitsCache,
|
|
2855
|
+
cacheExpiresAtMs,
|
|
2790
2856
|
runCommand,
|
|
2791
2857
|
extractGeminiOauthClientCredentials,
|
|
2792
2858
|
loadKimiCredentials,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as n,b9 as a}from"./main-DPyNBrFb.js";import{u as p}from"./DialogTitle-BrR7t4i0.js";import{u as c}from"./useScrollLock-D0Td7eQX.js";const x=n.forwardRef(function(e,r){const{render:l,className:u,style:m,id:s,...t}=e,{store:i}=p(),o=c(s);return i.useSyncedValueWithCleanup("descriptionElementId",o),a("p",e,{ref:r,props:[{id:o},t]})});export{x as D};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{c}from"./main-DPyNBrFb.js";const e=[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]],t=c("check",e);export{t as C};
|