Fix Codex reset expiry integration

This commit is contained in:
can1357
2026-06-27 01:22:45 +02:00
parent 393d068a22
commit a515c24735
6 changed files with 98 additions and 18 deletions
@@ -0,0 +1,15 @@
import { CODEX_BASE_URL } from "@oh-my-pi/pi-catalog/wire/codex";
export function normalizeCodexBaseUrl(baseUrl?: string): string {
const fallback = CODEX_BASE_URL;
const trimmed = baseUrl?.trim() ? baseUrl.trim() : fallback;
const base = trimmed.replace(/\/+$/, "");
const lower = base.toLowerCase();
if (
(lower.startsWith("https://chatgpt.com") || lower.startsWith("https://chat.openai.com")) &&
!lower.includes("/backend-api")
) {
return `${base}/backend-api`;
}
return base;
}
+1 -1
View File
@@ -20,7 +20,7 @@
*/
import type { FetchImpl } from "../types";
import { isRecord } from "../utils";
import { normalizeCodexBaseUrl } from "./openai-codex";
import { normalizeCodexBaseUrl } from "./openai-codex-base-url";
import { toNumber } from "./shared";
const RESET_CREDITS_PATH = "wham/rate-limit-reset-credits";
+1 -14
View File
@@ -1,5 +1,5 @@
import { Buffer } from "node:buffer";
import { CODEX_BASE_URL } from "@oh-my-pi/pi-catalog/wire/codex";
import { normalizeCodexBaseUrl } from "./openai-codex-base-url";
import type {
CredentialRankingStrategy,
UsageAmount,
@@ -201,19 +201,6 @@ function parseResetCredits(payload: unknown): UsageResetCredits | undefined {
return { availableCount: Math.max(0, Math.trunc(availableCount)) };
}
export function normalizeCodexBaseUrl(baseUrl?: string): string {
const fallback = CODEX_BASE_URL;
const trimmed = baseUrl?.trim() ? baseUrl.trim() : fallback;
const base = trimmed.replace(/\/+$/, "");
const lower = base.toLowerCase();
if (
(lower.startsWith("https://chatgpt.com") || lower.startsWith("https://chat.openai.com")) &&
!lower.includes("/backend-api")
) {
return `${base}/backend-api`;
}
return base;
}
function buildCodexUsageUrl(baseUrl: string): string {
const normalized = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
+6 -3
View File
@@ -330,16 +330,19 @@ function formatAccountHeader(
header += chalk.cyan(` · ✦ ${savedResets} saved reset${savedResets === 1 ? "" : "s"}`);
const credits = report.resetCredits?.credits;
if (credits) {
const upcoming = credits
const expiries = credits
.filter(c => c.expiresAt)
.map(c => ({ date: c.expiresAt!, ms: Date.parse(c.expiresAt!) }))
.filter(c => !Number.isNaN(c.ms))
.sort((a, b) => a.ms - b.ms)
.find(c => c.ms > nowMs);
.sort((a, b) => a.ms - b.ms);
const upcoming = expiries.find(c => c.ms > nowMs);
if (upcoming) {
header += chalk.dim(
` · soonest expires in ${formatDuration(upcoming.ms - nowMs)} (${upcoming.date.slice(0, 10)})`,
);
} else {
const lastExpired = expiries.at(-1);
if (lastExpired) header += chalk.dim(` · expired (${lastExpired.date.slice(0, 10)})`);
}
}
}
@@ -65,4 +65,47 @@ describe("CommandController /usage", () => {
expect(output).toContain("█");
expect(output).not.toContain("··········");
});
it("renders saved reset expiry lines for future and expired credits", async () => {
const present = vi.fn();
const ctx = {
session: {},
ui: { terminal: { columns: 100 } },
present,
showWarning: vi.fn(),
showError: vi.fn(),
} as unknown as InteractiveModeContext;
const controller = new CommandController(ctx);
const now = Date.now();
const dayMs = 24 * 60 * 60 * 1000;
const futureIso = new Date(now + 2 * dayMs).toISOString();
const expiredIso = new Date(now - 2 * dayMs).toISOString();
const reports: UsageReport[] = [
{
provider: "openai-codex",
fetchedAt: now,
limits: [],
metadata: { email: "user@example.com" },
resetCredits: {
availableCount: 2,
credits: [
{ id: "future-reset", expiresAt: futureIso },
{ id: "expired-reset", expiresAt: expiredIso },
],
},
},
];
await controller.handleUsageCommand(reports);
expect(present).toHaveBeenCalledTimes(1);
const firstCall = present.mock.calls[0];
expect(firstCall).toBeDefined();
const output = renderPresentedBlocks(firstCall?.[0]);
expect(output).toContain("Saved rate-limit resets");
expect(output).toContain("user@example.com: 2 saved resets");
expect(output).toContain(`expires in`);
expect(output).toContain(`(${futureIso.slice(0, 10)})`);
expect(output).toContain(`expired (${expiredIso.slice(0, 10)})`);
});
});
@@ -213,6 +213,38 @@ describe("formatUsageBreakdown", () => {
expect(disclaimerIdx).toBeLessThan(firstLimitIdx);
});
it("renders saved reset expiry state for future and expired credits", () => {
const now = Date.parse("2026-01-01T00:00:00.000Z");
const reports: UsageReport[] = [
{
provider: "openai-codex",
fetchedAt: now,
limits: [],
metadata: { email: "future@example.test" },
resetCredits: {
availableCount: 1,
credits: [{ id: "future-reset", expiresAt: "2026-01-03T00:00:00.000Z" }],
},
},
{
provider: "openai-codex",
fetchedAt: now,
limits: [],
metadata: { email: "expired@example.test" },
resetCredits: {
availableCount: 1,
credits: [{ id: "expired-reset", expiresAt: "2025-12-30T00:00:00.000Z" }],
},
},
];
const text = stripVTControlCharacters(formatUsageBreakdown(reports, [], now));
expect(text).toContain("future@example.test");
expect(text).toContain("soonest expires in 2d (2026-01-03)");
expect(text).toContain("expired@example.test");
expect(text).toContain("expired (2025-12-30)");
});
it("deduplicates identical per-limit notes across accounts sharing a window", () => {
const note = "Overage requests: 5";
const reports = [