diff --git a/.changeset/fix-codex-weekly-usage-label.md b/.changeset/fix-codex-weekly-usage-label.md new file mode 100644 index 0000000000..bd3cf63cab --- /dev/null +++ b/.changeset/fix-codex-weekly-usage-label.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": patch +--- + +summary: Show Codex weekly usage when OpenAI reports it as the primary quota window. +category: fix +dev: Classifies Codex quota windows by their declared duration while preserving legacy response support. diff --git a/packages/dashboard/src/__tests__/usage.test.ts b/packages/dashboard/src/__tests__/usage.test.ts index 8096a5058f..247ecfb3a4 100644 --- a/packages/dashboard/src/__tests__/usage.test.ts +++ b/packages/dashboard/src/__tests__/usage.test.ts @@ -2570,6 +2570,7 @@ describe("usage", () => { expect(codex.email).toBe("test@example.com"); expect(codex.plan).toBe("Pro"); expect(codex.windows).toHaveLength(2); + expect(codex.windows.map((window) => window.label)).toEqual(["Session (5h)", "Weekly"]); const sessionWindow = codex.windows.find((w) => w.label.includes("Session")); expect(sessionWindow).toBeDefined(); @@ -2577,6 +2578,56 @@ describe("usage", () => { expect(sessionWindow!.percentLeft).toBe(32.5); }); + it("labels a seven-day primary window as Weekly when no secondary window is returned", async () => { + const mockResponse = { + rate_limit: { + primary_window: { + used_percent: 67, + limit_window_seconds: 7 * 24 * 60 * 60, + reset_after_seconds: 4 * 24 * 60 * 60, + }, + secondary_window: null, + }, + }; + + mockReadFile.mockImplementation((filePath: string) => { + if (filePath.includes("codex")) { + return JSON.stringify({ + tokens: { + access_token: "test-token", + }, + }); + } + return Promise.reject(new Error("File not found")); + }); + + const mockReq = { on: vi.fn(), write: vi.fn(), end: vi.fn() }; + mockRequest.mockImplementation((_options: any, callback: any) => { + const mockRes = { + statusCode: 200, + headers: {}, + on: vi.fn((event: string, handler: any) => { + if (event === "data") handler(Buffer.from(JSON.stringify(mockResponse))); + if (event === "end") handler(); + }), + }; + callback(mockRes); + return mockReq; + }); + + const providers = await fetchAllProviderUsage(); + const codex = providers.find((provider) => provider.name === "Codex")!; + + expect(codex.status).toBe("ok"); + expect(codex.windows).toHaveLength(1); + expect(codex.windows[0]).toMatchObject({ + label: "Weekly", + percentUsed: 67, + windowDurationMs: 7 * 24 * 60 * 60 * 1000, + }); + expect(codex.windows.some((window) => window.label === "Session (5h)")).toBe(false); + }); + it("sets resetAt from reset_at timestamp", async () => { const resetAtTimestamp = Math.floor(Date.now() / 1000) + 2 * 60 * 60; // 2 hours from now in seconds const mockResponse = { diff --git a/packages/dashboard/src/usage.ts b/packages/dashboard/src/usage.ts index 499afa2dfa..bf8e171bff 100644 --- a/packages/dashboard/src/usage.ts +++ b/packages/dashboard/src/usage.ts @@ -1320,10 +1320,22 @@ async function fetchCodexUsage(): Promise { }; }; - // Main rate limits + const getWindowLabel = (win: unknown, fallbackLabel: string): string => { + if (!win || typeof win !== "object") return fallbackLabel; + const durationSeconds = (win as { limit_window_seconds?: unknown }).limit_window_seconds; + if (durationSeconds === 7 * 24 * 60 * 60) return "Weekly"; + if (durationSeconds === 5 * 60 * 60) return "Session (5h)"; + return fallbackLabel; + }; + + // Codex has used both positional shapes: primary=session + secondary=weekly, + // and primary=weekly with no secondary. Prefer the declared duration so the + // same quota keeps its meaning across both API responses. if (data.rate_limit) { - const primary = parseWindow(data.rate_limit.primary_window, "Session (5h)"); - const secondary = parseWindow(data.rate_limit.secondary_window, "Weekly"); + const primaryWindow = data.rate_limit.primary_window; + const secondaryWindow = data.rate_limit.secondary_window; + const primary = parseWindow(primaryWindow, getWindowLabel(primaryWindow, "Session (5h)")); + const secondary = parseWindow(secondaryWindow, getWindowLabel(secondaryWindow, "Weekly")); if (primary) usage.windows.push(primary); if (secondary) usage.windows.push(secondary); }