codex-multi-auth 2.3.0-beta.3 → 2.3.0
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/.codex-plugin/plugin.json +1 -1
- package/README.md +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/lib/accounts/rate-limits.d.ts +8 -0
- package/dist/lib/accounts/rate-limits.d.ts.map +1 -1
- package/dist/lib/accounts/rate-limits.js +13 -0
- package/dist/lib/accounts.d.ts +19 -1
- package/dist/lib/accounts.d.ts.map +1 -1
- package/dist/lib/accounts.js +31 -2
- package/dist/lib/request/stream-failover-runtime.d.ts.map +1 -1
- package/dist/lib/request/stream-failover-runtime.js +7 -0
- package/dist/lib/runtime/rotation-proxy-state.d.ts.map +1 -1
- package/dist/lib/runtime/rotation-proxy-state.js +24 -0
- package/dist/lib/runtime-rotation-proxy.js +16 -3
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codex-multi-auth",
|
|
3
|
-
"version": "2.3.0
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Install and operate codex-multi-auth for the official @openai/codex CLI with multi-account OAuth rotation, switching, health checks, and recovery tools.",
|
|
5
5
|
"interface": {
|
|
6
6
|
"composerIcon": "./assets/codex-multi-auth-icon.svg"
|
package/README.md
CHANGED
|
@@ -383,8 +383,8 @@ codex-multi-auth doctor --json
|
|
|
383
383
|
|
|
384
384
|
## Release Notes
|
|
385
385
|
|
|
386
|
-
- Current
|
|
387
|
-
-
|
|
386
|
+
- Current stable: [docs/releases/v2.3.0.md](docs/releases/v2.3.0.md) — install via `npm i -g codex-multi-auth`
|
|
387
|
+
- Previous stable: [docs/releases/v2.2.2.md](docs/releases/v2.2.2.md)
|
|
388
388
|
- Previous stable: [docs/releases/v2.2.1.md](docs/releases/v2.2.1.md)
|
|
389
389
|
- Previous stable: [docs/releases/v2.2.0.md](docs/releases/v2.2.0.md)
|
|
390
390
|
- Previous stable: [docs/releases/v2.1.12.md](docs/releases/v2.1.12.md)
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,kBAAkB,CAAC;AA2V5D;;;;;;;;;;;;;;;GAeG;AAEH,eAAO,MAAM,iBAAiB,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,kBAAkB,CAAC;AA2V5D;;;;;;;;;;;;;;;GAeG;AAEH,eAAO,MAAM,iBAAiB,EAAE,MA2uI/B,CAAC;AAEF,eAAO,MAAM,gBAAgB,KAAoB,CAAC;AAElD,eAAe,iBAAiB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1360,6 +1360,12 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1360
1360
|
// regardless of the cooldown mark (AUDIT-H3 / D-07).
|
|
1361
1361
|
accountManager.markRateLimitedWithReason(account, cooldownMs, modelFamily, parseRateLimitReason(rateLimit?.code), model);
|
|
1362
1362
|
accountManager.recordRateLimit(account, modelFamily, model);
|
|
1363
|
+
// Persist the rate-limit window like the full-rotation
|
|
1364
|
+
// branch below (line ~2327). markRateLimitedWithReason
|
|
1365
|
+
// mutates `rateLimitResetTimes`, which is serialized to
|
|
1366
|
+
// disk; without this a crash during the retry sleep loses
|
|
1367
|
+
// the cooldown and the account is re-selected on restart.
|
|
1368
|
+
accountManager.saveToDiskDebounced();
|
|
1363
1369
|
if (accountManager.shouldShowAccountToast(account.index, rateLimitToastDebounceMs)) {
|
|
1364
1370
|
await showRuntimeToast(client, `Rate limited. Retrying in ${waitLabel} (attempt ${shortRateLimitRetryCount})...`, "warning", { duration: toastDurationMs });
|
|
1365
1371
|
accountManager.markToastShown(account.index);
|
|
@@ -16,6 +16,14 @@ export interface RateLimitedEntity {
|
|
|
16
16
|
rateLimitResetTimes: RateLimitState;
|
|
17
17
|
}
|
|
18
18
|
export declare function clearExpiredRateLimits(entity: RateLimitedEntity): void;
|
|
19
|
+
/**
|
|
20
|
+
* Remove every rate-limit reset entry from `entity`, regardless of whether the
|
|
21
|
+
* window has expired. Unlike {@link clearExpiredRateLimits}, this drops
|
|
22
|
+
* still-future entries too. Used by the stale-runtime recovery path to give a
|
|
23
|
+
* reloaded account pool a clean slate so transient state persisted to disk
|
|
24
|
+
* cannot keep the pool wedged (issue #606).
|
|
25
|
+
*/
|
|
26
|
+
export declare function clearAllRateLimits(entity: RateLimitedEntity): void;
|
|
19
27
|
export declare function isRateLimitedForQuotaKey(entity: RateLimitedEntity, key: QuotaKey): boolean;
|
|
20
28
|
export declare function isRateLimitedForFamily(entity: RateLimitedEntity, family: ModelFamily, model?: string | null): boolean;
|
|
21
29
|
export declare function formatWaitTime(ms: number): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limits.d.ts","sourceRoot":"","sources":["../../../lib/accounts/rate-limits.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,MAAM,MAAM,YAAY,GAAG,WAAW,CAAC;AACvC,MAAM,MAAM,QAAQ,GAAG,YAAY,GAAG,GAAG,YAAY,IAAI,MAAM,EAAE,CAAC;AAElE,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,QAAQ,GAAG,YAAY,GAAG,SAAS,CAAC;AAE5E,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,eAAe,CAO9E;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,QAAQ,CAKhF;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAK5E;AAED,MAAM,WAAW,cAAc;IAC9B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CAClC;AAED,MAAM,WAAW,iBAAiB;IACjC,mBAAmB,EAAE,cAAc,CAAC;CACpC;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAStE;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,iBAAiB,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAG1F;AAED,wBAAgB,sBAAsB,CACrC,MAAM,EAAE,iBAAiB,EACzB,MAAM,EAAE,WAAW,EACnB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GACnB,OAAO,CAYT;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAMjD"}
|
|
1
|
+
{"version":3,"file":"rate-limits.d.ts","sourceRoot":"","sources":["../../../lib/accounts/rate-limits.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,MAAM,MAAM,YAAY,GAAG,WAAW,CAAC;AACvC,MAAM,MAAM,QAAQ,GAAG,YAAY,GAAG,GAAG,YAAY,IAAI,MAAM,EAAE,CAAC;AAElE,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,QAAQ,GAAG,YAAY,GAAG,SAAS,CAAC;AAE5E,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,eAAe,CAO9E;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,QAAQ,CAKhF;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAK5E;AAED,MAAM,WAAW,cAAc;IAC9B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CAClC;AAED,MAAM,WAAW,iBAAiB;IACjC,mBAAmB,EAAE,cAAc,CAAC;CACpC;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAStE;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAKlE;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,iBAAiB,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAG1F;AAED,wBAAgB,sBAAsB,CACrC,MAAM,EAAE,iBAAiB,EACzB,MAAM,EAAE,WAAW,EACnB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GACnB,OAAO,CAYT;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAMjD"}
|
|
@@ -37,6 +37,19 @@ export function clearExpiredRateLimits(entity) {
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Remove every rate-limit reset entry from `entity`, regardless of whether the
|
|
42
|
+
* window has expired. Unlike {@link clearExpiredRateLimits}, this drops
|
|
43
|
+
* still-future entries too. Used by the stale-runtime recovery path to give a
|
|
44
|
+
* reloaded account pool a clean slate so transient state persisted to disk
|
|
45
|
+
* cannot keep the pool wedged (issue #606).
|
|
46
|
+
*/
|
|
47
|
+
export function clearAllRateLimits(entity) {
|
|
48
|
+
const keys = Object.keys(entity.rateLimitResetTimes);
|
|
49
|
+
for (const key of keys) {
|
|
50
|
+
delete entity.rateLimitResetTimes[key];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
40
53
|
export function isRateLimitedForQuotaKey(entity, key) {
|
|
41
54
|
const resetTime = entity.rateLimitResetTimes[key];
|
|
42
55
|
return resetTime !== undefined && nowMs() < resetTime;
|
package/dist/lib/accounts.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { type ModelFamily } from "./request/helpers/model-map.js";
|
|
|
6
6
|
import { type HybridSelectionOptions } from "./rotation.js";
|
|
7
7
|
import { type RoutingMutexMode, type SelectionRecord } from "./routing-mutex.js";
|
|
8
8
|
export { extractAccountId, extractAccountEmail, getAccountIdCandidates, selectBestAccountCandidate, resolveRuntimeRequestIdentity, shouldUpdateAccountIdFromToken, resolveRequestAccountId, sanitizeEmail, type AccountIdCandidate, } from "./auth/token-utils.js";
|
|
9
|
-
export { parseRateLimitReason, getQuotaKey, clampNonNegativeInt, clearExpiredRateLimits, isRateLimitedForQuotaKey, isRateLimitedForFamily, formatWaitTime, type QuotaKey, type BaseQuotaKey, type RateLimitReason, type RateLimitState, type RateLimitedEntity, } from "./accounts/rate-limits.js";
|
|
9
|
+
export { parseRateLimitReason, getQuotaKey, clampNonNegativeInt, clearExpiredRateLimits, clearAllRateLimits, isRateLimitedForQuotaKey, isRateLimitedForFamily, formatWaitTime, type QuotaKey, type BaseQuotaKey, type RateLimitReason, type RateLimitState, type RateLimitedEntity, } from "./accounts/rate-limits.js";
|
|
10
10
|
export { lookupCodexCliTokensByEmail, isCodexCliSyncEnabled, type CodexCliTokenCacheEntry, } from "./codex-cli/state.js";
|
|
11
11
|
import { type RateLimitReason } from "./accounts/rate-limits.js";
|
|
12
12
|
export declare function getRuntimeTrackerKey(account: ManagedAccount): string | number;
|
|
@@ -80,6 +80,24 @@ export declare class AccountManager {
|
|
|
80
80
|
isAccountAvailableForFamily(index: number, family: ModelFamily, model?: string | null): boolean;
|
|
81
81
|
getAccountRuntimeSkipReason(index: number, family: ModelFamily, model?: string | null): string | null;
|
|
82
82
|
static resetVolatileRuntimeState(): void;
|
|
83
|
+
/**
|
|
84
|
+
* Wipe per-account transient state — active cooldowns and all rate-limit
|
|
85
|
+
* reset windows — across every managed account, then schedule a debounced
|
|
86
|
+
* persist of the cleared pool.
|
|
87
|
+
*
|
|
88
|
+
* `resetVolatileRuntimeState` only clears process-global singletons (rotation
|
|
89
|
+
* trackers, circuit breakers); the cooldown timestamps and `rateLimitResetTimes`
|
|
90
|
+
* maps live on the `ManagedAccount` objects and are serialized to disk by
|
|
91
|
+
* `buildStorageSnapshot`. The stale-runtime recovery path reloads accounts
|
|
92
|
+
* from that snapshot, so without this the same transient state that wedged the
|
|
93
|
+
* pool is restored verbatim and recovery never escapes it (issue #606).
|
|
94
|
+
*
|
|
95
|
+
* The in-memory clear is immediate (it is what unblocks the live pool). The
|
|
96
|
+
* disk write is debounced via `saveToDiskDebounced`, so a caller that needs
|
|
97
|
+
* the cleared state to survive a restart should `await flushPendingSave()`
|
|
98
|
+
* afterwards rather than rely on the debounce window completing.
|
|
99
|
+
*/
|
|
100
|
+
clearAccountTransientState(): void;
|
|
83
101
|
setActiveIndex(index: number): ManagedAccount | null;
|
|
84
102
|
syncCodexCliActiveSelectionForIndex(index: number): Promise<void>;
|
|
85
103
|
getCurrentAccount(): ManagedAccount | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accounts.d.ts","sourceRoot":"","sources":["../../lib/accounts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAG1C,OAAO,EAKN,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EAGrB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAClF,OAAO,EAMN,KAAK,sBAAsB,EAC3B,MAAM,eAAe,CAAC;AACvB,OAAO,EAEN,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,MAAM,oBAAoB,CAAC;AAqB5B,OAAO,EACN,gBAAgB,EAChB,mBAAmB,EACnB,sBAAsB,EACtB,0BAA0B,EAC1B,6BAA6B,EAC7B,8BAA8B,EAC9B,uBAAuB,EACvB,aAAa,EACb,KAAK,kBAAkB,GACvB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACN,oBAAoB,EACpB,WAAW,EACX,mBAAmB,EACnB,sBAAsB,EACtB,wBAAwB,EACxB,sBAAsB,EACtB,cAAc,EACd,KAAK,QAAQ,EACb,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,iBAAiB,GACtB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACN,2BAA2B,EAC3B,qBAAqB,EACrB,KAAK,uBAAuB,GAC5B,MAAM,sBAAsB,CAAC;AAQ9B,OAAO,
|
|
1
|
+
{"version":3,"file":"accounts.d.ts","sourceRoot":"","sources":["../../lib/accounts.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/accounts.js
CHANGED
|
@@ -14,10 +14,10 @@ import { getAccountIdentityKey, getRuntimeAccountIdentityKey, } from "./storage/
|
|
|
14
14
|
import { getCircuitBreaker, resetAllCircuitBreakers, removeCircuitBreaker } from "./circuit-breaker.js";
|
|
15
15
|
import { getStoragePathState, runWithStoragePathState, } from "./storage/path-state.js";
|
|
16
16
|
export { extractAccountId, extractAccountEmail, getAccountIdCandidates, selectBestAccountCandidate, resolveRuntimeRequestIdentity, shouldUpdateAccountIdFromToken, resolveRequestAccountId, sanitizeEmail, } from "./auth/token-utils.js";
|
|
17
|
-
export { parseRateLimitReason, getQuotaKey, clampNonNegativeInt, clearExpiredRateLimits, isRateLimitedForQuotaKey, isRateLimitedForFamily, formatWaitTime, } from "./accounts/rate-limits.js";
|
|
17
|
+
export { parseRateLimitReason, getQuotaKey, clampNonNegativeInt, clearExpiredRateLimits, clearAllRateLimits, isRateLimitedForQuotaKey, isRateLimitedForFamily, formatWaitTime, } from "./accounts/rate-limits.js";
|
|
18
18
|
export { lookupCodexCliTokensByEmail, isCodexCliSyncEnabled, } from "./codex-cli/state.js";
|
|
19
19
|
import { extractAccountId, extractAccountEmail, shouldUpdateAccountIdFromToken, sanitizeEmail, } from "./auth/token-utils.js";
|
|
20
|
-
import { clampNonNegativeInt, getQuotaKey, clearExpiredRateLimits, isRateLimitedForFamily, formatWaitTime, } from "./accounts/rate-limits.js";
|
|
20
|
+
import { clampNonNegativeInt, getQuotaKey, clearExpiredRateLimits, clearAllRateLimits, isRateLimitedForFamily, formatWaitTime, } from "./accounts/rate-limits.js";
|
|
21
21
|
const log = createLogger("accounts");
|
|
22
22
|
let nextRuntimeCircuitKeyId = 0;
|
|
23
23
|
function getAccountCircuitKey(account) {
|
|
@@ -449,6 +449,35 @@ export class AccountManager {
|
|
|
449
449
|
resetTrackers();
|
|
450
450
|
resetAllCircuitBreakers();
|
|
451
451
|
}
|
|
452
|
+
/**
|
|
453
|
+
* Wipe per-account transient state — active cooldowns and all rate-limit
|
|
454
|
+
* reset windows — across every managed account, then schedule a debounced
|
|
455
|
+
* persist of the cleared pool.
|
|
456
|
+
*
|
|
457
|
+
* `resetVolatileRuntimeState` only clears process-global singletons (rotation
|
|
458
|
+
* trackers, circuit breakers); the cooldown timestamps and `rateLimitResetTimes`
|
|
459
|
+
* maps live on the `ManagedAccount` objects and are serialized to disk by
|
|
460
|
+
* `buildStorageSnapshot`. The stale-runtime recovery path reloads accounts
|
|
461
|
+
* from that snapshot, so without this the same transient state that wedged the
|
|
462
|
+
* pool is restored verbatim and recovery never escapes it (issue #606).
|
|
463
|
+
*
|
|
464
|
+
* The in-memory clear is immediate (it is what unblocks the live pool). The
|
|
465
|
+
* disk write is debounced via `saveToDiskDebounced`, so a caller that needs
|
|
466
|
+
* the cleared state to survive a restart should `await flushPendingSave()`
|
|
467
|
+
* afterwards rather than rely on the debounce window completing.
|
|
468
|
+
*/
|
|
469
|
+
clearAccountTransientState() {
|
|
470
|
+
if (this.accounts.length === 0)
|
|
471
|
+
return;
|
|
472
|
+
// Snapshot the reference list so a concurrent mutation of `this.accounts`
|
|
473
|
+
// (e.g. removeAccount) cannot reshape the array mid-iteration.
|
|
474
|
+
for (const account of [...this.accounts]) {
|
|
475
|
+
this.clearAccountCooldown(account);
|
|
476
|
+
clearAllRateLimits(account);
|
|
477
|
+
account.lastRateLimitReason = undefined;
|
|
478
|
+
}
|
|
479
|
+
this.saveToDiskDebounced();
|
|
480
|
+
}
|
|
452
481
|
setActiveIndex(index) {
|
|
453
482
|
if (!Number.isFinite(index))
|
|
454
483
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream-failover-runtime.d.ts","sourceRoot":"","sources":["../../../lib/request/stream-failover-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AAEtF,eAAO,MAAM,kBAAkB,aAW7B,CAAC;AAWH,wBAAgB,wBAAwB,CAAC,eAAe,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAUzF;AAED,wBAAsB,WAAW,CAAC,CAAC,EAClC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,IAAI,EACrB,OAAO,EAAE,MAAM,GACb,OAAO,CAAC,CAAC,CAAC,CAmBZ;AAED,wBAAsB,aAAa,CAClC,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,MAAM,EACjB,QAAQ,SAAc,GACpB,OAAO,CAAC,MAAM,CAAC,CAuDjB;AAoBD,wBAAsB,wBAAwB,CAC7C,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,0BAA0B,EAClC,aAAa,EAAE,MAAM,IAAI,EACzB,oBAAoB,EAAE,MAAM,GAC1B,OAAO,CAAC,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"stream-failover-runtime.d.ts","sourceRoot":"","sources":["../../../lib/request/stream-failover-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AAEtF,eAAO,MAAM,kBAAkB,aAW7B,CAAC;AAWH,wBAAgB,wBAAwB,CAAC,eAAe,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAUzF;AAED,wBAAsB,WAAW,CAAC,CAAC,EAClC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,IAAI,EACrB,OAAO,EAAE,MAAM,GACb,OAAO,CAAC,CAAC,CAAC,CAmBZ;AAED,wBAAsB,aAAa,CAClC,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,MAAM,EACjB,QAAQ,SAAc,GACpB,OAAO,CAAC,MAAM,CAAC,CAuDjB;AAoBD,wBAAsB,wBAAwB,CAC7C,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,0BAA0B,EAClC,aAAa,EAAE,MAAM,IAAI,EACzB,oBAAoB,EAAE,MAAM,GAC1B,OAAO,CAAC,OAAO,CAAC,CAqDlB"}
|
|
@@ -149,6 +149,13 @@ export async function forwardStreamingResponse(upstream, res, status, onStreamEr
|
|
|
149
149
|
if (done)
|
|
150
150
|
break;
|
|
151
151
|
if (value && value.byteLength > 0) {
|
|
152
|
+
// If the response is already finished (clean end by a concurrent
|
|
153
|
+
// close-then-reader-cancel path), stop writing. Do NOT guard on
|
|
154
|
+
// res.destroyed here: a socket-error-during-backpressure scenario sets
|
|
155
|
+
// destroyed=true and then lets the next res.write() throw so the catch
|
|
156
|
+
// block can record the error and fire onStreamError correctly.
|
|
157
|
+
if (res.writableEnded)
|
|
158
|
+
break;
|
|
152
159
|
// Respect backpressure: when the client's socket buffer is full,
|
|
153
160
|
// pause upstream reads until it drains instead of buffering the
|
|
154
161
|
// whole stream in memory for a slow consumer.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rotation-proxy-state.d.ts","sourceRoot":"","sources":["../../../lib/runtime/rotation-proxy-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAKhD,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAC7E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAEnE;;;;;;GAMG;AACH,MAAM,WAAW,sBAAsB;IACtC,oBAAoB,EAAE,cAAc,CAAC;IACrC,gBAAgB,EAAE,SAAS,GAAG,QAAQ,CAAC;IACvC,kBAAkB,EAAE,QAAQ,GAAG,YAAY,CAAC;IAC5C,SAAS,EAAE,OAAO,KAAK,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,sBAAsB,EAAE,MAAM,CAAC;IAC/B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,2BAA2B,EAAE,MAAM,CAAC;IACpC,qBAAqB,EAAE,MAAM,CAAC;IAC9B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,yBAAyB,EAAE,MAAM,CAAC;IAClC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,8BAA8B,EAAE,MAAM,CAAC;IACvC,oBAAoB,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAClD,8BAA8B,EAAE,MAAM,CAAC;CACvC;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,kBAAmB,SAAQ,sBAAsB;IACjE,QAAQ,CAAC,oBAAoB,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC;IACnD,QAAQ,CAAC,MAAM,EAAE,0BAA0B,CAAC;IAC5C,QAAQ,CAAC,mBAAmB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;IACzD,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,yBAAyB,EAAE,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IACjE,wBAAwB,EAAE,MAAM,CAAC;CACjC;AAID,gBAAgB;AAChB,wBAAgB,wBAAwB,CACvC,IAAI,EAAE,sBAAsB,GAC1B,kBAAkB,CAuBpB;AAED,gBAAgB;AAChB,wBAAsB,wBAAwB,CAC7C,KAAK,EAAE,kBAAkB,GACvB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"rotation-proxy-state.d.ts","sourceRoot":"","sources":["../../../lib/runtime/rotation-proxy-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAKhD,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAC7E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAEnE;;;;;;GAMG;AACH,MAAM,WAAW,sBAAsB;IACtC,oBAAoB,EAAE,cAAc,CAAC;IACrC,gBAAgB,EAAE,SAAS,GAAG,QAAQ,CAAC;IACvC,kBAAkB,EAAE,QAAQ,GAAG,YAAY,CAAC;IAC5C,SAAS,EAAE,OAAO,KAAK,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,sBAAsB,EAAE,MAAM,CAAC;IAC/B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,2BAA2B,EAAE,MAAM,CAAC;IACpC,qBAAqB,EAAE,MAAM,CAAC;IAC9B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,yBAAyB,EAAE,MAAM,CAAC;IAClC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,8BAA8B,EAAE,MAAM,CAAC;IACvC,oBAAoB,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAClD,8BAA8B,EAAE,MAAM,CAAC;CACvC;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,kBAAmB,SAAQ,sBAAsB;IACjE,QAAQ,CAAC,oBAAoB,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC;IACnD,QAAQ,CAAC,MAAM,EAAE,0BAA0B,CAAC;IAC5C,QAAQ,CAAC,mBAAmB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;IACzD,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,yBAAyB,EAAE,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IACjE,wBAAwB,EAAE,MAAM,CAAC;CACjC;AAID,gBAAgB;AAChB,wBAAgB,wBAAwB,CACvC,IAAI,EAAE,sBAAsB,GAC1B,kBAAkB,CAuBpB;AAED,gBAAgB;AAChB,wBAAsB,wBAAwB,CAC7C,KAAK,EAAE,kBAAkB,GACvB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAkDhC"}
|
|
@@ -39,6 +39,30 @@ export async function recoverStaleRuntimeState(state) {
|
|
|
39
39
|
recordRuntimeReset("pool-exhausted-no-account");
|
|
40
40
|
const reloaded = await AccountManager.loadFromDisk();
|
|
41
41
|
reloaded.setRoutingMutexMode(state.routingMutexMode);
|
|
42
|
+
// Wipe per-account cooldowns and rate-limit windows on the freshly
|
|
43
|
+
// reloaded pool. `resetVolatileRuntimeState` above only cleared global
|
|
44
|
+
// singletons (trackers, circuit breakers); the per-account transient
|
|
45
|
+
// state is serialized to disk, so loadFromDisk restores the same state
|
|
46
|
+
// that wedged the pool. Clearing it here gives recovery a real clean
|
|
47
|
+
// slate before any request can pick up the manager (issue #606). Runs
|
|
48
|
+
// before the `state.activeAccountManager` assignment so no concurrent
|
|
49
|
+
// request can observe the reloaded manager with stale state.
|
|
50
|
+
//
|
|
51
|
+
// This also drops still-future rate-limit windows from genuine upstream
|
|
52
|
+
// 429s, not just stale ones from a dead prior process. That is the
|
|
53
|
+
// intended trade-off: recovery only runs at full pool exhaustion, where
|
|
54
|
+
// the alternative is a hard 503 anyway, and the 1s reload dedupe bounds
|
|
55
|
+
// re-probing to ~1/sec — the upstream simply re-429s and re-populates the
|
|
56
|
+
// window until the next exhaustion. Availability is preferred over
|
|
57
|
+
// honoring backoff in this already-degraded state.
|
|
58
|
+
reloaded.clearAccountTransientState();
|
|
59
|
+
// Force the cleared snapshot to disk now rather than waiting out the
|
|
60
|
+
// debounce window inside clearAccountTransientState. If the process
|
|
61
|
+
// exited during that window the next startup would reload the wedged
|
|
62
|
+
// snapshot; flushing here makes the "next reload starts clean" guarantee
|
|
63
|
+
// durable across a restart, not just best-effort. Recovery is rare
|
|
64
|
+
// (full-pool exhaustion), so the extra synchronous write is cheap.
|
|
65
|
+
await reloaded.flushPendingSave();
|
|
42
66
|
state.activeAccountManager = reloaded;
|
|
43
67
|
state.knownAccountManagers.add(reloaded);
|
|
44
68
|
state.lastStaleRuntimeReloadAt = Date.now();
|
|
@@ -738,9 +738,16 @@ async function handleRequestInner(state, req, res, traceId) {
|
|
|
738
738
|
accountCount > 0 &&
|
|
739
739
|
exhaustionReason === "no-account" &&
|
|
740
740
|
(policyDecision?.blockedAccountIndexes.size ?? 0) === 0 &&
|
|
741
|
-
![...accountSkipReasons.values()].some(
|
|
742
|
-
|
|
743
|
-
|
|
741
|
+
![...accountSkipReasons.values()].some(
|
|
742
|
+
// Only policy blocks still suppress stale-state recovery: a policy
|
|
743
|
+
// decision is external and won't change across a disk reload, so
|
|
744
|
+
// reloading cannot help. "rate-limited" and "cooling-down*" are
|
|
745
|
+
// transient states that recovery is *designed* to escape — they are
|
|
746
|
+
// persisted to disk (buildStorageSnapshot) and so survive a reload,
|
|
747
|
+
// which previously deadlocked the pool against the very recovery that
|
|
748
|
+
// would clear them. recoverStaleRuntimeState now wipes that transient
|
|
749
|
+
// state after reloading, so let those reasons through (issue #606).
|
|
750
|
+
(reason) => reason === "policy-blocked")) {
|
|
744
751
|
reloadedAfterNoAccount = true;
|
|
745
752
|
const reloadedManager = await recoverStaleRuntimeState(state);
|
|
746
753
|
if (reloadedManager) {
|
|
@@ -802,6 +809,12 @@ async function handleRequestInner(state, req, res, traceId) {
|
|
|
802
809
|
accountManager.refundToken(refreshed.account, context.family, context.model);
|
|
803
810
|
accountManager.recordFailure(refreshed.account, context.family, context.model);
|
|
804
811
|
accountManager.markAccountCoolingDown(refreshed.account, DEFAULT_AUTH_FAILURE_COOLDOWN_MS, "auth-failure");
|
|
812
|
+
// Persist the cooldown like every other cooldown branch in this loop
|
|
813
|
+
// (network-error, 429, server-error, 401). `coolingDownUntil`/
|
|
814
|
+
// `cooldownReason` are serialized in the V3 snapshot, so without this
|
|
815
|
+
// a restart inside the cooldown window loses it and immediately
|
|
816
|
+
// re-selects the still-broken account.
|
|
817
|
+
accountManager.saveToDiskDebounced();
|
|
805
818
|
exhaustionReason = "auth-failure";
|
|
806
819
|
transientAttempts += 1;
|
|
807
820
|
transientExhaustionReason = "auth-failure";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codex-multi-auth",
|
|
3
|
-
"version": "2.3.0
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Codex CLI multi-account OAuth manager with account switching, health checks, runtime rotation, diagnostics, and recovery tools for @openai/codex",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|