codex-multi-auth 0.1.2 → 0.1.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.
Files changed (66) hide show
  1. package/README.md +6 -9
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +51 -13
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/auth/auth.d.ts +6 -2
  6. package/dist/lib/auth/auth.d.ts.map +1 -1
  7. package/dist/lib/auth/auth.js +34 -11
  8. package/dist/lib/auth/auth.js.map +1 -1
  9. package/dist/lib/auth/server.d.ts +1 -1
  10. package/dist/lib/auth/server.js +3 -3
  11. package/dist/lib/codex-cli/state.d.ts +9 -0
  12. package/dist/lib/codex-cli/state.d.ts.map +1 -1
  13. package/dist/lib/codex-cli/state.js +14 -0
  14. package/dist/lib/codex-cli/state.js.map +1 -1
  15. package/dist/lib/codex-cli/writer.d.ts.map +1 -1
  16. package/dist/lib/codex-cli/writer.js +106 -14
  17. package/dist/lib/codex-cli/writer.js.map +1 -1
  18. package/dist/lib/codex-manager/settings-hub.d.ts +16 -1
  19. package/dist/lib/codex-manager/settings-hub.d.ts.map +1 -1
  20. package/dist/lib/codex-manager/settings-hub.js +28 -2
  21. package/dist/lib/codex-manager/settings-hub.js.map +1 -1
  22. package/dist/lib/codex-manager.d.ts +1 -0
  23. package/dist/lib/codex-manager.d.ts.map +1 -1
  24. package/dist/lib/codex-manager.js +320 -18
  25. package/dist/lib/codex-manager.js.map +1 -1
  26. package/dist/lib/oauth-success.html +192 -260
  27. package/dist/lib/parallel-probe.d.ts +7 -0
  28. package/dist/lib/parallel-probe.d.ts.map +1 -1
  29. package/dist/lib/parallel-probe.js +37 -9
  30. package/dist/lib/parallel-probe.js.map +1 -1
  31. package/dist/lib/refresh-queue.d.ts +4 -5
  32. package/dist/lib/refresh-queue.d.ts.map +1 -1
  33. package/dist/lib/refresh-queue.js +99 -21
  34. package/dist/lib/refresh-queue.js.map +1 -1
  35. package/dist/lib/request/fetch-helpers.d.ts +12 -4
  36. package/dist/lib/request/fetch-helpers.d.ts.map +1 -1
  37. package/dist/lib/request/fetch-helpers.js +53 -25
  38. package/dist/lib/request/fetch-helpers.js.map +1 -1
  39. package/dist/lib/request/helpers/tool-utils.d.ts.map +1 -1
  40. package/dist/lib/request/helpers/tool-utils.js +36 -6
  41. package/dist/lib/request/helpers/tool-utils.js.map +1 -1
  42. package/dist/lib/request/rate-limit-backoff.d.ts +7 -0
  43. package/dist/lib/request/rate-limit-backoff.d.ts.map +1 -1
  44. package/dist/lib/request/rate-limit-backoff.js +24 -4
  45. package/dist/lib/request/rate-limit-backoff.js.map +1 -1
  46. package/dist/lib/request/request-transformer.d.ts +10 -0
  47. package/dist/lib/request/request-transformer.d.ts.map +1 -1
  48. package/dist/lib/request/request-transformer.js +61 -37
  49. package/dist/lib/request/request-transformer.js.map +1 -1
  50. package/dist/lib/rotation.d.ts +19 -0
  51. package/dist/lib/rotation.d.ts.map +1 -1
  52. package/dist/lib/rotation.js +60 -39
  53. package/dist/lib/rotation.js.map +1 -1
  54. package/dist/lib/ui/copy.d.ts +1 -1
  55. package/dist/lib/ui/copy.js +1 -1
  56. package/dist/lib/utils.d.ts +6 -0
  57. package/dist/lib/utils.d.ts.map +1 -1
  58. package/dist/lib/utils.js +11 -0
  59. package/dist/lib/utils.js.map +1 -1
  60. package/package.json +9 -5
  61. package/scripts/audit-dev-allowlist.js +186 -94
  62. package/scripts/benchmark-runtime-path.mjs +166 -0
  63. package/scripts/codex.js +57 -1
  64. package/scripts/copy-oauth-success.js +42 -3
  65. package/scripts/install-codex-auth-utils.js +69 -0
  66. package/scripts/install-codex-auth.js +16 -7
package/README.md CHANGED
@@ -129,11 +129,6 @@ codex auth doctor --fix
129
129
  | `codex auth fix --live --model gpt-5-codex` | Run repairs with live probe model |
130
130
  | `codex auth doctor --fix` | Diagnose and apply safe fixes |
131
131
 
132
- Compatibility aliases are also supported:
133
- - `codex multi auth ...`
134
- - `codex multi-auth ...`
135
- - `codex multiauth ...`
136
-
137
132
  ---
