@henryqw/pi-multi-codex 1.0.4 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -25,6 +25,8 @@ Run `/login` and authenticate `OpenAI Codex` for slot 1 first. Run `/codex-add`,
25
25
 
26
26
  `/codex-status` lists the new slot. It shows cached quota when available, or `unavailable` until the first successful snapshot.
27
27
 
28
+ The footer and `/codex-status` show the five-hour reset countdown for Free, Go, and Plus tiers. They show the seven-day reset countdown for Pro Lite and other tiers.
29
+
28
30
  | Surface | Type | Purpose |
29
31
  | --- | --- | --- |
30
32
  | `/codex-add` | command | Create the next numbered slot, then authenticate that slot. |
@@ -54,6 +54,7 @@ type UsageSnapshot = {
54
54
  fetchedAt?: number;
55
55
  remaining?: number;
56
56
  reset?: number;
57
+ fiveHourReset?: number;
57
58
  limitedUntil?: number | null;
58
59
  };
59
60
 
@@ -70,7 +71,7 @@ type UsageState = {
70
71
  untrusted?: true;
71
72
  };
72
73
 
73
- type ParsedUsage = { remaining: number; reset: number; limitedUntil?: number; tier?: string };
74
+ type ParsedUsage = { remaining: number; reset: number; fiveHourReset?: number; limitedUntil?: number; tier?: string };
74
75
  type Config = { autoSwitchOn429: boolean };
75
76
 
76
77
  const NATIVE_PROVIDER_ID = "openai-codex";
@@ -187,9 +188,10 @@ function readSnapshot(value: unknown): UsageSnapshot | undefined {
187
188
  typeof value.remaining !== "number" ||
188
189
  !Number.isFinite(value.remaining) ||
189
190
  !validTime(value.reset) ||
191
+ (value.fiveHourReset !== undefined && !validTime(value.fiveHourReset)) ||
190
192
  (value.limitedUntil !== null && !validTime(value.limitedUntil))
191
193
  )) return undefined;
192
- if (!successful && value.limitedUntil !== undefined) return undefined;
194
+ if (!successful && (value.fiveHourReset !== undefined || value.limitedUntil !== undefined)) return undefined;
193
195
  return {
194
196
  slot: value.slot,
195
197
  accountHash: value.accountHash,
@@ -200,6 +202,7 @@ function readSnapshot(value: unknown): UsageSnapshot | undefined {
200
202
  fetchedAt: value.fetchedAt as number,
201
203
  remaining: Math.max(0, Math.min(100, value.remaining as number)),
202
204
  reset: value.reset as number,
205
+ ...(validTime(value.fiveHourReset) ? { fiveHourReset: value.fiveHourReset } : {}),
203
206
  limitedUntil: value.limitedUntil as number | null,
204
207
  }
205
208
  : {}),
@@ -312,6 +315,13 @@ function isFiveHourLimited(snapshot: UsageSnapshot, now: number): boolean {
312
315
  return validTime(snapshot.limitedUntil) && snapshot.limitedUntil > now;
313
316
  }
314
317
 
