codex-multi-auth 2.13.0 → 2.15.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 +6 -1
- package/dist/lib/request/error-classification.d.ts +13 -6
- package/dist/lib/request/error-classification.d.ts.map +1 -1
- package/dist/lib/request/error-classification.js +45 -0
- package/dist/lib/request/fetch-helpers.d.ts +1 -1
- package/dist/lib/request/fetch-helpers.d.ts.map +1 -1
- package/dist/lib/request/fetch-helpers.js +1 -1
- package/dist/lib/runtime/config-toml.d.ts.map +1 -1
- package/dist/lib/runtime/config-toml.js +1 -0
- package/dist/lib/runtime/rotation-proxy-state.d.ts +6 -0
- package/dist/lib/runtime/rotation-proxy-state.d.ts.map +1 -1
- package/dist/lib/runtime/rotation-server-types.d.ts +7 -0
- package/dist/lib/runtime/rotation-server-types.d.ts.map +1 -1
- package/dist/lib/runtime-rotation-proxy.d.ts +10 -0
- package/dist/lib/runtime-rotation-proxy.d.ts.map +1 -1
- package/dist/lib/runtime-rotation-proxy.js +268 -13
- package/dist/lib/usage/ledger.d.ts.map +1 -1
- package/dist/lib/usage/ledger.js +1 -0
- package/dist/lib/usage/redaction.d.ts.map +1 -1
- package/dist/lib/usage/redaction.js +1 -0
- package/dist/lib/usage/types.d.ts +1 -1
- package/dist/lib/usage/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/scripts/codex.js +1 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codex-multi-auth",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.15.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
|
@@ -29,6 +29,8 @@ Use it when you need a local Codex CLI multi-account workflow with visible accou
|
|
|
29
29
|
- Local usage ledger, budget guards, account policy controls, routing profiles, and model/account capability views
|
|
30
30
|
- Runtime counters, budget/cooldown state, and multi-auth probe visibility in `codex-multi-auth status` / `codex-multi-auth report`
|
|
31
31
|
- Default-on loopback Responses proxy for live account rotation inside forwarded Codex CLI/app sessions
|
|
32
|
+
- [Image generation/edit transport](docs/reference/image-routes.md) through the same authenticated runtime proxy
|
|
33
|
+
- [Built-in image_gen provider compatibility](docs/reference/imagegen-provider-compatibility.md) through the same authenticated runtime proxy
|
|
32
34
|
- Optional loopback-only local bridge for `/health`, `/v1/models`, and `/v1/responses`, protected by hashed local client tokens
|
|
33
35
|
- Reversible packaged Codex app bind and user-level launcher routing helpers that do not patch official app binaries
|
|
34
36
|
- Session affinity, live account sync, proactive refresh, and preemptive quota deferral controls
|
|
@@ -298,6 +300,7 @@ Selected runtime/environment overrides:
|
|
|
298
300
|
| `CODEX_MULTI_AUTH_BYPASS=1` | Skip multi-auth intercept and forward straight to official Codex |
|
|
299
301
|
| `CODEX_MULTI_AUTH_RUNTIME_ROTATION_PROXY=0/1` | Opt out/in of live Responses proxy rotation for forwarded Codex CLI/app sessions |
|
|
300
302
|
| `CODEX_MULTI_AUTH_RUNTIME_PROXY_UPSTREAM_BASE_URL=http://127.0.0.1:<port>/<path>` | Route the runtime rotation proxy through an explicit loopback HTTP upstream; requires an explicit port and a numeric loopback host (`127.0.0.0/8` or `[::1]`), and rejects credentials, query strings, fragments, HTTPS, and every other host |
|
|
303
|
+
| `CODEX_MULTI_AUTH_MODEL_CAPACITY_RETRY_MS=<ms>` | Wall-clock ceiling on how long one request waits out a "selected model is at capacity" response before giving up (default 600000, max 3600000, `0` disables) |
|
|
301
304
|
| `CODEX_MULTI_AUTH_APP_ROTATION_IDLE_MS=<ms>` | Override automatic Codex app helper idle shutdown |
|
|
302
305
|
| `CODEX_MULTI_AUTH_APP_BIND_INSTALL=0/1` | Opt out/in of packaged Codex app bind self-heal on first CLI run or rotation enable |
|
|
303
306
|
| `CODEX_MULTI_AUTH_APP_LAUNCHER_INSTALL=0/1` | Opt out/in of routing supported app shortcuts on first CLI run or rotation enable |
|
|
@@ -322,6 +325,8 @@ Runtime rotation is enabled by default for request-bearing wrapper-launched Code
|
|
|
322
325
|
|
|
323
326
|
Advanced local proxy chains can set `CODEX_MULTI_AUTH_RUNTIME_PROXY_UPSTREAM_BASE_URL` for one wrapper process. The wrapper passes that URL to both the shadow-runtime and interactive-helper rotation paths and fails closed if the configured upstream is invalid or unavailable; it never falls back silently to the direct backend while an explicit upstream is required. Because the request that reaches the upstream carries the managed OAuth bearer token, the host must be a numeric loopback literal: a name such as `localhost` is rejected, since a hosts-file entry can point it at a routable address. "Fails closed" also covers the case where runtime rotation itself is off (`CODEX_MULTI_AUTH_RUNTIME_ROTATION_PROXY=0`, `CODEX_MULTI_AUTH_BYPASS=1`, or the setting disabled): a request-bearing invocation exits non-zero rather than bypassing the configured upstream. Subcommands that make no backend requests still run normally.
|
|
324
327
|
|
|
328
|
+
When the backend answers that the selected model is at capacity, the runtime proxy waits and re-sends the request instead of burning the pool. Capacity is a property of the model, not of an account, so rotating spends the transient budget against accounts that all fail identically and the request ends as a 503 within seconds. The wait backs off (2s, 5s, 15s, 30s, then 60s), honors an upstream `retry-after` when one is sent, and stops at a wall-clock deadline set by `CODEX_MULTI_AUTH_MODEL_CAPACITY_RETRY_MS` (10 minutes by default, 1 hour maximum). Set that variable to `0` to restore the previous rotate-and-fail behavior. The responding account is left completely unpenalized: its pool token is refunded and it is never marked rate limited or cooled down, so selection runs normally on the next pass and may pick any healthy account. The wait is abandoned if the client disconnects, so no further upstream request is sent for a response nobody is reading. See [#689](https://github.com/ndycode/codex-multi-auth/issues/689).
|
|
329
|
+
|
|
325
330
|
Trust boundary: the loopback check bounds exposure to the local machine, and nothing more. It does not authenticate the process listening on that port, and plaintext HTTP is what a local inspection proxy needs, so any process that can bind the port you name receives the managed bearer token in cleartext. Set this variable only on a machine where you trust every local process that can bind a loopback port, and only for as long as you need the chain.
|
|
326
331
|
|
|
327
332
|
Installed wrappers may perform a best-effort daily npm version check during normal forwarded Codex startup. When a newer package is detected, the wrapper only prints a manual notice on an interactive TTY or when `CODEX_MULTI_AUTH_DEBUG=1`: `npm install -g codex-multi-auth@latest`. It never runs npm install or update commands for you.
|
|
@@ -412,7 +417,7 @@ codex-multi-auth doctor --json
|
|
|
412
417
|
|
|
413
418
|
## Release Notes
|
|
414
419
|
|
|
415
|
-
- Current stable: [docs/releases/v2.
|
|
420
|
+
- Current stable: [docs/releases/v2.15.0.md](docs/releases/v2.15.0.md) — install via `npm i -g codex-multi-auth`
|
|
416
421
|
- Previous stable: [docs/releases/v2.8.3.md](docs/releases/v2.8.3.md)
|
|
417
422
|
- Previous stable: [docs/releases/v2.8.2.md](docs/releases/v2.8.2.md)
|
|
418
423
|
- Previous stable: [docs/releases/v2.7.1.md](docs/releases/v2.7.1.md)
|
|
@@ -35,15 +35,22 @@ export declare function resolveUnsupportedCodexFallbackModel(options: ResolveUns
|
|
|
35
35
|
*/
|
|
36
36
|
export declare function shouldFallbackToGpt52OnUnsupportedGpt53(requestedModel: string | undefined, errorBody: unknown): boolean;
|
|
37
37
|
/**
|
|
38
|
-
* Detects
|
|
38
|
+
* Detects an upstream response saying the selected model is temporarily out of
|
|
39
|
+
* capacity, as opposed to the account being rate limited or the server being
|
|
40
|
+
* broken.
|
|
39
41
|
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
+
* The distinction matters because the two want opposite handling. A rate limit
|
|
43
|
+
* is per account, so rotating to another account clears it. Capacity is a
|
|
44
|
+
* property of the MODEL, so every account in the pool fails identically and
|
|
45
|
+
* rotating just burns the pool's transient budget in a few seconds, ending the
|
|
46
|
+
* request as a 503. The caller waits and re-sends on the same account instead.
|
|
47
|
+
* See issue #689.
|
|
42
48
|
*
|
|
43
|
-
* @param
|
|
44
|
-
* @param bodyText -
|
|
45
|
-
* @returns `true`
|
|
49
|
+
* @param status - Upstream HTTP status.
|
|
50
|
+
* @param bodyText - Upstream error body, already read.
|
|
51
|
+
* @returns `true` when the request should be waited out rather than rotated.
|
|
46
52
|
*/
|
|
53
|
+
export declare function isModelAtCapacityError(status: number, bodyText: string): boolean;
|
|
47
54
|
export declare function isEntitlementError(code: string, bodyText: string): boolean;
|
|
48
55
|
/**
|
|
49
56
|
* Detects whether an error indicates the workspace/account has been disabled or expired.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-classification.d.ts","sourceRoot":"","sources":["../../../lib/request/error-classification.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,MAAM,WAAW,gBAAgB;IACzB,aAAa,EAAE,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACvB;AAED,sGAAsG;AACtG,eAAO,MAAM,oCAAoC,6CAA6C,CAAC;AAQ/F,eAAO,MAAM,wCAAwC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAoD7E,CAAC;AAEF,MAAM,WAAW,yBAAyB;IACzC,aAAa,EAAE,OAAO,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,sCAAsC;IACtD,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,SAAS,EAAE,OAAO,CAAC;IACnB,eAAe,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACnC,+BAA+B,EAAE,OAAO,CAAC;IACzC,iCAAiC,EAAE,OAAO,CAAC;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CACvC;AAiDD,wBAAgB,oCAAoC,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAgBzF;AAED,sGAAsG;AACtG,wBAAgB,iCAAiC,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAQ3F;AAED,wBAAgB,4BAA4B,CAC3C,SAAS,EAAE,OAAO,GAChB,yBAAyB,CAqD3B;AAED,wBAAgB,oCAAoC,CACnD,OAAO,EAAE,sCAAsC,GAC7C,MAAM,GAAG,SAAS,CAkCpB;AAED;;GAEG;AACH,wBAAgB,uCAAuC,CACtD,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,SAAS,EAAE,OAAO,GAChB,OAAO,CAgBT;
|
|
1
|
+
{"version":3,"file":"error-classification.d.ts","sourceRoot":"","sources":["../../../lib/request/error-classification.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,MAAM,WAAW,gBAAgB;IACzB,aAAa,EAAE,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACvB;AAED,sGAAsG;AACtG,eAAO,MAAM,oCAAoC,6CAA6C,CAAC;AAQ/F,eAAO,MAAM,wCAAwC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAoD7E,CAAC;AAEF,MAAM,WAAW,yBAAyB;IACzC,aAAa,EAAE,OAAO,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,sCAAsC;IACtD,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,SAAS,EAAE,OAAO,CAAC;IACnB,eAAe,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACnC,+BAA+B,EAAE,OAAO,CAAC;IACzC,iCAAiC,EAAE,OAAO,CAAC;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CACvC;AAiDD,wBAAgB,oCAAoC,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAgBzF;AAED,sGAAsG;AACtG,wBAAgB,iCAAiC,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAQ3F;AAED,wBAAgB,4BAA4B,CAC3C,SAAS,EAAE,OAAO,GAChB,yBAAyB,CAqD3B;AAED,wBAAgB,oCAAoC,CACnD,OAAO,EAAE,sCAAsC,GAC7C,MAAM,GAAG,SAAS,CAkCpB;AAED;;GAEG;AACH,wBAAgB,uCAAuC,CACtD,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,SAAS,EAAE,OAAO,GAChB,OAAO,CAgBT;AAqCD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,sBAAsB,CACrC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACd,OAAO,CAIT;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAK1E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CAChC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO,EACb,QAAQ,EAAE,MAAM,GACrB,OAAO,CAoDT;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,8BAA8B,CAAC,SAAS,EAAE,MAAM,GAAG,QAAQ,CAmB1E"}
|
|
@@ -242,6 +242,51 @@ export function shouldFallbackToGpt52OnUnsupportedGpt53(requestedModel, errorBod
|
|
|
242
242
|
* @param bodyText - The response body text to inspect for entitlement-related phrases
|
|
243
243
|
* @returns `true` if the combined `code` or `bodyText` indicates an entitlement/subscription issue, `false` otherwise
|
|
244
244
|
*/
|
|
245
|
+
/**
|
|
246
|
+
* Statuses that are never a capacity signal, whatever the body says.
|
|
247
|
+
*
|
|
248
|
+
* Auth, payment, entitlement and not-found responses are terminal for this
|
|
249
|
+
* request and are each handled by their own branch upstream of the capacity
|
|
250
|
+
* check. Retrying them would spin until the deadline for no reason.
|
|
251
|
+
*/
|
|
252
|
+
const NON_CAPACITY_STATUSES = new Set([401, 402, 403, 404]);
|
|
253
|
+
/**
|
|
254
|
+
* Phrases and codes that mean the model itself is out of capacity right now.
|
|
255
|
+
*
|
|
256
|
+
* This is matched on the body TEXT rather than on a status code, because the
|
|
257
|
+
* status this arrives with is not documented and has been reported differently
|
|
258
|
+
* by different callers. Matching text the way `isEntitlementError` and
|
|
259
|
+
* `isWorkspaceDisabledError` already do means a wrong guess about the status
|
|
260
|
+
* leaves behavior exactly as it is today, rather than breaking a path.
|
|
261
|
+
*
|
|
262
|
+
* `at capacity` is deliberately broad. In an OpenAI error body that phrase does
|
|
263
|
+
* not occur outside capacity pressure, and a false positive costs a bounded
|
|
264
|
+
* wait and one re-send, not a wrong answer.
|
|
265
|
+
*/
|
|
266
|
+
const MODEL_AT_CAPACITY_PATTERN = /\bat capacity\b|\bmodel_capacity_exceeded\b|\bcapacity_exceeded\b|\bcurrently overloaded\b|\boverloaded_error\b|\bslow_down\b/i;
|
|
267
|
+
/**
|
|
268
|
+
* Detects an upstream response saying the selected model is temporarily out of
|
|
269
|
+
* capacity, as opposed to the account being rate limited or the server being
|
|
270
|
+
* broken.
|
|
271
|
+
*
|
|
272
|
+
* The distinction matters because the two want opposite handling. A rate limit
|
|
273
|
+
* is per account, so rotating to another account clears it. Capacity is a
|
|
274
|
+
* property of the MODEL, so every account in the pool fails identically and
|
|
275
|
+
* rotating just burns the pool's transient budget in a few seconds, ending the
|
|
276
|
+
* request as a 503. The caller waits and re-sends on the same account instead.
|
|
277
|
+
* See issue #689.
|
|
278
|
+
*
|
|
279
|
+
* @param status - Upstream HTTP status.
|
|
280
|
+
* @param bodyText - Upstream error body, already read.
|
|
281
|
+
* @returns `true` when the request should be waited out rather than rotated.
|
|
282
|
+
*/
|
|
283
|
+
export function isModelAtCapacityError(status, bodyText) {
|
|
284
|
+
if (NON_CAPACITY_STATUSES.has(status))
|
|
285
|
+
return false;
|
|
286
|
+
if (!bodyText)
|
|
287
|
+
return false;
|
|
288
|
+
return MODEL_AT_CAPACITY_PATTERN.test(bodyText);
|
|
289
|
+
}
|
|
245
290
|
export function isEntitlementError(code, bodyText) {
|
|
246
291
|
const haystack = `${code} ${bodyText}`.toLowerCase();
|
|
247
292
|
// "usage_not_included" means the subscription doesn't include this feature
|
|
@@ -6,7 +6,7 @@ import { type FastSessionInputTrimPlan } from "./request-transformer.js";
|
|
|
6
6
|
import type { UserConfig, RequestBody } from "../types.js";
|
|
7
7
|
import type { UsageTokenCounts } from "../usage/types.js";
|
|
8
8
|
export { shouldRefreshToken, refreshAndUpdateToken } from "./token-refresh.js";
|
|
9
|
-
export { DEFAULT_UNSUPPORTED_CODEX_FALLBACK_CHAIN, extractUnsupportedCodexModelFromText, getUnsupportedCodexModelInfo, resolveUnsupportedCodexFallbackModel, shouldFallbackToGpt52OnUnsupportedGpt53, isEntitlementError, isWorkspaceDisabledError, createEntitlementErrorResponse, } from "./error-classification.js";
|
|
9
|
+
export { DEFAULT_UNSUPPORTED_CODEX_FALLBACK_CHAIN, extractUnsupportedCodexModelFromText, getUnsupportedCodexModelInfo, resolveUnsupportedCodexFallbackModel, shouldFallbackToGpt52OnUnsupportedGpt53, isEntitlementError, isModelAtCapacityError, isWorkspaceDisabledError, createEntitlementErrorResponse, } from "./error-classification.js";
|
|
10
10
|
export type { EntitlementError, UnsupportedCodexModelInfo, ResolveUnsupportedCodexFallbackOptions, } from "./error-classification.js";
|
|
11
11
|
export { extractRequestUrl, rewriteUrlForCodex, resolveProxyUrlForRequest, closeSharedProxyDispatchers, applyProxyCompatibleInit, } from "./url-rewriting.js";
|
|
12
12
|
export type { ProxyCompatibleRequestInit } from "./url-rewriting.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch-helpers.d.ts","sourceRoot":"","sources":["../../../lib/request/fetch-helpers.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"fetch-helpers.d.ts","sourceRoot":"","sources":["../../../lib/request/fetch-helpers.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,7 +11,7 @@ import { HTTP_STATUS, ERROR_MESSAGES, LOG_STAGES, MAX_RATE_LIMIT_DELAY_MS, } fro
|
|
|
11
11
|
import { CHATGPT_CODEX_UNSUPPORTED_MODEL_CODE, createEntitlementErrorResponse, extractUnsupportedCodexModelFromText, isEntitlementError, isUnsupportedCodexModelForChatGpt, } from "./error-classification.js";
|
|
12
12
|
import { logDeprecationHeaders } from "./headers.js";
|
|
13
13
|
export { shouldRefreshToken, refreshAndUpdateToken } from "./token-refresh.js";
|
|
14
|
-
export { DEFAULT_UNSUPPORTED_CODEX_FALLBACK_CHAIN, extractUnsupportedCodexModelFromText, getUnsupportedCodexModelInfo, resolveUnsupportedCodexFallbackModel, shouldFallbackToGpt52OnUnsupportedGpt53, isEntitlementError, isWorkspaceDisabledError, createEntitlementErrorResponse, } from "./error-classification.js";
|
|
14
|
+
export { DEFAULT_UNSUPPORTED_CODEX_FALLBACK_CHAIN, extractUnsupportedCodexModelFromText, getUnsupportedCodexModelInfo, resolveUnsupportedCodexFallbackModel, shouldFallbackToGpt52OnUnsupportedGpt53, isEntitlementError, isModelAtCapacityError, isWorkspaceDisabledError, createEntitlementErrorResponse, } from "./error-classification.js";
|
|
15
15
|
export { extractRequestUrl, rewriteUrlForCodex, resolveProxyUrlForRequest, closeSharedProxyDispatchers, applyProxyCompatibleInit, } from "./url-rewriting.js";
|
|
16
16
|
export { createCodexHeaders } from "./headers.js";
|
|
17
17
|
// MAX_RATE_LIMIT_DELAY_MS is centralized in constants.ts (shared with the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-toml.d.ts","sourceRoot":"","sources":["../../../lib/runtime/config-toml.ts"],"names":[],"mappings":"AAEA,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAqBvD;AA4FD,wBAAgB,4BAA4B,CAC3C,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,GACpB,MAAM,CAiDR;AAED,wBAAgB,8BAA8B,CAC7C,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,GACpB,MAAM,CAkDR;
|
|
1
|
+
{"version":3,"file":"config-toml.d.ts","sourceRoot":"","sources":["../../../lib/runtime/config-toml.ts"],"names":[],"mappings":"AAEA,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAqBvD;AA4FD,wBAAgB,4BAA4B,CAC3C,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,GACpB,MAAM,CAiDR;AAED,wBAAgB,8BAA8B,CAC7C,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,GACpB,MAAM,CAkDR;AA4BD,wBAAgB,2CAA2C,CAC1D,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,YAAY,SAAK,GACf,MAAM,CAkBR;AAED,wBAAgB,4CAA4C,CAC3D,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,GACpB,MAAM,CAYR;AAED;;;;;;;GAOG;AACH,wBAAgB,gCAAgC,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAoB3E;AAED;;;;;;;GAOG;AACH,wBAAgB,yDAAyD,CACxE,aAAa,EAAE,MAAM,EACrB,eAAe,SAAW,GACxB,MAAM,CAkBR"}
|
|
@@ -202,6 +202,7 @@ function createRuntimeRotationProviderBlock(baseUrl, clientApiKey = "") {
|
|
|
202
202
|
'name = "codex-multi-auth"',
|
|
203
203
|
`base_url = ${tomlStringLiteral(baseUrl)}`,
|
|
204
204
|
"requires_openai_auth = false",
|
|
205
|
+
'http_headers = { "x-openai-actor-authorization" = "codex-multi-auth-local" }',
|
|
205
206
|
'wire_api = "responses"',
|
|
206
207
|
];
|
|
207
208
|
if (clientApiKey.trim().length > 0) {
|
|
@@ -21,6 +21,12 @@ export interface RotationProxyStateInit {
|
|
|
21
21
|
tokenRefreshSkewMs: number;
|
|
22
22
|
networkErrorCooldownMs: number;
|
|
23
23
|
serverErrorCooldownMs: number;
|
|
24
|
+
/**
|
|
25
|
+
* Wall-clock ceiling on how long ONE request may wait out a model-capacity
|
|
26
|
+
* response before giving up. `0` restores the pre-#689 behaviour of rotating
|
|
27
|
+
* the pool and returning the exhaustion 503.
|
|
28
|
+
*/
|
|
29
|
+
modelCapacityRetryMs: number;
|
|
24
30
|
tokenInvalidationCooldownMs: number;
|
|
25
31
|
minRotationIntervalMs: number;
|
|
26
32
|
pidOffsetEnabled: boolean;
|
|
@@ -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;AAChD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAKjF,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,wBAAwB,EAAE,wBAAwB,CAAC;IACnD,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,oBAAoB,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAClD,8BAA8B,EAAE,MAAM,CAAC;IACvC;;;;;OAKG;IACH,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;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"}
|
|
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;AAChD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAKjF,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;;;;OAIG;IACH,oBAAoB,EAAE,MAAM,CAAC;IAC7B,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,wBAAwB,EAAE,wBAAwB,CAAC;IACnD,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,oBAAoB,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAClD,8BAA8B,EAAE,MAAM,CAAC;IACvC;;;;;OAKG;IACH,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;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"}
|
|
@@ -48,6 +48,13 @@ export interface RuntimeRotationProxyOptions {
|
|
|
48
48
|
* the value survives the launcher -> detached app-helper process boundary.
|
|
49
49
|
*/
|
|
50
50
|
forcedAccountIndex?: number | null;
|
|
51
|
+
/**
|
|
52
|
+
* Wall-clock ceiling on how long ONE request may wait out a
|
|
53
|
+
* "selected model is at capacity" response before giving up (issue #689).
|
|
54
|
+
* `0` disables the wait entirely. Falls back to
|
|
55
|
+
* `CODEX_MULTI_AUTH_MODEL_CAPACITY_RETRY_MS`, then to a 10 minute default.
|
|
56
|
+
*/
|
|
57
|
+
modelCapacityRetryMs?: number;
|
|
51
58
|
}
|
|
52
59
|
export interface RequestContext {
|
|
53
60
|
body: Buffer;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rotation-server-types.d.ts","sourceRoot":"","sources":["../../../lib/runtime/rotation-server-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,MAAM,WAAW,0BAA0B;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,SAAS,EAAE,MAAM,0BAA0B,CAAC;IAC5C;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,MAAM,MAAM,CAAC;CACtC;AAED,MAAM,WAAW,0BAA0B;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AAED,MAAM,WAAW,2BAA2B;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,8BAA8B,CAAC,EAAE,MAAM,CAAC;IACxC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;OAOG;IACH,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"rotation-server-types.d.ts","sourceRoot":"","sources":["../../../lib/runtime/rotation-server-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,MAAM,WAAW,0BAA0B;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,SAAS,EAAE,MAAM,0BAA0B,CAAC;IAC5C;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,MAAM,MAAM,CAAC;CACtC;AAED,MAAM,WAAW,0BAA0B;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AAED,MAAM,WAAW,2BAA2B;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,8BAA8B,CAAC,EAAE,MAAM,CAAC;IACxC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;OAOG;IACH,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,KAAK,GAAG,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B;;;;;;;;;OASG;IACH,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,MAAM,MAAM,gBAAgB,GACzB,YAAY,GACZ,cAAc,GACd,eAAe,GACf,cAAc,GACd,QAAQ,GACR,aAAa,GACb,YAAY,CAAC;AAChB,MAAM,MAAM,qBAAqB,GAAG,KAAK,GAAG;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,MAAM,WAAW,8BAA8B;IAC9C,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;CAClB"}
|
|
@@ -26,6 +26,16 @@ export declare function buildQuotaScheduleKey(account: Pick<ManagedAccount, "acc
|
|
|
26
26
|
addedAt?: number;
|
|
27
27
|
recordId?: string;
|
|
28
28
|
}, family: ModelFamily, model?: string | null): string;
|
|
29
|
+
/**
|
|
30
|
+
* Total time one request may spend waiting out model capacity.
|
|
31
|
+
*
|
|
32
|
+
* `0` disables the behaviour entirely and restores the pre-#689 handling, where
|
|
33
|
+
* a capacity response rotates the pool and 503s. Anything unparseable falls
|
|
34
|
+
* back to the default rather than disabling, so a typo does not silently turn
|
|
35
|
+
* the feature off.
|
|
36
|
+
*/
|
|
37
|
+
export declare function normalizeModelCapacityRetryMs(value: unknown): number;
|
|
38
|
+
export declare function resolveModelCapacityRetryMs(env?: NodeJS.ProcessEnv): number;
|
|
29
39
|
/**
|
|
30
40
|
* Coerce a forced-account pin from an option (number) or the launcher's env
|
|
31
41
|
* string into a normalized 0-based index, or null when absent/invalid. Invalid
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-rotation-proxy.d.ts","sourceRoot":"","sources":["../../lib/runtime-rotation-proxy.ts"],"names":[],"mappings":"AAGA,OAAO,EAIN,KAAK,cAAc,EACnB,MAAM,eAAe,CAAC;AAkCvB,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"runtime-rotation-proxy.d.ts","sourceRoot":"","sources":["../../lib/runtime-rotation-proxy.ts"],"names":[],"mappings":"AAGA,OAAO,EAIN,KAAK,cAAc,EACnB,MAAM,eAAe,CAAC;AAkCvB,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAqDtE,OAAO,KAAK,EAKX,2BAA2B,EAC3B,0BAA0B,EAE1B,MAAM,oCAAoC,CAAC;AAe5C,YAAY,EACX,2BAA2B,EAC3B,0BAA0B,EAC1B,0BAA0B,GAC1B,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EACN,+BAA+B,EAC/B,0BAA0B,GAC1B,MAAM,kCAAkC,CAAC;AAC1C,YAAY,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAC;AACnF,OAAO,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AACxE,OAAO,EACN,+BAA+B,EAC/B,8BAA8B,EAC9B,uBAAuB,EACvB,uBAAuB,GACvB,MAAM,oCAAoC,CAAC;AAC5C,YAAY,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AA8DtE,kFAAkF;AAClF;;;;;;;GAOG;AACH,wBAAgB,+BAA+B,CAC9C,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,WAAW,GAAG,OAAO,GAAG,cAAc,CAAC,GAAG;IACvE,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB,GACC,MAAM,CAmBR;AAED,wBAAgB,qBAAqB,CACpC,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,WAAW,GAAG,OAAO,GAAG,cAAc,CAAC,GAAG;IACvE,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB,EACD,MAAM,EAAE,WAAW,EACnB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GACnB,MAAM,CAER;AA0ED;;;;;;;GAOG;AACH,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAKpE;AAED,wBAAgB,2BAA2B,CAC1C,GAAG,GAAE,MAAM,CAAC,UAAwB,GAClC,MAAM,CAQR;AA2fD;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CAC1C,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GACvC,MAAM,GAAG,IAAI,CAUf;AAED,wBAAsB,yBAAyB,CAC9C,OAAO,EAAE,2BAA2B,GAClC,OAAO,CAAC,0BAA0B,CAAC,CA6LrC"}
|
|
@@ -9,7 +9,7 @@ import { CURRENT_CODEX_MODEL } from "./request/helpers/model-map.js";
|
|
|
9
9
|
import { mutateRuntimeObservabilitySnapshot, recordRuntimeAccountRecovery, recordRuntimePoolExhaustion, } from "./runtime/runtime-observability.js";
|
|
10
10
|
import { createRuntimeUsageRecorder, evaluateRuntimePolicy, loadRuntimePolicyState, } from "./policy/runtime-policy.js";
|
|
11
11
|
import { createUsageStreamScanner } from "./usage/usage-extraction.js";
|
|
12
|
-
import { isWorkspaceDisabledError } from "./request/fetch-helpers.js";
|
|
12
|
+
import { isModelAtCapacityError, isWorkspaceDisabledError, } from "./request/fetch-helpers.js";
|
|
13
13
|
import { PreemptiveQuotaScheduler, readQuotaSchedulerSnapshot, } from "./preemptive-quota-scheduler.js";
|
|
14
14
|
import { ContextBudgetGuard } from "./context-budget-guard.js";
|
|
15
15
|
import { buildContextBudgetHeaders, createContextBudgetPauseResponse, } from "./context-budget-response.js";
|
|
@@ -144,6 +144,58 @@ function pinnedRetryBackoffMs(attempt) {
|
|
|
144
144
|
const index = Math.min(Math.max(0, attempt - 1), PINNED_RETRY_BACKOFF_STEPS_MS.length - 1);
|
|
145
145
|
return PINNED_RETRY_BACKOFF_STEPS_MS[index] ?? 0;
|
|
146
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Backoff schedule for a model-capacity wait (issue #689).
|
|
149
|
+
*
|
|
150
|
+
* Deliberately much longer than `PINNED_RETRY_BACKOFF_STEPS_MS`. That schedule
|
|
151
|
+
* spaces out re-sends of a request whose account might already be fine;
|
|
152
|
+
* capacity pressure is upstream and measured in minutes, so re-sending every
|
|
153
|
+
* 250ms would just be a tight poll against a busy backend. The last step
|
|
154
|
+
* repeats for every attempt past the table.
|
|
155
|
+
*/
|
|
156
|
+
const CAPACITY_RETRY_BACKOFF_STEPS_MS = [
|
|
157
|
+
2_000, 5_000, 15_000, 30_000, 60_000,
|
|
158
|
+
];
|
|
159
|
+
/**
|
|
160
|
+
* Default wall-clock ceiling on capacity waiting for ONE request.
|
|
161
|
+
*
|
|
162
|
+
* The reporter's case is a long-running task started before stepping away, so
|
|
163
|
+
* the default has to be long enough to outlast a real capacity blip. It is
|
|
164
|
+
* still a hard ceiling: the request ends with the normal pool-exhausted 503
|
|
165
|
+
* rather than hanging forever.
|
|
166
|
+
*/
|
|
167
|
+
const DEFAULT_MODEL_CAPACITY_RETRY_MS = 10 * 60_000;
|
|
168
|
+
const MAX_MODEL_CAPACITY_RETRY_MS = 60 * 60_000;
|
|
169
|
+
const MODEL_CAPACITY_RETRY_ENV = "CODEX_MULTI_AUTH_MODEL_CAPACITY_RETRY_MS";
|
|
170
|
+
function capacityRetryBackoffMs(attempt) {
|
|
171
|
+
const index = Math.min(Math.max(0, attempt - 1), CAPACITY_RETRY_BACKOFF_STEPS_MS.length - 1);
|
|
172
|
+
return CAPACITY_RETRY_BACKOFF_STEPS_MS[index] ?? 0;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Total time one request may spend waiting out model capacity.
|
|
176
|
+
*
|
|
177
|
+
* `0` disables the behaviour entirely and restores the pre-#689 handling, where
|
|
178
|
+
* a capacity response rotates the pool and 503s. Anything unparseable falls
|
|
179
|
+
* back to the default rather than disabling, so a typo does not silently turn
|
|
180
|
+
* the feature off.
|
|
181
|
+
*/
|
|
182
|
+
export function normalizeModelCapacityRetryMs(value) {
|
|
183
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
184
|
+
return DEFAULT_MODEL_CAPACITY_RETRY_MS;
|
|
185
|
+
}
|
|
186
|
+
return Math.min(Math.floor(value), MAX_MODEL_CAPACITY_RETRY_MS);
|
|
187
|
+
}
|
|
188
|
+
export function resolveModelCapacityRetryMs(env = process.env) {
|
|
189
|
+
const raw = (env[MODEL_CAPACITY_RETRY_ENV] ?? "").trim();
|
|
190
|
+
if (!raw)
|
|
191
|
+
return DEFAULT_MODEL_CAPACITY_RETRY_MS;
|
|
192
|
+
// An explicit 0 is the documented kill switch and must survive normalization,
|
|
193
|
+
// which otherwise treats "not a usable number" as "use the default".
|
|
194
|
+
const parsed = Number(raw);
|
|
195
|
+
if (parsed === 0)
|
|
196
|
+
return 0;
|
|
197
|
+
return normalizeModelCapacityRetryMs(parsed);
|
|
198
|
+
}
|
|
147
199
|
const MAX_REQUEST_BODY_BYTES = 64 * 1024 * 1024;
|
|
148
200
|
const MAX_THREAD_GOAL_FALLBACKS = 512;
|
|
149
201
|
const ALLOWED_RESPONSES_PATHS = new Set([
|
|
@@ -156,6 +208,12 @@ const ALLOWED_MODELS_PATHS = new Set([
|
|
|
156
208
|
URL_PATHS.MODELS,
|
|
157
209
|
`/v1${URL_PATHS.MODELS}`,
|
|
158
210
|
]);
|
|
211
|
+
const ALLOWED_IMAGE_PATHS = new Set([
|
|
212
|
+
"/images/generations",
|
|
213
|
+
"/images/edits",
|
|
214
|
+
"/v1/images/generations",
|
|
215
|
+
"/v1/images/edits",
|
|
216
|
+
]);
|
|
159
217
|
const ALLOWED_THREAD_GOAL_PATHS = new Set([
|
|
160
218
|
"/thread/goal/get",
|
|
161
219
|
"/thread/goal/set",
|
|
@@ -200,6 +258,8 @@ function createOutboundHeaders(incoming, account, accessToken, accountId) {
|
|
|
200
258
|
// header would ride along with the managed OAuth Bearer to OpenAI.
|
|
201
259
|
headers.delete("cookie");
|
|
202
260
|
headers.delete("proxy-authorization");
|
|
261
|
+
// Local capability marker for Codex image_gen, never an upstream credential.
|
|
262
|
+
headers.delete("x-openai-actor-authorization");
|
|
203
263
|
headers.set("authorization", `Bearer ${accessToken}`);
|
|
204
264
|
headers.set(OPENAI_HEADERS.ACCOUNT_ID, accountId);
|
|
205
265
|
headers.set(OPENAI_HEADERS.BETA, OPENAI_HEADER_VALUES.BETA_RESPONSES);
|
|
@@ -446,6 +506,13 @@ function buildResponsesRequestContext(req, body) {
|
|
|
446
506
|
stableSessionKey: resolveStableSessionKey(headers, parsedBody),
|
|
447
507
|
};
|
|
448
508
|
}
|
|
509
|
+
function buildImageRequestContext(req, body, pathname) {
|
|
510
|
+
return {
|
|
511
|
+
...buildResponsesRequestContext(req, body),
|
|
512
|
+
upstreamPath: `/codex${pathname.replace(/^\/v1/, "")}`,
|
|
513
|
+
family: "codex",
|
|
514
|
+
};
|
|
515
|
+
}
|
|
449
516
|
function buildModelsRequestContext(req) {
|
|
450
517
|
return {
|
|
451
518
|
body: Buffer.alloc(0),
|
|
@@ -503,7 +570,7 @@ function writeJson(res, status, payload) {
|
|
|
503
570
|
function writeMethodOrPathError(res) {
|
|
504
571
|
writeJson(res, 404, {
|
|
505
572
|
error: {
|
|
506
|
-
message: "Runtime rotation proxy only accepts Responses API, model discovery, and Codex thread goal requests.",
|
|
573
|
+
message: "Runtime rotation proxy only accepts Responses API, images, model discovery, and Codex thread goal requests.",
|
|
507
574
|
code: "runtime_rotation_proxy_not_found",
|
|
508
575
|
},
|
|
509
576
|
});
|
|
@@ -608,6 +675,14 @@ export async function startRuntimeRotationProxy(options) {
|
|
|
608
675
|
const tokenRefreshSkewMs = getTokenRefreshSkewMs(pluginConfig);
|
|
609
676
|
const networkErrorCooldownMs = getNetworkErrorCooldownMs(pluginConfig);
|
|
610
677
|
const serverErrorCooldownMs = getServerErrorCooldownMs(pluginConfig);
|
|
678
|
+
// Normalize the explicit option as well as the env var: a caller passing a
|
|
679
|
+
// negative or multi-hour value would otherwise bypass the documented cap.
|
|
680
|
+
// `0` stays 0, since it is the documented kill switch.
|
|
681
|
+
const modelCapacityRetryMs = options.modelCapacityRetryMs === 0
|
|
682
|
+
? 0
|
|
683
|
+
: options.modelCapacityRetryMs === undefined
|
|
684
|
+
? resolveModelCapacityRetryMs()
|
|
685
|
+
: normalizeModelCapacityRetryMs(options.modelCapacityRetryMs);
|
|
611
686
|
const tokenInvalidationCooldownMs = getTokenInvalidationCooldownMs(pluginConfig);
|
|
612
687
|
const minRotationIntervalMs = getMinRotationIntervalMs(pluginConfig);
|
|
613
688
|
const pidOffsetEnabled = getPidOffsetEnabled(pluginConfig);
|
|
@@ -654,6 +729,7 @@ export async function startRuntimeRotationProxy(options) {
|
|
|
654
729
|
tokenRefreshSkewMs,
|
|
655
730
|
networkErrorCooldownMs,
|
|
656
731
|
serverErrorCooldownMs,
|
|
732
|
+
modelCapacityRetryMs,
|
|
657
733
|
tokenInvalidationCooldownMs,
|
|
658
734
|
minRotationIntervalMs,
|
|
659
735
|
pidOffsetEnabled,
|
|
@@ -742,21 +818,26 @@ async function handleRequestInner(state, req, res, traceId) {
|
|
|
742
818
|
}
|
|
743
819
|
const isResponsesRequest = req.method === "POST" && isResponsesPath(incomingUrl.pathname);
|
|
744
820
|
const isModelsRequest = req.method === "GET" && isModelsPath(incomingUrl.pathname);
|
|
821
|
+
const isImageRequest = req.method === "POST" && ALLOWED_IMAGE_PATHS.has(incomingUrl.pathname);
|
|
745
822
|
const isThreadGoalRequest = (req.method === "GET" || req.method === "POST") &&
|
|
746
823
|
isThreadGoalPath(incomingUrl.pathname);
|
|
747
|
-
if (!isResponsesRequest && !isModelsRequest &&
|
|
824
|
+
if (!isResponsesRequest && !isModelsRequest &&
|
|
825
|
+
!isThreadGoalRequest && !isImageRequest) {
|
|
748
826
|
writeMethodOrPathError(res);
|
|
749
827
|
return;
|
|
750
828
|
}
|
|
751
829
|
state.status.totalRequests += 1;
|
|
752
|
-
const requestBody = isResponsesRequest ||
|
|
830
|
+
const requestBody = isResponsesRequest || isImageRequest ||
|
|
831
|
+
(isThreadGoalRequest && req.method === "POST")
|
|
753
832
|
? await readRequestBody(req, state.maxRequestBodyBytes)
|
|
754
833
|
: Buffer.alloc(0);
|
|
755
834
|
const context = isModelsRequest
|
|
756
835
|
? buildModelsRequestContext(req)
|
|
757
836
|
: isThreadGoalRequest
|
|
758
837
|
? buildThreadGoalRequestContext(req, requestBody, incomingUrl.pathname)
|
|
759
|
-
:
|
|
838
|
+
: isImageRequest
|
|
839
|
+
? buildImageRequestContext(req, requestBody, incomingUrl.pathname)
|
|
840
|
+
: buildResponsesRequestContext(req, requestBody);
|
|
760
841
|
const requestStartedAt = state.now();
|
|
761
842
|
let policyDecision = null;
|
|
762
843
|
let projectKey = null;
|
|
@@ -787,7 +868,9 @@ async function handleRequestInner(state, req, res, traceId) {
|
|
|
787
868
|
? "models"
|
|
788
869
|
: isThreadGoalRequest
|
|
789
870
|
? "thread-goal"
|
|
790
|
-
:
|
|
871
|
+
: isImageRequest
|
|
872
|
+
? "images"
|
|
873
|
+
: "responses",
|
|
791
874
|
model: context.model,
|
|
792
875
|
projectKey,
|
|
793
876
|
requestId: traceId,
|
|
@@ -863,6 +946,24 @@ async function handleRequestInner(state, req, res, traceId) {
|
|
|
863
946
|
let transientAttemptLimit = Math.max(1, Math.min(accountCount, state.maxRuntimeAccountAttempts));
|
|
864
947
|
let transientAttempts = 0;
|
|
865
948
|
let transientExhaustionReason = null;
|
|
949
|
+
// Capacity waits are counted separately from `transientAttempts` and from
|
|
950
|
+
// the pinned selection cap. Both of those bound how many DIFFERENT
|
|
951
|
+
// accounts or re-sends a failing request may burn; a capacity wait is not
|
|
952
|
+
// a failure of any account, it is the same request pausing for an
|
|
953
|
+
// upstream that is busy, and it has its own wall-clock ceiling. See #689.
|
|
954
|
+
let capacityRetries = 0;
|
|
955
|
+
// A wall-clock deadline, not a sum of sleeps. The upstream request and
|
|
956
|
+
// the error-body read also consume the budget, so summing only the
|
|
957
|
+
// planned sleeps let a slow capacity response push one request past the
|
|
958
|
+
// documented ceiling. Request-local, so concurrent requests never share
|
|
959
|
+
// a deadline.
|
|
960
|
+
let capacityDeadlineAt = null;
|
|
961
|
+
// Only the RESPONSE tells us the caller is still there. `req` is an
|
|
962
|
+
// IncomingMessage whose stream Node destroys once the request body has
|
|
963
|
+
// been fully read, which this proxy does up front, so `req.destroyed` is
|
|
964
|
+
// routinely true on a perfectly healthy request and must not be read as a
|
|
965
|
+
// disconnect.
|
|
966
|
+
const clientGone = () => res.destroyed || res.writableEnded;
|
|
866
967
|
const accountSkipReasons = new Map();
|
|
867
968
|
let reloadedAfterNoAccount = false;
|
|
868
969
|
// Read the manual pin and affinity generation from disk (mtime-cached)
|
|
@@ -940,11 +1041,79 @@ async function handleRequestInner(state, req, res, traceId) {
|
|
|
940
1041
|
if (isPinned) {
|
|
941
1042
|
transientAttemptLimit = Math.max(1, Math.min(state.maxRuntimeAccountAttempts, MAX_PINNED_TRANSIENT_ATTEMPTS));
|
|
942
1043
|
}
|
|
1044
|
+
/**
|
|
1045
|
+
* Wait out a "selected model is at capacity" response, then let the loop
|
|
1046
|
+
* re-send the request (issue #689).
|
|
1047
|
+
*
|
|
1048
|
+
* Capacity is a property of the MODEL, not of an account, so the normal
|
|
1049
|
+
* handling is actively wrong for it: rotating spends the pool's transient
|
|
1050
|
+
* budget against accounts that will all fail identically, and the request
|
|
1051
|
+
* ends as a pool-exhausted 503 within seconds. That is what kills a
|
|
1052
|
+
* long-running task started before the user stepped away.
|
|
1053
|
+
*
|
|
1054
|
+
* The responding account is left completely unpenalized: its pool token is
|
|
1055
|
+
* refunded, it is not marked rate limited, not cooled down, and it is
|
|
1056
|
+
* removed from `attemptedIndexes` so it stays selectable. Selection then
|
|
1057
|
+
* runs normally on the next pass, which may pick a different account. That
|
|
1058
|
+
* is deliberate: with capacity being model-wide, no account is a better bet,
|
|
1059
|
+
* and forcing a request-local pin here would duplicate the pinning path for
|
|
1060
|
+
* no gain.
|
|
1061
|
+
*
|
|
1062
|
+
* @param retryAfterMs Upstream hint when one was actually sent, else `null`
|
|
1063
|
+
* so the backoff table is used. Do NOT pass a synthesized default.
|
|
1064
|
+
* @param account Account that just saw the capacity response.
|
|
1065
|
+
* @returns `"retry"` to re-send, `"give-up"` to fall through to normal
|
|
1066
|
+
* handling, `"client-gone"` when the caller must abandon the request.
|
|
1067
|
+
*/
|
|
1068
|
+
const waitOutModelCapacity = async (retryAfterMs, account) => {
|
|
1069
|
+
const budgetMs = state.modelCapacityRetryMs;
|
|
1070
|
+
if (!Number.isFinite(budgetMs) || budgetMs <= 0)
|
|
1071
|
+
return "give-up";
|
|
1072
|
+
if (capacityDeadlineAt === null) {
|
|
1073
|
+
capacityDeadlineAt = state.now() + budgetMs;
|
|
1074
|
+
}
|
|
1075
|
+
const remainingMs = capacityDeadlineAt - state.now();
|
|
1076
|
+
if (remainingMs <= 0)
|
|
1077
|
+
return "give-up";
|
|
1078
|
+
if (clientGone())
|
|
1079
|
+
return "client-gone";
|
|
1080
|
+
const hinted = retryAfterMs !== null &&
|
|
1081
|
+
Number.isFinite(retryAfterMs) &&
|
|
1082
|
+
retryAfterMs > 0
|
|
1083
|
+
? retryAfterMs
|
|
1084
|
+
: capacityRetryBackoffMs(capacityRetries + 1);
|
|
1085
|
+
const waitMs = Math.min(Math.max(0, Math.floor(hinted)), remainingMs);
|
|
1086
|
+
if (waitMs <= 0)
|
|
1087
|
+
return "give-up";
|
|
1088
|
+
capacityRetries += 1;
|
|
1089
|
+
state.status.retries += 1;
|
|
1090
|
+
proxyLog.warn("model at capacity; waiting before re-sending", {
|
|
1091
|
+
traceId,
|
|
1092
|
+
waitMs,
|
|
1093
|
+
attempt: capacityRetries,
|
|
1094
|
+
remainingMs,
|
|
1095
|
+
budgetMs,
|
|
1096
|
+
});
|
|
1097
|
+
// The attempt debited a pool token before the upstream call. The account
|
|
1098
|
+
// did not fail, so refund it: without this a sustained capacity event
|
|
1099
|
+
// drains healthy accounts and later requests are refused admission with
|
|
1100
|
+
// `token-exhausted` well before the retry budget expires.
|
|
1101
|
+
refundConsumedPoolToken(account);
|
|
1102
|
+
attemptedIndexes.delete(account.index);
|
|
1103
|
+
await sleep(waitMs);
|
|
1104
|
+
// A capacity wait runs for tens of seconds. Re-sending an authenticated
|
|
1105
|
+
// upstream request for a response nobody is reading wastes upstream
|
|
1106
|
+
// capacity and extends how long the token is in flight.
|
|
1107
|
+
if (clientGone())
|
|
1108
|
+
return "client-gone";
|
|
1109
|
+
return "retry";
|
|
1110
|
+
};
|
|
943
1111
|
let runtimeSelectionIterations = 0;
|
|
944
1112
|
while ((isPinned || attemptedIndexes.size < accountCount) &&
|
|
945
1113
|
transientAttempts < transientAttemptLimit &&
|
|
946
1114
|
(!isPinned ||
|
|
947
|
-
runtimeSelectionIterations
|
|
1115
|
+
runtimeSelectionIterations - capacityRetries <
|
|
1116
|
+
MAX_PINNED_SELECTION_ITERATIONS)) {
|
|
948
1117
|
// Space out same-account re-sends. The loop has no other delay in it,
|
|
949
1118
|
// and the pinned retry deliberately waives the account's own cooldown
|
|
950
1119
|
// (below), so without this two copies of the same non-idempotent
|
|
@@ -1140,9 +1309,27 @@ async function handleRequestInner(state, req, res, traceId) {
|
|
|
1140
1309
|
recordLastRuntimeAccount(state.status, accountIdentity);
|
|
1141
1310
|
const outboundHeaders = createOutboundHeaders(context.headers, refreshed.account, refreshed.accessToken, accountId);
|
|
1142
1311
|
let upstream;
|
|
1312
|
+
// Abort the in-flight upstream fetch when the client disconnects
|
|
1313
|
+
// before headers arrive. Image generation holds upstream capacity
|
|
1314
|
+
// for the full fetch timeout, so a caller that goes away right
|
|
1315
|
+
// after sending must not leave that work running. `forwardStreamingResponse`
|
|
1316
|
+
// already cancels the stream once headers are written; this covers
|
|
1317
|
+
// the pre-header window instead. `writableEnded` distinguishes a
|
|
1318
|
+
// premature close from the clean `res.end()` that ends every request.
|
|
1319
|
+
//
|
|
1320
|
+
// The listener is removed in `finally` once the fetch settles. Without
|
|
1321
|
+
// that, every retry above the 10-listener default threshold that reaches
|
|
1322
|
+
// this fetch leaks another one-shot `close` handler onto the same `res`,
|
|
1323
|
+
// emitting `MaxListenersExceededWarning` when `retryAllAccountsMaxRetries`
|
|
1324
|
+
// is high and the account pool is large.
|
|
1325
|
+
const fetchAbortController = new AbortController();
|
|
1326
|
+
const onClientClose = () => {
|
|
1327
|
+
if (!res.writableEnded)
|
|
1328
|
+
fetchAbortController.abort();
|
|
1329
|
+
};
|
|
1143
1330
|
try {
|
|
1144
1331
|
state.status.upstreamRequests += 1;
|
|
1145
|
-
|
|
1332
|
+
res.once("close", onClientClose);
|
|
1146
1333
|
const upstreamRequestInit = {
|
|
1147
1334
|
method: context.method,
|
|
1148
1335
|
headers: outboundHeaders,
|
|
@@ -1151,7 +1338,10 @@ async function handleRequestInner(state, req, res, traceId) {
|
|
|
1151
1338
|
if (context.method === "POST") {
|
|
1152
1339
|
upstreamRequestInit.body = context.body;
|
|
1153
1340
|
}
|
|
1154
|
-
|
|
1341
|
+
const fetchTimeoutMs = isImageRequest
|
|
1342
|
+
? Math.max(state.fetchTimeoutMs, 300_000)
|
|
1343
|
+
: state.fetchTimeoutMs;
|
|
1344
|
+
upstream = await withTimeout(state.fetchImpl(upstreamUrl, upstreamRequestInit), fetchTimeoutMs, () => fetchAbortController.abort(), `upstream fetch timed out after ${fetchTimeoutMs}ms`);
|
|
1155
1345
|
}
|
|
1156
1346
|
catch (error) {
|
|
1157
1347
|
// errors-logging-08: a custom fetchImpl, a proxy agent, or an undici
|
|
@@ -1168,6 +1358,20 @@ async function handleRequestInner(state, req, res, traceId) {
|
|
|
1168
1358
|
error: transportError,
|
|
1169
1359
|
});
|
|
1170
1360
|
refundConsumedPoolToken(refreshed.account);
|
|
1361
|
+
// A timeout may occur after generation; do not retry within this request.
|
|
1362
|
+
if (isImageRequest) {
|
|
1363
|
+
writeJson(res, 502, {
|
|
1364
|
+
error: {
|
|
1365
|
+
code: "image_upstream_transport_error",
|
|
1366
|
+
message: "Image upstream transport failed; not retried by the proxy.",
|
|
1367
|
+
},
|
|
1368
|
+
});
|
|
1369
|
+
await usageRecorder.record({
|
|
1370
|
+
outcome: "failure", statusCode: 502,
|
|
1371
|
+
errorCode: "image_upstream_transport_error", account: refreshed.account,
|
|
1372
|
+
});
|
|
1373
|
+
return;
|
|
1374
|
+
}
|
|
1171
1375
|
// A pre-header transport exception is a property of the network path,
|
|
1172
1376
|
// not of this account's credentials or quota, so it must NOT feed the
|
|
1173
1377
|
// account's circuit breaker / health tracker: that is what would
|
|
@@ -1190,6 +1394,9 @@ async function handleRequestInner(state, req, res, traceId) {
|
|
|
1190
1394
|
noteRotation();
|
|
1191
1395
|
continue;
|
|
1192
1396
|
}
|
|
1397
|
+
finally {
|
|
1398
|
+
res.off("close", onClientClose);
|
|
1399
|
+
}
|
|
1193
1400
|
reconcileManualSelection();
|
|
1194
1401
|
const quotaSnapshot = readQuotaSchedulerSnapshot(upstream.headers, upstream.status, state.now());
|
|
1195
1402
|
if (quotaSnapshot) {
|
|
@@ -1197,11 +1404,32 @@ async function handleRequestInner(state, req, res, traceId) {
|
|
|
1197
1404
|
}
|
|
1198
1405
|
if (upstream.status === HTTP_STATUS.TOO_MANY_REQUESTS) {
|
|
1199
1406
|
const bodyText = await readErrorBody(upstream, state.streamStallTimeoutMs);
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1407
|
+
// Keep the upstream HINT separate from the 60s fallback. Passing the
|
|
1408
|
+
// synthesized default into the capacity wait would make every
|
|
1409
|
+
// hint-less capacity 429 sleep a full minute and leave the 2s/5s/15s
|
|
1410
|
+
// backoff table dead on this path.
|
|
1411
|
+
const retryAfterHintMs = parseRetryAfterHeaderMs(upstream.headers, state.now()) ??
|
|
1412
|
+
parseRetryAfterBodyMs(bodyText, state.now());
|
|
1413
|
+
const retryAfterMs = retryAfterHintMs ?? 60_000;
|
|
1203
1414
|
// Reading the body awaited I/O; a switch may have landed meanwhile.
|
|
1204
1415
|
reconcileManualSelection();
|
|
1416
|
+
// A capacity 429 is not this account's quota. Marking it rate
|
|
1417
|
+
// limited would take a healthy account out of the pool for the
|
|
1418
|
+
// retry-after window on an outage that affects every account.
|
|
1419
|
+
if (isModelAtCapacityError(upstream.status, bodyText)) {
|
|
1420
|
+
const outcome = await waitOutModelCapacity(retryAfterHintMs, refreshed.account);
|
|
1421
|
+
if (outcome === "client-gone") {
|
|
1422
|
+
await usageRecorder.record({
|
|
1423
|
+
outcome: "failure",
|
|
1424
|
+
statusCode: upstream.status,
|
|
1425
|
+
errorCode: "client_disconnected_during_capacity_wait",
|
|
1426
|
+
account: refreshed.account,
|
|
1427
|
+
});
|
|
1428
|
+
return;
|
|
1429
|
+
}
|
|
1430
|
+
if (outcome === "retry")
|
|
1431
|
+
continue;
|
|
1432
|
+
}
|
|
1205
1433
|
state.preemptiveQuotaScheduler.markRateLimited(quotaScheduleKey, retryAfterMs, state.now());
|
|
1206
1434
|
// A 429 is the upstream quota signal for the attempted account, so
|
|
1207
1435
|
// keep the consumed runtime token drained.
|
|
@@ -1339,8 +1567,35 @@ async function handleRequestInner(state, req, res, traceId) {
|
|
|
1339
1567
|
noteRotation();
|
|
1340
1568
|
continue;
|
|
1341
1569
|
}
|
|
1570
|
+
// Forward image validation/moderation/5xx errors without cross-account replay.
|
|
1571
|
+
// Explicit 429 quota and 401 auth handling above still use the existing pool.
|
|
1572
|
+
if (isImageRequest && upstream.status >= 400) {
|
|
1573
|
+
const forwarded = await forwardStreamingResponse(upstream, res, state.status, () => undefined, state.streamStallTimeoutMs);
|
|
1574
|
+
await usageRecorder.record({
|
|
1575
|
+
outcome: "failure", statusCode: upstream.status,
|
|
1576
|
+
errorCode: forwarded ? "image_upstream_error" : "stream_forward_failed",
|
|
1577
|
+
account: refreshed.account,
|
|
1578
|
+
});
|
|
1579
|
+
return;
|
|
1580
|
+
}
|
|
1342
1581
|
if (upstream.status >= 500) {
|
|
1343
|
-
await readErrorBody(upstream, state.streamStallTimeoutMs);
|
|
1582
|
+
const bodyText = await readErrorBody(upstream, state.streamStallTimeoutMs);
|
|
1583
|
+
// A capacity 5xx is upstream load, not a broken account, so it
|
|
1584
|
+
// must not cool the account down or count against the pool.
|
|
1585
|
+
if (isModelAtCapacityError(upstream.status, bodyText)) {
|
|
1586
|
+
const outcome = await waitOutModelCapacity(parseRetryAfterHeaderMs(upstream.headers, state.now()), refreshed.account);
|
|
1587
|
+
if (outcome === "client-gone") {
|
|
1588
|
+
await usageRecorder.record({
|
|
1589
|
+
outcome: "failure",
|
|
1590
|
+
statusCode: upstream.status,
|
|
1591
|
+
errorCode: "client_disconnected_during_capacity_wait",
|
|
1592
|
+
account: refreshed.account,
|
|
1593
|
+
});
|
|
1594
|
+
return;
|
|
1595
|
+
}
|
|
1596
|
+
if (outcome === "retry")
|
|
1597
|
+
continue;
|
|
1598
|
+
}
|
|
1344
1599
|
refundConsumedPoolToken(refreshed.account);
|
|
1345
1600
|
accountManager.recordFailure(refreshed.account, context.family, context.model);
|
|
1346
1601
|
accountManager.markAccountCoolingDown(refreshed.account, state.serverErrorCooldownMs, "server-error");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../../../lib/usage/ledger.ts"],"names":[],"mappings":"AASA,OAAO,EAEN,KAAK,sBAAsB,EAE3B,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EAErB,KAAK,cAAc,EAEnB,KAAK,YAAY,EAGjB,KAAK,iBAAiB,EAEtB,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../../../lib/usage/ledger.ts"],"names":[],"mappings":"AASA,OAAO,EAEN,KAAK,sBAAsB,EAE3B,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EAErB,KAAK,cAAc,EAEnB,KAAK,YAAY,EAGjB,KAAK,iBAAiB,EAEtB,MAAM,YAAY,CAAC;AAsOpB,wBAAgB,mBAAmB,IAAI,gBAAgB,CAMtD;AAED,wBAAsB,oBAAoB,CACzC,KAAK,EAAE,sBAAsB,GAC3B,OAAO,CAAC,cAAc,CAAC,CASzB;AAuKD,wBAAsB,mBAAmB,CACxC,KAAK,GAAE,gBAAqB,GAC1B,OAAO,CAAC,cAAc,EAAE,CAAC,CAc3B;AAuDD,wBAAgB,kBAAkB,CACjC,IAAI,EAAE,cAAc,EAAE,EACtB,KAAK,GAAE,iBAAsB,GAC3B,YAAY,CAwBd;AAED,wBAAsB,oBAAoB,CACzC,KAAK,GAAE,iBAAsB,GAC3B,OAAO,CAAC,YAAY,CAAC,CAEvB;AAED,wBAAsB,iBAAiB,CAAC,OAAO,GAAE;IAChD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,iBAAiB,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA2B9B;AAED,wBAAgB,6BAA6B,IAAI,IAAI,CAEpD"}
|
package/dist/lib/usage/ledger.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redaction.d.ts","sourceRoot":"","sources":["../../../lib/usage/redaction.ts"],"names":[],"mappings":"AACA,OAAO,EAEN,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAG3B,KAAK,cAAc,EAGnB,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"redaction.d.ts","sourceRoot":"","sources":["../../../lib/usage/redaction.ts"],"names":[],"mappings":"AACA,OAAO,EAEN,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAG3B,KAAK,cAAc,EAGnB,MAAM,YAAY,CAAC;AA4EpB,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE;IAC5C,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,GAAG,qBAAqB,GAAG,IAAI,CAkB/B;AAkCD,wBAAgB,uBAAuB,CACtC,KAAK,EAAE,sBAAsB,GAC3B,cAAc,CA0BhB;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,cAAc,GAAG,MAAM,CAE9D"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type UsageLedgerSource = "runtime-proxy" | "plugin-host" | "local-bridge" | "cli" | "unknown";
|
|
2
|
-
export type UsageLedgerOperation = "responses" | "models" | "thread-goal" | "auth-refresh" | "diagnostic" | "unknown";
|
|
2
|
+
export type UsageLedgerOperation = "responses" | "images" | "models" | "thread-goal" | "auth-refresh" | "diagnostic" | "unknown";
|
|
3
3
|
export type UsageLedgerOutcome = "success" | "failure" | "blocked" | "cancelled";
|
|
4
4
|
/**
|
|
5
5
|
* The service tier a response was actually billed at.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../lib/usage/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAC1B,eAAe,GACf,aAAa,GACb,cAAc,GACd,KAAK,GACL,SAAS,CAAC;AAEb,MAAM,MAAM,oBAAoB,GAC7B,WAAW,GACX,QAAQ,GACR,aAAa,GACb,cAAc,GACd,YAAY,GACZ,SAAS,CAAC;AAEb,MAAM,MAAM,kBAAkB,GAC3B,SAAS,GACT,SAAS,GACT,SAAS,GACT,WAAW,CAAC;AAEf;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,wEAOtB,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEpE;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,gBAAgB,CAK5E;AAED,MAAM,WAAW,gBAAgB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,gBAAgB,CAAC;CAC/B;AAED,MAAM,WAAW,qBAAqB;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,CAAC,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,SAAS,EAAE,oBAAoB,CAAC;IAChC,OAAO,EAAE,kBAAkB,CAAC;IAC5B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,qBAAqB,GAAG,IAAI,CAAC;IACtC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,gBAAgB,CAAC;IACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,sBAAsB;IACtC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,SAAS,CAAC,EAAE,oBAAoB,CAAC;IACjC,OAAO,EAAE,kBAAkB,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;OAGG;IACH,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAC/B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,MAAM,MAAM,mBAAmB,GAC5B,OAAO,GACP,SAAS,GACT,SAAS,GACT,SAAS,GACT,KAAK,CAAC;AAET,MAAM,WAAW,gBAAgB;IAChC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAAC;IAC/B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAAC;IAC/B,eAAe,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAkB,SAAQ,gBAAgB;IAC1D,EAAE,CAAC,EAAE,mBAAmB,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,gBAAgB,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC5B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,EAAE,EAAE,mBAAmB,CAAC;IACxB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,OAAO,EAAE,kBAAkB,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,gBAAgB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CAChB"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../lib/usage/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codex-multi-auth",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.15.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",
|
package/scripts/codex.js
CHANGED
|
@@ -3890,6 +3890,7 @@ function createRuntimeRotationProxyCanonicalCodexHome(
|
|
|
3890
3890
|
`${providerTable}.base_url=${configTomlModule.tomlStringLiteral(proxyBaseUrl)}`,
|
|
3891
3891
|
`${providerTable}.env_key=${configTomlModule.tomlStringLiteral("OPENAI_API_KEY")}`,
|
|
3892
3892
|
`${providerTable}.requires_openai_auth=false`,
|
|
3893
|
+
`${providerTable}.http_headers.x-openai-actor-authorization=${configTomlModule.tomlStringLiteral("codex-multi-auth-local")}`,
|
|
3893
3894
|
`${providerTable}.wire_api=${configTomlModule.tomlStringLiteral("responses")}`,
|
|
3894
3895
|
"disable_response_storage=false",
|
|
3895
3896
|
].flatMap((assignment) => ["-c", assignment]);
|