oc-codex-multi-auth 6.14.2 → 6.14.4
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/dist/index.d.ts.map +1 -1
- package/dist/index.js +48 -14
- package/dist/index.js.map +1 -1
- package/dist/lib/accounts/rate-limits.d.ts +11 -0
- package/dist/lib/accounts/rate-limits.d.ts.map +1 -1
- package/dist/lib/accounts/rate-limits.js +19 -2
- package/dist/lib/accounts/rate-limits.js.map +1 -1
- package/dist/lib/accounts/rotation.d.ts +33 -2
- package/dist/lib/accounts/rotation.d.ts.map +1 -1
- package/dist/lib/accounts/rotation.js +38 -3
- package/dist/lib/accounts/rotation.js.map +1 -1
- package/dist/lib/auth/auth.d.ts +30 -5
- package/dist/lib/auth/auth.d.ts.map +1 -1
- package/dist/lib/auth/auth.js +100 -28
- package/dist/lib/auth/auth.js.map +1 -1
- package/dist/lib/quota-windows.d.ts +17 -0
- package/dist/lib/quota-windows.d.ts.map +1 -1
- package/dist/lib/quota-windows.js +38 -3
- package/dist/lib/quota-windows.js.map +1 -1
- package/dist/lib/request/fetch-helpers.d.ts +5 -0
- package/dist/lib/request/fetch-helpers.d.ts.map +1 -1
- package/dist/lib/request/fetch-helpers.js +57 -19
- package/dist/lib/request/fetch-helpers.js.map +1 -1
- package/dist/lib/rotation.d.ts.map +1 -1
- package/dist/lib/rotation.js +6 -1
- package/dist/lib/rotation.js.map +1 -1
- package/dist/lib/types.d.ts +0 -4
- package/dist/lib/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAMH,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAC;AAsU/D;;;;;;;;;;;;;;;GAeG;AAEH,eAAO,MAAM,iBAAiB,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAMH,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAC;AAsU/D;;;;;;;;;;;;;;;GAeG;AAEH,eAAO,MAAM,iBAAiB,EAAE,MAu8H/B,CAAC;AAEF,eAAO,MAAM,gBAAgB,QAAoB,CAAC;AAElD,eAAe,iBAAiB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -314,7 +314,9 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
314
314
|
return "No authorization code found. Paste the full callback URL (e.g., http://localhost:1455/auth/callback?code=...). If browser callback keeps failing, retry with Device Code.";
|
|
315
315
|
}
|
|
316
316
|
if (!parsed.state) {
|
|
317
|
-
return
|
|
317
|
+
return parsed.source === "raw"
|
|
318
|
+
? "That is a bare authorization code. This flow needs the full callback URL, including the state parameter (e.g., http://localhost:1455/auth/callback?code=...&state=...). If needed, retry with Device Code."
|
|
319
|
+
: "Missing OAuth state. Paste the full callback URL including both code and state parameters. If needed, retry with Device Code.";
|
|
318
320
|
}
|
|
319
321
|
if (parsed.state !== expectedState) {
|
|
320
322
|
return "OAuth state mismatch. Restart login and paste the callback URL generated for this login attempt, or retry with Device Code.";
|
|
@@ -429,26 +431,43 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
429
431
|
* Take an account out of rotation whenever the backend reports one of its
|
|
430
432
|
* quota windows as fully spent (issue #218).
|
|
431
433
|
*
|
|
432
|
-
*
|
|
433
|
-
* and
|
|
434
|
-
* can record it instead of rediscovering it with a failed request
|
|
435
|
-
* subsequent prompt. The block lands on the persisted
|
|
436
|
-
* map, so it is remembered across restarts and shared
|
|
437
|
-
* and it clears itself once the window rolls over.
|
|
434
|
+
* Codex carries `x-codex-*-used-percent` and the matching reset time on a
|
|
435
|
+
* served request and on a refused one alike, so the moment an account hits
|
|
436
|
+
* 0% left we can record it instead of rediscovering it with a failed request
|
|
437
|
+
* on every subsequent prompt. The block lands on the persisted
|
|
438
|
+
* `rateLimitResetTimes` map, so it is remembered across restarts and shared
|
|
439
|
+
* with other processes, and it clears itself once the window rolls over.
|
|
440
|
+
*
|
|
441
|
+
* Call this only for responses whose headers are authoritative: one the
|
|
442
|
+
* backend served, or one it refused for a confirmed usage limit. Every other
|
|
443
|
+
* error class — entitlement (issues #16/#17), auth, 5xx, or an upstream
|
|
444
|
+
* overload dressed up as a 429 — can echo a stale quota snapshot next to an
|
|
445
|
+
* unrelated failure, and writing that echo locked healthy accounts out for
|
|
446
|
+
* hours. `handleErrorResponse` makes that call once and reports it back as
|
|
447
|
+
* `quotaHeadersAuthoritative`.
|
|
448
|
+
*
|
|
449
|
+
* The resulting gap is deliberate: an account that really is spent but gets
|
|
450
|
+
* a 5xx stays selectable until its next genuine 429, which costs one wasted
|
|
451
|
+
* request — cheaper than a false multi-hour block on a healthy account.
|
|
452
|
+
*
|
|
453
|
+
* @returns true when the headers report a spent window, whether or not the
|
|
454
|
+
* stored block moved. Callers use it to stop retrying this account.
|
|
438
455
|
*/
|
|
439
456
|
const applyQuotaExhaustion = (manager, headers, account, family, model) => {
|
|
440
457
|
try {
|
|
441
458
|
const resetAtMs = getQuotaExhaustedResetAtMs(headers);
|
|
442
459
|
if (resetAtMs === undefined)
|
|
443
|
-
return;
|
|
460
|
+
return false;
|
|
444
461
|
if (!manager.markQuotaExhausted(account, resetAtMs, family, model))
|
|
445
|
-
return;
|
|
462
|
+
return true;
|
|
446
463
|
account.lastSwitchReason = "rate-limit";
|
|
447
464
|
manager.saveToDiskDebounced();
|
|
448
465
|
logWarn(`Account ${account.index + 1} (${account.email ?? "unknown"}) has no ${family} quota left; skipping it for ${formatWaitTime(resetAtMs - Date.now())}.`);
|
|
466
|
+
return true;
|
|
449
467
|
}
|
|
450
468
|
catch (error) {
|
|
451
469
|
logDebug(`[${PLUGIN_NAME}] Failed to apply quota exhaustion: ${error instanceof Error ? error.message : String(error)}`);
|
|
470
|
+
return false;
|
|
452
471
|
}
|
|
453
472
|
};
|
|
454
473
|
const resolveActiveIndex = (storage, family = "codex") => {
|
|
@@ -1937,17 +1956,27 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1937
1956
|
latencyMs: fetchLatencyMs,
|
|
1938
1957
|
headers: Object.fromEntries(response.headers.entries()),
|
|
1939
1958
|
});
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1959
|
+
// The TUI snapshot and the durable rotation block read the same
|
|
1960
|
+
// `x-codex-*` headers, so they share one authority decision — gating
|
|
1961
|
+
// only the block would leave the status line reporting "0% left" for
|
|
1962
|
+
// an account the router considers healthy.
|
|
1963
|
+
const recordQuotaHeaders = () => {
|
|
1964
|
+
void recordPromptQuotaHeaders(response, account, accountCount);
|
|
1965
|
+
return applyQuotaExhaustion(accountManager, response.headers, account, modelFamily, model);
|
|
1966
|
+
};
|
|
1967
|
+
if (response.ok) {
|
|
1968
|
+
recordQuotaHeaders();
|
|
1969
|
+
}
|
|
1970
|
+
else {
|
|
1943
1971
|
const contextOverflowResult = await handleContextOverflow(response, model);
|
|
1944
1972
|
if (contextOverflowResult.handled) {
|
|
1945
1973
|
return contextOverflowResult.response;
|
|
1946
1974
|
}
|
|
1947
|
-
const { response: errorResponse, rateLimit, errorBody, retryAsServerError } = await handleErrorResponse(response, {
|
|
1975
|
+
const { response: errorResponse, rateLimit, errorBody, retryAsServerError, quotaHeadersAuthoritative, } = await handleErrorResponse(response, {
|
|
1948
1976
|
requestCorrelationId,
|
|
1949
1977
|
threadId: threadIdCandidate,
|
|
1950
1978
|
});
|
|
1979
|
+
const quotaExhausted = quotaHeadersAuthoritative === true && recordQuotaHeaders();
|
|
1951
1980
|
const workspaceDeactivated = isDeactivatedWorkspaceError(errorBody, response.status);
|
|
1952
1981
|
if (workspaceDeactivated) {
|
|
1953
1982
|
const accountLabel = formatAccountLabel(account, account.index, {
|
|
@@ -2190,7 +2219,12 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2190
2219
|
runtimeMetrics.rateLimitedResponses++;
|
|
2191
2220
|
const { attempt, delayMs } = getRateLimitBackoff(account.index, quotaKey, rateLimit.retryAfterMs);
|
|
2192
2221
|
const waitLabel = formatWaitTime(delayMs);
|
|
2193
|
-
|
|
2222
|
+
// A spent window will still be spent in a second, and the block
|
|
2223
|
+
// just written for it is monotonic — a short retry that happened
|
|
2224
|
+
// to succeed could not walk it back, so the account would serve
|
|
2225
|
+
// traffic while rotation still considers it blocked. Rotate.
|
|
2226
|
+
if (!quotaExhausted &&
|
|
2227
|
+
delayMs <= RATE_LIMIT_SHORT_RETRY_THRESHOLD_MS &&
|
|
2194
2228
|
consumeRetryBudget("rateLimitShort", `Short 429 retry for account ${account.index + 1} after ${delayMs}ms`)) {
|
|
2195
2229
|
if (accountManager.shouldShowAccountToast(account.index, rateLimitToastDebounceMs)) {
|
|
2196
2230
|
await showToast(`Rate limited. Retrying in ${waitLabel} (attempt ${attempt})...`, "warning", { duration: toastDurationMs });
|