feat(quota): live real-time codex quota on the page (cascade-safe serialized refresh)

Symptom: freshly-added Codex accounts (e.g. davi/gabriel) showed "No quota data"
even when healthy. Root cause: the quota path reuses the access_token without
refreshing rotating providers (#3019, anti Auth0 family-revocation cascade), so a
Codex account whose short-lived access_token has expired can never surface quota
from the sync — the live fetch returns "Codex token expired".

Fix (opt-in, cascade-safe):
- refreshAndUpdateCredentials gains `allowRotatingRefresh` + a pure exported gate
  `shouldAttemptRotatingRefresh`. The actual token mint is wrapped in
  `serializeRefresh` (one refresh at a time per Auth0 rotation group) — so even N
  concurrent per-account requests can never refresh siblings in parallel.
- The BULK scheduler (syncAllProviderLimits, concurrent) keeps the flag OFF →
  #3019 fully preserved (guardian test codex-quota-sync-no-proactive-refresh stays
  green). Only the on-demand, per-connection path (`GET /api/usage/[connectionId]`)
  opts in.
- Frontend: the quota page auto-fetches LIVE on open for the VISIBLE connections
  that have no cached quota (scoped to what's on screen — not all connections —
  and skips entries already cached), so expired-token Codex accounts surface real
  quota automatically and cascade-safely.

Adds unit coverage for the gate (bulk skips rotating, on-demand allows; non-rotating
always eligible). typecheck / lint clean.
This commit is contained in:
diegosouzapw
2026-06-02 09:53:50 -03:00
parent c4a993184e
commit e438139b03
4 changed files with 94 additions and 16 deletions

View File

@@ -3,6 +3,13 @@ import { fetchAndPersistProviderLimits } from "@/lib/usage/providerLimits";
/**
* GET /api/usage/[connectionId] - Get live usage data for a specific connection
* and persist the refreshed Provider Limits cache.
*
* This is the on-demand, per-connection path (the dashboard quota page fetches
* only the connections it shows through here, not all of them at once). It opts
* into refreshing rotating-refresh providers (Codex/OpenAI) so an account with an
* expired access_token still surfaces live quota — made cascade-safe by
* `serializeRefresh` (one token mint at a time per Auth0 group). The bulk
* scheduler keeps the #3019 behaviour of never refreshing rotating providers.
*/
export async function GET(
_request: Request,
@@ -10,7 +17,9 @@ export async function GET(
) {
try {
const { connectionId } = await params;
const { usage } = await fetchAndPersistProviderLimits(connectionId, "manual");
const { usage } = await fetchAndPersistProviderLimits(connectionId, "manual", {
allowRotatingRefresh: true,
});
return Response.json(usage);
} catch (error) {
const status =