@slkiser/opencode-quota 2.17.0 → 3.0.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 (125) hide show
  1. package/README.md +150 -50
  2. package/dist/bin/opencode-quota.d.ts +4 -0
  3. package/dist/bin/opencode-quota.d.ts.map +1 -0
  4. package/dist/bin/opencode-quota.js +52 -0
  5. package/dist/bin/opencode-quota.js.map +1 -0
  6. package/dist/lib/config-file-utils.d.ts +18 -0
  7. package/dist/lib/config-file-utils.d.ts.map +1 -0
  8. package/dist/lib/config-file-utils.js +88 -0
  9. package/dist/lib/config-file-utils.js.map +1 -0
  10. package/dist/lib/config.d.ts +10 -7
  11. package/dist/lib/config.d.ts.map +1 -1
  12. package/dist/lib/config.js +111 -31
  13. package/dist/lib/config.js.map +1 -1
  14. package/dist/lib/display-sanitize.d.ts +7 -0
  15. package/dist/lib/display-sanitize.d.ts.map +1 -1
  16. package/dist/lib/display-sanitize.js +49 -0
  17. package/dist/lib/display-sanitize.js.map +1 -1
  18. package/dist/lib/entries.d.ts +1 -1
  19. package/dist/lib/entries.d.ts.map +1 -1
  20. package/dist/lib/format.js +1 -1
  21. package/dist/lib/format.js.map +1 -1
  22. package/dist/lib/google-antigravity-companion.d.ts +29 -0
  23. package/dist/lib/google-antigravity-companion.d.ts.map +1 -0
  24. package/dist/lib/google-antigravity-companion.js +122 -0
  25. package/dist/lib/google-antigravity-companion.js.map +1 -0
  26. package/dist/lib/google.d.ts +1 -0
  27. package/dist/lib/google.d.ts.map +1 -1
  28. package/dist/lib/google.js +54 -16
  29. package/dist/lib/google.js.map +1 -1
  30. package/dist/lib/grouped-entry-normalization.d.ts.map +1 -1
  31. package/dist/lib/grouped-entry-normalization.js +83 -23
  32. package/dist/lib/grouped-entry-normalization.js.map +1 -1
  33. package/dist/lib/init-installer.d.ts +101 -0
  34. package/dist/lib/init-installer.d.ts.map +1 -0
  35. package/dist/lib/init-installer.js +507 -0
  36. package/dist/lib/init-installer.js.map +1 -0
  37. package/dist/lib/provider-metadata.d.ts +1 -0
  38. package/dist/lib/provider-metadata.d.ts.map +1 -1
  39. package/dist/lib/provider-metadata.js +5 -0
  40. package/dist/lib/provider-metadata.js.map +1 -1
  41. package/dist/lib/quota-render-data.d.ts +72 -0
  42. package/dist/lib/quota-render-data.d.ts.map +1 -0
  43. package/dist/lib/quota-render-data.js +315 -0
  44. package/dist/lib/quota-render-data.js.map +1 -0
  45. package/dist/lib/quota-status.d.ts +9 -0
  46. package/dist/lib/quota-status.d.ts.map +1 -1
  47. package/dist/lib/quota-status.js +37 -0
  48. package/dist/lib/quota-status.js.map +1 -1
  49. package/dist/lib/session-tokens-format.d.ts +16 -3
  50. package/dist/lib/session-tokens-format.d.ts.map +1 -1
  51. package/dist/lib/session-tokens-format.js +65 -11
  52. package/dist/lib/session-tokens-format.js.map +1 -1
  53. package/dist/lib/toast-format-grouped.js +1 -1
  54. package/dist/lib/toast-format-grouped.js.map +1 -1
  55. package/dist/lib/tui-config-diagnostics.d.ts +12 -0
  56. package/dist/lib/tui-config-diagnostics.d.ts.map +1 -0
  57. package/dist/lib/tui-config-diagnostics.js +49 -0
  58. package/dist/lib/tui-config-diagnostics.js.map +1 -0
  59. package/dist/lib/tui-line-style.d.ts +3 -0
  60. package/dist/lib/tui-line-style.d.ts.map +1 -0
  61. package/dist/lib/tui-line-style.js +7 -0
  62. package/dist/lib/tui-line-style.js.map +1 -0
  63. package/dist/lib/tui-panel-state.d.ts +7 -0
  64. package/dist/lib/tui-panel-state.d.ts.map +1 -0
  65. package/dist/lib/tui-panel-state.js +13 -0
  66. package/dist/lib/tui-panel-state.js.map +1 -0
  67. package/dist/lib/tui-runtime.d.ts +11 -0
  68. package/dist/lib/tui-runtime.d.ts.map +1 -0
  69. package/dist/lib/tui-runtime.js +107 -0
  70. package/dist/lib/tui-runtime.js.map +1 -0
  71. package/dist/lib/tui-sidebar-format.d.ts +13 -0
  72. package/dist/lib/tui-sidebar-format.d.ts.map +1 -0
  73. package/dist/lib/tui-sidebar-format.js +29 -0
  74. package/dist/lib/tui-sidebar-format.js.map +1 -0
  75. package/dist/lib/types.d.ts +8 -5
  76. package/dist/lib/types.d.ts.map +1 -1
  77. package/dist/lib/types.js +2 -2
  78. package/dist/lib/types.js.map +1 -1
  79. package/dist/plugin.d.ts.map +1 -1
  80. package/dist/plugin.js +66 -320
  81. package/dist/plugin.js.map +1 -1
  82. package/dist/providers/alibaba-coding-plan.d.ts.map +1 -1
  83. package/dist/providers/alibaba-coding-plan.js +12 -19
  84. package/dist/providers/alibaba-coding-plan.js.map +1 -1
  85. package/dist/providers/anthropic.d.ts.map +1 -1
  86. package/dist/providers/anthropic.js +12 -19
  87. package/dist/providers/anthropic.js.map +1 -1
  88. package/dist/providers/chutes.d.ts.map +1 -1
  89. package/dist/providers/chutes.js +10 -17
  90. package/dist/providers/chutes.js.map +1 -1
  91. package/dist/providers/copilot.js +1 -1
  92. package/dist/providers/copilot.js.map +1 -1
  93. package/dist/providers/cursor.js +1 -1
  94. package/dist/providers/cursor.js.map +1 -1
  95. package/dist/providers/firmware.d.ts.map +1 -1
  96. package/dist/providers/firmware.js +11 -18
  97. package/dist/providers/firmware.js.map +1 -1
  98. package/dist/providers/google-antigravity.js +4 -4
  99. package/dist/providers/google-antigravity.js.map +1 -1
  100. package/dist/providers/minimax-coding-plan.d.ts.map +1 -1
  101. package/dist/providers/minimax-coding-plan.js +14 -29
  102. package/dist/providers/minimax-coding-plan.js.map +1 -1
  103. package/dist/providers/nanogpt.d.ts.map +1 -1
  104. package/dist/providers/nanogpt.js +5 -12
  105. package/dist/providers/nanogpt.js.map +1 -1
  106. package/dist/providers/openai.d.ts.map +1 -1
  107. package/dist/providers/openai.js +13 -28
  108. package/dist/providers/openai.js.map +1 -1
  109. package/dist/providers/opencode-go.d.ts.map +1 -1
  110. package/dist/providers/opencode-go.js +15 -40
  111. package/dist/providers/opencode-go.js.map +1 -1
  112. package/dist/providers/qwen-code.d.ts.map +1 -1
  113. package/dist/providers/qwen-code.js +16 -19
  114. package/dist/providers/qwen-code.js.map +1 -1
  115. package/dist/providers/result-helpers.d.ts +5 -0
  116. package/dist/providers/result-helpers.d.ts.map +1 -0
  117. package/dist/providers/result-helpers.js +10 -0
  118. package/dist/providers/result-helpers.js.map +1 -0
  119. package/dist/providers/zai.d.ts.map +1 -1
  120. package/dist/providers/zai.js +13 -28
  121. package/dist/providers/zai.js.map +1 -1
  122. package/dist/tui.d.ts +7 -0
  123. package/dist/tui.d.ts.map +1 -0
  124. package/dist/tui.tsx +155 -0
  125. package/package.json +27 -9
