tokentracker-cli 0.74.0 → 0.74.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-DkoSqxrd.js → ActivityHeatmap-D5DFI_JT.js} +1 -1
- package/dashboard/dist/assets/{Card-DleS-r54.js → Card-C0dBE40G.js} +1 -1
- package/dashboard/dist/assets/{CommunityStatsModal-DV41-F9D.js → CommunityStatsModal-EoqdsEND.js} +1 -1
- package/dashboard/dist/assets/{ConfirmModal-Dgp07Uka.js → ConfirmModal-Dmmph0Fh.js} +1 -1
- package/dashboard/dist/assets/{DashboardPage-EKy2BzUx.js → DashboardPage-CCj2poZF.js} +1 -1
- package/dashboard/dist/assets/{DevicePage-eJInQ2UX.js → DevicePage-DeAi9xZM.js} +1 -1
- package/dashboard/dist/assets/{DialogClose-DEfesqFq.js → DialogClose-D3RkFfxL.js} +1 -1
- package/dashboard/dist/assets/DialogDescription-SXe3669h.js +1 -0
- package/dashboard/dist/assets/{DialogTitle-v1JrTvr4.js → DialogTitle-B_MCherE.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-hqIBbKaJ.js → FadeIn-DNN2K13r.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-C4zauuP2.js → HeaderGithubStar-lFcC5cJF.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-CiRcccks.js → IpCheckPage-CeLaiU7v.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-CbOHycnd.js → LandingPage-DAoPUMvI.js} +97 -37
- package/dashboard/dist/assets/{LeaderboardAvatar-CQL1iOTS.js → LeaderboardAvatar-FQi0zZEF.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardPage-BuEp247d.js → LeaderboardPage-D5b5VXQC.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardProfileModal-DT8hrPkI.js → LeaderboardProfileModal-B4zRhrdW.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardProfilePage-B-M0AXMb.js → LeaderboardProfilePage-Bq8rq1En.js} +1 -1
- package/dashboard/dist/assets/{LimitsPage-Df2es3Xs.js → LimitsPage-DWM80BaS.js} +1 -1
- package/dashboard/dist/assets/{LocalOnlyNotice-DJIfKTKo.js → LocalOnlyNotice-B8m8CUt0.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-DtaaIy_8.js → LoginPage-BUW1YAd4.js} +1 -1
- package/dashboard/dist/assets/{PopoverPopup-CqWH6aB9.js → PopoverPopup-Bpt95eH1.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-DCH5GU1c.js → ResetPasswordPage-BbIfq0Oo.js} +1 -1
- package/dashboard/dist/assets/{Select-BwyliVKJ.js → Select-gTXpf9KQ.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-B5_DKKEv.js → SelectItemText-DEkgwu_p.js} +1 -1
- package/dashboard/dist/assets/{SettingsPage-BoAFdOUa.js → SettingsPage-DI9CabCH.js} +1 -1
- package/dashboard/dist/assets/{SkillsPage-BVdre-kF.js → SkillsPage-BfCZDHaD.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-CtjV405P.js → WidgetsPage-rU0VeGYf.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-DGmPdtHt.js → WrappedPage-CZa-5-KZ.js} +1 -1
- package/dashboard/dist/assets/{agent-logos-DqevdWvx.js → agent-logos-EmiprqVo.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-DA5Pybq1.js → arrow-up-right-k-vRvS_D.js} +1 -1
- package/dashboard/dist/assets/check-BBoXV3Xq.js +1 -0
- package/dashboard/dist/assets/{copy-CsQRTd89.js → copy-syqMmWHW.js} +1 -1
- package/dashboard/dist/assets/{download-TEHkAKT0.js → download-DZcuOKDP.js} +1 -1
- package/dashboard/dist/assets/{format-tokens-CtFO_jkz.js → format-tokens-D7k8OXQS.js} +1 -1
- package/dashboard/dist/assets/{info-Dxn3fiaX.js → info-CEBgCIZx.js} +1 -1
- package/dashboard/dist/assets/main--nUrKVF3.css +1 -0
- package/dashboard/dist/assets/{main-Dxi4sSXf.js → main-BALrkVr_.js} +2 -2
- package/dashboard/dist/assets/{use-community-stats-BLNxr-bQ.js → use-community-stats-DUttYzRH.js} +1 -1
- package/dashboard/dist/assets/{use-limits-display-prefs-CC7gb303.js → use-limits-display-prefs-BhIvYvqB.js} +1 -1
- package/dashboard/dist/assets/{use-native-settings-SYtjLlxZ.js → use-native-settings-BhOpUBnt.js} +1 -1
- package/dashboard/dist/assets/{use-ordered-list-_O8onJV7.js → use-ordered-list-Cd6yagUF.js} +1 -1
- package/dashboard/dist/assets/{use-quality-per-dollar-pref-tuspH_6n.js → use-quality-per-dollar-pref-DBeuBvln.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-whCVzQbf.js → use-usage-limits-DBhYqpBW.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-BiDjYxfm.js → useCurrency-Cy2Q8nWt.js} +1 -1
- package/dashboard/dist/assets/{useScrollLock-DK-ndHcw.js → useScrollLock-C6Eqqoge.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/pricing/seed-snapshot.json +1 -1
- package/src/lib/usage-limits.js +24 -0
- package/src/lib/zcode-limits.js +214 -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
|
}
|
|
@@ -380,6 +461,103 @@ async function fetchZcodeBilling(apiKey, { fetchImpl = fetch, baseUrl, env, home
|
|
|
380
461
|
return res.json();
|
|
381
462
|
}
|
|
382
463
|
|
|
464
|
+
async function fetchZcodeCodingPlanQuota(apiKey, { fetchImpl = fetch, quotaUrl } = {}) {
|
|
465
|
+
const res = await fetchImpl(quotaUrl, {
|
|
466
|
+
method: "GET",
|
|
467
|
+
headers: {
|
|
468
|
+
authorization: apiKey,
|
|
469
|
+
Accept: "application/json",
|
|
470
|
+
},
|
|
471
|
+
});
|
|
472
|
+
if (res.status === 401 || res.status === 403) {
|
|
473
|
+
throw new Error("Not authenticated with ZCode coding plan. Run `zcode` in Terminal to log in.");
|
|
474
|
+
}
|
|
475
|
+
let body = null;
|
|
476
|
+
try {
|
|
477
|
+
body = await res.json();
|
|
478
|
+
} catch (_error) {
|
|
479
|
+
body = null;
|
|
480
|
+
}
|
|
481
|
+
if (!res.ok) {
|
|
482
|
+
const code = body?.code != null ? ` code=${body.code}` : "";
|
|
483
|
+
const msg = body?.msg || body?.message ? ` msg=${body.msg || body.message}` : "";
|
|
484
|
+
throw new Error(`ZCode coding plan API returned ${res.status}${code}${msg}`);
|
|
485
|
+
}
|
|
486
|
+
return body;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
function quotaTimestampToIso(value) {
|
|
490
|
+
if (typeof value === "string" && value.trim()) {
|
|
491
|
+
const parsed = new Date(value.trim());
|
|
492
|
+
return Number.isNaN(parsed.getTime()) ? null : parsed.toISOString();
|
|
493
|
+
}
|
|
494
|
+
const n = zcodeValNumber(value);
|
|
495
|
+
if (n == null || n <= 0) return null;
|
|
496
|
+
return new Date(n > 10_000_000_000 ? n : n * 1000).toISOString();
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
function normalizeZcodeQuotaLimit(limit) {
|
|
500
|
+
if (!limit || typeof limit !== "object") return null;
|
|
501
|
+
const total = zcodeValNumber(limit.number) ?? zcodeValNumber(limit.unit);
|
|
502
|
+
const used = zcodeValNumber(limit.usage) ?? zcodeValNumber(limit.currentValue);
|
|
503
|
+
const remaining = zcodeValNumber(limit.remaining);
|
|
504
|
+
const rawPercentage = zcodeValNumber(limit.percentage);
|
|
505
|
+
let usedPercent = null;
|
|
506
|
+
if (total != null && total > 0 && used != null) {
|
|
507
|
+
usedPercent = (used / total) * 100;
|
|
508
|
+
} else if (total != null && total > 0 && remaining != null) {
|
|
509
|
+
usedPercent = ((total - remaining) / total) * 100;
|
|
510
|
+
} else if (rawPercentage != null) {
|
|
511
|
+
usedPercent = rawPercentage <= 1 ? rawPercentage * 100 : rawPercentage;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const detail = Array.isArray(limit.usageDetails) ? limit.usageDetails.find((item) => item && typeof item === "object") : null;
|
|
515
|
+
const showName =
|
|
516
|
+
(typeof detail?.displayName === "string" && detail.displayName.trim())
|
|
517
|
+
|| (typeof detail?.modelCode === "string" && detail.modelCode.trim())
|
|
518
|
+
|| (typeof limit.type === "string" && limit.type.trim())
|
|
519
|
+
|| "Coding plan";
|
|
520
|
+
|
|
521
|
+
return {
|
|
522
|
+
show_name: showName,
|
|
523
|
+
entitlement_id: typeof limit.type === "string" ? limit.type : "",
|
|
524
|
+
total_units: total,
|
|
525
|
+
used_units: used,
|
|
526
|
+
remaining_units: remaining,
|
|
527
|
+
window: buildWindow({ usedPercent, resetAt: quotaTimestampToIso(limit.nextResetTime) }),
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
function normalizeZcodeCodingPlanQuotaResponse(body) {
|
|
532
|
+
const code = typeof body?.code === "number" ? body.code : null;
|
|
533
|
+
if (code !== null && code !== 0 && code !== 200) {
|
|
534
|
+
throw new Error(`ZCode coding plan API error: code=${code} msg=${body?.msg || body?.message || "unknown"}`);
|
|
535
|
+
}
|
|
536
|
+
if (body?.success === false) {
|
|
537
|
+
throw new Error(`ZCode coding plan API error: msg=${body?.msg || body?.message || "unknown"}`);
|
|
538
|
+
}
|
|
539
|
+
const data = body?.data;
|
|
540
|
+
if (!data || typeof data !== "object") {
|
|
541
|
+
throw new Error("Could not parse ZCode coding plan quota: missing data");
|
|
542
|
+
}
|
|
543
|
+
const limits = Array.isArray(data.limits) ? data.limits : [];
|
|
544
|
+
const buckets = limits.map(normalizeZcodeQuotaLimit).filter((bucket) => bucket?.window);
|
|
545
|
+
const sorted = buckets.slice().sort((a, b) => {
|
|
546
|
+
const aTotal = a.total_units || 0;
|
|
547
|
+
const bTotal = b.total_units || 0;
|
|
548
|
+
return bTotal - aTotal;
|
|
549
|
+
});
|
|
550
|
+
const level = typeof data.level === "string" && data.level.trim() ? data.level.trim() : null;
|
|
551
|
+
return {
|
|
552
|
+
server_time: null,
|
|
553
|
+
plan_id: level,
|
|
554
|
+
plan_label: level ? deriveZcodePlanLabel(level) || level : "Coding",
|
|
555
|
+
buckets: sorted,
|
|
556
|
+
primary_window: sorted[0]?.window || null,
|
|
557
|
+
secondary_window: sorted[1]?.window || null,
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
|
|
383
561
|
function parseZcodeLogTimestamp(line) {
|
|
384
562
|
const match = line.match(/^\[(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})\.(\d{3})\]/);
|
|
385
563
|
if (!match) return null;
|
|
@@ -433,7 +611,13 @@ function extractZcodeBalanceLogRecord(line) {
|
|
|
433
611
|
}
|
|
434
612
|
}
|
|
435
613
|
|
|
436
|
-
function loadLatestZcodeBalanceFromLogs({
|
|
614
|
+
function loadLatestZcodeBalanceFromLogs({
|
|
615
|
+
home,
|
|
616
|
+
env = process.env,
|
|
617
|
+
providerKeys = [],
|
|
618
|
+
nowMs = Date.now(),
|
|
619
|
+
requireProviderMatch = false,
|
|
620
|
+
} = {}) {
|
|
437
621
|
if (env.TOKENTRACKER_ZCODE_DISABLE_LOG_FALLBACK === "1") return null;
|
|
438
622
|
const maxAgeMs = parsePositiveInteger(env.TOKENTRACKER_ZCODE_LOG_MAX_AGE_MS, DEFAULT_ZCODE_LOG_FALLBACK_MAX_AGE_MS);
|
|
439
623
|
const preferredProviders = new Set(providerKeys.filter(Boolean));
|
|
@@ -459,7 +643,8 @@ function loadLatestZcodeBalanceFromLogs({ home, env = process.env, providerKeys
|
|
|
459
643
|
}
|
|
460
644
|
}
|
|
461
645
|
}
|
|
462
|
-
|
|
646
|
+
if (preferred) return preferred;
|
|
647
|
+
return requireProviderMatch && preferredProviders.size > 0 ? null : fallback;
|
|
463
648
|
}
|
|
464
649
|
|
|
465
650
|
function zcodeLogFallbackResult(logRecord, errors = []) {
|
|
@@ -489,17 +674,26 @@ async function fetchZcodeLimits({ home, env, fetchImpl = fetch, nowMs = Date.now
|
|
|
489
674
|
let emptySuccess = null;
|
|
490
675
|
for (const auth of authCandidates) {
|
|
491
676
|
try {
|
|
492
|
-
const body =
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
677
|
+
const body = auth.planKind === "coding-plan"
|
|
678
|
+
? await fetchZcodeCodingPlanQuota(auth.apiKey, {
|
|
679
|
+
fetchImpl,
|
|
680
|
+
quotaUrl: auth.quotaUrl,
|
|
681
|
+
})
|
|
682
|
+
: await fetchZcodeBilling(auth.apiKey, {
|
|
683
|
+
fetchImpl,
|
|
684
|
+
baseUrl: auth.billingBaseUrl,
|
|
685
|
+
env,
|
|
686
|
+
home,
|
|
687
|
+
});
|
|
688
|
+
const normalized = auth.planKind === "coding-plan"
|
|
689
|
+
? normalizeZcodeCodingPlanQuotaResponse(body)
|
|
690
|
+
: normalizeZcodeBalanceResponse(body);
|
|
691
|
+
if (auth.planKind !== "coding-plan") {
|
|
692
|
+
const apiCode = typeof body?.code === "number" ? body.code : null;
|
|
693
|
+
if (apiCode !== null && apiCode !== 0) {
|
|
694
|
+
throw new Error(`ZCode billing API error: code=${apiCode} msg=${body?.msg || "unknown"}`);
|
|
695
|
+
}
|
|
501
696
|
}
|
|
502
|
-
const normalized = normalizeZcodeBalanceResponse(body);
|
|
503
697
|
const result = {
|
|
504
698
|
configured: true,
|
|
505
699
|
error: null,
|
|
@@ -521,6 +715,7 @@ async function fetchZcodeLimits({ home, env, fetchImpl = fetch, nowMs = Date.now
|
|
|
521
715
|
env,
|
|
522
716
|
providerKeys: authCandidates.map((auth) => auth.providerKey),
|
|
523
717
|
nowMs,
|
|
718
|
+
requireProviderMatch: authCandidates.some((auth) => auth.planKind === "coding-plan"),
|
|
524
719
|
});
|
|
525
720
|
if (logFallback) return zcodeLogFallbackResult(logFallback, errors);
|
|
526
721
|
return {
|
|
@@ -536,13 +731,17 @@ module.exports = {
|
|
|
536
731
|
resolveZcodeAppVersion,
|
|
537
732
|
isZcodeInstalled,
|
|
538
733
|
loadZcodeProviderAvailability,
|
|
734
|
+
loadZcodeSelectedPlanProviderKeys,
|
|
539
735
|
loadZcodeCredential,
|
|
540
736
|
loadLatestZcodeBalanceFromLogs,
|
|
541
737
|
resolveZcodeProviderBillingBaseUrl,
|
|
738
|
+
resolveZcodeProviderQuotaUrl,
|
|
542
739
|
loadZcodeAuthCandidates,
|
|
543
740
|
loadZcodeApiKey,
|
|
544
741
|
deriveZcodePlanLabel,
|
|
545
742
|
normalizeZcodeBalanceResponse,
|
|
743
|
+
normalizeZcodeCodingPlanQuotaResponse,
|
|
546
744
|
fetchZcodeBilling,
|
|
745
|
+
fetchZcodeCodingPlanQuota,
|
|
547
746
|
fetchZcodeLimits,
|
|
548
747
|
};
|
|
@@ -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};
|