Fix Codex reset expiry integration
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -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,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}/`;
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
Reference in New Issue
Block a user