138
133
 
139
134
  ## Dashboard Hotkeys
@@ -226,7 +221,7 @@ codex auth login
226
221
  <details>
227
222
  <summary><b>Common symptoms</b></summary>
228
223
 
229
- - `codex auth` unrecognized: run `where codex`, then try `codex multi auth status`
224
+ - `codex auth` unrecognized: run `where codex`, then follow `docs/troubleshooting.md` for routing fallback commands
230
225
  - Switch succeeds but wrong account appears active: run `codex auth switch <index>`, then restart session
231
226
  - OAuth callback on port `1455` fails: free the port and re-run `codex auth login`
232
227
  - `missing field id_token` / `token_expired` / `refresh_token_reused`: re-login affected account
@@ -259,6 +254,8 @@ codex auth doctor --json
259
254
  - Configuration: [docs/configuration.md](docs/configuration.md)
260
255
  - Troubleshooting: [docs/troubleshooting.md](docs/troubleshooting.md)
261
256
  - Commands reference: [docs/reference/commands.md](docs/reference/commands.md)
257
+ - Public API contract: [docs/reference/public-api.md](docs/reference/public-api.md)
258
+ - Error contracts: [docs/reference/error-contracts.md](docs/reference/error-contracts.md)
262
259
  - Settings reference: [docs/reference/settings.md](docs/reference/settings.md)
263
260
  - Storage paths: [docs/reference/storage-paths.md](docs/reference/storage-paths.md)
264
261
  - Upgrade guide: [docs/upgrade.md](docs/upgrade.md)
@@ -268,9 +265,9 @@ codex auth doctor --json
268
265
 
269
266
  ## Release Notes
270
267
 
271
- - Current stable: [docs/releases/v0.1.2.md](docs/releases/v0.1.2.md)
272
- - Previous stable: [docs/releases/v0.1.1.md](docs/releases/v0.1.1.md)
273
- - Earlier stable: [docs/releases/v0.1.0.md](docs/releases/v0.1.0.md)
268
+ - Current stable: [docs/releases/v0.1.4.md](docs/releases/v0.1.4.md)
269
+ - Previous stable: [docs/releases/v0.1.3.md](docs/releases/v0.1.3.md)
270
+ - Earlier stable: [docs/releases/v0.1.2.md](docs/releases/v0.1.2.md)
274
271
  - Archived prerelease: [docs/releases/v0.1.0-beta.0.md](docs/releases/v0.1.0-beta.0.md)
275
272
 
276
273
  ## License
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,kBAAkB,CAAC;AAmL5D;;;;;;;;;;;;;;;GAeG;AAEH,eAAO,MAAM,iBAAiB,EAAE,MAwzH/B,CAAC;AAEF,eAAO,MAAM,gBAAgB,KAAoB,CAAC;AAElD,eAAe,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,kBAAkB,CAAC;AAmL5D;;;;;;;;;;;;;;;GAeG;AAEH,eAAO,MAAM,iBAAiB,EAAE,MAg2H/B,CAAC;AAEF,eAAO,MAAM,gBAAgB,KAAoB,CAAC;AAElD,eAAe,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -117,6 +117,38 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
117
117
  const parsed = Number.parseInt(value, 10);
118
118
  return Number.isFinite(parsed) ? parsed : undefined;
119
119
  };
