diff --git a/changelog.d/features/provider-quota-connection-visibility.md b/changelog.d/features/provider-quota-connection-visibility.md
new file mode 100644
index 0000000000..46e18fd848
--- /dev/null
+++ b/changelog.d/features/provider-quota-connection-visibility.md
@@ -0,0 +1,3 @@
+- Provider connections can now be shown or hidden individually on the Provider Quota page. The
+ visibility setting is available on each account in the provider detail view and does not affect
+ routing or account activation.
diff --git a/config/quality/complexity-baseline.json b/config/quality/complexity-baseline.json
index 6917dbb5e4..262870277a 100644
--- a/config/quality/complexity-baseline.json
+++ b/config/quality/complexity-baseline.json
@@ -1,6 +1,7 @@
{
"_comment": "Catraca de complexidade (check-complexity.mjs, ESLint core rules complexity>=15 e max-lines-per-function>80 sobre src+open-sse+electron+bin via eslint.complexity.config.mjs). Conta total de violacoes; so pode cair. --update ratcheta.",
- "count": 2058,
+ "count": 2059,
+ "_rebaseline_2026_07_18_pr7360_quota_visibility_resync": "2058->2059 (+1 vs recorded ceiling; measured 2056 fresh on release tip cab9e5f0c alone, so this ceiling still carries 2 units of un-banked slack from prior shrinkage — real regression from this merge is 2056->2059, +3). PR #7360 (JxnLexn) release-resync: merging origin/release/v3.8.49 to resolve the 3-file conflict (ConnectionRow.tsx/ConnectionsListPanel.tsx/useProviderConnections.ts) unions two already-compliant features in the same already-oversized god-component: release's confirm-delete-account wiring (#7361) and this PR's per-connection quota-visibility wiring. Diffed release-tip-only vs merged violation lists (scripts dumped via getComplexityEslintReport): most entries are the SAME pre-existing violations shifted a few lines (ConnectionRow/getStatusPresentation/inferErrorType — no count change) or marginally bigger (ConnectionRow function complexity 85->86, ConnectionsListPanel function 498->510 lines) from the two ConnectionRow call sites each gaining both PRs' multi-line JSX props. The 2 genuinely NEW crossings are the 'no tag' and 'tagged groups' .map() render callbacks in ConnectionsListPanel.tsx (83 and 85 lines, was <=80 on both parents individually) tipping over 80 lines specifically because both PRs' props land on the same call sites. No new logic was written during the resync itself (only import-statement unions); the growth is inherent to combining the two already-reviewed feature branches. Structural shrink tracked in #3501. Tighten via --update next cycle (true floor is 2056, not 2058).",
"_rebaseline_2026_07_17_v3849_ownerprs_providers": "2056->2058 (+2). v3.8.49 owner-PR merge campaign own-growth: the new provider handlers/dispatch branches merged this cycle (freetheai/felo/notion/segmind/deepinfra/novita/msdesigner image+video handlers, each adding a format-dispatch guard) pushed cyclomatic violations 2056->2058. Fast-gates PR->release do not run the complexity ratchet, so this surfaced only on re-sync. Spread across the new leaf handlers (not a single extractable function); measured on the release tip. Structural shrink tracked in #3501.",
"_rebaseline_2026_07_10_v3847_merge_burst": "2053->2054 (+1). Drift herdado do merge burst do dia em release/v3.8.47 (campanha /implement-prs: ~36 PRs mergeados — órfãos, features do dono, ports). O check:complexity NÃO roda no fast-path PR->release, então o ramo acumulou o +1 sem rebaselinar (mesma família de todos os rebaselines abaixo). Trust-but-verify: medido 2054 no tip da release pós-burst; a única função flagada nova é pré-existente (getResolvedModelCapabilities em modelCapabilities.ts, já >teto antes de #6714). Nenhum PR órfão/feature introduz violação NOVA — os fixes deste ciclo são complexity-net-zero. Rebaseline aprovado pelo dono (2026-07-10) para destravar o FQG dos ~7 órfãos verdes-exceto-complexity. Tighten via --update next cycle.",
"_rebaseline_2026_07_10_gcf_v3_2": "2054->2056 (+2). PR feat/headroom-gcf-v3.2-nested-flattening: own growth from re-vendoring the GCF (Headroom) codec to spec v3.2 (nested flattening). The 2 new over-threshold functions are the v3.2 `>`-path flatten/unflatten walk in the vendored generic-profile encode/decode paths (open-sse/services/compression/engines/headroom/gcf/{generic,decode_generic}.ts). This is imported third-party code kept byte-faithful to upstream gcf-typescript, not extractable without diverging from the vendored source; local measures 2055 on the merged tree; frozen at 2056 = the base's CI-observed 2054 + this PR's 2 new functions, matching the documented local-vs-CI off-by-one convention (see _rebaseline_2026_07_02_v3844_ci_observed) so the GitHub runner stays green. Round-trip guarded by tests/unit/compression/headroom-smartcrusher.test.ts (deep-nested case). Structural shrink belongs upstream in gcf, not here.",
diff --git a/config/quality/file-size-baseline.json b/config/quality/file-size-baseline.json
index 6d9c34b3e8..383f7fb39a 100644
--- a/config/quality/file-size-baseline.json
+++ b/config/quality/file-size-baseline.json
@@ -1,4 +1,5 @@
{
+ "_rebaseline_2026_07_18_pr7360_quota_visibility_resync": "PR #7360 (JxnLexn, per-connection Provider Quota visibility) release-resync: merging origin/release/v3.8.49 into the PR's own branch to resolve its 3-file conflict (ConnectionRow.tsx/ConnectionsListPanel.tsx/useProviderConnections.ts import unions) surfaced ProviderDetailPageClient.tsx 785->791 (measured via check-file-size.mjs's split('\\n').length; wc -l reports 790). Both parents were individually within the 786 cap: release tip alone measures 785 (its own confirm-delete-account #7361 wiring), and the PR's own pre-merge tip also measured 785 (its own quota-visibility wiring) — the two independent, already-cohesive features simply sum past the frozen ceiling once unioned in this same already-oversized god-component (tracked for decomposition under #3501). No new logic was written during the resync; the only edits were import-statement unions. Frozen raised 786->791.",
"_rebaseline_2026_07_18_pr7653_chat_tracker_import": "PR #7653 merge-interaction growth: release moved chat.ts to its 1796 cap while this PR adds the single side-effect import 'quotaTrackersBatch.ts' (line 130) — chat.ts IS the canonical quota-fetcher registration point (codex/bailian/deepseek/openrouter/opencode/generic all import+register there), so the +1 is irreducible call-site wiring. 1796->1797. Covered by tests/unit/{agentrouter,v0,freemodel}-quota-fetcher.test.ts.",
"_rebaseline_2026_07_17_pr7653_agentrouter_console_fields": "PR #7653 own growth (missing acceptance criterion: the AgentRouter quota tracker (#6850) read providerSpecificData.consoleApiKey/newApiUserId but neither field had dashboard UI for provider agentrouter — consoleApiKey was gated to bailian-coding-plan only and newApiUserId had zero UI). AddApiKeyModal.tsx 961->967 (+6) and EditConnectionModal.tsx 1278->1286 (+8) = import + a single render call plus the newApiUserId formData init field. The actual Input rendering (both consoleApiKey reuse + the new newApiUserId field) was EXTRACTED into a new leaf src/app/(dashboard)/dashboard/providers/[id]/components/modals/AgentrouterConsoleFields.tsx (48 LOC, 2462 (+1, irreducible at the existing model-aware preflight chokepoint — the `provider === \"codex\"` check that forwards requestedModel into the connection arg is extended to also cover `openrouter`, one added boolean + a doc comment, offset to a single net line by dropping the now-redundant inline condition). Enforcement itself lives in open-sse/services/openrouterQuotaFetcher.ts (not frozen) and the dispatch-time record/correct hooks live in open-sse/executors/base.ts (not frozen). Covered by tests/unit/openrouter-free-window-wiring-6842.test.ts.",
@@ -213,7 +214,7 @@
"src/app/(dashboard)/dashboard/endpoint/EndpointPageClient.tsx": 2612,
"src/app/(dashboard)/dashboard/health/page.tsx": 1091,
"src/app/(dashboard)/dashboard/playground/components/tabs/ApiTab.tsx": 847,
- "src/app/(dashboard)/dashboard/providers/[id]/ProviderDetailPageClient.tsx": 786,
+ "src/app/(dashboard)/dashboard/providers/[id]/ProviderDetailPageClient.tsx": 791,
"src/app/(dashboard)/dashboard/providers/[id]/components/ConnectionRow.tsx": 942,
"src/app/(dashboard)/dashboard/providers/[id]/components/modals/AddApiKeyModal.tsx": 967,
"src/app/(dashboard)/dashboard/providers/[id]/components/modals/EditConnectionModal.tsx": 1286,
diff --git a/src/app/(dashboard)/dashboard/providers/[id]/ProviderDetailPageClient.tsx b/src/app/(dashboard)/dashboard/providers/[id]/ProviderDetailPageClient.tsx
index 1c4465a2b8..7340e7e6fc 100644
--- a/src/app/(dashboard)/dashboard/providers/[id]/ProviderDetailPageClient.tsx
+++ b/src/app/(dashboard)/dashboard/providers/[id]/ProviderDetailPageClient.tsx
@@ -123,6 +123,7 @@ export default function ProviderDetailPageClient() {
deleteConfirm,
handleUpdateConnectionStatus,
handleToggleRateLimit,
+ handleToggleQuotaVisibility,
handleToggleClaudeExtraUsage,
handleToggleCodexLimit,
handleToggleCliproxyapiMode,
@@ -486,7 +487,10 @@ export default function ProviderDetailPageClient() {
)}
{!isUpstreamProxyProvider && !isFreeNoAuth && (
-
+
{
"fetchProxyConfig",
"handleUpdateConnectionStatus",
"handleToggleRateLimit",
+ "handleToggleQuotaVisibility",
"handleToggleClaudeExtraUsage",
"handleToggleCodexLimit",
"handleToggleCliproxyapiMode",
@@ -334,10 +335,7 @@ describe("useProviderModels — initial state", () => {
// Resolve the hooks dir from the repo root (vitest runs from cwd). Was a
// hardcoded absolute worktree path that broke the test outside that worktree
// (#3501 Phase 1g-1j).
-const HOOKS_DIR = path.join(
- process.cwd(),
- "src/app/(dashboard)/dashboard/providers/[id]/hooks"
-);
+const HOOKS_DIR = path.join(process.cwd(), "src/app/(dashboard)/dashboard/providers/[id]/hooks");
describe("Cycle-safety — hooks do not import ProviderDetailPageClient", () => {
// We allow the name in JSDoc comments; what we forbid is an actual ES import statement.
diff --git a/src/app/(dashboard)/dashboard/providers/[id]/components/ConnectionRow.tsx b/src/app/(dashboard)/dashboard/providers/[id]/components/ConnectionRow.tsx
index 72e9229c02..6a9ff815a0 100644
--- a/src/app/(dashboard)/dashboard/providers/[id]/components/ConnectionRow.tsx
+++ b/src/app/(dashboard)/dashboard/providers/[id]/components/ConnectionRow.tsx
@@ -21,6 +21,7 @@ import {
ERROR_TYPE_LABELS,
} from "../providerPageHelpers";
import { getCodexPlanLabel } from "../codexPlanLabel";
+import ProviderQuotaVisibilityToggle from "./ProviderQuotaVisibilityToggle";
// ---------------------------------------------------------------------------
// Types (exported so the client can reference them without re-importing)
@@ -48,6 +49,7 @@ export interface ConnectionRowConnection {
authType?: string;
proxyEnabled?: boolean;
perKeyProxyEnabled?: boolean;
+ quotaVisible?: boolean;
}
export interface ConnectionRowProps {
@@ -64,6 +66,7 @@ export interface ConnectionRowProps {
onMoveDown: () => void;
onToggleActive: (isActive?: boolean) => void | Promise;
onToggleRateLimit: (enabled?: boolean) => void;
+ onToggleQuotaVisibility?: (visible: boolean) => void;
onToggleClaudeExtraUsage?: (enabled?: boolean) => void;
onToggleCodex5h?: (enabled?: boolean) => void;
onToggleCodexWeekly?: (enabled?: boolean) => void;
@@ -347,6 +350,7 @@ export default function ConnectionRow({
onMoveDown,
onToggleActive,
onToggleRateLimit,
+ onToggleQuotaVisibility,
onToggleClaudeExtraUsage,
onToggleCodex5h,
onToggleCodexWeekly,
@@ -447,6 +451,7 @@ export default function ConnectionRow({
const statusPresentation = getStatusPresentation(connection, effectiveStatus, isCooldown, t);
const rateLimitEnabled = !!connection.rateLimitProtection;
+ const quotaVisible = connection.quotaVisible !== false;
const codexPolicy =
connection.providerSpecificData &&
typeof connection.providerSpecificData === "object" &&
@@ -623,6 +628,12 @@ export default function ConnectionRow({
shield
{rateLimitEnabled ? t("rateLimitProtected") : t("rateLimitUnprotected")}
+ {onToggleQuotaVisibility && (
+
+ )}
{isClaude && (
<>
|
diff --git a/src/app/(dashboard)/dashboard/providers/[id]/components/ConnectionsListPanel.tsx b/src/app/(dashboard)/dashboard/providers/[id]/components/ConnectionsListPanel.tsx
index 266ac8707d..fc1de3069a 100644
--- a/src/app/(dashboard)/dashboard/providers/[id]/components/ConnectionsListPanel.tsx
+++ b/src/app/(dashboard)/dashboard/providers/[id]/components/ConnectionsListPanel.tsx
@@ -7,6 +7,7 @@ import { pickDisplayValue } from "@/shared/utils/maskEmail";
import { readBooleanToggle, providerCountText } from "../providerPageHelpers";
import { compareTr } from "@/shared/utils/turkishText";
import type { CodexGlobalServiceMode } from "@/lib/providers/codexFastTier";
+import { supportsProviderQuota } from "@/shared/utils/providerQuotaVisibility";
import type { ConnectionDeleteConfirmState } from "../hooks/useConnectionDeleteConfirm";
type ConnectionsListPanelProps = {
@@ -41,6 +42,7 @@ type ConnectionsListPanelProps = {
deleteConfirm: ConnectionDeleteConfirmState;
handleUpdateConnectionStatus: (id: string, isActive: boolean) => void;
handleToggleRateLimit: (id: string, enabled: boolean) => void;
+ handleToggleQuotaVisibility: (id: string, visible: boolean) => void;
handleToggleClaudeExtraUsage: (id: string, enabled: boolean) => void;
handleToggleCliproxyapiMode: (id: string, enabled: boolean) => void;
handleToggleCodexLimit: (id: string, type: "use5h" | "useWeekly", enabled: boolean) => void;
@@ -68,6 +70,14 @@ type ConnectionsListPanelProps = {
t: any; // ProviderMessageTranslator
};
+function quotaVisibilityHandler(
+ supported: boolean,
+ connectionId: string,
+ toggle: (id: string, visible: boolean) => void
+) {
+ return supported ? (visible: boolean) => toggle(connectionId, visible) : undefined;
+}
+
export default function ConnectionsListPanel({
connections,
providerId,
@@ -98,6 +108,7 @@ export default function ConnectionsListPanel({
deleteConfirm,
handleUpdateConnectionStatus,
handleToggleRateLimit,
+ handleToggleQuotaVisibility,
handleToggleClaudeExtraUsage,
handleToggleCliproxyapiMode,
handleToggleCodexLimit,
@@ -124,6 +135,7 @@ export default function ConnectionsListPanel({
t,
}: ConnectionsListPanelProps) {
const sorted = [...connections].sort((a, b) => (a.priority || 0) - (b.priority || 0));
+ const quotaSupported = supportsProviderQuota(providerId);
const hasAnyTag = sorted.some((c) => c.providerSpecificData?.tag as string | undefined);
const allSelected = selectedIds.size === connections.length && connections.length > 0;
const someSelected = selectedIds.size > 0 && selectedIds.size < connections.length;
@@ -332,6 +344,11 @@ export default function ConnectionsListPanel({
onMoveDown={() => handleSwapPriority(conn, sorted[index + 1])}
onToggleActive={(isActive) => handleUpdateConnectionStatus(conn.id, isActive)}
onToggleRateLimit={(enabled) => handleToggleRateLimit(conn.id, enabled)}
+ onToggleQuotaVisibility={quotaVisibilityHandler(
+ quotaSupported,
+ conn.id,
+ handleToggleQuotaVisibility
+ )}
onToggleClaudeExtraUsage={(enabled) =>
handleToggleClaudeExtraUsage(conn.id, enabled)
}
@@ -504,6 +521,11 @@ export default function ConnectionsListPanel({
onMoveDown={() => handleSwapPriority(conn, sorted[sorted.indexOf(conn) + 1])}
onToggleActive={(isActive) => handleUpdateConnectionStatus(conn.id, isActive)}
onToggleRateLimit={(enabled) => handleToggleRateLimit(conn.id, enabled)}
+ onToggleQuotaVisibility={quotaVisibilityHandler(
+ quotaSupported,
+ conn.id,
+ handleToggleQuotaVisibility
+ )}
onToggleClaudeExtraUsage={(enabled) =>
handleToggleClaudeExtraUsage(conn.id, enabled)
}
diff --git a/src/app/(dashboard)/dashboard/providers/[id]/components/ProviderQuotaVisibilityToggle.tsx b/src/app/(dashboard)/dashboard/providers/[id]/components/ProviderQuotaVisibilityToggle.tsx
new file mode 100644
index 0000000000..cebeb3c755
--- /dev/null
+++ b/src/app/(dashboard)/dashboard/providers/[id]/components/ProviderQuotaVisibilityToggle.tsx
@@ -0,0 +1,43 @@
+"use client";
+
+import { useTranslations } from "next-intl";
+
+import { providerText } from "../providerPageHelpers";
+
+interface ProviderQuotaVisibilityToggleProps {
+ visible: boolean;
+ onToggle: (visible: boolean) => void;
+}
+
+export default function ProviderQuotaVisibilityToggle({
+ visible,
+ onToggle,
+}: ProviderQuotaVisibilityToggleProps) {
+ const t = useTranslations("providers");
+ const actionLabel = visible
+ ? providerText(t, "hideConnectionFromProviderQuota", "Hide this account from Provider Quota")
+ : providerText(t, "showConnectionInProviderQuota", "Show this account in Provider Quota");
+
+ return (
+ <>
+ |
+
+ >
+ );
+}
diff --git a/src/app/(dashboard)/dashboard/providers/[id]/components/__tests__/phase1d.test.tsx b/src/app/(dashboard)/dashboard/providers/[id]/components/__tests__/phase1d.test.tsx
index ae3cdacb47..577b292c4b 100644
--- a/src/app/(dashboard)/dashboard/providers/[id]/components/__tests__/phase1d.test.tsx
+++ b/src/app/(dashboard)/dashboard/providers/[id]/components/__tests__/phase1d.test.tsx
@@ -119,6 +119,31 @@ describe("phase-1d extractions (#3501)", () => {
expect(c).toBeDefined();
});
+ it("ConnectionRow toggles Provider Quota visibility", () => {
+ const onToggleQuotaVisibility = vi.fn();
+ const c = renderComponent(
+
+ );
+
+ const button = c.querySelector('button[aria-pressed="false"]') as HTMLButtonElement;
+ expect(button).not.toBeNull();
+ act(() => button.click());
+ expect(onToggleQuotaVisibility).toHaveBeenCalledWith(true);
+ });
+
it("ConnectionRow renders cooldown badge when rateLimitedUntil is in the future", () => {
const conn = {
id: "conn-3",
@@ -180,22 +205,14 @@ describe("phase-1d extractions (#3501)", () => {
it("SiliconFlowEndpointModal mounts when isOpen=false (renders nothing visible)", () => {
const c = renderComponent(
-
+
);
expect(c).toBeDefined();
});
it("SiliconFlowEndpointModal mounts when isOpen=true without throwing", () => {
const c = renderComponent(
-
+
);
expect(c).toBeDefined();
});
diff --git a/src/app/(dashboard)/dashboard/providers/[id]/hooks/useProviderConnections.ts b/src/app/(dashboard)/dashboard/providers/[id]/hooks/useProviderConnections.ts
index fc358c358d..8d6b9f3109 100644
--- a/src/app/(dashboard)/dashboard/providers/[id]/hooks/useProviderConnections.ts
+++ b/src/app/(dashboard)/dashboard/providers/[id]/hooks/useProviderConnections.ts
@@ -27,6 +27,7 @@ import { useNotificationStore } from "@/store/notificationStore";
import { isClaudeCodeCompatibleProvider } from "@/shared/constants/providers";
import type { ConnectionRowConnection } from "../components/ConnectionRow";
import { normalizeCodexLimitPolicy } from "../providerPageHelpers";
+import { useProviderQuotaVisibility } from "./useProviderQuotaVisibility";
import { useReorderByAvailability } from "./useReorderByAvailability";
import {
useConnectionDeleteConfirm,
@@ -74,8 +75,7 @@ export interface UseProviderConnectionsReturn {
setBatchTestResults: (r: BatchTestResults) => void;
setConnections: (
updater:
- | ConnectionRowConnection[]
- | ((prev: ConnectionRowConnection[]) => ConnectionRowConnection[])
+ ConnectionRowConnection[] | ((prev: ConnectionRowConnection[]) => ConnectionRowConnection[])
) => void;
setProviderNode: (node: any) => void;
@@ -87,6 +87,7 @@ export interface UseProviderConnectionsReturn {
deleteConfirm: ConnectionDeleteConfirmState;
handleUpdateConnectionStatus: (id: string, isActive: boolean) => Promise;
handleToggleRateLimit: (connectionId: string, enabled: boolean) => Promise;
+ handleToggleQuotaVisibility: (connectionId: string, visible: boolean) => Promise;
handleToggleClaudeExtraUsage: (connectionId: string, enabled: boolean) => Promise;
handleToggleCodexLimit: (connectionId: string, field: string, enabled: boolean) => Promise;
handleToggleCliproxyapiMode: (connectionId: string, enabled: boolean) => Promise;
@@ -135,6 +136,7 @@ export function useProviderConnections(
// ── core state ──────────────────────────────────────────────────────────
const [connections, setConnections] = useState([]);
+ const handleToggleQuotaVisibility = useProviderQuotaVisibility(setConnections, notify, t);
const [providerNode, setProviderNode] = useState(null);
const [loading, setLoading] = useState(true);
@@ -894,6 +896,7 @@ export function useProviderConnections(
deleteConfirm,
handleUpdateConnectionStatus,
handleToggleRateLimit,
+ handleToggleQuotaVisibility,
handleToggleClaudeExtraUsage,
handleToggleCodexLimit,
handleToggleCliproxyapiMode,
diff --git a/src/app/(dashboard)/dashboard/providers/[id]/hooks/useProviderQuotaVisibility.ts b/src/app/(dashboard)/dashboard/providers/[id]/hooks/useProviderQuotaVisibility.ts
new file mode 100644
index 0000000000..dda6f29564
--- /dev/null
+++ b/src/app/(dashboard)/dashboard/providers/[id]/hooks/useProviderQuotaVisibility.ts
@@ -0,0 +1,45 @@
+"use client";
+
+import { useCallback, type Dispatch, type SetStateAction } from "react";
+
+import type { ConnectionRowConnection } from "../components/ConnectionRow";
+import { providerText, type ProviderMessageTranslator } from "../providerPageHelpers";
+
+interface NotificationStore {
+ error: (message: string) => void;
+}
+
+export function useProviderQuotaVisibility(
+ setConnections: Dispatch>,
+ notify: NotificationStore,
+ t: ProviderMessageTranslator
+) {
+ return useCallback(
+ async (connectionId: string, visible: boolean) => {
+ try {
+ const response = await fetch(`/api/providers/${connectionId}`, {
+ method: "PUT",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ quotaVisible: visible }),
+ });
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
+
+ setConnections((previous) =>
+ previous.map((connection) =>
+ connection.id === connectionId ? { ...connection, quotaVisible: visible } : connection
+ )
+ );
+ } catch (error) {
+ console.error("Error toggling provider quota visibility:", error);
+ notify.error(
+ providerText(
+ t,
+ "quotaVisibilityUpdateFailed",
+ "Failed to update Provider Quota visibility"
+ )
+ );
+ }
+ },
+ [notify, setConnections, t]
+ );
+}
diff --git a/src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.tsx b/src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.tsx
index c7381721cf..2ead860172 100644
--- a/src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.tsx
+++ b/src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.tsx
@@ -29,6 +29,7 @@ import { formatAutoRefreshCountdown } from "./formatters";
import { translateUsageOrFallback, type UsageTranslationValues } from "./i18nFallback";
import { compareTr } from "@/shared/utils/turkishText";
import { fetchWithTimeout } from "@/shared/utils/fetchTimeout";
+import { isProviderQuotaVisible } from "@/shared/utils/providerQuotaVisibility";
// Bound the two first-paint requests so a stalled connection cannot wedge
// `initialLoading` on `true` and freeze the quota page on its skeleton forever
@@ -517,6 +518,7 @@ export default function ProviderLimits({
() =>
connections.filter(
(conn) =>
+ isProviderQuotaVisible(conn) &&
USAGE_SUPPORTED_PROVIDERS.includes(conn.provider) &&
(conn.authType === "oauth" || conn.authType === "apikey")
),
diff --git a/src/app/(dashboard)/home/ProviderQuotaWidget.tsx b/src/app/(dashboard)/home/ProviderQuotaWidget.tsx
index 79c4204bd2..0144ac458e 100644
--- a/src/app/(dashboard)/home/ProviderQuotaWidget.tsx
+++ b/src/app/(dashboard)/home/ProviderQuotaWidget.tsx
@@ -6,6 +6,7 @@ import Card from "@/shared/components/Card";
import ProviderIcon from "@/shared/components/ProviderIcon";
import { USAGE_SUPPORTED_PROVIDERS } from "@/shared/constants/providers";
import { translateUsageOrFallback } from "../dashboard/usage/components/ProviderLimits/i18nFallback";
+import { isProviderQuotaVisible } from "@/shared/utils/providerQuotaVisibility";
type Connection = {
id: string;
@@ -13,6 +14,7 @@ type Connection = {
authType?: string;
email?: string;
name?: string;
+ quotaVisible?: boolean;
};
type QuotaData = Record;
@@ -121,6 +123,7 @@ export default function ProviderQuotaWidget({ autoRefreshInterval = 0 }: Provide
// Only keep connections that are usage/quota supported
const relevant = conns.filter(
(c) =>
+ isProviderQuotaVisible(c) &&
USAGE_SUPPORTED_PROVIDERS.includes(c.provider) &&
(c.authType === "oauth" || c.authType === "apikey")
);
diff --git a/src/app/api/providers/[id]/route.ts b/src/app/api/providers/[id]/route.ts
index c92a7a2c67..a60b4a5c3a 100644
--- a/src/app/api/providers/[id]/route.ts
+++ b/src/app/api/providers/[id]/route.ts
@@ -24,7 +24,10 @@ import {
} from "@/lib/providers/claudeExtraUsage";
import { requireManagementAuth } from "@/lib/api/requireManagementAuth";
import { isApiKeyRevealEnabled, maskStoredApiKey } from "@/lib/apiKeyExposure";
-import { refreshConnectionRateLimits, enableRateLimitProtection } from "@/../open-sse/services/rateLimitManager";
+import {
+ refreshConnectionRateLimits,
+ enableRateLimitProtection,
+} from "@/../open-sse/services/rateLimitManager";
function normalizeCodexLimitPolicy(
incoming: unknown,
@@ -135,6 +138,7 @@ export async function PUT(request: Request, { params }: { params: Promise<{ id:
quotaWindowThresholds: incomingWindowThresholds,
proxyEnabled,
perKeyProxyEnabled,
+ quotaVisible,
projectId,
providerSpecificData: incomingPsd,
rateLimitOverrides,
@@ -191,6 +195,7 @@ export async function PUT(request: Request, { params }: { params: Promise<{ id:
if (rateLimitOverrides !== undefined) updateData.rateLimitOverrides = rateLimitOverrides;
if (proxyEnabled !== undefined) updateData.proxyEnabled = proxyEnabled;
if (perKeyProxyEnabled !== undefined) updateData.perKeyProxyEnabled = perKeyProxyEnabled;
+ if (quotaVisible !== undefined) updateData.quotaVisible = quotaVisible;
// Merge providerSpecificData (partial update — preserve existing keys not sent by caller)
if (incomingPsd !== undefined && incomingPsd !== null && typeof incomingPsd === "object") {
diff --git a/src/i18n/messages/de.json b/src/i18n/messages/de.json
index 9f2f0c98d2..4d2d19f944 100644
--- a/src/i18n/messages/de.json
+++ b/src/i18n/messages/de.json
@@ -3837,6 +3837,10 @@
"removeModel": "Modell entfernen",
"rateLimitProtected": "Geschützt",
"rateLimitUnprotected": "Ungeschützt",
+ "providerQuotaShort": "Quota",
+ "hideConnectionFromProviderQuota": "Diesen Account auf der Seite „Provider Quota“ ausblenden",
+ "showConnectionInProviderQuota": "Diesen Account auf der Seite „Provider Quota“ anzeigen",
+ "quotaVisibilityUpdateFailed": "Die Sichtbarkeit unter „Provider Quota“ konnte nicht aktualisiert werden",
"enableRateLimitProtection": "Klicken Sie hier, um den Ratenbegrenzungsschutz zu aktivieren",
"disableRateLimitProtection": "Klicken Sie hier, um den Ratenbegrenzungsschutz zu deaktivieren",
"productionKey": "Produktionsschlüssel",
diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json
index 3580989e7b..c23daa0855 100644
--- a/src/i18n/messages/en.json
+++ b/src/i18n/messages/en.json
@@ -4227,6 +4227,10 @@
"removeModel": "Remove model",
"rateLimitProtected": "Protected",
"rateLimitUnprotected": "Unprotected",
+ "providerQuotaShort": "Quota",
+ "hideConnectionFromProviderQuota": "Hide this account from Provider Quota",
+ "showConnectionInProviderQuota": "Show this account in Provider Quota",
+ "quotaVisibilityUpdateFailed": "Failed to update Provider Quota visibility",
"enableRateLimitProtection": "Click to enable rate limit protection",
"disableRateLimitProtection": "Click to disable rate limit protection",
"testAllProgress": "Tested {done}/{total}",
diff --git a/src/i18n/messages/pt-BR.json b/src/i18n/messages/pt-BR.json
index 4a2114f86d..1838f76179 100644
--- a/src/i18n/messages/pt-BR.json
+++ b/src/i18n/messages/pt-BR.json
@@ -4227,6 +4227,10 @@
"removeModel": "Remover modelo",
"rateLimitProtected": "Protegido",
"rateLimitUnprotected": "Desprotegido",
+ "providerQuotaShort": "Cota",
+ "hideConnectionFromProviderQuota": "Ocultar esta conta da página de Cotas dos Provedores",
+ "showConnectionInProviderQuota": "Mostrar esta conta na página de Cotas dos Provedores",
+ "quotaVisibilityUpdateFailed": "Não foi possível atualizar a visibilidade em Cotas dos Provedores",
"enableRateLimitProtection": "Clique para habilitar proteção de limite de taxa",
"disableRateLimitProtection": "Clique para desabilitar proteção de limite de taxa",
"testAllProgress": "__MISSING__:Tested {done}/{total}",
diff --git a/src/lib/db/caseMapping.ts b/src/lib/db/caseMapping.ts
index 007a3d3d72..1cbb0e602b 100644
--- a/src/lib/db/caseMapping.ts
+++ b/src/lib/db/caseMapping.ts
@@ -11,6 +11,14 @@
type JsonRecord = Record;
+const BOOLEAN_CAMEL_COLUMNS = new Set([
+ "isActive",
+ "rateLimitProtection",
+ "proxyEnabled",
+ "perKeyProxyEnabled",
+ "quotaVisible",
+]);
+
export function toSnakeCase(str: string): string {
return str.replace(/([A-Z])/g, "_$1").toLowerCase();
}
@@ -33,12 +41,7 @@ export function rowToCamel(row: unknown): JsonRecord | null {
const result: JsonRecord = {};
for (const [k, v] of Object.entries(row as JsonRecord)) {
const camelKey = toCamelCase(k);
- if (
- camelKey === "isActive" ||
- camelKey === "rateLimitProtection" ||
- camelKey === "proxyEnabled" ||
- camelKey === "perKeyProxyEnabled"
- ) {
+ if (BOOLEAN_CAMEL_COLUMNS.has(camelKey)) {
result[camelKey] = v === 1 || v === true;
} else if (camelKey === "providerSpecificData" && typeof v === "string") {
try {
diff --git a/src/lib/db/core.ts b/src/lib/db/core.ts
index ee3b303e1b..165ecae456 100644
--- a/src/lib/db/core.ts
+++ b/src/lib/db/core.ts
@@ -241,6 +241,7 @@ const SCHEMA_SQL = `
max_concurrent INTEGER,
proxy_enabled INTEGER NOT NULL DEFAULT 1,
per_key_proxy_enabled INTEGER NOT NULL DEFAULT 0,
+ quota_visible INTEGER NOT NULL DEFAULT 1,
quota_window_thresholds_json TEXT,
rate_limit_overrides_json TEXT,
created_at TEXT NOT NULL,
@@ -1089,7 +1090,11 @@ export function getDbInstance(): SqliteDatabase {
// V8 heap (sql.js loads the whole file into WASM memory). Throwing
// immediately gives the user a clear "increase --max-old-space-size"
// signal instead of silently renaming a perfectly good DB.
- if (/out of memory|allocation failure|Array buffer allocation failed|allocation failed/i.test(message)) {
+ if (
+ /out of memory|allocation failure|Array buffer allocation failed|allocation failed/i.test(
+ message
+ )
+ ) {
// Cycle-breaker (#6835): the OOM path never renames the file away,
// so it never trips the generic probe-failed/restore cap above. Cap
// it independently after 3 consecutive OOM failures (same threshold
diff --git a/src/lib/db/migrations/125_provider_connection_quota_visibility.sql b/src/lib/db/migrations/125_provider_connection_quota_visibility.sql
new file mode 100644
index 0000000000..26bf92df36
--- /dev/null
+++ b/src/lib/db/migrations/125_provider_connection_quota_visibility.sql
@@ -0,0 +1,5 @@
+-- 125_provider_connection_quota_visibility.sql
+-- Controls whether a provider connection is shown in quota-focused dashboard views.
+-- This is presentation-only and does not affect routing or provider-limit collection.
+
+ALTER TABLE provider_connections ADD COLUMN quota_visible INTEGER NOT NULL DEFAULT 1;
diff --git a/src/lib/db/providers.ts b/src/lib/db/providers.ts
index e3f6e1241e..fdaa5f6a37 100644
--- a/src/lib/db/providers.ts
+++ b/src/lib/db/providers.ts
@@ -380,6 +380,7 @@ export async function createProviderConnection(data: JsonRecord) {
updatedAt: now,
proxyEnabled: normalizeBooleanColumn(data.proxyEnabled, true),
perKeyProxyEnabled: normalizeBooleanColumn(data.perKeyProxyEnabled, false),
+ quotaVisible: normalizeBooleanColumn(data.quotaVisible, true),
};
// Optional fields
@@ -411,6 +412,7 @@ export async function createProviderConnection(data: JsonRecord) {
"maxConcurrent",
"proxyEnabled",
"perKeyProxyEnabled",
+ "quotaVisible",
"quotaWindowThresholds",
"rateLimitOverrides",
"healthCheckInterval",
@@ -469,7 +471,7 @@ function _insertConnectionRow(db: DbLike, conn: JsonRecord) {
last_tested, api_key, id_token, provider_specific_data,
expires_in, display_name, global_priority, default_model,
token_type, consecutive_use_count, rate_limit_protection, last_used_at, "group", max_concurrent,
- proxy_enabled, per_key_proxy_enabled, quota_window_thresholds_json, rate_limit_overrides_json,
+ proxy_enabled, per_key_proxy_enabled, quota_visible, quota_window_thresholds_json, rate_limit_overrides_json,
created_at, updated_at
) VALUES (
@id, @provider, @authType, @name, @email, @priority, @isActive,
@@ -480,7 +482,7 @@ function _insertConnectionRow(db: DbLike, conn: JsonRecord) {
@lastTested, @apiKey, @idToken, @providerSpecificData,
@expiresIn, @displayName, @globalPriority, @defaultModel,
@tokenType, @consecutiveUseCount, @rateLimitProtection, @lastUsedAt, @group, @maxConcurrent,
- @proxyEnabled, @perKeyProxyEnabled, @quotaWindowThresholdsJson, @rateLimitOverridesJson,
+ @proxyEnabled, @perKeyProxyEnabled, @quotaVisible, @quotaWindowThresholdsJson, @rateLimitOverridesJson,
@createdAt, @updatedAt
)
`
@@ -527,6 +529,7 @@ function _insertConnectionRow(db: DbLike, conn: JsonRecord) {
maxConcurrent: conn.maxConcurrent ?? null,
proxyEnabled: normalizeBooleanColumn(conn.proxyEnabled, true) ? 1 : 0,
perKeyProxyEnabled: normalizeBooleanColumn(conn.perKeyProxyEnabled, false) ? 1 : 0,
+ quotaVisible: normalizeBooleanColumn(conn.quotaVisible, true) ? 1 : 0,
quotaWindowThresholdsJson: serializeJsonField(conn.quotaWindowThresholds),
rateLimitOverridesJson: serializeJsonField(conn.rateLimitOverrides),
createdAt: conn.createdAt,
@@ -534,37 +537,11 @@ function _insertConnectionRow(db: DbLike, conn: JsonRecord) {
});
}
-function _updateConnectionRow(db: DbLike, id: string, data: JsonRecord) {
- const now = data.updatedAt || new Date().toISOString();
- db.prepare(
- `
- UPDATE provider_connections SET
- provider = @provider, auth_type = @authType, name = @name, email = @email,
- priority = @priority, is_active = @isActive, access_token = @accessToken,
- refresh_token = @refreshToken, expires_at = @expiresAt, token_expires_at = @tokenExpiresAt,
- scope = @scope, project_id = @projectId, test_status = @testStatus, error_code = @errorCode,
- last_error = @lastError, last_error_at = @lastErrorAt, last_error_type = @lastErrorType,
- last_error_source = @lastErrorSource, backoff_level = @backoffLevel,
- rate_limited_until = @rateLimitedUntil, health_check_interval = @healthCheckInterval,
- last_health_check_at = @lastHealthCheckAt, last_tested = @lastTested, api_key = @apiKey,
- id_token = @idToken, provider_specific_data = @providerSpecificData,
- expires_in = @expiresIn, display_name = @displayName, global_priority = @globalPriority,
- default_model = @defaultModel, token_type = @tokenType,
- consecutive_use_count = @consecutiveUseCount,
- rate_limit_protection = @rateLimitProtection,
- last_used_at = @lastUsedAt,
- "group" = @group,
- max_concurrent = @maxConcurrent,
- quota_window_thresholds_json = @quotaWindowThresholdsJson,
- proxy_enabled = @proxyEnabled,
- per_key_proxy_enabled = @perKeyProxyEnabled,
- rate_limit_overrides_json = @rateLimitOverridesJson,
- last_ping_at = @lastPingAt,
- last_pinged_reset_key = @lastPingedResetKey,
- updated_at = @updatedAt
- WHERE id = @id
- `
- ).run({
+// Assembles the `.run()` params for _updateConnectionRow's UPDATE statement.
+// Split out purely to keep _updateConnectionRow under the max-lines-per-function
+// gate — same field mapping/normalization as before, just relocated.
+function _buildUpdateConnectionRowParams(id: string, data: JsonRecord, now: unknown) {
+ return {
id,
provider: data.provider,
authType: data.authType || null,
@@ -608,11 +585,46 @@ function _updateConnectionRow(db: DbLike, id: string, data: JsonRecord) {
quotaWindowThresholdsJson: serializeJsonField(data.quotaWindowThresholds),
proxyEnabled: normalizeBooleanColumn(data.proxyEnabled, true) ? 1 : 0,
perKeyProxyEnabled: normalizeBooleanColumn(data.perKeyProxyEnabled, false) ? 1 : 0,
+ quotaVisible: normalizeBooleanColumn(data.quotaVisible, true) ? 1 : 0,
rateLimitOverridesJson: serializeJsonField(data.rateLimitOverrides),
lastPingAt: data.lastPingAt || null,
lastPingedResetKey: data.lastPingedResetKey || null,
updatedAt: now,
- });
+ };
+}
+
+function _updateConnectionRow(db: DbLike, id: string, data: JsonRecord) {
+ const now = data.updatedAt || new Date().toISOString();
+ db.prepare(
+ `
+ UPDATE provider_connections SET
+ provider = @provider, auth_type = @authType, name = @name, email = @email,
+ priority = @priority, is_active = @isActive, access_token = @accessToken,
+ refresh_token = @refreshToken, expires_at = @expiresAt, token_expires_at = @tokenExpiresAt,
+ scope = @scope, project_id = @projectId, test_status = @testStatus, error_code = @errorCode,
+ last_error = @lastError, last_error_at = @lastErrorAt, last_error_type = @lastErrorType,
+ last_error_source = @lastErrorSource, backoff_level = @backoffLevel,
+ rate_limited_until = @rateLimitedUntil, health_check_interval = @healthCheckInterval,
+ last_health_check_at = @lastHealthCheckAt, last_tested = @lastTested, api_key = @apiKey,
+ id_token = @idToken, provider_specific_data = @providerSpecificData,
+ expires_in = @expiresIn, display_name = @displayName, global_priority = @globalPriority,
+ default_model = @defaultModel, token_type = @tokenType,
+ consecutive_use_count = @consecutiveUseCount,
+ rate_limit_protection = @rateLimitProtection,
+ last_used_at = @lastUsedAt,
+ "group" = @group,
+ max_concurrent = @maxConcurrent,
+ quota_window_thresholds_json = @quotaWindowThresholdsJson,
+ proxy_enabled = @proxyEnabled,
+ per_key_proxy_enabled = @perKeyProxyEnabled,
+ quota_visible = @quotaVisible,
+ rate_limit_overrides_json = @rateLimitOverridesJson,
+ last_ping_at = @lastPingAt,
+ last_pinged_reset_key = @lastPingedResetKey,
+ updated_at = @updatedAt
+ WHERE id = @id
+ `
+ ).run(_buildUpdateConnectionRowParams(id, data, now));
}
export async function updateProviderConnection(id: string, data: JsonRecord) {
diff --git a/src/lib/db/schemaColumns.ts b/src/lib/db/schemaColumns.ts
index ab8ec16525..2951e214e8 100644
--- a/src/lib/db/schemaColumns.ts
+++ b/src/lib/db/schemaColumns.ts
@@ -48,6 +48,12 @@ export function ensureProviderConnectionsColumns(db: SqliteDatabase) {
);
console.log("[DB] Added provider_connections.per_key_proxy_enabled column");
}
+ if (!columnNames.has("quota_visible")) {
+ db.exec(
+ "ALTER TABLE provider_connections ADD COLUMN quota_visible INTEGER NOT NULL DEFAULT 1"
+ );
+ console.log("[DB] Added provider_connections.quota_visible column");
+ }
if (!columnNames.has("quota_window_thresholds_json")) {
db.exec("ALTER TABLE provider_connections ADD COLUMN quota_window_thresholds_json TEXT");
console.log("[DB] Added provider_connections.quota_window_thresholds_json column");
diff --git a/src/shared/utils/providerQuotaVisibility.ts b/src/shared/utils/providerQuotaVisibility.ts
new file mode 100644
index 0000000000..3968005204
--- /dev/null
+++ b/src/shared/utils/providerQuotaVisibility.ts
@@ -0,0 +1,13 @@
+import { USAGE_SUPPORTED_PROVIDERS } from "@/shared/constants/providers";
+
+export interface ProviderQuotaVisibilityConnection {
+ quotaVisible?: boolean;
+}
+
+export function isProviderQuotaVisible(connection: ProviderQuotaVisibilityConnection): boolean {
+ return connection.quotaVisible !== false;
+}
+
+export function supportsProviderQuota(providerId: string): boolean {
+ return USAGE_SUPPORTED_PROVIDERS.includes(providerId);
+}
diff --git a/src/shared/validation/schemas/provider.ts b/src/shared/validation/schemas/provider.ts
index 95ee820d55..a315489958 100644
--- a/src/shared/validation/schemas/provider.ts
+++ b/src/shared/validation/schemas/provider.ts
@@ -433,6 +433,7 @@ export const updateProviderConnectionSchema = z
.optional(),
proxyEnabled: z.boolean().optional(),
perKeyProxyEnabled: z.boolean().optional(),
+ quotaVisible: z.boolean().optional(),
// Partial patch of per-connection provider-specific settings (e.g. quota toggles)
providerSpecificData: z
.record(z.string(), z.unknown())
diff --git a/tests/unit/db-schema-columns-split.test.ts b/tests/unit/db-schema-columns-split.test.ts
index f4f9643e9f..0e89a5fa1f 100644
--- a/tests/unit/db-schema-columns-split.test.ts
+++ b/tests/unit/db-schema-columns-split.test.ts
@@ -7,6 +7,7 @@ import assert from "node:assert/strict";
import { tryOpenSync } from "../../src/lib/db/adapters/driverFactory.ts";
import {
ensureUsageHistoryColumns,
+ ensureProviderConnectionsColumns,
hasColumn,
hasTable,
quoteIdentifier,
@@ -63,3 +64,24 @@ test("ensureUsageHistoryColumns adds missing columns and is idempotent", () => {
db.close?.();
}
});
+
+test("ensureProviderConnectionsColumns repairs quota visibility with a visible default", () => {
+ const db = openMemoryDb();
+ try {
+ db.exec("CREATE TABLE provider_connections (id TEXT PRIMARY KEY, provider TEXT NOT NULL)");
+ assert.equal(hasColumn(db, "provider_connections", "quota_visible"), false);
+
+ ensureProviderConnectionsColumns(db);
+ assert.equal(hasColumn(db, "provider_connections", "quota_visible"), true);
+ const column = db
+ .prepare("PRAGMA table_info(provider_connections)")
+ .all()
+ .find((entry: { name?: string }) => entry.name === "quota_visible") as
+ { notnull?: number; dflt_value?: string } | undefined;
+ assert.equal(column?.notnull, 1);
+ assert.equal(column?.dflt_value, "1");
+ assert.doesNotThrow(() => ensureProviderConnectionsColumns(db));
+ } finally {
+ db.close?.();
+ }
+});
diff --git a/tests/unit/provider-connections-quota-threshold.test.ts b/tests/unit/provider-connections-quota-threshold.test.ts
index d9bfebe935..5caaeea369 100644
--- a/tests/unit/provider-connections-quota-threshold.test.ts
+++ b/tests/unit/provider-connections-quota-threshold.test.ts
@@ -162,3 +162,26 @@ test("updateProviderConnectionSchema rejects out-of-range values", () => {
assert.equal(result.success, false, `expected window5h=${v} to be rejected`);
}
});
+
+test("provider quota visibility defaults to visible and persists explicit changes", async () => {
+ const created = await providersDb.createProviderConnection({
+ provider: "codex",
+ authType: "apikey",
+ name: "Codex Visibility",
+ apiKey: "sk-visibility",
+ });
+ assert.equal(created.quotaVisible, true);
+
+ const hidden = await providersDb.updateProviderConnection(created.id, { quotaVisible: false });
+ assert.equal(hidden.quotaVisible, false);
+ assert.equal((await providersDb.getProviderConnectionById(created.id)).quotaVisible, false);
+
+ const visible = await providersDb.updateProviderConnection(created.id, { quotaVisible: true });
+ assert.equal(visible.quotaVisible, true);
+ assert.equal((await providersDb.getProviderConnectionById(created.id)).quotaVisible, true);
+});
+
+test("updateProviderConnectionSchema accepts only boolean quota visibility", () => {
+ assert.equal(updateProviderConnectionSchema.safeParse({ quotaVisible: false }).success, true);
+ assert.equal(updateProviderConnectionSchema.safeParse({ quotaVisible: "false" }).success, false);
+});
diff --git a/tests/unit/provider-quota-visibility.test.ts b/tests/unit/provider-quota-visibility.test.ts
new file mode 100644
index 0000000000..665687feed
--- /dev/null
+++ b/tests/unit/provider-quota-visibility.test.ts
@@ -0,0 +1,18 @@
+import test from "node:test";
+import assert from "node:assert/strict";
+
+import {
+ isProviderQuotaVisible,
+ supportsProviderQuota,
+} from "../../src/shared/utils/providerQuotaVisibility.ts";
+
+test("provider quota visibility is opt-out so existing connections stay visible", () => {
+ assert.equal(isProviderQuotaVisible({}), true);
+ assert.equal(isProviderQuotaVisible({ quotaVisible: true }), true);
+ assert.equal(isProviderQuotaVisible({ quotaVisible: false }), false);
+});
+
+test("quota visibility controls are limited to providers with quota support", () => {
+ assert.equal(supportsProviderQuota("codex"), true);
+ assert.equal(supportsProviderQuota("openai"), false);
+});