rate limit the quota update: once every 2 min
This commit is contained in:
parent
a1fe7c7a87
commit
ac29d26e12
@ -238,7 +238,8 @@ instead of being shown as zero.
|
||||
The sidebar refreshes after each completed turn (`session.idle`, plus `session.status`,
|
||||
`session.updated`, and `message.updated` as fallbacks since `session.idle` is a server-plugin
|
||||
event that may never reach the TUI bus — all debounced) and when the active session changes,
|
||||
plus every 3 minutes as a fallback (`COMMANDCODE_QUOTA_INTERVAL_MS` overrides it) and the
|
||||
but never more than once every 2 minutes; every 5 minutes as a fallback
|
||||
(`COMMANDCODE_QUOTA_INTERVAL_MS` overrides the fallback interval) and the
|
||||
countdown ticks every 30 seconds. Key resolution and fetch failures are redacted and shown
|
||||
inline; the panel never stays on `loading…` and never blocks the provider. Extra trace lines
|
||||
(`tui-quota`) are appended to the debug log only when debug tracing is on.
|
||||
|
||||
20
src/tui.tsx
20
src/tui.tsx
@ -69,9 +69,11 @@ type TuiApi = {
|
||||
const ID = "commandcode-toggles";
|
||||
const CATEGORY = "CommandCode";
|
||||
const SIDEBAR_ORDER = 90;
|
||||
const DEFAULT_QUOTA_INTERVAL_MS = 180_000;
|
||||
const DEFAULT_QUOTA_INTERVAL_MS = 300_000;
|
||||
const MIN_REFRESH_INTERVAL_MS = 120_000;
|
||||
// Extra TUI-bus signals that mark the end of a turn. `session.idle` is a server
|
||||
// plugin event and may never fire here; these keep the panel fresh regardless.
|
||||
// All event-driven refreshes are floored to MIN_REFRESH_INTERVAL_MS (hard skip).
|
||||
const QUOTA_TRIGGER_EVENTS = ["session.idle", "session.status", "session.updated", "message.updated"] as const;
|
||||
|
||||
function safeEnv(name: string): string | undefined {
|
||||
@ -168,6 +170,11 @@ function quotaIntervalMs(): number {
|
||||
return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_QUOTA_INTERVAL_MS;
|
||||
}
|
||||
|
||||
function formatClock(ms: number): string {
|
||||
if (!(ms > 0)) return "--:--:--";
|
||||
return new Date(ms).toTimeString().slice(0, 8);
|
||||
}
|
||||
|
||||
function shortWindow(w: QuotaWindow, nowMs: number): string {
|
||||
const label = w.id === "fiveHour" ? "5h" : w.id === "weekly" ? "7d" : "mo";
|
||||
const reset = formatReset(w.resetAtMs, nowMs);
|
||||
@ -180,9 +187,11 @@ export const tui = async (api: TuiApi): Promise<void> => {
|
||||
const [toggles, setToggles] = createSignal(readToggles());
|
||||
const [quota, setQuota] = createSignal<QuotaResult | null>(null);
|
||||
const [now, setNow] = createSignal(Date.now());
|
||||
const [updatedAt, setUpdatedAt] = createSignal(0);
|
||||
|
||||
let inflight = false;
|
||||
let pending = false;
|
||||
let lastRefreshAt = 0;
|
||||
let debounceTimer: ReturnType<typeof setTimeout> | undefined;
|
||||
let lastSessionId: string | undefined;
|
||||
|
||||
@ -194,6 +203,11 @@ export const tui = async (api: TuiApi): Promise<void> => {
|
||||
trace("coalesced", "inflight");
|
||||
return;
|
||||
}
|
||||
const since = Date.now() - lastRefreshAt;
|
||||
if (lastRefreshAt > 0 && since < MIN_REFRESH_INTERVAL_MS) {
|
||||
trace("rate-limited", `remainingMs=${MIN_REFRESH_INTERVAL_MS - since}`);
|
||||
return;
|
||||
}
|
||||
inflight = true;
|
||||
try {
|
||||
const { key: apiKey, source } = resolveApiKeySource(api);
|
||||
@ -222,7 +236,9 @@ export const tui = async (api: TuiApi): Promise<void> => {
|
||||
trace("fetch-throw", message);
|
||||
} finally {
|
||||
inflight = false;
|
||||
lastRefreshAt = Date.now();
|
||||
setNow(Date.now());
|
||||
setUpdatedAt(Date.now());
|
||||
if (pending) {
|
||||
pending = false;
|
||||
trace("flush-pending");
|
||||
@ -284,6 +300,7 @@ export const tui = async (api: TuiApi): Promise<void> => {
|
||||
) : quota() === null ? (
|
||||
<text fg={theme.current.textMuted}>quota: loading…</text>
|
||||
) : null}
|
||||
<text fg={theme.current.textMuted}>updated @ {formatClock(updatedAt())}</text>
|
||||
</box>
|
||||
);
|
||||
};
|
||||
@ -365,6 +382,7 @@ export const tui = async (api: TuiApi): Promise<void> => {
|
||||
}
|
||||
setQuota(result);
|
||||
setNow(Date.now());
|
||||
setUpdatedAt(Date.now());
|
||||
api.ui.toast({ title: CATEGORY, message: formatQuota(result.quota), variant: "info", duration: 15000 });
|
||||
},
|
||||
},
|
||||
|
||||
Loading…
Reference in New Issue
Block a user