@slkiser/opencode-quota 3.2.0 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +278 -557
- package/dist/bin/opencode-quota.d.ts.map +1 -1
- package/dist/bin/opencode-quota.js +6 -0
- package/dist/bin/opencode-quota.js.map +1 -1
- package/dist/lib/anthropic.js +1 -1
- package/dist/lib/anthropic.js.map +1 -1
- package/dist/lib/cli-show.d.ts +8 -0
- package/dist/lib/cli-show.d.ts.map +1 -0
- package/dist/lib/cli-show.js +178 -0
- package/dist/lib/cli-show.js.map +1 -0
- package/dist/lib/config-file-utils.d.ts +13 -0
- package/dist/lib/config-file-utils.d.ts.map +1 -1
- package/dist/lib/config-file-utils.js +33 -0
- package/dist/lib/config-file-utils.js.map +1 -1
- package/dist/lib/config.d.ts +16 -3
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +452 -216
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/copilot.d.ts.map +1 -1
- package/dist/lib/copilot.js +3 -2
- package/dist/lib/copilot.js.map +1 -1
- package/dist/lib/entries.d.ts +3 -2
- package/dist/lib/entries.d.ts.map +1 -1
- package/dist/lib/format-utils.d.ts.map +1 -1
- package/dist/lib/format-utils.js +3 -2
- package/dist/lib/format-utils.js.map +1 -1
- package/dist/lib/format.d.ts.map +1 -1
- package/dist/lib/format.js +4 -2
- package/dist/lib/format.js.map +1 -1
- package/dist/lib/google-gemini-cli-companion.d.ts +29 -0
- package/dist/lib/google-gemini-cli-companion.d.ts.map +1 -0
- package/dist/lib/google-gemini-cli-companion.js +166 -0
- package/dist/lib/google-gemini-cli-companion.js.map +1 -0
- package/dist/lib/google-gemini-cli.d.ts +48 -0
- package/dist/lib/google-gemini-cli.d.ts.map +1 -0
- package/dist/lib/google-gemini-cli.js +447 -0
- package/dist/lib/google-gemini-cli.js.map +1 -0
- package/dist/lib/grouped-entry-normalization.js +1 -1
- package/dist/lib/grouped-entry-normalization.js.map +1 -1
- package/dist/lib/init-installer.js +2 -2
- package/dist/lib/opencode-config-providers.d.ts +6 -0
- package/dist/lib/opencode-config-providers.d.ts.map +1 -0
- package/dist/lib/opencode-config-providers.js +79 -0
- package/dist/lib/opencode-config-providers.js.map +1 -0
- package/dist/lib/opencode-go.d.ts +5 -4
- package/dist/lib/opencode-go.d.ts.map +1 -1
- package/dist/lib/opencode-go.js +32 -18
- package/dist/lib/opencode-go.js.map +1 -1
- package/dist/lib/provider-metadata.d.ts +1 -1
- package/dist/lib/provider-metadata.d.ts.map +1 -1
- package/dist/lib/provider-metadata.js +19 -0
- package/dist/lib/provider-metadata.js.map +1 -1
- package/dist/lib/provider-model-matching.d.ts +12 -0
- package/dist/lib/provider-model-matching.d.ts.map +1 -0
- package/dist/lib/provider-model-matching.js +23 -0
- package/dist/lib/provider-model-matching.js.map +1 -0
- package/dist/lib/quota-render-data.d.ts +4 -0
- package/dist/lib/quota-render-data.d.ts.map +1 -1
- package/dist/lib/quota-render-data.js +4 -1
- package/dist/lib/quota-render-data.js.map +1 -1
- package/dist/lib/quota-runtime-context.d.ts +43 -0
- package/dist/lib/quota-runtime-context.d.ts.map +1 -0
- package/dist/lib/quota-runtime-context.js +62 -0
- package/dist/lib/quota-runtime-context.js.map +1 -0
- package/dist/lib/quota-state.d.ts +1 -0
- package/dist/lib/quota-state.d.ts.map +1 -1
- package/dist/lib/quota-state.js +18 -4
- package/dist/lib/quota-state.js.map +1 -1
- package/dist/lib/quota-status.d.ts +18 -1
- package/dist/lib/quota-status.d.ts.map +1 -1
- package/dist/lib/quota-status.js +106 -22
- package/dist/lib/quota-status.js.map +1 -1
- package/dist/lib/toast-format-grouped.d.ts.map +1 -1
- package/dist/lib/toast-format-grouped.js +5 -3
- package/dist/lib/toast-format-grouped.js.map +1 -1
- package/dist/lib/tui-config-diagnostics.d.ts +7 -2
- package/dist/lib/tui-config-diagnostics.d.ts.map +1 -1
- package/dist/lib/tui-config-diagnostics.js +27 -8
- package/dist/lib/tui-config-diagnostics.js.map +1 -1
- package/dist/lib/tui-runtime.d.ts.map +1 -1
- package/dist/lib/tui-runtime.js +25 -16
- package/dist/lib/tui-runtime.js.map +1 -1
- package/dist/lib/types.d.ts +55 -9
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/types.js +1 -0
- package/dist/lib/types.js.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +423 -159
- package/dist/plugin.js.map +1 -1
- package/dist/providers/chutes.d.ts.map +1 -1
- package/dist/providers/chutes.js +2 -4
- package/dist/providers/chutes.js.map +1 -1
- package/dist/providers/copilot.d.ts.map +1 -1
- package/dist/providers/copilot.js +5 -10
- package/dist/providers/copilot.js.map +1 -1
- package/dist/providers/cursor.js +2 -2
- package/dist/providers/cursor.js.map +1 -1
- package/dist/providers/google-account-format.d.ts +6 -0
- package/dist/providers/google-account-format.d.ts.map +1 -0
- package/dist/providers/google-account-format.js +21 -0
- package/dist/providers/google-account-format.js.map +1 -0
- package/dist/providers/google-antigravity.d.ts.map +1 -1
- package/dist/providers/google-antigravity.js +7 -29
- package/dist/providers/google-antigravity.js.map +1 -1
- package/dist/providers/google-gemini-cli.d.ts +3 -0
- package/dist/providers/google-gemini-cli.d.ts.map +1 -0
- package/dist/providers/google-gemini-cli.js +63 -0
- package/dist/providers/google-gemini-cli.js.map +1 -0
- package/dist/providers/minimax-coding-plan.js +2 -2
- package/dist/providers/minimax-coding-plan.js.map +1 -1
- package/dist/providers/nanogpt.d.ts.map +1 -1
- package/dist/providers/nanogpt.js +2 -2
- package/dist/providers/nanogpt.js.map +1 -1
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +2 -4
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/opencode-go.d.ts +2 -2
- package/dist/providers/opencode-go.d.ts.map +1 -1
- package/dist/providers/opencode-go.js +60 -11
- package/dist/providers/opencode-go.js.map +1 -1
- package/dist/providers/registry.d.ts.map +1 -1
- package/dist/providers/registry.js +2 -0
- package/dist/providers/registry.js.map +1 -1
- package/dist/providers/synthetic.d.ts.map +1 -1
- package/dist/providers/synthetic.js +2 -4
- package/dist/providers/synthetic.js.map +1 -1
- package/package.json +2 -1
package/dist/plugin.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Supports GitHub Copilot and Google (via opencode-antigravity-auth).
|
|
7
7
|
*/
|
|
8
8
|
import { DEFAULT_CONFIG } from "./lib/types.js";
|
|
9
|
-
import {
|
|
9
|
+
import { createLoadConfigMeta } from "./lib/config.js";
|
|
10
10
|
import { clearCache, getOrFetchWithCacheControl } from "./lib/cache.js";
|
|
11
11
|
import { formatQuotaRows } from "./lib/format.js";
|
|
12
12
|
import { formatQuotaCommand } from "./lib/quota-command-format.js";
|
|
@@ -28,6 +28,8 @@ import { renderCommandHeading } from "./lib/format-utils.js";
|
|
|
28
28
|
import { sanitizeDisplayText } from "./lib/display-sanitize.js";
|
|
29
29
|
import { ALL_WINDOWS_FORMAT_STYLE, SINGLE_WINDOW_PER_PROVIDER_FORMAT_STYLE, resolveQuotaFormatStyle, } from "./lib/quota-format-style.js";
|
|
30
30
|
import { collectQuotaRenderData, collectQuotaStatusLiveProbes, matchesQuotaProviderCurrentSelection, resolveQuotaRenderSelection, } from "./lib/quota-render-data.js";
|
|
31
|
+
import { createQuotaProviderRuntimeContext, createQuotaRuntimeRequestContext, resolveQuotaRuntimeContext, } from "./lib/quota-runtime-context.js";
|
|
32
|
+
const DEFERRED_QUOTA_REFRESH_DELAYS_MS = [3_000, 15_000, 60_000, 300_000];
|
|
31
33
|
/** All token report command specifications */
|
|
32
34
|
const TOKEN_REPORT_COMMANDS = [
|
|
33
35
|
{
|
|
@@ -158,8 +160,65 @@ export const QuotaToastPlugin = async ({ client }) => {
|
|
|
158
160
|
let configLoaded = false;
|
|
159
161
|
let configInFlight = null;
|
|
160
162
|
let configMeta = createLoadConfigMeta();
|
|
163
|
+
let runtimeProviders = getProviders();
|
|
161
164
|
// Track last session token error for /quota_status diagnostics
|
|
162
165
|
let lastSessionTokenError;
|
|
166
|
+
const deferredQuotaRefreshes = new Map();
|
|
167
|
+
function getDeferredQuotaRefreshDelayMs(attempts) {
|
|
168
|
+
const index = Math.min(Math.max(0, attempts), DEFERRED_QUOTA_REFRESH_DELAYS_MS.length - 1);
|
|
169
|
+
return DEFERRED_QUOTA_REFRESH_DELAYS_MS[index];
|
|
170
|
+
}
|
|
171
|
+
function clearDeferredQuotaRefresh(sessionID) {
|
|
172
|
+
const state = deferredQuotaRefreshes.get(sessionID);
|
|
173
|
+
if (state?.timer) {
|
|
174
|
+
clearTimeout(state.timer);
|
|
175
|
+
}
|
|
176
|
+
deferredQuotaRefreshes.delete(sessionID);
|
|
177
|
+
}
|
|
178
|
+
function clearDeferredQuotaRefreshTimer(state) {
|
|
179
|
+
if (!state.timer)
|
|
180
|
+
return;
|
|
181
|
+
clearTimeout(state.timer);
|
|
182
|
+
state.timer = null;
|
|
183
|
+
}
|
|
184
|
+
function scheduleDeferredQuotaRefresh(params) {
|
|
185
|
+
let state = deferredQuotaRefreshes.get(params.sessionID);
|
|
186
|
+
if (!state) {
|
|
187
|
+
state = {
|
|
188
|
+
sessionID: params.sessionID,
|
|
189
|
+
attempts: 0,
|
|
190
|
+
reason: params.reason,
|
|
191
|
+
queuedAtMs: Date.now(),
|
|
192
|
+
timer: null,
|
|
193
|
+
inFlight: false,
|
|
194
|
+
};
|
|
195
|
+
deferredQuotaRefreshes.set(params.sessionID, state);
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
if (params.incrementAttempts) {
|
|
199
|
+
state.attempts += 1;
|
|
200
|
+
}
|
|
201
|
+
state.reason = params.reason;
|
|
202
|
+
clearDeferredQuotaRefreshTimer(state);
|
|
203
|
+
}
|
|
204
|
+
const delayMs = getDeferredQuotaRefreshDelayMs(state.attempts);
|
|
205
|
+
state.timer = setTimeout(() => {
|
|
206
|
+
void runDeferredQuotaRefresh(params.sessionID);
|
|
207
|
+
}, delayMs);
|
|
208
|
+
state.timer.unref?.();
|
|
209
|
+
void log("Deferred quota refresh scheduled", {
|
|
210
|
+
sessionID: params.sessionID,
|
|
211
|
+
reason: params.reason,
|
|
212
|
+
attempts: state.attempts,
|
|
213
|
+
delayMs,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
async function runDeferredQuotaRefresh(sessionID) {
|
|
217
|
+
const state = deferredQuotaRefreshes.get(sessionID);
|
|
218
|
+
if (!state || state.inFlight)
|
|
219
|
+
return;
|
|
220
|
+
await showQuotaToast(sessionID, "deferred.retry", { deferredRetry: true });
|
|
221
|
+
}
|
|
163
222
|
function asRecord(value) {
|
|
164
223
|
return value && typeof value === "object" ? value : null;
|
|
165
224
|
}
|
|
@@ -224,13 +283,42 @@ export const QuotaToastPlugin = async ({ client }) => {
|
|
|
224
283
|
}
|
|
225
284
|
return false;
|
|
226
285
|
}
|
|
286
|
+
function getPluginRuntimeRootHints() {
|
|
287
|
+
const cwd = process.cwd();
|
|
288
|
+
return {
|
|
289
|
+
workspaceRoot: cwd,
|
|
290
|
+
configRoot: cwd,
|
|
291
|
+
fallbackDirectory: cwd,
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
async function resolvePluginRuntimeContext(params = {}) {
|
|
295
|
+
if (!configLoaded) {
|
|
296
|
+
await refreshConfig();
|
|
297
|
+
}
|
|
298
|
+
return resolveQuotaRuntimeContext({
|
|
299
|
+
client: typedClient,
|
|
300
|
+
roots: getPluginRuntimeRootHints(),
|
|
301
|
+
config,
|
|
302
|
+
configMeta,
|
|
303
|
+
providers: runtimeProviders,
|
|
304
|
+
sessionID: params.sessionID,
|
|
305
|
+
sessionMeta: params.sessionMeta,
|
|
306
|
+
resolveSessionMeta: (sessionID) => getSessionModelMeta(sessionID),
|
|
307
|
+
includeSessionMeta: params.includeSessionMeta,
|
|
308
|
+
});
|
|
309
|
+
}
|
|
227
310
|
async function refreshConfig() {
|
|
228
311
|
if (configInFlight)
|
|
229
312
|
return configInFlight;
|
|
230
313
|
configInFlight = (async () => {
|
|
231
314
|
try {
|
|
232
|
-
|
|
233
|
-
|
|
315
|
+
const runtime = await resolveQuotaRuntimeContext({
|
|
316
|
+
client: typedClient,
|
|
317
|
+
roots: getPluginRuntimeRootHints(),
|
|
318
|
+
});
|
|
319
|
+
configMeta = runtime.configMeta;
|
|
320
|
+
config = runtime.config;
|
|
321
|
+
runtimeProviders = runtime.providers;
|
|
234
322
|
setPricingSnapshotAutoRefresh(config.pricingSnapshot.autoRefresh);
|
|
235
323
|
setPricingSnapshotSelection(config.pricingSnapshot.source);
|
|
236
324
|
configLoaded = true;
|
|
@@ -239,6 +327,8 @@ export const QuotaToastPlugin = async ({ client }) => {
|
|
|
239
327
|
catch {
|
|
240
328
|
// Leave configLoaded=false so we can retry on next trigger.
|
|
241
329
|
config = DEFAULT_CONFIG;
|
|
330
|
+
configMeta = createLoadConfigMeta();
|
|
331
|
+
runtimeProviders = getProviders();
|
|
242
332
|
setPricingSnapshotAutoRefresh(DEFAULT_CONFIG.pricingSnapshot.autoRefresh);
|
|
243
333
|
setPricingSnapshotSelection(DEFAULT_CONFIG.pricingSnapshot.source);
|
|
244
334
|
}
|
|
@@ -394,11 +484,12 @@ export const QuotaToastPlugin = async ({ client }) => {
|
|
|
394
484
|
}
|
|
395
485
|
return "current session";
|
|
396
486
|
}
|
|
397
|
-
async function buildQuotaCommandUnavailableMessage(
|
|
487
|
+
async function buildQuotaCommandUnavailableMessage(runtime) {
|
|
398
488
|
const selection = await resolveQuotaRenderSelection({
|
|
399
|
-
client:
|
|
400
|
-
config,
|
|
401
|
-
request:
|
|
489
|
+
client: runtime.client,
|
|
490
|
+
config: runtime.config,
|
|
491
|
+
request: createQuotaRuntimeRequestContext(runtime),
|
|
492
|
+
providers: runtime.providers,
|
|
402
493
|
});
|
|
403
494
|
if (!selection) {
|
|
404
495
|
return "Quota unavailable\n\nNo enabled quota providers are configured.\n\nRun /quota_status for diagnostics.";
|
|
@@ -461,177 +552,349 @@ export const QuotaToastPlugin = async ({ client }) => {
|
|
|
461
552
|
function clearToastCacheForSession(params) {
|
|
462
553
|
clearCache(buildToastCacheKey(params));
|
|
463
554
|
}
|
|
464
|
-
|
|
555
|
+
function isProviderFetchFailureOnly(errors) {
|
|
556
|
+
return (errors.length > 0 && errors.every((error) => error.message === "Failed to read quota data"));
|
|
557
|
+
}
|
|
558
|
+
async function fetchQuotaMessageResult(params) {
|
|
465
559
|
// Ensure we have loaded config at least once. If load fails, we keep trying
|
|
466
|
-
// on subsequent triggers.
|
|
560
|
+
// on subsequent triggers and queue a deferred retry for toast paths.
|
|
467
561
|
if (!configLoaded) {
|
|
468
562
|
await refreshConfig();
|
|
469
563
|
}
|
|
564
|
+
if (!configLoaded) {
|
|
565
|
+
return {
|
|
566
|
+
message: config.debug
|
|
567
|
+
? formatDebugInfo({
|
|
568
|
+
trigger: params.trigger,
|
|
569
|
+
reason: "config load failed",
|
|
570
|
+
enabledProviders: config.enabledProviders,
|
|
571
|
+
})
|
|
572
|
+
: null,
|
|
573
|
+
cacheRenderedMessage: false,
|
|
574
|
+
retryable: true,
|
|
575
|
+
retryReason: "config_load_failed",
|
|
576
|
+
hasQuotaRows: false,
|
|
577
|
+
};
|
|
578
|
+
}
|
|
470
579
|
if (!config.enabled) {
|
|
471
|
-
return
|
|
472
|
-
|
|
473
|
-
|
|
580
|
+
return {
|
|
581
|
+
message: config.debug
|
|
582
|
+
? formatDebugInfo({ trigger: params.trigger, reason: "disabled", enabledProviders: [] })
|
|
583
|
+
: null,
|
|
584
|
+
cacheRenderedMessage: false,
|
|
585
|
+
retryable: false,
|
|
586
|
+
hasQuotaRows: false,
|
|
587
|
+
};
|
|
474
588
|
}
|
|
475
589
|
if (config.enabledProviders !== "auto" && config.enabledProviders.length === 0) {
|
|
476
|
-
return
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
590
|
+
return {
|
|
591
|
+
message: config.debug
|
|
592
|
+
? formatDebugInfo({
|
|
593
|
+
trigger: params.trigger,
|
|
594
|
+
reason: "enabledProviders empty",
|
|
595
|
+
enabledProviders: [],
|
|
596
|
+
})
|
|
597
|
+
: null,
|
|
598
|
+
cacheRenderedMessage: false,
|
|
599
|
+
retryable: false,
|
|
600
|
+
hasQuotaRows: false,
|
|
601
|
+
};
|
|
483
602
|
}
|
|
484
|
-
const
|
|
603
|
+
const runtime = await resolvePluginRuntimeContext({
|
|
485
604
|
sessionID: params.sessionID,
|
|
486
|
-
sessionMeta:
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
605
|
+
sessionMeta: params.sessionMeta,
|
|
606
|
+
includeSessionMeta: (config) => config.onlyCurrentModel,
|
|
607
|
+
});
|
|
608
|
+
const runtimeConfig = runtime.config;
|
|
609
|
+
const quotaRequestContext = createQuotaRuntimeRequestContext(runtime);
|
|
490
610
|
const quotaResult = await collectQuotaRenderData({
|
|
491
|
-
client:
|
|
492
|
-
config,
|
|
611
|
+
client: runtime.client,
|
|
612
|
+
config: runtimeConfig,
|
|
493
613
|
request: quotaRequestContext,
|
|
494
614
|
surfaceExplicitProviderIssues: true,
|
|
495
|
-
formatStyle: resolveQuotaFormatStyle(
|
|
615
|
+
formatStyle: resolveQuotaFormatStyle(runtimeConfig.formatStyle),
|
|
616
|
+
bypassProviderCache: params.bypassProviderCache,
|
|
617
|
+
providers: runtime.providers,
|
|
496
618
|
});
|
|
497
619
|
const { selection, availability, active, attemptedAny, hasExplicitProviderIssues, data } = quotaResult;
|
|
498
|
-
if (
|
|
620
|
+
if (runtimeConfig.showSessionTokens && params.sessionID) {
|
|
499
621
|
lastSessionTokenError = quotaResult.sessionTokenError;
|
|
500
622
|
}
|
|
501
623
|
const currentModel = selection?.currentModel;
|
|
502
624
|
const errors = data?.errors ?? [];
|
|
625
|
+
const hasProviderQuotaRows = Boolean(data?.entries.length);
|
|
626
|
+
const hasQuotaRows = Boolean(hasProviderQuotaRows || data?.sessionTokens);
|
|
627
|
+
const providerFetchFailureOnly = attemptedAny && isProviderFetchFailureOnly(errors);
|
|
628
|
+
const retryableAvailabilityFailure = active.length === 0 && availability.some((item) => !item.ok && item.error === true);
|
|
503
629
|
if (active.length === 0 && !(hasExplicitProviderIssues && errors.length > 0)) {
|
|
504
|
-
|
|
630
|
+
const message = runtimeConfig.debug
|
|
505
631
|
? formatDebugInfo({
|
|
506
632
|
trigger: params.trigger,
|
|
507
633
|
reason: "no enabled providers available",
|
|
508
634
|
currentModel,
|
|
509
|
-
enabledProviders:
|
|
635
|
+
enabledProviders: runtimeConfig.enabledProviders,
|
|
510
636
|
availability: availability.map((item) => ({
|
|
511
637
|
id: item.provider.id,
|
|
512
638
|
ok: item.ok,
|
|
513
639
|
})),
|
|
514
640
|
})
|
|
515
641
|
: null;
|
|
642
|
+
const retryableNoProviders = selection?.isAutoMode === true || retryableAvailabilityFailure;
|
|
643
|
+
return {
|
|
644
|
+
message,
|
|
645
|
+
cacheRenderedMessage: false,
|
|
646
|
+
retryable: retryableNoProviders,
|
|
647
|
+
retryReason: retryableNoProviders ? "no_available_providers" : undefined,
|
|
648
|
+
hasQuotaRows: false,
|
|
649
|
+
};
|
|
516
650
|
}
|
|
517
|
-
if (
|
|
651
|
+
if (hasQuotaRows) {
|
|
518
652
|
const formatted = formatQuotaRows({
|
|
519
653
|
version: "1.0.0",
|
|
520
|
-
layout:
|
|
521
|
-
entries: data
|
|
522
|
-
errors: data
|
|
523
|
-
style: resolveQuotaFormatStyle(
|
|
524
|
-
percentDisplayMode:
|
|
525
|
-
sessionTokens: data
|
|
654
|
+
layout: runtimeConfig.layout,
|
|
655
|
+
entries: data?.entries ?? [],
|
|
656
|
+
errors: data?.errors ?? [],
|
|
657
|
+
style: resolveQuotaFormatStyle(runtimeConfig.formatStyle),
|
|
658
|
+
percentDisplayMode: runtimeConfig.percentDisplayMode,
|
|
659
|
+
sessionTokens: data?.sessionTokens,
|
|
526
660
|
});
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
661
|
+
const retryableMaskedProviderFailure = !hasProviderQuotaRows && providerFetchFailureOnly;
|
|
662
|
+
if (!runtimeConfig.debug) {
|
|
663
|
+
return {
|
|
664
|
+
message: formatted,
|
|
665
|
+
cacheRenderedMessage: true,
|
|
666
|
+
retryable: retryableMaskedProviderFailure,
|
|
667
|
+
retryReason: retryableMaskedProviderFailure ? "provider_fetch_failed" : undefined,
|
|
668
|
+
hasQuotaRows: true,
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
const debugFooter = `\n\n[debug] src=${configMeta.source} providers=${runtimeConfig.enabledProviders === "auto" ? "(auto)" : runtimeConfig.enabledProviders.join(",") || "(none)"} avail=${availability
|
|
530
672
|
.map((item) => `${item.provider.id}:${item.ok ? "ok" : "no"}`)
|
|
531
673
|
.join(" ")}`;
|
|
532
|
-
return
|
|
674
|
+
return {
|
|
675
|
+
message: formatted + debugFooter,
|
|
676
|
+
cacheRenderedMessage: false,
|
|
677
|
+
retryable: retryableMaskedProviderFailure,
|
|
678
|
+
retryReason: retryableMaskedProviderFailure ? "provider_fetch_failed" : undefined,
|
|
679
|
+
hasQuotaRows: true,
|
|
680
|
+
};
|
|
533
681
|
}
|
|
534
682
|
// Show errors even without entries when:
|
|
535
683
|
// 1. showOnBothFail is enabled and at least one provider attempted (existing behavior)
|
|
536
684
|
// 2. OR we're in explicit mode and have "Not configured"/"Unavailable" errors (new behavior)
|
|
537
|
-
if ((
|
|
685
|
+
if ((runtimeConfig.showOnBothFail && attemptedAny && errors.length > 0) ||
|
|
686
|
+
hasExplicitProviderIssues) {
|
|
538
687
|
const errorLines = errors.map((error) => `${error.label}: ${error.message}`).join("\n");
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
"
|
|
543
|
-
|
|
688
|
+
const retryableFetchFailure = !hasExplicitProviderIssues && providerFetchFailureOnly;
|
|
689
|
+
const retryableFailure = retryableFetchFailure || retryableAvailabilityFailure;
|
|
690
|
+
const retryReason = retryableFetchFailure
|
|
691
|
+
? "provider_fetch_failed"
|
|
692
|
+
: retryableAvailabilityFailure
|
|
693
|
+
? "no_available_providers"
|
|
694
|
+
: undefined;
|
|
695
|
+
const message = !runtimeConfig.debug
|
|
696
|
+
? errorLines || "Quota unavailable"
|
|
697
|
+
: (errorLines || "Quota unavailable") +
|
|
698
|
+
"\n\n" +
|
|
699
|
+
formatDebugInfo({
|
|
700
|
+
trigger: params.trigger,
|
|
701
|
+
reason: hasExplicitProviderIssues
|
|
702
|
+
? "providers missing/unavailable"
|
|
703
|
+
: "all providers failed",
|
|
704
|
+
currentModel,
|
|
705
|
+
enabledProviders: runtimeConfig.enabledProviders,
|
|
706
|
+
availability: availability.map((item) => ({
|
|
707
|
+
id: item.provider.id,
|
|
708
|
+
ok: item.ok,
|
|
709
|
+
})),
|
|
710
|
+
});
|
|
711
|
+
return {
|
|
712
|
+
message,
|
|
713
|
+
cacheRenderedMessage: false,
|
|
714
|
+
retryable: retryableFailure,
|
|
715
|
+
retryReason,
|
|
716
|
+
hasQuotaRows: false,
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
const retryableNoData = providerFetchFailureOnly ||
|
|
720
|
+
(selection?.isAutoMode === true && active.length > 0 && errors.length === 0);
|
|
721
|
+
return {
|
|
722
|
+
message: runtimeConfig.debug
|
|
723
|
+
? formatDebugInfo({
|
|
544
724
|
trigger: params.trigger,
|
|
545
|
-
reason:
|
|
546
|
-
? "providers missing/unavailable"
|
|
547
|
-
: "all providers failed",
|
|
725
|
+
reason: "no entries",
|
|
548
726
|
currentModel,
|
|
549
|
-
enabledProviders:
|
|
727
|
+
enabledProviders: runtimeConfig.enabledProviders,
|
|
550
728
|
availability: availability.map((item) => ({
|
|
551
729
|
id: item.provider.id,
|
|
552
730
|
ok: item.ok,
|
|
553
731
|
})),
|
|
554
|
-
})
|
|
732
|
+
})
|
|
733
|
+
: null,
|
|
734
|
+
cacheRenderedMessage: false,
|
|
735
|
+
retryable: retryableNoData,
|
|
736
|
+
retryReason: providerFetchFailureOnly
|
|
737
|
+
? "provider_fetch_failed"
|
|
738
|
+
: retryableNoData
|
|
739
|
+
? "no_reportable_data"
|
|
740
|
+
: undefined,
|
|
741
|
+
hasQuotaRows: false,
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
async function fetchQuotaMessage(params) {
|
|
745
|
+
const result = await fetchQuotaMessageResult(params);
|
|
746
|
+
return result.message;
|
|
747
|
+
}
|
|
748
|
+
async function reconcileDeferredQuotaRefresh(params) {
|
|
749
|
+
const existing = deferredQuotaRefreshes.get(params.sessionID);
|
|
750
|
+
if (!params.result.retryable) {
|
|
751
|
+
if (existing) {
|
|
752
|
+
clearDeferredQuotaRefresh(params.sessionID);
|
|
753
|
+
await log("Deferred quota refresh cleared", {
|
|
754
|
+
sessionID: params.sessionID,
|
|
755
|
+
trigger: params.trigger,
|
|
756
|
+
reason: params.result.hasQuotaRows ? "quota_rows_available" : "not_retryable",
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
return;
|
|
555
760
|
}
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
ok: item.ok,
|
|
565
|
-
})),
|
|
566
|
-
})
|
|
567
|
-
: null;
|
|
761
|
+
if (!params.result.retryReason) {
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
scheduleDeferredQuotaRefresh({
|
|
765
|
+
sessionID: params.sessionID,
|
|
766
|
+
reason: params.result.retryReason,
|
|
767
|
+
incrementAttempts: params.consumedDeferredRetry,
|
|
768
|
+
});
|
|
568
769
|
}
|
|
569
770
|
/**
|
|
570
771
|
* Show quota toast for a session
|
|
571
772
|
*/
|
|
572
|
-
async function showQuotaToast(sessionID, trigger) {
|
|
773
|
+
async function showQuotaToast(sessionID, trigger, options = {}) {
|
|
573
774
|
if (!configLoaded) {
|
|
574
775
|
await refreshConfig();
|
|
575
776
|
}
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
// Do not cache when output is only error rows (rendered as "label: message").
|
|
586
|
-
const lines = msg.split("\n");
|
|
587
|
-
return lines.some((l) => /\b\d{1,3}%\b/.test(l) && !/:\s/.test(l));
|
|
588
|
-
}
|
|
589
|
-
const sessionMeta = await getSessionModelMeta(sessionID);
|
|
590
|
-
const bypassMessageCache = config.debug
|
|
591
|
-
? true
|
|
592
|
-
: await shouldBypassToastCacheForLiveLocalUsage({ trigger, sessionID, sessionMeta });
|
|
593
|
-
const toastCacheKey = buildToastCacheKey({ sessionID, sessionMeta });
|
|
594
|
-
const message = bypassMessageCache
|
|
595
|
-
? await fetchQuotaMessage({ trigger, sessionID, sessionMeta })
|
|
596
|
-
: await getOrFetchWithCacheControl(toastCacheKey, async () => {
|
|
597
|
-
const msg = await fetchQuotaMessage({ trigger, sessionID, sessionMeta });
|
|
598
|
-
const cache = msg ? shouldCacheToastMessage(msg) : true;
|
|
599
|
-
return { message: msg, cache };
|
|
600
|
-
}, config.minIntervalMs);
|
|
601
|
-
if (!message) {
|
|
602
|
-
await log("No quota message to display", { trigger });
|
|
603
|
-
return;
|
|
604
|
-
}
|
|
605
|
-
if (!config.enableToast) {
|
|
606
|
-
await log("Toast disabled (enableToast=false)", { trigger });
|
|
607
|
-
return;
|
|
777
|
+
const pendingDeferred = deferredQuotaRefreshes.get(sessionID);
|
|
778
|
+
const consumedDeferredRetry = options.deferredRetry === true || Boolean(pendingDeferred);
|
|
779
|
+
if (pendingDeferred) {
|
|
780
|
+
if (pendingDeferred.inFlight && !options.deferredRetry) {
|
|
781
|
+
await log("Skipping duplicate deferred quota refresh", { sessionID, trigger });
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
pendingDeferred.inFlight = true;
|
|
785
|
+
clearDeferredQuotaRefreshTimer(pendingDeferred);
|
|
608
786
|
}
|
|
609
|
-
// Show toast
|
|
610
787
|
try {
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
}
|
|
788
|
+
// Check if session is a subagent session
|
|
789
|
+
if (await isSubagentSession(sessionID)) {
|
|
790
|
+
if (consumedDeferredRetry) {
|
|
791
|
+
clearDeferredQuotaRefresh(sessionID);
|
|
792
|
+
}
|
|
793
|
+
await log("Skipping toast for subagent session", { sessionID, trigger });
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
// Get or fetch quota (with caching/throttling)
|
|
797
|
+
// If debug is enabled, bypass caching so the toast reflects current state.
|
|
798
|
+
function shouldCacheToastMessage(msg) {
|
|
799
|
+
// Cache when we have any quota row (which always includes a "NN%" token).
|
|
800
|
+
// Do not cache when output is only error rows (rendered as "label: message").
|
|
801
|
+
const lines = msg.split("\n");
|
|
802
|
+
return lines.some((l) => /\b\d+%\b/.test(l) && !/:\s/.test(l));
|
|
803
|
+
}
|
|
804
|
+
const sessionMeta = await getSessionModelMeta(sessionID);
|
|
805
|
+
const bypassForLiveLocalUsage = await shouldBypassToastCacheForLiveLocalUsage({
|
|
806
|
+
trigger,
|
|
807
|
+
sessionID,
|
|
808
|
+
sessionMeta,
|
|
617
809
|
});
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
810
|
+
const bypassMessageCache = config.debug || consumedDeferredRetry || bypassForLiveLocalUsage;
|
|
811
|
+
const bypassProviderCache = consumedDeferredRetry || bypassForLiveLocalUsage;
|
|
812
|
+
const toastCacheKey = buildToastCacheKey({ sessionID, sessionMeta });
|
|
813
|
+
let fetchResult;
|
|
814
|
+
const fetchForToast = () => fetchQuotaMessageResult({
|
|
815
|
+
trigger,
|
|
816
|
+
sessionID,
|
|
817
|
+
sessionMeta,
|
|
818
|
+
bypassProviderCache,
|
|
623
819
|
});
|
|
820
|
+
const message = bypassMessageCache
|
|
821
|
+
? await (async () => {
|
|
822
|
+
fetchResult = await fetchForToast();
|
|
823
|
+
return fetchResult.message;
|
|
824
|
+
})()
|
|
825
|
+
: await (async () => {
|
|
826
|
+
const fetched = {};
|
|
827
|
+
const cachedMessage = await getOrFetchWithCacheControl(toastCacheKey, async () => {
|
|
828
|
+
const result = await fetchForToast();
|
|
829
|
+
fetched.result = result;
|
|
830
|
+
const cache = result.message
|
|
831
|
+
? result.cacheRenderedMessage && shouldCacheToastMessage(result.message)
|
|
832
|
+
: result.cacheRenderedMessage;
|
|
833
|
+
return { message: result.message, cache };
|
|
834
|
+
}, config.minIntervalMs);
|
|
835
|
+
fetchResult = fetched.result;
|
|
836
|
+
return cachedMessage;
|
|
837
|
+
})();
|
|
838
|
+
if (fetchResult) {
|
|
839
|
+
await reconcileDeferredQuotaRefresh({
|
|
840
|
+
sessionID,
|
|
841
|
+
result: fetchResult,
|
|
842
|
+
consumedDeferredRetry,
|
|
843
|
+
trigger,
|
|
844
|
+
});
|
|
845
|
+
}
|
|
846
|
+
if (options.deferredRetry && fetchResult && !fetchResult.hasQuotaRows) {
|
|
847
|
+
await log("Deferred quota refresh did not produce reportable data", {
|
|
848
|
+
sessionID,
|
|
849
|
+
trigger,
|
|
850
|
+
retryable: fetchResult.retryable,
|
|
851
|
+
retryReason: fetchResult.retryReason,
|
|
852
|
+
});
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
if (!message) {
|
|
856
|
+
await log("No quota message to display", { trigger });
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
if (!config.enableToast) {
|
|
860
|
+
await log("Toast disabled (enableToast=false)", { trigger });
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
// Show toast
|
|
864
|
+
try {
|
|
865
|
+
await typedClient.tui.showToast({
|
|
866
|
+
body: {
|
|
867
|
+
message: sanitizeDisplayText(message),
|
|
868
|
+
variant: "info",
|
|
869
|
+
duration: config.toastDurationMs,
|
|
870
|
+
},
|
|
871
|
+
});
|
|
872
|
+
await log("Displayed quota toast", { message, trigger });
|
|
873
|
+
}
|
|
874
|
+
catch (err) {
|
|
875
|
+
await log("Failed to show toast", {
|
|
876
|
+
error: err instanceof Error ? err.message : String(err),
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
finally {
|
|
881
|
+
const state = deferredQuotaRefreshes.get(sessionID);
|
|
882
|
+
if (state) {
|
|
883
|
+
state.inFlight = false;
|
|
884
|
+
}
|
|
624
885
|
}
|
|
625
886
|
}
|
|
626
|
-
async function fetchQuotaCommandData(
|
|
887
|
+
async function fetchQuotaCommandData(runtime) {
|
|
888
|
+
const request = createQuotaRuntimeRequestContext(runtime);
|
|
627
889
|
const quotaResult = await collectQuotaRenderData({
|
|
628
|
-
client:
|
|
629
|
-
config,
|
|
630
|
-
request
|
|
890
|
+
client: runtime.client,
|
|
891
|
+
config: runtime.config,
|
|
892
|
+
request,
|
|
631
893
|
surfaceExplicitProviderIssues: false,
|
|
632
894
|
formatStyle: ALL_WINDOWS_FORMAT_STYLE,
|
|
895
|
+
providers: runtime.providers,
|
|
633
896
|
});
|
|
634
|
-
if (config.showSessionTokens &&
|
|
897
|
+
if (runtime.config.showSessionTokens && request.sessionID) {
|
|
635
898
|
lastSessionTokenError = quotaResult.sessionTokenError;
|
|
636
899
|
}
|
|
637
900
|
return quotaResult.data;
|
|
@@ -656,11 +919,15 @@ export const QuotaToastPlugin = async ({ client }) => {
|
|
|
656
919
|
});
|
|
657
920
|
}
|
|
658
921
|
async function buildStatusReport(params) {
|
|
659
|
-
await
|
|
660
|
-
|
|
922
|
+
const runtime = await resolvePluginRuntimeContext({
|
|
923
|
+
sessionID: params.sessionID,
|
|
924
|
+
includeSessionMeta: true,
|
|
925
|
+
});
|
|
926
|
+
const runtimeConfig = runtime.config;
|
|
927
|
+
if (!runtimeConfig.enabled)
|
|
661
928
|
return null;
|
|
662
929
|
await kickPricingRefresh({ reason: "status", maxWaitMs: 750 });
|
|
663
|
-
const currentSession =
|
|
930
|
+
const currentSession = runtime.session.sessionMeta ?? {};
|
|
664
931
|
const currentModel = currentSession.modelID;
|
|
665
932
|
const currentProviderID = currentSession.providerID;
|
|
666
933
|
const sessionModelLookup = !params.sessionID
|
|
@@ -668,24 +935,13 @@ export const QuotaToastPlugin = async ({ client }) => {
|
|
|
668
935
|
: currentModel
|
|
669
936
|
? "ok"
|
|
670
937
|
: "not_found";
|
|
671
|
-
const isAutoMode =
|
|
672
|
-
const providers =
|
|
938
|
+
const isAutoMode = runtimeConfig.enabledProviders === "auto";
|
|
939
|
+
const providers = runtime.providers;
|
|
940
|
+
const providerContext = createQuotaProviderRuntimeContext(runtime);
|
|
673
941
|
const availability = await Promise.all(providers.map(async (p) => {
|
|
674
942
|
let ok = false;
|
|
675
943
|
try {
|
|
676
|
-
ok = await p.isAvailable(
|
|
677
|
-
client: typedClient,
|
|
678
|
-
config: {
|
|
679
|
-
googleModels: config.googleModels,
|
|
680
|
-
anthropicBinaryPath: config.anthropicBinaryPath,
|
|
681
|
-
alibabaCodingPlanTier: config.alibabaCodingPlanTier,
|
|
682
|
-
cursorPlan: config.cursorPlan,
|
|
683
|
-
cursorIncludedApiUsd: config.cursorIncludedApiUsd,
|
|
684
|
-
cursorBillingCycleStartDay: config.cursorBillingCycleStartDay,
|
|
685
|
-
currentModel,
|
|
686
|
-
currentProviderID,
|
|
687
|
-
},
|
|
688
|
-
});
|
|
944
|
+
ok = await p.isAvailable(providerContext);
|
|
689
945
|
}
|
|
690
946
|
catch {
|
|
691
947
|
ok = false;
|
|
@@ -693,7 +949,7 @@ export const QuotaToastPlugin = async ({ client }) => {
|
|
|
693
949
|
return {
|
|
694
950
|
id: p.id,
|
|
695
951
|
// In auto mode, a provider is effectively "enabled" if it's available.
|
|
696
|
-
enabled: isAutoMode ? ok :
|
|
952
|
+
enabled: isAutoMode ? ok : runtimeConfig.enabledProviders.includes(p.id),
|
|
697
953
|
available: ok,
|
|
698
954
|
matchesCurrentModel: currentModel || isCursorProviderId(currentProviderID)
|
|
699
955
|
? matchesQuotaProviderCurrentSelection({
|
|
@@ -716,12 +972,9 @@ export const QuotaToastPlugin = async ({ client }) => {
|
|
|
716
972
|
if (liveProbeProviders.length > 0) {
|
|
717
973
|
try {
|
|
718
974
|
providerLiveProbes = await collectQuotaStatusLiveProbes({
|
|
719
|
-
client:
|
|
720
|
-
config,
|
|
721
|
-
request:
|
|
722
|
-
sessionID: params.sessionID,
|
|
723
|
-
sessionMeta: currentSession,
|
|
724
|
-
},
|
|
975
|
+
client: runtime.client,
|
|
976
|
+
config: runtimeConfig,
|
|
977
|
+
request: createQuotaRuntimeRequestContext(runtime),
|
|
725
978
|
formatStyle: SINGLE_WINDOW_PER_PROVIDER_FORMAT_STYLE,
|
|
726
979
|
providers: liveProbeProviders,
|
|
727
980
|
});
|
|
@@ -743,20 +996,24 @@ export const QuotaToastPlugin = async ({ client }) => {
|
|
|
743
996
|
const refresh = params.refreshGoogleTokens
|
|
744
997
|
? await refreshGoogleTokensForAllAccounts({ skewMs: params.skewMs, force: params.force })
|
|
745
998
|
: null;
|
|
746
|
-
const tuiDiagnostics = await inspectTuiConfig();
|
|
999
|
+
const tuiDiagnostics = await inspectTuiConfig({ roots: runtime.roots });
|
|
747
1000
|
return await buildQuotaStatusReport({
|
|
748
1001
|
tuiDiagnostics,
|
|
749
|
-
configSource: configMeta.source,
|
|
750
|
-
configPaths: configMeta.paths,
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
1002
|
+
configSource: runtime.configMeta.source,
|
|
1003
|
+
configPaths: runtime.configMeta.paths,
|
|
1004
|
+
globalConfigPaths: runtime.configMeta.globalConfigPaths,
|
|
1005
|
+
workspaceConfigPaths: runtime.configMeta.workspaceConfigPaths,
|
|
1006
|
+
settingSources: runtime.configMeta.settingSources,
|
|
1007
|
+
configIssues: runtime.configMeta.configIssues,
|
|
1008
|
+
enabledProviders: runtimeConfig.enabledProviders,
|
|
1009
|
+
anthropicBinaryPath: runtimeConfig.anthropicBinaryPath,
|
|
1010
|
+
alibabaCodingPlanTier: runtimeConfig.alibabaCodingPlanTier,
|
|
1011
|
+
cursorPlan: runtimeConfig.cursorPlan,
|
|
1012
|
+
cursorIncludedApiUsd: runtimeConfig.cursorIncludedApiUsd,
|
|
1013
|
+
cursorBillingCycleStartDay: runtimeConfig.cursorBillingCycleStartDay,
|
|
1014
|
+
opencodeGoWindows: runtimeConfig.opencodeGoWindows,
|
|
1015
|
+
pricingSnapshotSource: runtimeConfig.pricingSnapshot.source,
|
|
1016
|
+
onlyCurrentModel: runtimeConfig.onlyCurrentModel,
|
|
760
1017
|
currentModel,
|
|
761
1018
|
sessionModelLookup,
|
|
762
1019
|
providerAvailability: availability,
|
|
@@ -770,6 +1027,7 @@ export const QuotaToastPlugin = async ({ client }) => {
|
|
|
770
1027
|
}
|
|
771
1028
|
: { attempted: false },
|
|
772
1029
|
sessionTokenError: lastSessionTokenError,
|
|
1030
|
+
geminiCliClient: typedClient,
|
|
773
1031
|
generatedAtMs: params.generatedAtMs,
|
|
774
1032
|
});
|
|
775
1033
|
}
|
|
@@ -832,19 +1090,21 @@ export const QuotaToastPlugin = async ({ client }) => {
|
|
|
832
1090
|
async function handleQuotaSlashCommand(input) {
|
|
833
1091
|
const sessionID = input.sessionID;
|
|
834
1092
|
const generatedAtMs = Date.now();
|
|
835
|
-
const
|
|
1093
|
+
const sessionMeta = sessionID ? await getSessionModelMeta(sessionID) : undefined;
|
|
1094
|
+
const runtime = await resolvePluginRuntimeContext({
|
|
836
1095
|
sessionID,
|
|
837
|
-
sessionMeta
|
|
838
|
-
|
|
839
|
-
|
|
1096
|
+
sessionMeta,
|
|
1097
|
+
includeSessionMeta: (config) => config.onlyCurrentModel,
|
|
1098
|
+
});
|
|
1099
|
+
const reportData = await fetchQuotaCommandData(runtime);
|
|
840
1100
|
if (!reportData) {
|
|
841
1101
|
if (!configLoaded) {
|
|
842
1102
|
return await injectCommandOutputAndHandle(sessionID, "Quota unavailable (config not loaded, try again)");
|
|
843
1103
|
}
|
|
844
|
-
if (!config.enabled) {
|
|
1104
|
+
if (!runtime.config.enabled) {
|
|
845
1105
|
return await injectCommandOutputAndHandle(sessionID, "Quota disabled in config (enabled: false)");
|
|
846
1106
|
}
|
|
847
|
-
return await injectCommandOutputAndHandle(sessionID, await buildQuotaCommandUnavailableMessage(
|
|
1107
|
+
return await injectCommandOutputAndHandle(sessionID, await buildQuotaCommandUnavailableMessage(runtime));
|
|
848
1108
|
}
|
|
849
1109
|
return await injectCommandOutputAndHandle(sessionID, formatQuotaCommand({
|
|
850
1110
|
...reportData,
|
|
@@ -1087,8 +1347,10 @@ export const QuotaToastPlugin = async ({ client }) => {
|
|
|
1087
1347
|
if (!configLoaded) {
|
|
1088
1348
|
await refreshConfig();
|
|
1089
1349
|
}
|
|
1090
|
-
if (!config.enabled)
|
|
1350
|
+
if (!config.enabled) {
|
|
1351
|
+
clearDeferredQuotaRefresh(sessionID);
|
|
1091
1352
|
return;
|
|
1353
|
+
}
|
|
1092
1354
|
if (event.type === "session.idle" && config.showOnIdle) {
|
|
1093
1355
|
await showQuotaToast(sessionID, "session.idle");
|
|
1094
1356
|
}
|
|
@@ -1103,8 +1365,10 @@ export const QuotaToastPlugin = async ({ client }) => {
|
|
|
1103
1365
|
if (!configLoaded) {
|
|
1104
1366
|
await refreshConfig();
|
|
1105
1367
|
}
|
|
1106
|
-
if (!config.enabled)
|
|
1368
|
+
if (!config.enabled) {
|
|
1369
|
+
clearDeferredQuotaRefresh(input.sessionID);
|
|
1107
1370
|
return;
|
|
1371
|
+
}
|
|
1108
1372
|
if (isSuccessfulQuestionExecution(output)) {
|
|
1109
1373
|
const sessionMeta = await getSessionModelMeta(input.sessionID);
|
|
1110
1374
|
const model = sessionMeta.modelID;
|