fix(dashboard): show Codex weekly usage
This commit is contained in:
7
.changeset/fix-codex-weekly-usage-label.md
Normal file
7
.changeset/fix-codex-weekly-usage-label.md
Normal file
@@ -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.
|
||||||
@@ -2570,6 +2570,7 @@ describe("usage", () => {
|
|||||||
expect(codex.email).toBe("test@example.com");
|
expect(codex.email).toBe("test@example.com");
|
||||||
expect(codex.plan).toBe("Pro");
|
expect(codex.plan).toBe("Pro");
|
||||||
expect(codex.windows).toHaveLength(2);
|
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"));
|
const sessionWindow = codex.windows.find((w) => w.label.includes("Session"));
|
||||||
expect(sessionWindow).toBeDefined();
|
expect(sessionWindow).toBeDefined();
|
||||||
@@ -2577,6 +2578,56 @@ describe("usage", () => {
|
|||||||
expect(sessionWindow!.percentLeft).toBe(32.5);
|
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 () => {
|
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 resetAtTimestamp = Math.floor(Date.now() / 1000) + 2 * 60 * 60; // 2 hours from now in seconds
|
||||||
const mockResponse = {
|
const mockResponse = {
|
||||||
|
|||||||
@@ -1320,10 +1320,22 @@ async function fetchCodexUsage(): Promise<ProviderUsage> {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// 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) {
|
if (data.rate_limit) {
|
||||||
const primary = parseWindow(data.rate_limit.primary_window, "Session (5h)");
|
const primaryWindow = data.rate_limit.primary_window;
|
||||||
const secondary = parseWindow(data.rate_limit.secondary_window, "Weekly");
|
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 (primary) usage.windows.push(primary);
|
||||||
if (secondary) usage.windows.push(secondary);
|
if (secondary) usage.windows.push(secondary);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user