120
+ const MAX_RETRY_HINT_MS = 5 * 60 * 1000;
121
+ const clampRetryHintMs = (value) => {
122
+ if (!Number.isFinite(value))
123
+ return null;
124
+ const normalized = Math.floor(value);
125
+ if (normalized <= 0)
126
+ return null;
127
+ return Math.min(normalized, MAX_RETRY_HINT_MS);
128
+ };
129
+ const parseRetryAfterHintMs = (headers) => {
130
+ const retryAfterMsHeader = headers.get("retry-after-ms")?.trim();
131
+ if (retryAfterMsHeader && /^\d+$/.test(retryAfterMsHeader)) {
132
+ return clampRetryHintMs(Number.parseInt(retryAfterMsHeader, 10));
133
+ }
134
+ const retryAfterHeader = headers.get("retry-after")?.trim();
135
+ if (retryAfterHeader && /^\d+$/.test(retryAfterHeader)) {
136
+ return clampRetryHintMs(Number.parseInt(retryAfterHeader, 10) * 1000);
137
+ }
138
+ if (retryAfterHeader) {
139
+ const retryAtMs = Date.parse(retryAfterHeader);
140
+ if (Number.isFinite(retryAtMs)) {
141
+ return clampRetryHintMs(retryAtMs - Date.now());
142
+ }
143
+ }
144
+ const resetAtHeader = headers.get("x-ratelimit-reset")?.trim();
145
+ if (resetAtHeader && /^\d+$/.test(resetAtHeader)) {
146
+ const resetRaw = Number.parseInt(resetAtHeader, 10);
147
+ const resetAtMs = resetRaw < 10_000_000_000 ? resetRaw * 1000 : resetRaw;
148
+ return clampRetryHintMs(resetAtMs - Date.now());
149
+ }
150
+ return null;
151
+ };
120
152
  const sanitizeResponseHeadersForLog = (headers) => {
121
153
  const allowed = new Set([
122
154
  "content-type",
@@ -1131,7 +1163,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
1131
1163
  runtimeMetrics.lastError =
1132
1164
  `Local token bucket depleted for account ${account.index + 1} (${modelFamily}${model ? `:${model}` : ""})`;
1133
1165
  logWarn(`Skipping account ${account.index + 1}: local token bucket depleted for ${modelFamily}${model ? `:${model}` : ""}`);
1134
- break;
1166
+ continue;
1135
1167
  }
1136
1168
  let sameAccountRetryCount = 0;
1137
1169
  let successAccountForResponse = account;
@@ -1141,7 +1173,12 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
1141
1173
  // Merge user AbortSignal with timeout (Node 18 compatible - no AbortSignal.any)
1142
1174
  const fetchController = new AbortController();
1143
1175
  const requestTimeoutMs = fetchTimeoutMs;
1144
- const fetchTimeoutId = setTimeout(() => fetchController.abort(new Error("Request timeout")), requestTimeoutMs);
1176
+ let requestTimedOut = false;
1177
+ const timeoutReason = new Error("Request timeout");
1178
+ const fetchTimeoutId = setTimeout(() => {
1179
+ requestTimedOut = true;
1180
+ fetchController.abort(timeoutReason);
1181
+ }, requestTimeoutMs);
1145
1182
  const onUserAbort = abortSignal
1146
1183
  ? () => fetchController.abort(abortSignal.reason ?? new Error("Aborted by user"))
1147
1184
  : null;
@@ -1161,14 +1198,19 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
1161
1198
  });
1162
1199
  }
1163
1200
  catch (networkError) {
1164
- const isUserAbort = abortSignal?.aborted ||
1165
- (networkError instanceof Error &&
1166
- (networkError.name === "AbortError" || /abort/i.test(networkError.message)));
1201
+ const fetchAbortReason = fetchController.signal.reason;
1202
+ const isTimeoutAbort = requestTimedOut ||
1203
+ (fetchAbortReason instanceof Error &&
1204
+ fetchAbortReason.message === timeoutReason.message);
1205
+ const isUserAbort = Boolean(abortSignal?.aborted) && !isTimeoutAbort;
1167
1206
  if (isUserAbort) {
1207
+ accountManager.refundToken(account, modelFamily, model);
1168
1208
  runtimeMetrics.userAborts++;
1169
1209
  runtimeMetrics.lastError = "request aborted by user";
1170
1210
  sessionAffinityStore?.forgetSession(sessionAffinityKey);
1171
- break;
1211
+ throw (fetchAbortReason instanceof Error
1212
+ ? fetchAbortReason
1213
+ : new Error("Aborted by user"));
1172
1214
  }
1173
1215
  const errorMsg = networkError instanceof Error ? networkError.message : String(networkError);
1174
1216
  logWarn(`Network error for account ${account.index + 1}: ${errorMsg}`);
@@ -1347,7 +1389,8 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
1347
1389
  runtimeMetrics.serverErrors++;
1348
1390
  runtimeMetrics.accountRotations++;
1349
1391
  runtimeMetrics.lastError = `HTTP ${response.status}`;
1350
- const policy = evaluateFailurePolicy({ kind: "server", failoverMode }, { serverCooldownMs: serverErrorCooldownMs });
1392
+ const serverRetryAfterMs = parseRetryAfterHintMs(response.headers);
1393
+ const policy = evaluateFailurePolicy({ kind: "server", failoverMode, serverRetryAfterMs: serverRetryAfterMs ?? undefined }, { serverCooldownMs: serverErrorCooldownMs });
1351
1394
  if (policy.refundToken) {
1352
1395
  accountManager.refundToken(account, modelFamily, model);
1353
1396
  }
@@ -1491,12 +1534,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
1491
1534
  // Best effort cleanup before trying next fallback account.
1492
1535
  }
1493
1536
  if (fallbackResponse.status === 429) {
1494
- const retryAfterRaw = fallbackResponse.headers
1495
- .get("retry-after")
1496
- ?.trim();
1497
- const retryAfterMs = retryAfterRaw && /^\d+$/.test(retryAfterRaw)
1498
- ? Number.parseInt(retryAfterRaw, 10) * 1000
1499
- : 60_000;
1537
+ const retryAfterMs = parseRetryAfterHintMs(fallbackResponse.headers) ?? 60_000;
1500
1538
  accountManager.markRateLimitedWithReason(fallbackAccount, retryAfterMs, modelFamily, "quota", model);
1501
1539
  accountManager.recordRateLimit(fallbackAccount, modelFamily, model);
1502
1540
  }