tokentracker-cli 0.74.0 → 0.74.2
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-DkoSqxrd.js → ActivityHeatmap-DGqoFvkZ.js} +1 -1
- package/dashboard/dist/assets/{Card-DleS-r54.js → Card-CKxmfosU.js} +1 -1
- package/dashboard/dist/assets/{CommunityStatsModal-DV41-F9D.js → CommunityStatsModal-C6h6BKVI.js} +1 -1
- package/dashboard/dist/assets/{ConfirmModal-Dgp07Uka.js → ConfirmModal-wqW-7Vs_.js} +1 -1
- package/dashboard/dist/assets/{DashboardPage-EKy2BzUx.js → DashboardPage-10hyXbuM.js} +1 -1
- package/dashboard/dist/assets/{DevicePage-eJInQ2UX.js → DevicePage-DosHEknI.js} +1 -1
- package/dashboard/dist/assets/{DialogClose-DEfesqFq.js → DialogClose-CRzzOKwV.js} +1 -1
- package/dashboard/dist/assets/DialogDescription-DuiVFMP6.js +1 -0
- package/dashboard/dist/assets/{DialogTitle-v1JrTvr4.js → DialogTitle-CgVdc49w.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-hqIBbKaJ.js → FadeIn-CVkirQtu.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-C4zauuP2.js → HeaderGithubStar-DHW75YEC.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-CiRcccks.js → IpCheckPage-CMOjeIuH.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-CbOHycnd.js → LandingPage-Cca1heHL.js} +97 -37
- package/dashboard/dist/assets/{LeaderboardAvatar-CQL1iOTS.js → LeaderboardAvatar-CVMcytB1.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardPage-BuEp247d.js → LeaderboardPage-BEfsEAZY.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardProfileModal-DT8hrPkI.js → LeaderboardProfileModal-5LuPOMoM.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardProfilePage-B-M0AXMb.js → LeaderboardProfilePage-DUQOF16T.js} +1 -1
- package/dashboard/dist/assets/{LimitsPage-Df2es3Xs.js → LimitsPage-C6cPHrnU.js} +4 -4
- package/dashboard/dist/assets/{LocalOnlyNotice-DJIfKTKo.js → LocalOnlyNotice-B2SjP1L6.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-DtaaIy_8.js → LoginPage-D0vP9cX4.js} +1 -1
- package/dashboard/dist/assets/{PopoverPopup-CqWH6aB9.js → PopoverPopup-AtVz9OM2.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-DCH5GU1c.js → ResetPasswordPage-6eUhDnAl.js} +1 -1
- package/dashboard/dist/assets/{Select-BwyliVKJ.js → Select-Dt-nyHat.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-B5_DKKEv.js → SelectItemText-CFxs6Ipf.js} +1 -1
- package/dashboard/dist/assets/{SettingsPage-BoAFdOUa.js → SettingsPage-Ccv_PRTi.js} +1 -1
- package/dashboard/dist/assets/{SkillsPage-BVdre-kF.js → SkillsPage-Ckmq2qWZ.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-CtjV405P.js → WidgetsPage-CuMpK7JS.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-DGmPdtHt.js → WrappedPage-C3sjJiS5.js} +1 -1
- package/dashboard/dist/assets/{agent-logos-DqevdWvx.js → agent-logos-CORiXh7-.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-DA5Pybq1.js → arrow-up-right-D-CX3rHC.js} +1 -1
- package/dashboard/dist/assets/check-frbadzCO.js +1 -0
- package/dashboard/dist/assets/{copy-CsQRTd89.js → copy-BbbIDe0D.js} +1 -1
- package/dashboard/dist/assets/{download-TEHkAKT0.js → download-CnLKP9Xi.js} +1 -1
- package/dashboard/dist/assets/{format-tokens-CtFO_jkz.js → format-tokens-Q8buFewN.js} +1 -1
- package/dashboard/dist/assets/{info-Dxn3fiaX.js → info-Bg3JHknV.js} +1 -1
- package/dashboard/dist/assets/main--nUrKVF3.css +1 -0
- package/dashboard/dist/assets/{main-Dxi4sSXf.js → main-BZBZhXIl.js} +20 -17
- package/dashboard/dist/assets/{use-community-stats-BLNxr-bQ.js → use-community-stats-BBUKlTpE.js} +1 -1
- package/dashboard/dist/assets/{use-limits-display-prefs-CC7gb303.js → use-limits-display-prefs-Dnl2W8Rt.js} +1 -1
- package/dashboard/dist/assets/{use-native-settings-SYtjLlxZ.js → use-native-settings-DlQEd871.js} +1 -1
- package/dashboard/dist/assets/{use-ordered-list-_O8onJV7.js → use-ordered-list-4WtaZhJf.js} +1 -1
- package/dashboard/dist/assets/{use-quality-per-dollar-pref-tuspH_6n.js → use-quality-per-dollar-pref-CS0EFKxa.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-whCVzQbf.js → use-usage-limits-CJ25xM-V.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-BiDjYxfm.js → useCurrency-CaevR9XR.js} +1 -1
- package/dashboard/dist/assets/{useScrollLock-DK-ndHcw.js → useScrollLock-BLGPKPXr.js} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/dashboard/dist/share.html +2 -2
- package/package.json +2 -2
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/usage-limits.js +24 -0
- package/src/lib/zcode-limits.js +251 -15
- package/dashboard/dist/assets/DialogDescription-D6txiEc4.js +0 -1
- package/dashboard/dist/assets/check-BWzAT1oX.js +0 -1
- package/dashboard/dist/assets/main-DXM2UPde.css +0 -1
package/src/lib/usage-limits.js
CHANGED
|
@@ -2929,6 +2929,12 @@ async function fetchUsageLimitsUncached({
|
|
|
2929
2929
|
seven_day_opus: claudeResult.value.seven_day_opus,
|
|
2930
2930
|
weekly_scoped: claudeResult.value.weekly_scoped,
|
|
2931
2931
|
extra_usage: claudeResult.value.extra_usage,
|
|
2932
|
+
// A live read is current as of now. `stale`/`cached_at` are always present so
|
|
2933
|
+
// the client can render an "Updated Xm ago" age without guessing whether the
|
|
2934
|
+
// response was live or served from the disk-cache fallback (the cached paths
|
|
2935
|
+
// above already carry them). Mirrors the Codex live-success block below.
|
|
2936
|
+
stale: false,
|
|
2937
|
+
cached_at: new Date(nowMs).toISOString(),
|
|
2932
2938
|
};
|
|
2933
2939
|
writeClaudeLimitsCache(claude, { home, nowMs });
|
|
2934
2940
|
clearClaudeRateLimitCooldown({ home });
|
|
@@ -2954,6 +2960,19 @@ async function fetchUsageLimitsUncached({
|
|
|
2954
2960
|
}
|
|
2955
2961
|
}
|
|
2956
2962
|
|
|
2963
|
+
// Expose the active 429 cool-down expiry (if any) on the Claude object so the
|
|
2964
|
+
// client can (a) show when the next refresh is due next to stale bars and
|
|
2965
|
+
// (b) schedule a one-shot refresh the instant the cool-down lifts instead of
|
|
2966
|
+
// waiting out the poll interval. Re-read here (not the pre-fetch value) so a
|
|
2967
|
+
// cool-down just armed by this cycle's 429 is included; a successful read above
|
|
2968
|
+
// cleared the file, so this is null in the happy path.
|
|
2969
|
+
if (claude.configured) {
|
|
2970
|
+
const claudeCooldownMs = readClaudeRateLimitRetryAtMs({ home, nowMs });
|
|
2971
|
+
if (claudeCooldownMs) {
|
|
2972
|
+
claude.retry_at = new Date(claudeCooldownMs).toISOString();
|
|
2973
|
+
}
|
|
2974
|
+
}
|
|
2975
|
+
|
|
2957
2976
|
let codex;
|
|
2958
2977
|
if (!codexToken) {
|
|
2959
2978
|
codex = { configured: false };
|
|
@@ -2986,6 +3005,11 @@ async function fetchUsageLimitsUncached({
|
|
|
2986
3005
|
spark_primary_window: codexResult.value.spark_primary_window,
|
|
2987
3006
|
spark_secondary_window: codexResult.value.spark_secondary_window,
|
|
2988
3007
|
reset_credits: codexResult.value.reset_credits,
|
|
3008
|
+
// Live read is current as of now; the stale fallback path above serves the
|
|
3009
|
+
// disk cache with `stale: true`. Always emitting both lets the client show a
|
|
3010
|
+
// data-age label uniformly (see the Claude live-success block).
|
|
3011
|
+
stale: false,
|
|
3012
|
+
cached_at: new Date(nowMs).toISOString(),
|
|
2989
3013
|
};
|
|
2990
3014
|
writeCodexLimitsCache(codex, { home, nowMs });
|
|
2991
3015
|
}
|
package/src/lib/zcode-limits.js
CHANGED
|
@@ -4,6 +4,9 @@ const os = require("node:os");
|
|
|
4
4
|
const path = require("node:path");
|
|
5
5
|
|
|
6
6
|
const DEFAULT_BILLING_BASE_URL = "https://zcode.z.ai/api/v1/zcode-plan";
|
|
7
|
+
const DEFAULT_ZAI_MONITOR_BASE_URL = "https://api.z.ai";
|
|
8
|
+
const DEFAULT_BIGMODEL_MONITOR_BASE_URL = "https://bigmodel.cn";
|
|
9
|
+
const ZCODE_MONITOR_QUOTA_PATH = "/api/monitor/usage/quota/limit";
|
|
7
10
|
const DEFAULT_ZCODE_APP_VERSION = "3.2.5";
|
|
8
11
|
const DEFAULT_ZCODE_LOG_FALLBACK_MAX_AGE_MS = 6 * 60 * 60 * 1000;
|
|
9
12
|
|
|
@@ -92,6 +95,28 @@ function loadZcodeProviderAvailability({ home, env } = {}) {
|
|
|
92
95
|
}
|
|
93
96
|
}
|
|
94
97
|
|
|
98
|
+
function loadZcodeSelectedPlanProviderKeys({ home, env } = {}) {
|
|
99
|
+
const zcodeHome = resolveZcodeHome({ home, env });
|
|
100
|
+
const settingPath = path.join(zcodeHome, "v2", "setting.json");
|
|
101
|
+
if (!fs.existsSync(settingPath)) return [];
|
|
102
|
+
try {
|
|
103
|
+
const setting = JSON.parse(fs.readFileSync(settingPath, "utf8"));
|
|
104
|
+
const selected = setting?.modelProviderFamilySelectedKeys;
|
|
105
|
+
if (!selected || typeof selected !== "object") return [];
|
|
106
|
+
const domain = typeof setting?.providerFamilyDomain === "string" ? setting.providerFamilyDomain : "";
|
|
107
|
+
const domains = [domain, ...Object.keys(selected)].filter(Boolean);
|
|
108
|
+
const out = [];
|
|
109
|
+
for (const key of domains) {
|
|
110
|
+
const raw = typeof selected[key] === "string" ? selected[key].trim() : "";
|
|
111
|
+
const match = raw.match(/builtin:(?:bigmodel|zai)-(?:start|coding)-plan/);
|
|
112
|
+
if (match && !out.includes(match[0])) out.push(match[0]);
|
|
113
|
+
}
|
|
114
|
+
return out;
|
|
115
|
+
} catch (_error) {
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
95
120
|
function resolveZcodeCredentialsPath({ home, env } = {}) {
|
|
96
121
|
return path.join(resolveZcodeHome({ home, env }), "v2", "credentials.json");
|
|
97
122
|
}
|
|
@@ -165,15 +190,62 @@ function isZcodeBuiltinPlanProvider(providerKey) {
|
|
|
165
190
|
return /^builtin:(bigmodel|zai)-(start|coding)-plan$/.test(providerKey);
|
|
166
191
|
}
|
|
167
192
|
|
|
193
|
+
function isZcodeCodingPlanProvider(providerKey) {
|
|
194
|
+
return /^builtin:(bigmodel|zai)-coding-plan$/.test(providerKey);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function isZcodeStartPlanProvider(providerKey) {
|
|
198
|
+
return /^builtin:(bigmodel|zai)-start-plan$/.test(providerKey);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function readEnvString(env, names) {
|
|
202
|
+
for (const name of names) {
|
|
203
|
+
const value = typeof env?.[name] === "string" ? env[name].trim() : "";
|
|
204
|
+
if (value) return value;
|
|
205
|
+
}
|
|
206
|
+
return "";
|
|
207
|
+
}
|
|
208
|
+
|
|
168
209
|
function resolveZcodeProviderBillingBaseUrl(providerKey, provider, env = process.env) {
|
|
169
210
|
const explicit = resolveZcodeBillingBaseUrl(env);
|
|
170
211
|
if (explicit !== DEFAULT_BILLING_BASE_URL) return explicit;
|
|
171
|
-
if (
|
|
212
|
+
if (isZcodeStartPlanProvider(providerKey)) return DEFAULT_BILLING_BASE_URL;
|
|
172
213
|
const baseUrl = typeof provider?.options?.baseURL === "string" ? provider.options.baseURL.trim() : "";
|
|
173
214
|
if (/\/zcode-plan\/anthropic\/?$/i.test(baseUrl)) return baseUrl.replace(/\/anthropic\/?$/i, "");
|
|
174
215
|
return null;
|
|
175
216
|
}
|
|
176
217
|
|
|
218
|
+
function normalizeUrlOrigin(value) {
|
|
219
|
+
try {
|
|
220
|
+
const url = new URL(value);
|
|
221
|
+
return url.origin.replace(/\/$/, "");
|
|
222
|
+
} catch (_error) {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function resolveZcodeProviderQuotaUrl(providerKey, provider, env = process.env) {
|
|
228
|
+
if (!isZcodeCodingPlanProvider(providerKey)) return null;
|
|
229
|
+
const explicit = readEnvString(env, ["TOKENTRACKER_ZCODE_MONITOR_QUOTA_URL"]);
|
|
230
|
+
if (explicit) return explicit;
|
|
231
|
+
|
|
232
|
+
const baseUrl = typeof provider?.options?.baseURL === "string" ? provider.options.baseURL.trim() : "";
|
|
233
|
+
const baseOrigin = normalizeUrlOrigin(baseUrl);
|
|
234
|
+
const isZai = providerKey === "builtin:zai-coding-plan";
|
|
235
|
+
const origin = isZai
|
|
236
|
+
? readEnvString(env, [
|
|
237
|
+
"TOKENTRACKER_ZCODE_ZAI_MONITOR_BASE_URL",
|
|
238
|
+
"ZAI_BUSINESS_BASE_URL",
|
|
239
|
+
"ZAI_PRODUCTION_BUSINESS_BASE_URL",
|
|
240
|
+
]) || (baseOrigin && /(^|\.)api\.z\.ai$/i.test(new URL(baseOrigin).hostname) ? baseOrigin : DEFAULT_ZAI_MONITOR_BASE_URL)
|
|
241
|
+
: readEnvString(env, [
|
|
242
|
+
"TOKENTRACKER_ZCODE_BIGMODEL_MONITOR_BASE_URL",
|
|
243
|
+
"BIGMODEL_API_BASE_URL",
|
|
244
|
+
"BIGMODEL_PRODUCTION_API_BASE_URL",
|
|
245
|
+
]) || DEFAULT_BIGMODEL_MONITOR_BASE_URL;
|
|
246
|
+
return `${origin.replace(/\/$/, "")}${ZCODE_MONITOR_QUOTA_PATH}`;
|
|
247
|
+
}
|
|
248
|
+
|
|
177
249
|
function loadZcodeAuthCandidates({ home, env } = {}) {
|
|
178
250
|
const zcodeHome = resolveZcodeHome({ home, env });
|
|
179
251
|
const configPath = path.join(zcodeHome, "v2", "config.json");
|
|
@@ -190,8 +262,14 @@ function loadZcodeAuthCandidates({ home, env } = {}) {
|
|
|
190
262
|
];
|
|
191
263
|
const availability = loadZcodeProviderAvailability({ home, env });
|
|
192
264
|
const hasAvailability = Object.keys(availability).length > 0;
|
|
265
|
+
const selectedCandidates = loadZcodeSelectedPlanProviderKeys({ home, env })
|
|
266
|
+
.filter((key) => defaultCandidates.includes(key));
|
|
193
267
|
const availableCandidates = defaultCandidates.filter((key) => availability?.[key]?.status === "available");
|
|
194
|
-
const candidates = [
|
|
268
|
+
const candidates = [
|
|
269
|
+
...selectedCandidates,
|
|
270
|
+
...availableCandidates,
|
|
271
|
+
...defaultCandidates,
|
|
272
|
+
].filter((key, index, all) => all.indexOf(key) === index);
|
|
195
273
|
const auths = [];
|
|
196
274
|
for (const key of candidates) {
|
|
197
275
|
const provider = providers[key];
|
|
@@ -200,6 +278,7 @@ function loadZcodeAuthCandidates({ home, env } = {}) {
|
|
|
200
278
|
if (hasAvailability && availability?.[key]?.status && availability[key].status !== "available") continue;
|
|
201
279
|
const apiKey = typeof provider?.options?.apiKey === "string" ? provider.options.apiKey.trim() : "";
|
|
202
280
|
const billingBaseUrl = resolveZcodeProviderBillingBaseUrl(key, provider, env);
|
|
281
|
+
const quotaUrl = resolveZcodeProviderQuotaUrl(key, provider, env);
|
|
203
282
|
const credentialApiKey = resolveZcodeCredentialAuth(key, { home, env });
|
|
204
283
|
const authEntries = [
|
|
205
284
|
credentialApiKey ? { apiKey: credentialApiKey, authSource: "credential:zcodejwttoken" } : null,
|
|
@@ -207,14 +286,16 @@ function loadZcodeAuthCandidates({ home, env } = {}) {
|
|
|
207
286
|
].filter(Boolean);
|
|
208
287
|
const seenKeys = new Set();
|
|
209
288
|
for (const entry of authEntries) {
|
|
210
|
-
if (!billingBaseUrl || seenKeys.has(entry.apiKey)) continue;
|
|
289
|
+
if ((!billingBaseUrl && !quotaUrl) || seenKeys.has(entry.apiKey)) continue;
|
|
211
290
|
seenKeys.add(entry.apiKey);
|
|
212
291
|
auths.push({
|
|
213
292
|
apiKey: entry.apiKey,
|
|
214
293
|
auth_source: entry.authSource,
|
|
215
294
|
providerKey: key,
|
|
295
|
+
planKind: isZcodeCodingPlanProvider(key) ? "coding-plan" : "start-plan",
|
|
216
296
|
baseUrl: provider?.options?.baseURL || null,
|
|
217
297
|
billingBaseUrl,
|
|
298
|
+
quotaUrl,
|
|
218
299
|
availability: availability?.[key]?.status || null,
|
|
219
300
|
});
|
|
220
301
|
}
|
|
@@ -297,11 +378,13 @@ function normalizeZcodeBalanceResponse(body) {
|
|
|
297
378
|
if (!balances.length) {
|
|
298
379
|
return {
|
|
299
380
|
server_time: zcodeValNumber(data.server_time),
|
|
381
|
+
plan_kind: "start-plan",
|
|
300
382
|
plan_id: null,
|
|
301
383
|
plan_label: null,
|
|
302
384
|
buckets: [],
|
|
303
385
|
primary_window: null,
|
|
304
386
|
secondary_window: null,
|
|
387
|
+
tertiary_window: null,
|
|
305
388
|
};
|
|
306
389
|
}
|
|
307
390
|
|
|
@@ -336,11 +419,13 @@ function normalizeZcodeBalanceResponse(body) {
|
|
|
336
419
|
const planId = typeof balances[0]?.plan_id === "string" ? balances[0].plan_id : null;
|
|
337
420
|
return {
|
|
338
421
|
server_time: serverTime,
|
|
422
|
+
plan_kind: "start-plan",
|
|
339
423
|
plan_id: planId,
|
|
340
424
|
plan_label: deriveZcodePlanLabel(planId),
|
|
341
425
|
buckets: sorted,
|
|
342
426
|
primary_window: sorted[0]?.window || null,
|
|
343
427
|
secondary_window: sorted[1]?.window || null,
|
|
428
|
+
tertiary_window: sorted[2]?.window || null,
|
|
344
429
|
};
|
|
345
430
|
}
|
|
346
431
|
|
|
@@ -380,6 +465,136 @@ async function fetchZcodeBilling(apiKey, { fetchImpl = fetch, baseUrl, env, home
|
|
|
380
465
|
return res.json();
|
|
381
466
|
}
|
|
382
467
|
|
|
468
|
+
async function fetchZcodeCodingPlanQuota(apiKey, { fetchImpl = fetch, quotaUrl } = {}) {
|
|
469
|
+
const res = await fetchImpl(quotaUrl, {
|
|
470
|
+
method: "GET",
|
|
471
|
+
headers: {
|
|
472
|
+
authorization: apiKey,
|
|
473
|
+
Accept: "application/json",
|
|
474
|
+
},
|
|
475
|
+
});
|
|
476
|
+
if (res.status === 401 || res.status === 403) {
|
|
477
|
+
throw new Error("Not authenticated with ZCode coding plan. Run `zcode` in Terminal to log in.");
|
|
478
|
+
}
|
|
479
|
+
let body = null;
|
|
480
|
+
try {
|
|
481
|
+
body = await res.json();
|
|
482
|
+
} catch (_error) {
|
|
483
|
+
body = null;
|
|
484
|
+
}
|
|
485
|
+
if (!res.ok) {
|
|
486
|
+
const code = body?.code != null ? ` code=${body.code}` : "";
|
|
487
|
+
const msg = body?.msg || body?.message ? ` msg=${body.msg || body.message}` : "";
|
|
488
|
+
throw new Error(`ZCode coding plan API returned ${res.status}${code}${msg}`);
|
|
489
|
+
}
|
|
490
|
+
return body;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
function quotaTimestampToIso(value) {
|
|
494
|
+
if (typeof value === "string" && value.trim()) {
|
|
495
|
+
const parsed = new Date(value.trim());
|
|
496
|
+
return Number.isNaN(parsed.getTime()) ? null : parsed.toISOString();
|
|
497
|
+
}
|
|
498
|
+
const n = zcodeValNumber(value);
|
|
499
|
+
if (n == null || n <= 0) return null;
|
|
500
|
+
return new Date(n > 10_000_000_000 ? n : n * 1000).toISOString();
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
function findZcodeQuotaLimit(limits, type, unit, number = null) {
|
|
504
|
+
if (!Array.isArray(limits)) return null;
|
|
505
|
+
return (
|
|
506
|
+
limits.find((limit) => {
|
|
507
|
+
if (!limit || typeof limit !== "object") return false;
|
|
508
|
+
if (limit.type !== type) return false;
|
|
509
|
+
if (zcodeValNumber(limit.unit) !== unit) return false;
|
|
510
|
+
if (number == null) return true;
|
|
511
|
+
return zcodeValNumber(limit.number) === number;
|
|
512
|
+
}) || null
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// ZCode 3.3.x coding-plan quota rows expose `percentage` as already-used %
|
|
517
|
+
// (0–100). `unit`/`number` identify the window (5h / weekly / tools), not a
|
|
518
|
+
// token total — deriving used% from usage/number turns an unused TIME_LIMIT
|
|
519
|
+
// (usage=100, number=1, percentage=0) into a false 100% bar.
|
|
520
|
+
function normalizeZcodeQuotaLimit(limit, { showName } = {}) {
|
|
521
|
+
if (!limit || typeof limit !== "object") return null;
|
|
522
|
+
const total = zcodeValNumber(limit.number);
|
|
523
|
+
const used = zcodeValNumber(limit.usage) ?? zcodeValNumber(limit.currentValue);
|
|
524
|
+
const remaining = zcodeValNumber(limit.remaining);
|
|
525
|
+
const rawPercentage = zcodeValNumber(limit.percentage);
|
|
526
|
+
let usedPercent = null;
|
|
527
|
+
if (rawPercentage != null) {
|
|
528
|
+
usedPercent = rawPercentage;
|
|
529
|
+
} else if (total != null && total > 0 && used != null) {
|
|
530
|
+
usedPercent = (used / total) * 100;
|
|
531
|
+
} else if (total != null && total > 0 && remaining != null && remaining <= total) {
|
|
532
|
+
usedPercent = ((total - remaining) / total) * 100;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
const detail = Array.isArray(limit.usageDetails) ? limit.usageDetails.find((item) => item && typeof item === "object") : null;
|
|
536
|
+
const resolvedShowName =
|
|
537
|
+
(typeof showName === "string" && showName.trim())
|
|
538
|
+
|| (typeof detail?.displayName === "string" && detail.displayName.trim())
|
|
539
|
+
|| (typeof detail?.modelCode === "string" && detail.modelCode.trim())
|
|
540
|
+
|| (typeof limit.type === "string" && limit.type.trim())
|
|
541
|
+
|| "Coding plan";
|
|
542
|
+
|
|
543
|
+
return {
|
|
544
|
+
show_name: resolvedShowName,
|
|
545
|
+
entitlement_id: typeof limit.type === "string" ? limit.type : "",
|
|
546
|
+
total_units: rawPercentage != null ? null : total,
|
|
547
|
+
used_units: rawPercentage != null ? null : used,
|
|
548
|
+
remaining_units: rawPercentage != null ? null : remaining,
|
|
549
|
+
window: buildWindow({ usedPercent, resetAt: quotaTimestampToIso(limit.nextResetTime) }),
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
function normalizeZcodeCodingPlanQuotaResponse(body) {
|
|
554
|
+
const code = typeof body?.code === "number" ? body.code : null;
|
|
555
|
+
if (code !== null && code !== 0 && code !== 200) {
|
|
556
|
+
throw new Error(`ZCode coding plan API error: code=${code} msg=${body?.msg || body?.message || "unknown"}`);
|
|
557
|
+
}
|
|
558
|
+
if (body?.success === false) {
|
|
559
|
+
throw new Error(`ZCode coding plan API error: msg=${body?.msg || body?.message || "unknown"}`);
|
|
560
|
+
}
|
|
561
|
+
const data = body?.data;
|
|
562
|
+
if (!data || typeof data !== "object") {
|
|
563
|
+
throw new Error("Could not parse ZCode coding plan quota: missing data");
|
|
564
|
+
}
|
|
565
|
+
const limits = Array.isArray(data.limits) ? data.limits : [];
|
|
566
|
+
// Match ZCode's own sidebar: TOKENS_LIMIT(unit=3,number=5)=5h,
|
|
567
|
+
// TOKENS_LIMIT(unit=6)=weekly, TIME_LIMIT(unit=5,number=1)=tool calls.
|
|
568
|
+
const fiveHourLimit = findZcodeQuotaLimit(limits, "TOKENS_LIMIT", 3, 5);
|
|
569
|
+
const weeklyLimit = findZcodeQuotaLimit(limits, "TOKENS_LIMIT", 6);
|
|
570
|
+
const toolsLimit = findZcodeQuotaLimit(limits, "TIME_LIMIT", 5, 1);
|
|
571
|
+
const hasNamedWindows = Boolean(fiveHourLimit || weeklyLimit || toolsLimit);
|
|
572
|
+
|
|
573
|
+
let buckets;
|
|
574
|
+
if (hasNamedWindows) {
|
|
575
|
+
buckets = [
|
|
576
|
+
fiveHourLimit ? normalizeZcodeQuotaLimit(fiveHourLimit, { showName: "5h" }) : null,
|
|
577
|
+
weeklyLimit ? normalizeZcodeQuotaLimit(weeklyLimit, { showName: "Weekly" }) : null,
|
|
578
|
+
toolsLimit ? normalizeZcodeQuotaLimit(toolsLimit, { showName: "Tools" }) : null,
|
|
579
|
+
].filter((bucket) => bucket?.window);
|
|
580
|
+
} else {
|
|
581
|
+
buckets = limits.map((limit) => normalizeZcodeQuotaLimit(limit)).filter((bucket) => bucket?.window);
|
|
582
|
+
buckets.sort((a, b) => (b.total_units || 0) - (a.total_units || 0));
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
const level = typeof data.level === "string" && data.level.trim() ? data.level.trim() : null;
|
|
586
|
+
return {
|
|
587
|
+
server_time: null,
|
|
588
|
+
plan_kind: "coding-plan",
|
|
589
|
+
plan_id: level,
|
|
590
|
+
plan_label: level ? deriveZcodePlanLabel(level) || level : "Coding",
|
|
591
|
+
buckets,
|
|
592
|
+
primary_window: buckets[0]?.window || null,
|
|
593
|
+
secondary_window: buckets[1]?.window || null,
|
|
594
|
+
tertiary_window: buckets[2]?.window || null,
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
|
|
383
598
|
function parseZcodeLogTimestamp(line) {
|
|
384
599
|
const match = line.match(/^\[(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})\.(\d{3})\]/);
|
|
385
600
|
if (!match) return null;
|
|
@@ -433,7 +648,13 @@ function extractZcodeBalanceLogRecord(line) {
|
|
|
433
648
|
}
|
|
434
649
|
}
|
|
435
650
|
|
|
436
|
-
function loadLatestZcodeBalanceFromLogs({
|
|
651
|
+
function loadLatestZcodeBalanceFromLogs({
|
|
652
|
+
home,
|
|
653
|
+
env = process.env,
|
|
654
|
+
providerKeys = [],
|
|
655
|
+
nowMs = Date.now(),
|
|
656
|
+
requireProviderMatch = false,
|
|
657
|
+
} = {}) {
|
|
437
658
|
if (env.TOKENTRACKER_ZCODE_DISABLE_LOG_FALLBACK === "1") return null;
|
|
438
659
|
const maxAgeMs = parsePositiveInteger(env.TOKENTRACKER_ZCODE_LOG_MAX_AGE_MS, DEFAULT_ZCODE_LOG_FALLBACK_MAX_AGE_MS);
|
|
439
660
|
const preferredProviders = new Set(providerKeys.filter(Boolean));
|
|
@@ -459,7 +680,8 @@ function loadLatestZcodeBalanceFromLogs({ home, env = process.env, providerKeys
|
|
|
459
680
|
}
|
|
460
681
|
}
|
|
461
682
|
}
|
|
462
|
-
|
|
683
|
+
if (preferred) return preferred;
|
|
684
|
+
return requireProviderMatch && preferredProviders.size > 0 ? null : fallback;
|
|
463
685
|
}
|
|
464
686
|
|
|
465
687
|
function zcodeLogFallbackResult(logRecord, errors = []) {
|
|
@@ -489,17 +711,26 @@ async function fetchZcodeLimits({ home, env, fetchImpl = fetch, nowMs = Date.now
|
|
|
489
711
|
let emptySuccess = null;
|
|
490
712
|
for (const auth of authCandidates) {
|
|
491
713
|
try {
|
|
492
|
-
const body =
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
714
|
+
const body = auth.planKind === "coding-plan"
|
|
715
|
+
? await fetchZcodeCodingPlanQuota(auth.apiKey, {
|
|
716
|
+
fetchImpl,
|
|
717
|
+
quotaUrl: auth.quotaUrl,
|
|
718
|
+
})
|
|
719
|
+
: await fetchZcodeBilling(auth.apiKey, {
|
|
720
|
+
fetchImpl,
|
|
721
|
+
baseUrl: auth.billingBaseUrl,
|
|
722
|
+
env,
|
|
723
|
+
home,
|
|
724
|
+
});
|
|
725
|
+
const normalized = auth.planKind === "coding-plan"
|
|
726
|
+
? normalizeZcodeCodingPlanQuotaResponse(body)
|
|
727
|
+
: normalizeZcodeBalanceResponse(body);
|
|
728
|
+
if (auth.planKind !== "coding-plan") {
|
|
729
|
+
const apiCode = typeof body?.code === "number" ? body.code : null;
|
|
730
|
+
if (apiCode !== null && apiCode !== 0) {
|
|
731
|
+
throw new Error(`ZCode billing API error: code=${apiCode} msg=${body?.msg || "unknown"}`);
|
|
732
|
+
}
|
|
501
733
|
}
|
|
502
|
-
const normalized = normalizeZcodeBalanceResponse(body);
|
|
503
734
|
const result = {
|
|
504
735
|
configured: true,
|
|
505
736
|
error: null,
|
|
@@ -521,6 +752,7 @@ async function fetchZcodeLimits({ home, env, fetchImpl = fetch, nowMs = Date.now
|
|
|
521
752
|
env,
|
|
522
753
|
providerKeys: authCandidates.map((auth) => auth.providerKey),
|
|
523
754
|
nowMs,
|
|
755
|
+
requireProviderMatch: authCandidates.some((auth) => auth.planKind === "coding-plan"),
|
|
524
756
|
});
|
|
525
757
|
if (logFallback) return zcodeLogFallbackResult(logFallback, errors);
|
|
526
758
|
return {
|
|
@@ -536,13 +768,17 @@ module.exports = {
|
|
|
536
768
|
resolveZcodeAppVersion,
|
|
537
769
|
isZcodeInstalled,
|
|
538
770
|
loadZcodeProviderAvailability,
|
|
771
|
+
loadZcodeSelectedPlanProviderKeys,
|
|
539
772
|
loadZcodeCredential,
|
|
540
773
|
loadLatestZcodeBalanceFromLogs,
|
|
541
774
|
resolveZcodeProviderBillingBaseUrl,
|
|
775
|
+
resolveZcodeProviderQuotaUrl,
|
|
542
776
|
loadZcodeAuthCandidates,
|
|
543
777
|
loadZcodeApiKey,
|
|
544
778
|
deriveZcodePlanLabel,
|
|
545
779
|
normalizeZcodeBalanceResponse,
|
|
780
|
+
normalizeZcodeCodingPlanQuotaResponse,
|
|
546
781
|
fetchZcodeBilling,
|
|
782
|
+
fetchZcodeCodingPlanQuota,
|
|
547
783
|
fetchZcodeLimits,
|
|
548
784
|
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as n,b9 as a}from"./main-Dxi4sSXf.js";import{u as p}from"./DialogTitle-v1JrTvr4.js";import{u as c}from"./useScrollLock-DK-ndHcw.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-Dxi4sSXf.js";const e=[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]],t=c("check",e);export{t as C};
|