package/dist/plugin.js CHANGED
@@ -13,12 +13,11 @@ import { formatQuotaCommand } from "./lib/quota-command-format.js";
13
13
  import { getProviders } from "./providers/registry.js";
14
14
  import { tool } from "@opencode-ai/plugin";
15
15
  import { aggregateUsage, resolveSessionTree, SessionNotFoundError, } from "./lib/quota-stats.js";
16
- import { fetchSessionTokensForDisplay } from "./lib/session-tokens.js";
17
16
  import { formatQuotaStatsReport } from "./lib/quota-stats-format.js";
18
17
  import { buildQuotaStatusReport } from "./lib/quota-status.js";
18
+ import { inspectTuiConfig } from "./lib/tui-config-diagnostics.js";
19
19
  import { getPricingSnapshotMeta, getPricingSnapshotSource, getRuntimePricingRefreshStatePath, getRuntimePricingSnapshotPath, maybeRefreshPricingSnapshot, setPricingSnapshotAutoRefresh, setPricingSnapshotSelection, } from "./lib/modelsdev-pricing.js";
20
20
  import { refreshGoogleTokensForAllAccounts } from "./lib/google.js";
21
- import { getQuotaProviderDisplayLabel } from "./lib/provider-metadata.js";
22
21
  import { DEFAULT_ALIBABA_AUTH_CACHE_MAX_AGE_MS, isAlibabaModelId, resolveAlibabaCodingPlanAuthCached, } from "./lib/alibaba-auth.js";
