tokentracker-cli 0.64.3 → 0.65.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/README.de.md +476 -0
- package/README.ja.md +1 -1
- package/README.ko.md +1 -1
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dashboard/dist/assets/{ActivityHeatmap-BSA4KPkJ.js → ActivityHeatmap-N0MiuKiD.js} +1 -1
- package/dashboard/dist/assets/{Card-BniOg7K8.js → Card-C-UUcB6K.js} +1 -1
- package/dashboard/dist/assets/{ConfirmModal-CxThrcXK.js → ConfirmModal-Ck7qQwqO.js} +1 -1
- package/dashboard/dist/assets/{DashboardPage-DygQTlzO.js → DashboardPage-BdZPqztW.js} +2 -2
- package/dashboard/dist/assets/{DevicePage-BfRGbds7.js → DevicePage-Wr-LIowe.js} +1 -1
- package/dashboard/dist/assets/{DialogTitle-Cd0Nw0hW.js → DialogTitle-BNVIjz-M.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-DxDHmb9-.js → FadeIn-D8XF2ui8.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-2aQXPupt.js → HeaderGithubStar-Dv3PacNv.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-m_W_l5Xa.js → IpCheckPage-DfDXn5N1.js} +1 -1
- package/dashboard/dist/assets/LandingPage-QlEpH3fR.js +4415 -0
- package/dashboard/dist/assets/{LeaderboardAvatar-BmpmwHwD.js → LeaderboardAvatar-CEV0WCgO.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardPage-B6wWBsUW.js → LeaderboardPage-Bte3fdxj.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardProfileModal-CLlbxbZQ.js → LeaderboardProfileModal-DBSiwRmn.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardProfilePage-en0u_bG-.js → LeaderboardProfilePage-DL1xrz2u.js} +1 -1
- package/dashboard/dist/assets/LimitsPage-CU71xh0N.js +3 -0
- package/dashboard/dist/assets/{LocalOnlyNotice-DNHw_KVs.js → LocalOnlyNotice-B0O7VftN.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-D38KpNMd.js → LoginPage-R_gddHQq.js} +1 -1
- package/dashboard/dist/assets/{PopoverPopup-C4Ndfprc.js → PopoverPopup-Dt3vUklH.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-DYF1Iz64.js → ResetPasswordPage-C9OkcYCJ.js} +1 -1
- package/dashboard/dist/assets/{Select-6qFjabMe.js → Select-hvb85wtR.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-B234IuCW.js → SelectItemText-BkSrV5-F.js} +1 -1
- package/dashboard/dist/assets/SettingsPage-D6NSX-54.js +1 -0
- package/dashboard/dist/assets/{SkillsPage-37lQlleG.js → SkillsPage-CjrVT_6o.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-D9ANC2dF.js → WidgetsPage-BA7CYY1i.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-hjymf4DR.js → WrappedPage-BxxXLdaK.js} +1 -1
- package/dashboard/dist/assets/{agent-logos-B-NxiwlS.js → agent-logos-DW_otOmk.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-B_iC7jeO.js → arrow-up-right-BNZE25jN.js} +1 -1
- package/dashboard/dist/assets/{download-DSBqpgG5.js → download-B3dZN1Qo.js} +1 -1
- package/dashboard/dist/assets/{main-CGx8BJ4O.css → main-BHwig9Um.css} +1 -1
- package/dashboard/dist/assets/{main-DEwjlMQP.js → main-XJt43zJ1.js} +21 -20
- package/dashboard/dist/assets/{use-limits-display-prefs-OhLCvWa8.js → use-limits-display-prefs-CILTJ0ib.js} +1 -1
- package/dashboard/dist/assets/{use-native-settings-BOQcPSo9.js → use-native-settings-CMJ5wI4Y.js} +1 -1
- package/dashboard/dist/assets/{use-ordered-list-DKOLCb2L.js → use-ordered-list-Bvt0pvNt.js} +1 -1
- package/dashboard/dist/assets/{use-quality-per-dollar-pref-EzQkuFZg.js → use-quality-per-dollar-pref-D634C_GV.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-DD8zzK6Q.js → use-usage-limits-DU_6J0Y3.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-D1ldsRAh.js → useCurrency-DInjo-qq.js} +1 -1
- package/dashboard/dist/assets/{useScrollLock-eUzPJJ4w.js → useScrollLock-DTTYcDSy.js} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/dashboard/dist/share.html +2 -2
- package/package.json +1 -1
- package/src/commands/init.js +3 -2
- package/src/commands/status.js +26 -17
- package/src/commands/sync.js +672 -98
- package/src/lib/openclaw-session-plugin.js +2 -2
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/rollout.js +602 -178
- package/src/lib/usage-limits.js +49 -1
- package/src/lib/wsl-probe.js +181 -0
- package/dashboard/dist/assets/LandingPage-BkusMtg4.js +0 -4411
- package/dashboard/dist/assets/LimitsPage-CZXzDyPl.js +0 -3
- package/dashboard/dist/assets/SettingsPage-C-2m2LzE.js +0 -1
package/src/lib/usage-limits.js
CHANGED
|
@@ -144,6 +144,34 @@ function formatClaudeRateLimitMessage(retryAfterSec) {
|
|
|
144
144
|
return "Claude API rate limited (429) — retry shortly.";
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
+
// Model-scoped weekly windows (e.g. Fable) arrive only in the newer generic `limits`
|
|
148
|
+
// array (`kind: "weekly_scoped"` + `scope.model.display_name`) — the legacy top-level
|
|
149
|
+
// fields (`seven_day_opus` etc.) stay null for them. Extract them into
|
|
150
|
+
// `weekly_scoped: [{ label, utilization, resets_at }]`. Entries that duplicate a
|
|
151
|
+
// populated legacy field (an "Opus" scoped entry alongside `seven_day_opus`) are
|
|
152
|
+
// dropped so the same window never renders twice.
|
|
153
|
+
function extractClaudeScopedWeekly(body) {
|
|
154
|
+
if (!Array.isArray(body?.limits)) return null;
|
|
155
|
+
const out = [];
|
|
156
|
+
for (const entry of body.limits) {
|
|
157
|
+
if (!entry || typeof entry !== "object" || entry.kind !== "weekly_scoped") continue;
|
|
158
|
+
const model = entry.scope?.model;
|
|
159
|
+
const label = typeof model?.display_name === "string" && model.display_name.trim()
|
|
160
|
+
? model.display_name.trim()
|
|
161
|
+
: typeof model?.id === "string" && model.id.trim() ? model.id.trim() : null;
|
|
162
|
+
if (!label) continue;
|
|
163
|
+
if (body.seven_day_opus && label.toLowerCase() === "opus") continue;
|
|
164
|
+
const utilization = Number(entry.percent);
|
|
165
|
+
if (!Number.isFinite(utilization)) continue;
|
|
166
|
+
out.push({
|
|
167
|
+
label,
|
|
168
|
+
utilization,
|
|
169
|
+
resets_at: typeof entry.resets_at === "string" ? entry.resets_at : null,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
return out.length > 0 ? out : null;
|
|
173
|
+
}
|
|
174
|
+
|
|
147
175
|
async function fetchClaudeUsageLimits(accessToken, { fetchImpl = fetch, maxAttempts = 3 } = {}) {
|
|
148
176
|
const url = "https://api.anthropic.com/api/oauth/usage";
|
|
149
177
|
const headers = {
|
|
@@ -178,6 +206,7 @@ async function fetchClaudeUsageLimits(accessToken, { fetchImpl = fetch, maxAttem
|
|
|
178
206
|
five_hour: body.five_hour ?? null,
|
|
179
207
|
seven_day: body.seven_day ?? null,
|
|
180
208
|
seven_day_opus: body.seven_day_opus ?? null,
|
|
209
|
+
weekly_scoped: extractClaudeScopedWeekly(body),
|
|
181
210
|
extra_usage: body.extra_usage ?? null,
|
|
182
211
|
};
|
|
183
212
|
}
|
|
@@ -1848,7 +1877,21 @@ function isClaudeCacheWindowUsable(window, { nowMs } = {}) {
|
|
|
1848
1877
|
}
|
|
1849
1878
|
|
|
1850
1879
|
function hasClaudeWindow(limits) {
|
|
1851
|
-
return Boolean(
|
|
1880
|
+
return Boolean(
|
|
1881
|
+
limits?.five_hour || limits?.seven_day || limits?.seven_day_opus ||
|
|
1882
|
+
(Array.isArray(limits?.weekly_scoped) && limits.weekly_scoped.length > 0),
|
|
1883
|
+
);
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
// Cached scoped windows are filtered per-entry like the fixed windows: a scoped
|
|
1887
|
+
// window whose reset has passed is stale data, not a usable fallback.
|
|
1888
|
+
function normalizeClaudeCachedScopedWeekly(raw, { nowMs } = {}) {
|
|
1889
|
+
if (!Array.isArray(raw)) return null;
|
|
1890
|
+
const usable = raw.filter(
|
|
1891
|
+
(w) => w && typeof w.label === "string" && Number.isFinite(Number(w.utilization)) &&
|
|
1892
|
+
isClaudeCacheWindowUsable(w, { nowMs }),
|
|
1893
|
+
);
|
|
1894
|
+
return usable.length > 0 ? usable : null;
|
|
1852
1895
|
}
|
|
1853
1896
|
|
|
1854
1897
|
function normalizeClaudeCachedLimits(
|
|
@@ -1870,6 +1913,7 @@ function normalizeClaudeCachedLimits(
|
|
|
1870
1913
|
five_hour: isClaudeCacheWindowUsable(raw?.five_hour, { nowMs }) ? raw.five_hour : null,
|
|
1871
1914
|
seven_day: isClaudeCacheWindowUsable(raw?.seven_day, { nowMs }) ? raw.seven_day : null,
|
|
1872
1915
|
seven_day_opus: isClaudeCacheWindowUsable(raw?.seven_day_opus, { nowMs }) ? raw.seven_day_opus : null,
|
|
1916
|
+
weekly_scoped: normalizeClaudeCachedScopedWeekly(raw?.weekly_scoped, { nowMs }),
|
|
1873
1917
|
extra_usage: raw?.extra_usage ?? null,
|
|
1874
1918
|
stale,
|
|
1875
1919
|
cached_at: raw.cached_at,
|
|
@@ -1909,6 +1953,9 @@ function writeClaudeLimitsCache(limits, { home, nowMs = Date.now() } = {}) {
|
|
|
1909
1953
|
five_hour: limits.five_hour || null,
|
|
1910
1954
|
seven_day: limits.seven_day || null,
|
|
1911
1955
|
seven_day_opus: limits.seven_day_opus || null,
|
|
1956
|
+
weekly_scoped: Array.isArray(limits.weekly_scoped) && limits.weekly_scoped.length > 0
|
|
1957
|
+
? limits.weekly_scoped
|
|
1958
|
+
: null,
|
|
1912
1959
|
extra_usage: limits.extra_usage || null,
|
|
1913
1960
|
cached_at: new Date(nowMs).toISOString(),
|
|
1914
1961
|
},
|
|
@@ -2646,6 +2693,7 @@ async function fetchUsageLimitsUncached({
|
|
|
2646
2693
|
five_hour: claudeResult.value.five_hour,
|
|
2647
2694
|
seven_day: claudeResult.value.seven_day,
|
|
2648
2695
|
seven_day_opus: claudeResult.value.seven_day_opus,
|
|
2696
|
+
weekly_scoped: claudeResult.value.weekly_scoped,
|
|
2649
2697
|
extra_usage: claudeResult.value.extra_usage,
|
|
2650
2698
|
};
|
|
2651
2699
|
writeClaudeLimitsCache(claude, { home, nowMs });
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
const fssync = require("node:fs");
|
|
2
|
+
const path = require("node:path");
|
|
3
|
+
|
|
4
|
+
const DEFAULT_EXEC_OPTS = { timeout: 15000, windowsHide: true, maxBuffer: 1024 * 1024, stdio: ["ignore", "pipe", "ignore"] };
|
|
5
|
+
|
|
6
|
+
let _cachedDistros = null;
|
|
7
|
+
const _cachedWslUsers = new Map();
|
|
8
|
+
|
|
9
|
+
function resetWslProbeCache() {
|
|
10
|
+
_cachedDistros = null;
|
|
11
|
+
_cachedWslUsers.clear();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const WSL_MODES = new Set([
|
|
15
|
+
"wsl-first",
|
|
16
|
+
"native-first",
|
|
17
|
+
"wsl-only",
|
|
18
|
+
"native-only",
|
|
19
|
+
]);
|
|
20
|
+
|
|
21
|
+
function defaultRunWsl(args, { utf16 = false } = {}) {
|
|
22
|
+
const { execFileSync } = require("node:child_process");
|
|
23
|
+
const buf = execFileSync("wsl.exe", args, DEFAULT_EXEC_OPTS);
|
|
24
|
+
return utf16 ? buf.toString("utf16le") : buf.toString("utf8");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function parseWslListVerbose(raw) {
|
|
28
|
+
if (typeof raw !== "string") return [];
|
|
29
|
+
const distros = [];
|
|
30
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
31
|
+
const clean = line.replace(/\0/g, "").replace(/\uFEFF/g, "").trim();
|
|
32
|
+
if (!clean) continue;
|
|
33
|
+
const cells = clean.split(/\s+/);
|
|
34
|
+
let isDefault = false;
|
|
35
|
+
let idx = 0;
|
|
36
|
+
if (cells[0] === "*") {
|
|
37
|
+
isDefault = true;
|
|
38
|
+
idx = 1;
|
|
39
|
+
}
|
|
40
|
+
const name = cells[idx];
|
|
41
|
+
if (!name || name === "NAME") continue;
|
|
42
|
+
const version = parseInt(cells[cells.length - 1], 10);
|
|
43
|
+
distros.push({ name, version: Number.isFinite(version) ? version : null, isDefault });
|
|
44
|
+
}
|
|
45
|
+
return distros;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function probeWslDistros(deps = {}) {
|
|
49
|
+
const hasDeps = Object.keys(deps).length > 0;
|
|
50
|
+
if (!hasDeps && _cachedDistros) return _cachedDistros;
|
|
51
|
+
const runWsl = deps.runWsl || defaultRunWsl;
|
|
52
|
+
let raw;
|
|
53
|
+
try {
|
|
54
|
+
raw = runWsl(["-l", "-v"], { utf16: true });
|
|
55
|
+
} catch (_e) {
|
|
56
|
+
if (!hasDeps) _cachedDistros = [];
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
const distros = parseWslListVerbose(raw);
|
|
60
|
+
const sorted = distros.sort((a, b) => (b.isDefault ? 1 : 0) - (a.isDefault ? 1 : 0));
|
|
61
|
+
if (!hasDeps) _cachedDistros = sorted;
|
|
62
|
+
return sorted;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function normalizeWslMode(value) {
|
|
66
|
+
return String(value ?? "")
|
|
67
|
+
.trim()
|
|
68
|
+
.toLowerCase()
|
|
69
|
+
.replaceAll("_", "-");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function getWslMode(env = process.env) {
|
|
73
|
+
const raw = normalizeWslMode(env.TOKENTRACKER_WSL_MODE);
|
|
74
|
+
return WSL_MODES.has(raw) ? raw : "wsl-first";
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function isInvalidWslMode(env = process.env) {
|
|
78
|
+
const value = env.TOKENTRACKER_WSL_MODE;
|
|
79
|
+
if (value == null || String(value).trim() === "") return false;
|
|
80
|
+
return !WSL_MODES.has(normalizeWslMode(value));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function shouldProbeWsl(env = process.env) {
|
|
84
|
+
return getWslMode(env) !== "native-only";
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function shouldProbeNative(env = process.env) {
|
|
88
|
+
return getWslMode(env) !== "wsl-only";
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function pickWin32Path({
|
|
92
|
+
wslValue,
|
|
93
|
+
nativeValue,
|
|
94
|
+
env = process.env,
|
|
95
|
+
platform = process.platform,
|
|
96
|
+
}) {
|
|
97
|
+
if (platform !== "win32") return null;
|
|
98
|
+
|
|
99
|
+
const mode = getWslMode(env);
|
|
100
|
+
|
|
101
|
+
if (mode === "wsl-only") return wslValue ?? null;
|
|
102
|
+
if (mode === "native-only") return nativeValue ?? null;
|
|
103
|
+
if (mode === "native-first") return nativeValue ?? wslValue ?? null;
|
|
104
|
+
|
|
105
|
+
return wslValue ?? nativeValue ?? null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function lookupWslUser(distroName, runWsl, useCache) {
|
|
109
|
+
if (useCache && _cachedWslUsers.has(distroName)) {
|
|
110
|
+
return _cachedWslUsers.get(distroName);
|
|
111
|
+
}
|
|
112
|
+
let user = "";
|
|
113
|
+
try {
|
|
114
|
+
user = String(runWsl(["-d", distroName, "-e", "whoami"], { utf16: false }) || "").trim();
|
|
115
|
+
} catch (_e) { }
|
|
116
|
+
if (useCache) _cachedWslUsers.set(distroName, user);
|
|
117
|
+
return user;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function discoverWslHome(providerDir, deps = {}) {
|
|
121
|
+
if (!shouldProbeWsl(deps.env)) return null;
|
|
122
|
+
|
|
123
|
+
const runWsl = deps.runWsl || defaultRunWsl;
|
|
124
|
+
const existsSync = deps.existsSync || fssync.existsSync;
|
|
125
|
+
const distros = deps.runWsl ? probeWslDistros({ runWsl: deps.runWsl }) : probeWslDistros();
|
|
126
|
+
const useCache = !deps.runWsl;
|
|
127
|
+
for (const distro of distros) {
|
|
128
|
+
const user = lookupWslUser(distro.name, runWsl, useCache);
|
|
129
|
+
if (!user) continue;
|
|
130
|
+
const roots = distro.version === 1
|
|
131
|
+
? ["\\\\wsl.localhost\\", "\\\\wsl$\\"]
|
|
132
|
+
: ["\\\\wsl$\\", "\\\\wsl.localhost\\"];
|
|
133
|
+
for (const root of roots) {
|
|
134
|
+
const candidate = `${root}${distro.name}\\home\\${user}\\${providerDir}`;
|
|
135
|
+
try {
|
|
136
|
+
if (existsSync(candidate)) return candidate;
|
|
137
|
+
} catch (_e) { }
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function isUncPath(p) {
|
|
144
|
+
return typeof p === "string" && (p.startsWith("\\\\") || p.startsWith("//"));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function snapshotSqliteDb(dbPath) {
|
|
148
|
+
const tmpRoot = fssync.mkdtempSync(
|
|
149
|
+
path.join(require("node:os").tmpdir(), "tokentracker-wsl-snap-"),
|
|
150
|
+
);
|
|
151
|
+
const target = path.join(tmpRoot, path.basename(dbPath));
|
|
152
|
+
fssync.copyFileSync(dbPath, target);
|
|
153
|
+
for (const suffix of ["-wal", "-shm", "-journal"]) {
|
|
154
|
+
const src = dbPath + suffix;
|
|
155
|
+
try {
|
|
156
|
+
if (fssync.existsSync(src)) fssync.copyFileSync(src, target + suffix);
|
|
157
|
+
} catch (_e) { }
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
path: target,
|
|
161
|
+
cleanup() {
|
|
162
|
+
try { fssync.rmSync(tmpRoot, { recursive: true, force: true }); } catch (_e) { }
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
module.exports = {
|
|
168
|
+
defaultRunWsl,
|
|
169
|
+
parseWslListVerbose,
|
|
170
|
+
probeWslDistros,
|
|
171
|
+
resetWslProbeCache,
|
|
172
|
+
discoverWslHome,
|
|
173
|
+
isUncPath,
|
|
174
|
+
snapshotSqliteDb,
|
|
175
|
+
getWslMode,
|
|
176
|
+
isInvalidWslMode,
|
|
177
|
+
shouldProbeWsl,
|
|
178
|
+
shouldProbeNative,
|
|
179
|
+
pickWin32Path,
|
|
180
|
+
normalizeWslMode,
|
|
181
|
+
};
|