Skip to content

v6.14.3

Choose a tag to compare

@ndycode ndycode released this 27 Aug 15:57
· 157 commits to main since this release

https://www.npmjs.com/package/oc-codex-multi-auth/v/6.14.3

npx oc-codex-multi-auth@6.14.3 install

Fixed

An implausible quota-reset header took an account out of rotation permanently. The x-codex-* reset headers were parsed with no upper bound. -reset-after-seconds was multiplied out and added to the clock as-is; -reset-at was accepted verbatim as an epoch stamp or an ISO date. A garbled value resolved to an arbitrary point in the future:

-reset-after-seconds: 4000000000 → a reset 127 years out
-reset-at: 9999-12-31T00:00:00Z → a reset 2,912,203 days out

That is not just a long backoff. The value lands in the persisted rateLimitResetTimes map through a deliberately monotonic writer, and expiry only drops resets already in the past — so nothing in the product could shorten it again. A single malformed header, from the backend, an intermediary, or a configured OPENAI_BASE_URL gateway, removed the account from rotation until the user edited the state file by hand. The same value also reached the retry-after delay derived from those headers.

Reset times further out than any real Codex window are now rejected rather than clamped. An implausible header tells you nothing about when the account actually recovers, so recording no reset — and letting the next request rediscover the truth — is safer than recording a month-long block on the strength of a garbled number. Plausible values are untouched: a 5h window, a full 7d weekly window, and a reset exactly on the horizon are all still honored.

Stale quota headers on an entitlement error blocked healthy accounts (#237). A response refused for a subscription entitlement problem can still carry the account's last-known quota snapshot, and those headers were consumed unconditionally — so an entitlement failure recorded a quota block against an account that had quota left. Quota headers are now believed only on a response the backend actually served, or one it refused with a confirmed usage limit (HTTP 429, including a 404 remapped to one). Entitlement failures, auth failures, 5xx, and upstream overloads dressed up as a 429 are all ignored for this purpose.

A backwards clock jump turned account health recovery into a penalty. Passive recovery scaled elapsed time since the last health update without clamping it at zero. That timestamp comes from the local clock, so an NTP correction or a resumed VM makes the interval negative and the recovery term subtracts instead of adding — a measured score of -17440 against a floor of 0, leaving the account ranked last in selection long after the clock settled.

NaN and Infinity reached users verbatim in wait-time messages. A non-finite duration rendered as the literal strings NaNs and Infinitym NaNs in toasts, status lines and log warnings. Non-finite input now reads as zero. The same formatter had no unit above minutes either, so a weekly quota block and process uptime both printed as a five-figure minute count (10080m 0s) — hours and days are now split out.

Internal

The decision about whether a response's quota headers are authoritative is now made once, in the error classifier, and reported to the caller as quotaHeadersAuthoritative (#237). It previously lived at a single call site, which left the two other consumers of the same headers on the old behavior: the retry-after parser still read the exhausted-window reset uncapped, in two separate places, and the TUI quota snapshot was still written from headers the router had just discarded — so the status line could report "0% left" for an account rotation considered healthy.

The short 429 retry no longer replays a request on an account whose quota window it has just blocked (#237). That block is monotonic, so a retry that happened to succeed could not walk it back, leaving the account serving traffic while rotation still considered it blocked.

The rotation selectors' contracts are documented accurately. getCurrentOrNextForFamilySticky claimed to match "the other selectors' contract" in returning null when no account is available; getCurrentOrNextForFamilyHybrid deliberately does the opposite, falling back to the least-recently-used account rather than hard-failing — which is what keeps a single-account pool usable when its only account is cooling down. Both now state the difference.


#237 by @lubshad.