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.
Files changed (54) hide show
  1. package/dashboard/dist/assets/{ActivityHeatmap-DkoSqxrd.js → ActivityHeatmap-D5DFI_JT.js} +1 -1
  2. package/dashboard/dist/assets/{Card-DleS-r54.js → Card-C0dBE40G.js} +1 -1
  3. package/dashboard/dist/assets/{CommunityStatsModal-DV41-F9D.js → CommunityStatsModal-EoqdsEND.js} +1 -1
  4. package/dashboard/dist/assets/{ConfirmModal-Dgp07Uka.js → ConfirmModal-Dmmph0Fh.js} +1 -1
  5. package/dashboard/dist/assets/{DashboardPage-EKy2BzUx.js → DashboardPage-CCj2poZF.js} +1 -1
  6. package/dashboard/dist/assets/{DevicePage-eJInQ2UX.js → DevicePage-DeAi9xZM.js} +1 -1
  7. package/dashboard/dist/assets/{DialogClose-DEfesqFq.js → DialogClose-D3RkFfxL.js} +1 -1
  8. package/dashboard/dist/assets/DialogDescription-SXe3669h.js +1 -0
  9. package/dashboard/dist/assets/{DialogTitle-v1JrTvr4.js → DialogTitle-B_MCherE.js} +1 -1
  10. package/dashboard/dist/assets/{FadeIn-hqIBbKaJ.js → FadeIn-DNN2K13r.js} +1 -1
  11. package/dashboard/dist/assets/{HeaderGithubStar-C4zauuP2.js → HeaderGithubStar-lFcC5cJF.js} +1 -1
  12. package/dashboard/dist/assets/{IpCheckPage-CiRcccks.js → IpCheckPage-CeLaiU7v.js} +1 -1
  13. package/dashboard/dist/assets/{LandingPage-CbOHycnd.js → LandingPage-DAoPUMvI.js} +97 -37
  14. package/dashboard/dist/assets/{LeaderboardAvatar-CQL1iOTS.js → LeaderboardAvatar-FQi0zZEF.js} +1 -1
  15. package/dashboard/dist/assets/{LeaderboardPage-BuEp247d.js → LeaderboardPage-D5b5VXQC.js} +2 -2
  16. package/dashboard/dist/assets/{LeaderboardProfileModal-DT8hrPkI.js → LeaderboardProfileModal-B4zRhrdW.js} +1 -1
  17. package/dashboard/dist/assets/{LeaderboardProfilePage-B-M0AXMb.js → LeaderboardProfilePage-Bq8rq1En.js} +1 -1
  18. package/dashboard/dist/assets/{LimitsPage-Df2es3Xs.js → LimitsPage-DWM80BaS.js} +1 -1
  19. package/dashboard/dist/assets/{LocalOnlyNotice-DJIfKTKo.js → LocalOnlyNotice-B8m8CUt0.js} +1 -1
  20. package/dashboard/dist/assets/{LoginPage-DtaaIy_8.js → LoginPage-BUW1YAd4.js} +1 -1
  21. package/dashboard/dist/assets/{PopoverPopup-CqWH6aB9.js → PopoverPopup-Bpt95eH1.js} +1 -1
  22. package/dashboard/dist/assets/{ResetPasswordPage-DCH5GU1c.js → ResetPasswordPage-BbIfq0Oo.js} +1 -1
  23. package/dashboard/dist/assets/{Select-BwyliVKJ.js → Select-gTXpf9KQ.js} +1 -1
  24. package/dashboard/dist/assets/{SelectItemText-B5_DKKEv.js → SelectItemText-DEkgwu_p.js} +1 -1
  25. package/dashboard/dist/assets/{SettingsPage-BoAFdOUa.js → SettingsPage-DI9CabCH.js} +1 -1
  26. package/dashboard/dist/assets/{SkillsPage-BVdre-kF.js → SkillsPage-BfCZDHaD.js} +1 -1
  27. package/dashboard/dist/assets/{WidgetsPage-CtjV405P.js → WidgetsPage-rU0VeGYf.js} +1 -1
  28. package/dashboard/dist/assets/{WrappedPage-DGmPdtHt.js → WrappedPage-CZa-5-KZ.js} +1 -1
  29. package/dashboard/dist/assets/{agent-logos-DqevdWvx.js → agent-logos-EmiprqVo.js} +1 -1
  30. package/dashboard/dist/assets/{arrow-up-right-DA5Pybq1.js → arrow-up-right-k-vRvS_D.js} +1 -1
  31. package/dashboard/dist/assets/check-BBoXV3Xq.js +1 -0
  32. package/dashboard/dist/assets/{copy-CsQRTd89.js → copy-syqMmWHW.js} +1 -1
  33. package/dashboard/dist/assets/{download-TEHkAKT0.js → download-DZcuOKDP.js} +1 -1
  34. package/dashboard/dist/assets/{format-tokens-CtFO_jkz.js → format-tokens-D7k8OXQS.js} +1 -1
  35. package/dashboard/dist/assets/{info-Dxn3fiaX.js → info-CEBgCIZx.js} +1 -1
  36. package/dashboard/dist/assets/main--nUrKVF3.css +1 -0
  37. package/dashboard/dist/assets/{main-Dxi4sSXf.js → main-BALrkVr_.js} +2 -2
  38. package/dashboard/dist/assets/{use-community-stats-BLNxr-bQ.js → use-community-stats-DUttYzRH.js} +1 -1
  39. package/dashboard/dist/assets/{use-limits-display-prefs-CC7gb303.js → use-limits-display-prefs-BhIvYvqB.js} +1 -1
  40. package/dashboard/dist/assets/{use-native-settings-SYtjLlxZ.js → use-native-settings-BhOpUBnt.js} +1 -1
  41. package/dashboard/dist/assets/{use-ordered-list-_O8onJV7.js → use-ordered-list-Cd6yagUF.js} +1 -1
  42. package/dashboard/dist/assets/{use-quality-per-dollar-pref-tuspH_6n.js → use-quality-per-dollar-pref-DBeuBvln.js} +1 -1
  43. package/dashboard/dist/assets/{use-usage-limits-whCVzQbf.js → use-usage-limits-DBhYqpBW.js} +1 -1
  44. package/dashboard/dist/assets/{useCurrency-BiDjYxfm.js → useCurrency-Cy2Q8nWt.js} +1 -1
  45. package/dashboard/dist/assets/{useScrollLock-DK-ndHcw.js → useScrollLock-C6Eqqoge.js} +1 -1
  46. package/dashboard/dist/index.html +2 -2
  47. package/dashboard/dist/share.html +2 -2
  48. package/package.json +1 -1
  49. package/src/lib/pricing/seed-snapshot.json +1 -1
  50. package/src/lib/usage-limits.js +24 -0
  51. package/src/lib/zcode-limits.js +214 -15
  52. package/dashboard/dist/assets/DialogDescription-D6txiEc4.js +0 -1
  53. package/dashboard/dist/assets/check-BWzAT1oX.js +0 -1
  54. package/dashboard/dist/assets/main-DXM2UPde.css +0 -1
@@ -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
  }
@@ -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 (isZcodeBuiltinPlanProvider(providerKey)) return DEFAULT_BILLING_BASE_URL;
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 = [...availableCandidates, ...defaultCandidates.filter((key) => !availableCandidates.includes(key))];
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({ home, env = process.env, providerKeys = [], nowMs = Date.now() } = {}) {
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
- return preferred || fallback;
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 = await fetchZcodeBilling(auth.apiKey, {
493
- fetchImpl,
494
- baseUrl: auth.billingBaseUrl,
495
- env,
496
- home,
497
- });
498
- const apiCode = typeof body?.code === "number" ? body.code : null;
499
- if (apiCode !== null && apiCode !== 0) {
500
- throw new Error(`ZCode billing API error: code=${apiCode} msg=${body?.msg || "unknown"}`);
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};