23
22
  import { isQwenCodeModelId, resolveQwenLocalPlanCached } from "./lib/qwen-auth.js";
24
23
  import { recordAlibabaCodingPlanCompletion, recordQwenCompletion } from "./lib/qwen-local-quota.js";
@@ -27,7 +26,7 @@ import { parseOptionalJsonArgs, parseQuotaBetweenArgs, startOfLocalDayMs, startO
27
26
  import { handled } from "./lib/command-handled.js";
28
27
  import { renderCommandHeading } from "./lib/format-utils.js";
29
28
  import { sanitizeDisplayText } from "./lib/display-sanitize.js";
30
- import { getAnthropicNoDataMessage } from "./providers/anthropic.js";
29
+ import { collectQuotaRenderData, matchesQuotaProviderCurrentSelection, resolveQuotaRenderSelection, } from "./lib/quota-render-data.js";
31
30
  /** All token report command specifications */
32
31
  const TOKEN_REPORT_COMMANDS = [
33
32
  {
@@ -243,101 +242,6 @@ export const QuotaToastPlugin = async ({ client }) => {
243
242
  return false;
244
243
  return true;
245
244
  }
246
- function makeProviderFetchCacheKey(providerId, ctx) {
247
- const style = ctx.config.toastStyle ?? "classic";
248
- const googleModels = ctx.config.googleModels.join(",");
249
- const alibabaCodingPlanTier = ctx.config.alibabaCodingPlanTier;
250
- const cursorPlan = ctx.config.cursorPlan;
251
- const cursorIncludedApiUsd = ctx.config.cursorIncludedApiUsd ?? "";
252
- const cursorBillingCycleStartDay = ctx.config.cursorBillingCycleStartDay ?? "";
253
- const onlyCurrentModel = ctx.config.onlyCurrentModel ? "yes" : "no";
254
- const currentModel = ctx.config.currentModel ?? "";
255
- const currentProviderID = ctx.config.currentProviderID ?? "";
256
- const anthropicBinaryPath = ctx.config.anthropicBinaryPath ?? "";
257
- return `${providerId}|style=${style}|anthropicBinaryPath=${anthropicBinaryPath}|googleModels=${googleModels}|alibabaTier=${alibabaCodingPlanTier}|cursorPlan=${cursorPlan}|cursorIncludedApiUsd=${cursorIncludedApiUsd}|cursorBillingCycleStartDay=${cursorBillingCycleStartDay}|onlyCurrentModel=${onlyCurrentModel}|currentModel=${currentModel}|currentProviderID=${currentProviderID}`;
258
- }
259
- async function fetchProviderWithCache(params) {
260
- const { provider, ctx, ttlMs } = params;
261
- // Live local-usage providers should update per completion for accurate local reports.
262
- if (LIVE_LOCAL_USAGE_PROVIDER_IDS.has(provider.id)) {
263
- return await provider.fetch(ctx);
264
- }
265
- const cacheKey = makeProviderFetchCacheKey(provider.id, ctx);
266
- const now = Date.now();
267
- const existing = providerFetchCache.get(cacheKey);
268
- if (existing?.result &&
269
- existing.timestamp > 0 &&
270
- ttlMs > 0 &&
271
- now - existing.timestamp < ttlMs) {
272
- return existing.result;
273
- }
274
- if (existing?.inFlight) {
275
- return existing.inFlight;
276
- }
277
- const promise = (async () => {
278
- try {
279
- const result = await provider.fetch(ctx);
280
- if (result.attempted) {
281
- providerFetchCache.set(cacheKey, { timestamp: Date.now(), result });
282
- }
283
- else {
284
- providerFetchCache.delete(cacheKey);
285
- }
286
- return result;
287
- }
288
- catch (err) {
289
- providerFetchCache.delete(cacheKey);
290
- throw err;
291
- }
292
- })();
293
- providerFetchCache.set(cacheKey, {
294
- timestamp: existing?.timestamp ?? 0,
295
- result: existing?.result,
296
- inFlight: promise,
297
- });
298
- return promise;
299
- }
300
- function makeProviderFetchFailure(provider) {
301
- return {
302
- attempted: true,
303
- entries: [],
304
- errors: [
305
- {
306
- label: getQuotaProviderDisplayLabel(provider.id),
307
- message: "Failed to read quota data",
308
- },
309
- ],
310
- };
311
- }
312
- async function fetchProviderResults(params) {
313
- const settled = await Promise.allSettled(params.providers.map((provider) => fetchProviderWithCache({
314
- provider,
315
- ctx: params.ctx,
316
- ttlMs: params.ttlMs,
317
- })));
318
- return settled.map((result, index) => result.status === "fulfilled"
319
- ? result.value
320
- : makeProviderFetchFailure(params.providers[index]));
321
- }
322
- function getExplicitNoDataMessage(provider) {
323
- if (provider.id === "cursor") {
324
- return "No local usage yet";
325
- }
326
- if (provider.id === "anthropic") {
327
- return getAnthropicNoDataMessage();
328
- }
329
- return "Not configured";
330
- }
331
- function shouldSurfaceNoDataMessage(params) {
332
- const { provider, result, isAutoMode, activeProviderCount } = params;
333
- if (result.attempted || result.entries.length > 0 || result.errors.length > 0) {
334
- return false;
335
- }
336
- if (!isAutoMode) {
337
- return true;
338
- }
339
- return activeProviderCount === 1 && (provider.id === "anthropic" || provider.id === "cursor");
340
- }
341
245
  function isProviderEnabled(providerId) {
342
246
  return config.enabledProviders === "auto" || config.enabledProviders.includes(providerId);
343
247
  }
@@ -514,16 +418,6 @@ export const QuotaToastPlugin = async ({ client }) => {
514
418
  return {};
515
419
  }
516
420
  }
517
- function matchesProviderCurrentSelection(params) {
518
- if (params.provider.id === "cursor" && isCursorProviderId(params.currentProviderID)) {
519
- return true;
520
- }
521
- if (!params.currentModel)
522
- return false;
523
- return params.provider.matchesCurrentModel
524
- ? params.provider.matchesCurrentModel(params.currentModel)
525
- : true;
526
- }
527
421
  function formatDebugInfo(params) {
528
422
  const availability = params.availability
529
423
  ? params.availability.map((x) => `${x.id}=${x.ok ? "ok" : "no"}`).join(" ")
@@ -543,55 +437,6 @@ export const QuotaToastPlugin = async ({ client }) => {
543
437
  `available=${availability}`,
544
438
  ].join("\n");
545
439
  }
546
- async function resolveQuotaCommandSelection(params = {}) {
547
- if (!configLoaded)
548
- await refreshConfig();
549
- if (!config.enabled)
550
- return null;
551
- const allProviders = getProviders();
552
- const isAutoMode = config.enabledProviders === "auto";
553
- const providers = isAutoMode
554
- ? allProviders
555
- : allProviders.filter((p) => config.enabledProviders.includes(p.id));
556
- if (!isAutoMode && providers.length === 0)
557
- return null;
558
- let currentModel;
559
- let currentProviderID;
560
- if (config.onlyCurrentModel && params.sessionID) {
561
- const currentSession = params.sessionMeta ?? (await getSessionModelMeta(params.sessionID));
562
- currentModel = currentSession.modelID;
563
- currentProviderID = currentSession.providerID;
564
- }
565
- const ctx = {
566
- client: typedClient,
567
- config: {
568
- googleModels: config.googleModels,
569
- anthropicBinaryPath: config.anthropicBinaryPath,
570
- alibabaCodingPlanTier: config.alibabaCodingPlanTier,
571
- cursorPlan: config.cursorPlan,
572
- cursorIncludedApiUsd: config.cursorIncludedApiUsd,
573
- cursorBillingCycleStartDay: config.cursorBillingCycleStartDay,
574
- // Always format /quota in grouped mode for a more dashboard-like look.
575
- toastStyle: "grouped",
576
- onlyCurrentModel: config.onlyCurrentModel,
577
- currentModel,
578
- currentProviderID,
579
- },
580
- };
581
- const filteringByCurrentSelection = config.onlyCurrentModel && Boolean(currentModel || isCursorProviderId(currentProviderID));
582
- const filtered = filteringByCurrentSelection
583
- ? providers.filter((p) => matchesProviderCurrentSelection({ provider: p, currentModel, currentProviderID }))
584
- : providers;
585
- return {
586
- isAutoMode,
587
- providers,
588
- filtered,
589
- ctx,
590
- currentModel,
591
- currentProviderID,
592
- filteringByCurrentSelection,
593
- };
594
- }
595
440
  function describeQuotaCommandCurrentSelection(params) {
596
441
  if (isCursorProviderId(params.currentProviderID)) {
597
442
  return `current provider: ${params.currentProviderID}`;
@@ -602,7 +447,12 @@ export const QuotaToastPlugin = async ({ client }) => {
602
447
  return "current session";
603
448
  }
604
449
  async function buildQuotaCommandUnavailableMessage(params = {}) {
605
- const selection = await resolveQuotaCommandSelection(params);
450
+ const selection = await resolveQuotaRenderSelection({
451
+ client: typedClient,
452
+ config,
453
+ request: params,
454
+ formatStyle: "grouped",
455
+ });
606
456
  if (!selection) {
607
457
  return "Quota unavailable\n\nNo enabled quota providers are configured.\n\nRun /quota_status for diagnostics.";
608
458
  }
@@ -648,140 +498,56 @@ export const QuotaToastPlugin = async ({ client }) => {
648
498
  ? formatDebugInfo({ trigger, reason: "disabled", enabledProviders: [] })
649
499
  : null;
650
500
  }
651
- const allProviders = getProviders();
652
- const isAutoMode = config.enabledProviders === "auto";
653
- const enabledProviderIds = isAutoMode ? [] : config.enabledProviders;
654
- // When enabledProviders is "auto", we'll filter by availability below.
655
- // When explicit, filter to just the listed providers.
656
- const providers = isAutoMode
657
- ? allProviders
658
- : allProviders.filter((p) => enabledProviderIds.includes(p.id));
659
- // Only bail on empty if user explicitly configured an empty list.
660
- if (!isAutoMode && providers.length === 0) {
501
+ if (config.enabledProviders !== "auto" && config.enabledProviders.length === 0) {
661
502
  return config.debug
662
503
  ? formatDebugInfo({ trigger, reason: "enabledProviders empty", enabledProviders: [] })
663
504
  : null;
664
505
  }
665
- let currentModel;
666
- let currentProviderID;
667
- if (config.onlyCurrentModel) {
668
- const currentSession = await getSessionModelMeta(sessionID);
669
- currentModel = currentSession.modelID;
670
- currentProviderID = currentSession.providerID;
671
- }
672
- const ctx = {
673
- client: typedClient,
674
- config: {
675
- googleModels: config.googleModels,
676
- anthropicBinaryPath: config.anthropicBinaryPath,
677
- alibabaCodingPlanTier: config.alibabaCodingPlanTier,
678
- cursorPlan: config.cursorPlan,
679
- cursorIncludedApiUsd: config.cursorIncludedApiUsd,
680
- cursorBillingCycleStartDay: config.cursorBillingCycleStartDay,
681
- toastStyle: config.toastStyle,
682
- onlyCurrentModel: config.onlyCurrentModel,
683
- currentModel,
684
- currentProviderID,
685
- },
506
+ const quotaRequestContext = {
507
+ sessionID,
508
+ sessionMeta: config.onlyCurrentModel && sessionID ? await getSessionModelMeta(sessionID) : undefined,
686
509
  };
687
- const filtered = config.onlyCurrentModel && (currentModel || isCursorProviderId(currentProviderID))
688
- ? providers.filter((p) => matchesProviderCurrentSelection({ provider: p, currentModel, currentProviderID }))
689
- : providers;
690
- // availability checks are cheap, do them in parallel
691
- const avail = await Promise.all(filtered.map(async (p) => ({ p, ok: await p.isAvailable(ctx) })));
692
- const active = avail.filter((x) => x.ok).map((x) => x.p);
693
- if (active.length === 0) {
510
+ const quotaResult = await collectQuotaRenderData({
511
+ client: typedClient,
512
+ config,
513
+ request: quotaRequestContext,
514
+ providerFetchCache,
515
+ surfaceExplicitProviderIssues: true,
516
+ formatStyle: config.formatStyle,
517
+ });
518
+ const { selection, availability, active, attemptedAny, hasExplicitProviderIssues, data } = quotaResult;
519
+ if (config.showSessionTokens && sessionID) {
520
+ lastSessionTokenError = quotaResult.sessionTokenError;
521
+ }
522
+ const currentModel = selection?.currentModel;
523
+ const errors = data?.errors ?? [];
524
+ if (active.length === 0 && !(hasExplicitProviderIssues && errors.length > 0)) {
694
525
  return config.debug
695
526
  ? formatDebugInfo({
696
527
  trigger,
697
528
  reason: "no enabled providers available",
698
529
  currentModel,
699
530
  enabledProviders: config.enabledProviders,
700
- availability: avail.map((x) => ({ id: x.p.id, ok: x.ok })),
531
+ availability: availability.map((item) => ({
532
+ id: item.provider.id,
533
+ ok: item.ok,
534
+ })),
701
535
  })
702
536
  : null;
703
537
  }
704
- const results = await fetchProviderResults({
705
- providers: active,
706
- ctx,
707
- ttlMs: config.minIntervalMs,
708
- });
709
- const entries = results.flatMap((r) => r.entries);
710
- const errors = results.flatMap((r) => r.errors);
711
- const attemptedAny = results.some((r) => r.attempted);
712
- let hasExplicitProviderIssues = false;
713
- for (let i = 0; i < active.length; i++) {
714
- const provider = active[i];
715
- const result = results[i];
716
- if (shouldSurfaceNoDataMessage({
717
- provider,
718
- result,
719
- isAutoMode,
720
- activeProviderCount: active.length,
721
- })) {
722
- errors.push({
723
- label: getQuotaProviderDisplayLabel(provider.id),
724
- message: getExplicitNoDataMessage(provider),
725
- });
726
- if (!isAutoMode) {
727
- hasExplicitProviderIssues = true;
728
- }
729
- }
730
- }
731
- // When enabledProviders is an explicit list, surface unavailable/skipped
732
- // providers instead of silently omitting them.
733
- if (!isAutoMode) {
734
- // If a user explicitly enabled providers that are unavailable (or skipped due
735
- // to model filtering), surface that instead of silently omitting them.
736
- const filteredIds = new Set(filtered.map((p) => p.id));
737
- const activeIds = new Set(active.map((p) => p.id));
738
- const availById = new Map(avail.map((x) => [x.p.id, x.ok]));
739
- for (const p of providers) {
740
- if (activeIds.has(p.id))
741
- continue;
742
- if (!filteredIds.has(p.id)) {
743
- const detail = config.onlyCurrentModel && currentModel ? `current model: ${currentModel}` : "filtered";
744
- errors.push({
745
- label: getQuotaProviderDisplayLabel(p.id),
746
- message: `Skipped (${detail})`,
747
- });
748
- hasExplicitProviderIssues = true;
749
- continue;
750
- }
751
- const ok = availById.get(p.id);
752
- if (ok === false) {
753
- errors.push({
754
- label: getQuotaProviderDisplayLabel(p.id),
755
- message: "Unavailable (not detected)",
756
- });
757
- hasExplicitProviderIssues = true;
758
- }
759
- }
760
- }
761
- // Fetch session tokens if enabled and sessionID is available
762
- let sessionTokens;
763
- if (config.showSessionTokens && sessionID) {
764
- const stResult = await fetchSessionTokensForDisplay({
765
- enabled: config.showSessionTokens,
766
- sessionID,
767
- });
768
- sessionTokens = stResult.sessionTokens;
769
- // Update diagnostics state: clear on success (no error returned), set on failure
770
- lastSessionTokenError = stResult.error;
771
- }
772
- if (entries.length > 0) {
538
+ if (data?.entries.length) {
773
539
  const formatted = formatQuotaRows({
774
540
  version: "1.0.0",
775
541
  layout: config.layout,
776
- entries,
777
- errors,
778
- style: config.toastStyle,
779
- sessionTokens,
542
+ entries: data.entries,
543
+ errors: data.errors,
544
+ style: config.formatStyle,
545
+ sessionTokens: data.sessionTokens,
780
546
  });
781
547
  if (!config.debug)
782
548
  return formatted;
783
- const debugFooter = `\n\n[debug] src=${configMeta.source} providers=${config.enabledProviders === "auto" ? "(auto)" : config.enabledProviders.join(",") || "(none)"} avail=${avail
784
- .map((x) => `${x.p.id}:${x.ok ? "ok" : "no"}`)
549
+ const debugFooter = `\n\n[debug] src=${configMeta.source} providers=${config.enabledProviders === "auto" ? "(auto)" : config.enabledProviders.join(",") || "(none)"} avail=${availability
550
+ .map((item) => `${item.provider.id}:${item.ok ? "ok" : "no"}`)
785
551
  .join(" ")}`;
786
552
  return formatted + debugFooter;
787
553
  }
@@ -789,8 +555,7 @@ export const QuotaToastPlugin = async ({ client }) => {
789
555
  // 1. showOnBothFail is enabled and at least one provider attempted (existing behavior)
790
556
  // 2. OR we're in explicit mode and have "Not configured"/"Unavailable" errors (new behavior)
791
557
  if ((config.showOnBothFail && attemptedAny && errors.length > 0) || hasExplicitProviderIssues) {
792
- // Format errors as individual lines
793
- const errorLines = errors.map((e) => `${e.label}: ${e.message}`).join("\n");
558
+ const errorLines = errors.map((error) => `${error.label}: ${error.message}`).join("\n");
794
559
  if (!config.debug)
795
560
  return errorLines || "Quota unavailable";
796
561
  return ((errorLines || "Quota unavailable") +
@@ -802,7 +567,10 @@ export const QuotaToastPlugin = async ({ client }) => {
802
567
  : "all providers failed",
803
568
  currentModel,
804
569
  enabledProviders: config.enabledProviders,
805
- availability: avail.map((x) => ({ id: x.p.id, ok: x.ok })),
570
+ availability: availability.map((item) => ({
571
+ id: item.provider.id,
572
+ ok: item.ok,
573
+ })),
806
574
  }));
807
575
  }
808
576
  return config.debug
@@ -811,7 +579,10 @@ export const QuotaToastPlugin = async ({ client }) => {
811
579
  reason: "no entries",
812
580
  currentModel,
813
581
  enabledProviders: config.enabledProviders,
814
- availability: avail.map((x) => ({ id: x.p.id, ok: x.ok })),
582
+ availability: availability.map((item) => ({
583
+ id: item.provider.id,
584
+ ok: item.ok,
585
+ })),
815
586
  })
816
587
  : null;
817
588
  }
@@ -871,50 +642,18 @@ export const QuotaToastPlugin = async ({ client }) => {
871
642
  }
872
643
  }
873
644
  async function fetchQuotaCommandData(trigger, params = {}) {
874
- const selection = await resolveQuotaCommandSelection(params);
875
- if (!selection)
876
- return null;
877
- const { isAutoMode, ctx } = selection;
878
- const avail = await Promise.all(selection.filtered.map(async (p) => ({ p, ok: await p.isAvailable(ctx) })));
879
- const active = avail.filter((x) => x.ok).map((x) => x.p);
880
- if (active.length === 0)
881
- return null;
882
- const results = await fetchProviderResults({
883
- providers: active,
884
- ctx,
885
- ttlMs: config.minIntervalMs,
645
+ const quotaResult = await collectQuotaRenderData({
646
+ client: typedClient,
647
+ config,
648
+ request: params,
649
+ providerFetchCache,
650
+ surfaceExplicitProviderIssues: false,
651
+ formatStyle: "grouped",
886
652
  });
887
- const entries = results.flatMap((r) => r.entries);
888
- const errors = results.flatMap((r) => r.errors);
889
- for (let i = 0; i < active.length; i++) {
890
- const provider = active[i];
891
- const result = results[i];
892
- if (shouldSurfaceNoDataMessage({
893
- provider,
894
- result,
895
- isAutoMode,
896
- activeProviderCount: active.length,
897
- })) {
898
- errors.push({
899
- label: getQuotaProviderDisplayLabel(provider.id),
900
- message: getExplicitNoDataMessage(provider),
901
- });
902
- }
903
- }
904
- // Fetch session tokens if enabled and sessionID is available
905
- let sessionTokens;
906
653
  if (config.showSessionTokens && params.sessionID) {
907
- const stResult = await fetchSessionTokensForDisplay({
908
- enabled: config.showSessionTokens,
909
- sessionID: params.sessionID,
910
- });
911
- sessionTokens = stResult.sessionTokens;
912
- // Update diagnostics state: clear on success (no error returned), set on failure
913
- lastSessionTokenError = stResult.error;
654
+ lastSessionTokenError = quotaResult.sessionTokenError;
914
655
  }
915
- if (entries.length === 0 && errors.length === 0 && !sessionTokens)
916
- return null;
917
- return { entries, errors, sessionTokens };
656
+ return quotaResult.data;
918
657
  }
919
658
  async function buildQuotaReport(params) {
920
659
  const result = await aggregateUsage({
@@ -976,16 +715,23 @@ export const QuotaToastPlugin = async ({ client }) => {
976
715
  enabled: isAutoMode ? ok : config.enabledProviders.includes(p.id),
977
716
  available: ok,
978
717
  matchesCurrentModel: currentModel || isCursorProviderId(currentProviderID)
979
- ? matchesProviderCurrentSelection({ provider: p, currentModel, currentProviderID })
718
+ ? matchesQuotaProviderCurrentSelection({
719
+ provider: p,
720
+ currentModel,
721
+ currentProviderID,
722
+ })
980
723
  : undefined,
981
724
  };
982
725
  }));
983
726
  const refresh = params.refreshGoogleTokens
984
727
  ? await refreshGoogleTokensForAllAccounts({ skewMs: params.skewMs, force: params.force })
985
728
  : null;
729
+ const tuiDiagnostics = await inspectTuiConfig();
986
730
  return await buildQuotaStatusReport({
731
+ tuiDiagnostics,
987
732
  configSource: configMeta.source,
988
733
  configPaths: configMeta.paths,
734
+ networkSettingSources: configMeta.networkSettingSources,
989
735
  enabledProviders: config.enabledProviders,
990
736
  anthropicBinaryPath: config.anthropicBinaryPath,
991
737
  alibabaCodingPlanTier: config.alibabaCodingPlanTier,
@@ -1249,7 +995,7 @@ export const QuotaToastPlugin = async ({ client }) => {
1249
995
  };
1250
996
  cfg.command["quota_status"] = {
1251
997
  template: "/quota_status",
1252
- description: "Diagnostics for toast + pricing + local storage (includes unknown pricing report).",
998
+ description: "Diagnostics for toast + TUI + pricing + local storage (includes unknown pricing report).",
1253
999
  };
1254
1000
  cfg.command["pricing_refresh"] = {
1255
1001
  template: "/pricing_refresh",
@@ -1312,7 +1058,7 @@ export const QuotaToastPlugin = async ({ client }) => {
1312
1058
  },
1313
1059
  tool: {
1314
1060
  quota_status: tool({
1315
- description: "Diagnostics for toast + pricing + local storage (includes unknown pricing report).",
1061
+ description: "Diagnostics for toast + TUI + pricing + local storage (includes unknown pricing report).",
1316
1062
  args: {
1317
1063
  refreshGoogleTokens: tool.schema
1318
1064
  .boolean()