diff --git a/packages/vscode/src/quotaProviders.ts b/packages/vscode/src/quotaProviders.ts index 6e5231de..5dde3b1c 100644 --- a/packages/vscode/src/quotaProviders.ts +++ b/packages/vscode/src/quotaProviders.ts @@ -487,16 +487,18 @@ const fetchCodexQuota = async (): Promise => { const windows: Record = {}; if (primary) { - windows['5h'] = toUsageWindow({ + const windowSeconds = toNumber(primary.limit_window_seconds); + windows[resolveWindowLabel(windowSeconds)] = toUsageWindow({ usedPercent: toNumber(primary.used_percent), - windowSeconds: toNumber(primary.limit_window_seconds), + windowSeconds, resetAt: toTimestamp(primary.reset_at), }); } if (secondary) { - windows['weekly'] = toUsageWindow({ + const windowSeconds = toNumber(secondary.limit_window_seconds); + windows[resolveWindowLabel(windowSeconds)] = toUsageWindow({ usedPercent: toNumber(secondary.used_percent), - windowSeconds: toNumber(secondary.limit_window_seconds), + windowSeconds, resetAt: toTimestamp(secondary.reset_at), }); } diff --git a/packages/web/server/lib/quota/providers/codex.js b/packages/web/server/lib/quota/providers/codex.js index 3f12cde0..20a5ffa2 100644 --- a/packages/web/server/lib/quota/providers/codex.js +++ b/packages/web/server/lib/quota/providers/codex.js @@ -6,6 +6,7 @@ import { toUsageWindow, toNumber, toTimestamp, + resolveWindowLabel, formatMoney } from '../utils/index.js'; @@ -65,16 +66,18 @@ export const fetchQuota = async () => { const windows = {}; if (primary) { - windows['5h'] = toUsageWindow({ + const windowSeconds = toNumber(primary.limit_window_seconds); + windows[resolveWindowLabel(windowSeconds)] = toUsageWindow({ usedPercent: toNumber(primary.used_percent), - windowSeconds: toNumber(primary.limit_window_seconds), + windowSeconds, resetAt: toTimestamp(primary.reset_at) }); } if (secondary) { - windows['weekly'] = toUsageWindow({ + const windowSeconds = toNumber(secondary.limit_window_seconds); + windows[resolveWindowLabel(windowSeconds)] = toUsageWindow({ usedPercent: toNumber(secondary.used_percent), - windowSeconds: toNumber(secondary.limit_window_seconds), + windowSeconds, resetAt: toTimestamp(secondary.reset_at) }); } diff --git a/packages/web/server/lib/quota/providers/codex.test.js b/packages/web/server/lib/quota/providers/codex.test.js new file mode 100644 index 00000000..a7253578 --- /dev/null +++ b/packages/web/server/lib/quota/providers/codex.test.js @@ -0,0 +1,48 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; + +vi.mock('../../opencode/auth.js', () => ({ + readAuthFile: () => ({ openai: { access: 'test-token' } }), +})); + +import { fetchQuota } from './codex.js'; + +afterEach(() => { + vi.unstubAllGlobals(); +}); + +const mockUsage = (rateLimit) => { + vi.stubGlobal('fetch', vi.fn().mockResolvedValue({ + ok: true, + json: async () => ({ rate_limit: rateLimit }), + })); +}; + +describe('Codex quota windows', () => { + it('labels a weekly-only primary window from its duration', async () => { + mockUsage({ + primary_window: { + used_percent: 3, + limit_window_seconds: 604800, + reset_at: 1784491827, + }, + secondary_window: null, + }); + + const result = await fetchQuota(); + + expect(result.usage.windows.weekly.usedPercent).toBe(3); + expect(result.usage.windows['5h']).toBeUndefined(); + }); + + it('labels five-hour and weekly windows from their durations', async () => { + mockUsage({ + primary_window: { used_percent: 10, limit_window_seconds: 18000 }, + secondary_window: { used_percent: 20, limit_window_seconds: 604800 }, + }); + + const result = await fetchQuota(); + + expect(result.usage.windows['5h'].usedPercent).toBe(10); + expect(result.usage.windows.weekly.usedPercent).toBe(20); + }); +});