318
+ function displayedReset(snapshot: UsageSnapshot, now: number): { label: "5h" | "7d"; time: number } {
319
+ if (["free", "go", "plus"].includes(snapshot.tier ?? "") && validTime(snapshot.fiveHourReset) && snapshot.fiveHourReset > now) {
320
+ return { label: "5h", time: snapshot.fiveHourReset };
321
+ }
322
+ return { label: "7d", time: snapshot.reset! };
323
+ }
324
+
315
325
  function checkedRecently(snapshot: UsageSnapshot | undefined, identity: SlotIdentity, now: number): boolean {
316
326
  return Boolean(
317
327
  snapshot &&
@@ -341,7 +351,7 @@ function resetTime(window: JsonRecord, now: number): number | undefined {
341
351
  return resetAfter === undefined || resetAfter < 0 ? undefined : now + resetAfter * 1000;
342
352
  }
343
353
 
344
- /** Extract seven-day quota and any active five-hour block. */
354
+ /** Extract seven-day quota and the five-hour window. */
345
355
  export function parseCodexUsage(value: unknown, now = Date.now()): ParsedUsage | undefined {
346
356
  if (!isRecord(value)) return undefined;
347
357
  const rateLimit = isRecord(value.rate_limit) ? value.rate_limit : value;
@@ -355,11 +365,13 @@ export function parseCodexUsage(value: unknown, now = Date.now()): ParsedUsage |
355
365
  ?? (windows.length === 1 && numberValue(windows[0].value.limit_window_seconds) === undefined ? windows[0] : undefined);
356
366
  if (!sevenDay || sevenDay.reset <= now) return undefined;
357
367
  const fiveHour = windows.find((window) => numberValue(window.value.limit_window_seconds) === 5 * 60 * 60);
358
- const limitedUntil = fiveHour && fiveHour.used >= 100 && fiveHour.reset > now ? fiveHour.reset : undefined;
368
+ const fiveHourReset = fiveHour && fiveHour.reset > now ? fiveHour.reset : undefined;
369
+ const limitedUntil = fiveHour && fiveHour.used >= 100 ? fiveHourReset : undefined;
359
370
  const tier = validTier(value.plan_type) ? value.plan_type : validTier(value.tier) ? value.tier : undefined;
360
371
  return {
361
372
  remaining: Math.max(0, Math.min(100, 100 - sevenDay.used)),
362
373
  reset: sevenDay.reset,
374
+ ...(fiveHourReset ? { fiveHourReset } : {}),
363
375
  ...(limitedUntil ? { limitedUntil } : {}),
364
376
  ...(tier ? { tier } : {}),
365
377
  };
@@ -598,6 +610,7 @@ class CodexQuotaStatus {
598
610
  fetchedAt: checkedAt,
599
611
  remaining: outcome.remaining,
600
612
  reset: outcome.reset,
613
+ ...(outcome.fiveHourReset && outcome.fiveHourReset > checkedAt ? { fiveHourReset: outcome.fiveHourReset } : {}),
601
614
  limitedUntil: outcome.limitedUntil && outcome.limitedUntil > checkedAt ? outcome.limitedUntil : null,
602
615
  });
603
616
  } else {
@@ -680,7 +693,9 @@ class CodexQuotaStatus {
680
693
  if (isFiveHourLimited(snapshot, now)) {
681
694
  return `Codex slot ${slot}${tier}: five-hour limit reached, resets in ${formatDuration(snapshot.limitedUntil! - now)} (${status})`;
682
695
  }
683
- return `Codex slot ${slot}${tier}: ${formatPercent(snapshot.remaining)}% remaining, resets in ${formatDuration(snapshot.reset - now)} (${status})`;
696
+ const display = displayedReset(snapshot, now);
697
+ const resetWindow = display.label === "5h" ? "five-hour" : "seven-day";
698
+ return `Codex slot ${slot}${tier}: ${formatPercent(snapshot.remaining)}% of the seven-day quota remaining; ${resetWindow} reset in ${formatDuration(display.time - now)} (${status})`;
684
699
  });
685
700
  }
686
701
  }
@@ -903,7 +918,8 @@ export default function multiCodex(pi: ExtensionAPI): void {
903
918
  return ctx.ui.theme?.fg ? ctx.ui.theme.fg("error", text) : text;
904
919
  }
905
920
  if (!isFresh(snapshot, identity, now)) return `${prefix} · stale`;
906
- const text = `${prefix} · ${formatPercent(snapshot.remaining)}% · 7d ${formatDuration(snapshot.reset - now)}`;
921
+ const display = displayedReset(snapshot, now);
922
+ const text = `${prefix} · ${formatPercent(snapshot.remaining)}% · ${display.label} ${formatDuration(display.time - now)}`;
907
923
  const color = snapshot.remaining >= 50 ? "success" : snapshot.remaining >= 25 ? "warning" : "error";
908
924
  return ctx.ui.theme?.fg ? ctx.ui.theme.fg(color, text) : text;
909
925
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@henryqw/pi-multi-codex",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "Use multiple ChatGPT Codex OAuth accounts in Pi.",
5
5
  "keywords": [
6
6
  "pi-package",