codex-multi-auth 1.2.2 → 1.2.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 (67) hide show
  1. package/README.md +3 -3
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +36 -13
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/accounts.d.ts +1 -0
  6. package/dist/lib/accounts.d.ts.map +1 -1
  7. package/dist/lib/accounts.js +11 -4
  8. package/dist/lib/accounts.js.map +1 -1
  9. package/dist/lib/codex-manager/commands/report.d.ts +2 -1
  10. package/dist/lib/codex-manager/commands/report.d.ts.map +1 -1
  11. package/dist/lib/codex-manager/commands/report.js +54 -49
  12. package/dist/lib/codex-manager/commands/report.js.map +1 -1
  13. package/dist/lib/codex-manager.d.ts.map +1 -1
  14. package/dist/lib/codex-manager.js +48 -40
  15. package/dist/lib/codex-manager.js.map +1 -1
  16. package/dist/lib/config.d.ts.map +1 -1
  17. package/dist/lib/config.js +172 -17
  18. package/dist/lib/config.js.map +1 -1
  19. package/dist/lib/forecast.d.ts.map +1 -1
  20. package/dist/lib/forecast.js +1 -19
  21. package/dist/lib/forecast.js.map +1 -1
  22. package/dist/lib/preemptive-quota-scheduler.d.ts.map +1 -1
  23. package/dist/lib/preemptive-quota-scheduler.js +6 -3
  24. package/dist/lib/preemptive-quota-scheduler.js.map +1 -1
  25. package/dist/lib/request/fetch-helpers.d.ts.map +1 -1
  26. package/dist/lib/request/fetch-helpers.js +116 -30
  27. package/dist/lib/request/fetch-helpers.js.map +1 -1
  28. package/dist/lib/runtime/account-check.d.ts +4 -0
  29. package/dist/lib/runtime/account-check.d.ts.map +1 -1
  30. package/dist/lib/runtime/account-check.js +15 -10
  31. package/dist/lib/runtime/account-check.js.map +1 -1
  32. package/dist/lib/runtime/account-state.d.ts +1 -12
  33. package/dist/lib/runtime/account-state.d.ts.map +1 -1
  34. package/dist/lib/runtime/account-state.js +1 -37
  35. package/dist/lib/runtime/account-state.js.map +1 -1
  36. package/dist/lib/runtime/verify-flagged.d.ts +4 -0
  37. package/dist/lib/runtime/verify-flagged.d.ts.map +1 -1
  38. package/dist/lib/runtime/verify-flagged.js +32 -23
  39. package/dist/lib/runtime/verify-flagged.js.map +1 -1
  40. package/dist/lib/storage/flagged-load-entry.d.ts +2 -0
  41. package/dist/lib/storage/flagged-load-entry.d.ts.map +1 -1
  42. package/dist/lib/storage/flagged-load-entry.js +1 -0
  43. package/dist/lib/storage/flagged-load-entry.js.map +1 -1
  44. package/dist/lib/storage/flagged-storage-file.d.ts +4 -0
  45. package/dist/lib/storage/flagged-storage-file.d.ts.map +1 -1
  46. package/dist/lib/storage/flagged-storage-file.js +1 -1
  47. package/dist/lib/storage/flagged-storage-file.js.map +1 -1
  48. package/dist/lib/storage/flagged-storage-io.d.ts +1 -0
  49. package/dist/lib/storage/flagged-storage-io.d.ts.map +1 -1
  50. package/dist/lib/storage/flagged-storage-io.js +130 -5
  51. package/dist/lib/storage/flagged-storage-io.js.map +1 -1
  52. package/dist/lib/storage/path-state.d.ts +1 -0
  53. package/dist/lib/storage/path-state.d.ts.map +1 -1
  54. package/dist/lib/storage/path-state.js +3 -0
  55. package/dist/lib/storage/path-state.js.map +1 -1
  56. package/dist/lib/storage/paths.d.ts.map +1 -1
  57. package/dist/lib/storage/paths.js +3 -2
  58. package/dist/lib/storage/paths.js.map +1 -1
  59. package/dist/lib/storage.d.ts.map +1 -1
  60. package/dist/lib/storage.js +7 -0
  61. package/dist/lib/storage.js.map +1 -1
  62. package/dist/lib/unified-settings.d.ts.map +1 -1
  63. package/dist/lib/unified-settings.js +226 -28
  64. package/dist/lib/unified-settings.js.map +1 -1
  65. package/package.json +8 -3
  66. package/scripts/codex-bin-resolver.js +99 -0
  67. package/scripts/codex.js +709 -81
package/scripts/codex.js CHANGED
@@ -1,13 +1,70 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { spawn, spawnSync } from "node:child_process";
4
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { spawn } from "node:child_process";
4
+ import {
5
+ chmodSync,
6
+ copyFileSync,
7
+ existsSync,
8
+ mkdirSync,
9
+ mkdtempSync,
10
+ renameSync,
11
+ readFileSync,
12
+ rmSync,
13
+ statSync,
14
+ writeFileSync,
15
+ } from "node:fs";
5
16
  import { createRequire } from "node:module";
17
+ import { homedir, tmpdir } from "node:os";
6
18
  import { basename, delimiter, dirname, join, resolve as resolvePath } from "node:path";
7
19
  import process from "node:process";
8
20
  import { fileURLToPath } from "node:url";
21
+ import { resolveRealCodexBin as resolveRealCodexBinFromEnvironment } from "./codex-bin-resolver.js";
9
22
  import { normalizeAuthAlias, shouldHandleMultiAuthAuth } from "./codex-routing.js";
10
23
 
24
+ const RETRYABLE_SHADOW_HOME_CLEANUP_CODES = new Set(["EBUSY", "EPERM", "ENOTEMPTY"]);
25
+ const SHADOW_HOME_CLEANUP_BACKOFF_MS = [20, 60, 120];
26
+ const SHADOW_HOME_STATE_FILES = ["auth.json", "accounts.json", ".codex-global-state.json"];
27
+ let shadowHomeCleanupBusyFailuresRemaining = Number.parseInt(
28
+ process.env.CODEX_MULTI_AUTH_TEST_SHADOW_CLEANUP_BUSY_FAILURES ?? "0",
29
+ 10,
30
+ );
31
+ let shadowHomeCleanupPreflightReadBusyFailuresRemaining = Number.parseInt(
32
+ process.env.CODEX_MULTI_AUTH_TEST_SHADOW_PREFLIGHT_READ_BUSY_FAILURES ?? "0",
33
+ 10,
34
+ );
35
+ const shadowHomeCleanupRetryMarkerDir =
36
+ (process.env.CODEX_MULTI_AUTH_TEST_SHADOW_RETRY_MARKER_DIR ?? "").trim();
37
+
38
+ function isRetryableShadowHomeCleanupError(error) {
39
+ const code = error && typeof error === "object" && "code" in error ? error.code : undefined;
40
+ return typeof code === "string" && RETRYABLE_SHADOW_HOME_CLEANUP_CODES.has(code);
41
+ }
42
+
43
+ function sleepSync(ms) {
44
+ if (!Number.isFinite(ms) || ms <= 0) {
45
+ return;
46
+ }
47
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
48
+ }
49
+
50
+ function removeDirectoryWithRetry(targetPath) {
51
+ for (let attempt = 0; attempt <= SHADOW_HOME_CLEANUP_BACKOFF_MS.length; attempt += 1) {
52
+ try {
53
+ maybeThrowSimulatedShadowHomeBusyError();
54
+ rmSync(targetPath, { recursive: true, force: true });
55
+ return;
56
+ } catch (error) {
57
+ if (
58
+ !isRetryableShadowHomeCleanupError(error) ||
59
+ attempt === SHADOW_HOME_CLEANUP_BACKOFF_MS.length
60
+ ) {
61
+ throw error;
62
+ }
63
+ sleepSync(SHADOW_HOME_CLEANUP_BACKOFF_MS[attempt]);
64
+ }
65
+ }
66
+ }
67
+
11
68
  function hydrateCliVersionEnv() {
12
69
  try {
13
70
  const require = createRequire(import.meta.url);
@@ -74,73 +131,37 @@ function resolveRealCodexBin() {
74
131
  return null;
75
132
  }
76
133
 
77
- try {
78
- const require = createRequire(import.meta.url);
79
- const resolved = require.resolve("@openai/codex/bin/codex.js");
80
- if (existsSync(resolved)) return resolved;
81
- } catch {
82
- // Fall through to sibling lookup.
83
- }
84
-
85
- const searchRoots = [];
86
- const scriptDir = dirname(fileURLToPath(import.meta.url));
87
- searchRoots.push(join(scriptDir, "..", ".."));
88
-
89
- const invokedScript = process.argv[1];
90
- if (typeof invokedScript === "string" && invokedScript.length > 0) {
91
- searchRoots.push(join(dirname(invokedScript), "..", ".."));
92
- }
93
-
94
- const npmPrefix = (process.env.npm_config_prefix ?? process.env.PREFIX ?? "").trim();
95
- if (npmPrefix.length > 0) {
96
- searchRoots.push(join(npmPrefix, "node_modules"));
97
- searchRoots.push(join(npmPrefix, "lib", "node_modules"));
98
- }
99
-
100
- for (const root of searchRoots) {
101
- const candidate = join(root, "@openai", "codex", "bin", "codex.js");
102
- if (existsSync(candidate)) return candidate;
103
- }
104
-
105
- const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
106
- try {
107
- const rootResult = spawnSync(npmCmd, ["root", "-g"], {
108
- encoding: "utf8",
109
- stdio: ["ignore", "pipe", "ignore"],
110
- });
111
- if (rootResult.status === 0) {
112
- const globalRoot = rootResult.stdout.trim();
113
- if (globalRoot.length > 0) {
114
- const globalBin = join(globalRoot, "@openai", "codex", "bin", "codex.js");
115
- if (existsSync(globalBin)) return globalBin;
116
- }
117
- }
118
- } catch {
119
- // Ignore and fall through to null.
120
- }
121
-
122
- return null;
134
+ return resolveRealCodexBinFromEnvironment({ moduleUrl: import.meta.url });
123
135
  }
124
136
 
125
- function forwardToRealCodex(codexBin, args) {
137
+ function forwardToRealCodex(codexBin, args, env = process.env, cleanup) {
126
138
  return new Promise((resolve) => {
139
+ const finalize = (exitCode) => {
140
+ try {
141
+ cleanup?.();
142
+ } catch {
143
+ // Best-effort cleanup only.
144
+ }
145
+ resolve(exitCode);
146
+ };
147
+
127
148
  const child = spawn(process.execPath, [codexBin, ...args], {
128
149
  stdio: "inherit",
129
- env: process.env,
150
+ env,
130
151
  });
131
152
 
132
153
  child.once("error", (error) => {
133
154
  console.error(`Failed to launch real Codex CLI: ${String(error)}`);
134
- resolve(1);
155
+ finalize(1);
135
156
  });
136
157
 
137
158
  child.once("exit", (code, signal) => {
138
159
  if (signal) {
139
160
  const signalNumber = signal === "SIGINT" ? 130 : 1;
140
- resolve(signalNumber);
161
+ finalize(signalNumber);
141
162
  return;
142
163
  }
143
- resolve(typeof code === "number" ? code : 1);
164
+ finalize(typeof code === "number" ? code : 1);
144
165
  });
145
166
  });
146
167
  }
@@ -168,16 +189,604 @@ function hasCliAuthCredentialsStoreOverride(args) {
168
189
  return false;
169
190
  }
170
191
 
192
+ // IMPORTANT: Keep this mapping in sync with
193
+ // `lib/request/helpers/model-map.ts` `MODEL_PROFILES`.
194
+ // This wrapper runs before the TypeScript build, so it cannot import that source.
195
+ const SUPPORTED_REASONING_EFFORTS_BY_MODEL = {
196
+ "gpt-5-codex": ["low", "medium", "high", "xhigh"],
197
+ "gpt-5.1-codex-max": ["medium", "high", "xhigh"],
198
+ "gpt-5.1-codex-mini": ["medium", "high"],
199
+ "gpt-5.4": ["none", "low", "medium", "high", "xhigh"],
200
+ "gpt-5.4-pro": ["medium", "high", "xhigh"],
201
+ "gpt-5.2-pro": ["medium", "high", "xhigh"],
202
+ "gpt-5-pro": ["high"],
203
+ "gpt-5.2": ["none", "low", "medium", "high", "xhigh"],
204
+ "gpt-5.1": ["none", "low", "medium", "high"],
205
+ "gpt-5": ["minimal", "low", "medium", "high"],
206
+ "gpt-5-mini": ["medium"],
207
+ "gpt-5-nano": ["medium"],
208
+ };
209
+
210
+ const REASONING_FALLBACKS = {
211
+ none: ["none", "low", "minimal", "medium", "high", "xhigh"],
212
+ minimal: ["minimal", "low", "none", "medium", "high", "xhigh"],
213
+ low: ["low", "minimal", "none", "medium", "high", "xhigh"],
214
+ medium: ["medium", "low", "high", "minimal", "none", "xhigh"],
215
+ high: ["high", "medium", "xhigh", "low", "minimal", "none"],
216
+ xhigh: ["xhigh", "high", "medium", "low", "minimal", "none"],
217
+ };
218
+
219
+ const KNOWN_REASONING_EFFORTS = new Set(Object.keys(REASONING_FALLBACKS));
220
+ const REQUESTED_MODEL_ALIASES = new Map();
221
+
222
+ function addRequestedModelAlias(alias, normalizedModel) {
223
+ REQUESTED_MODEL_ALIASES.set(alias, normalizedModel);
224
+ }
225
+
226
+ function addRequestedModelReasoningAliases(alias, normalizedModel) {
227
+ addRequestedModelAlias(alias, normalizedModel);
228
+ for (const effort of KNOWN_REASONING_EFFORTS) {
229
+ addRequestedModelAlias(`${alias}-${effort}`, normalizedModel);
230
+ }
231
+ }
232
+
233
+ function maybeThrowSimulatedShadowHomeBusyError() {
234
+ if (shadowHomeCleanupBusyFailuresRemaining > 0) {
235
+ shadowHomeCleanupBusyFailuresRemaining -= 1;
236
+ const error = new Error("simulated busy shadow-home operation");
237
+ error.code = "EBUSY";
238
+ throw error;
239
+ }
240
+ }
241
+
242
+ function maybeThrowSimulatedShadowHomePreflightReadBusyError() {
243
+ if (shadowHomeCleanupPreflightReadBusyFailuresRemaining > 0) {
244
+ shadowHomeCleanupPreflightReadBusyFailuresRemaining -= 1;
245
+ const error = new Error("simulated busy shadow-home preflight read");
246
+ error.code = "EBUSY";
247
+ throw error;
248
+ }
249
+ }
250
+
251
+ function writeShadowHomeCleanupRetryMarker(destinationPath, attempt) {
252
+ if (shadowHomeCleanupRetryMarkerDir.length === 0) {
253
+ return;
254
+ }
255
+ try {
256
+ mkdirSync(shadowHomeCleanupRetryMarkerDir, { recursive: true });
257
+ writeFileSync(
258
+ join(
259
+ shadowHomeCleanupRetryMarkerDir,
260
+ `${basename(destinationPath)}.retry-${attempt + 1}`,
261
+ ),
262
+ `${attempt + 1}\n`,
263
+ "utf8",
264
+ );
265
+ } catch {
266
+ // Best-effort test hook only.
267
+ }
268
+ }
269
+
270
+ function ensureShadowHomeDestinationMatchesSnapshot(destinationPath, expectedState) {
271
+ if (!expectedState) {
272
+ return;
273
+ }
274
+ const currentState = captureShadowHomeState(destinationPath, {
275
+ rethrowRetryableReadErrors: true,
276
+ });
277
+ if (!shadowHomeStateMatches(currentState, expectedState)) {
278
+ const error = new Error("shadow-home destination changed during sync-back retry");
279
+ error.code = "EEXIST";
280
+ throw error;
281
+ }
282
+ }
283
+
284
+ function renameFileWithRetry(sourcePath, destinationPath, expectedDestinationState) {
285
+ for (let attempt = 0; attempt <= SHADOW_HOME_CLEANUP_BACKOFF_MS.length; attempt += 1) {
286
+ try {
287
+ ensureShadowHomeDestinationMatchesSnapshot(
288
+ destinationPath,
289
+ expectedDestinationState,
290
+ );
291
+ maybeThrowSimulatedShadowHomeBusyError();
292
+ renameSync(sourcePath, destinationPath);
293
+ return;
294
+ } catch (error) {
295
+ if (
296
+ !isRetryableShadowHomeCleanupError(error) ||
297
+ attempt === SHADOW_HOME_CLEANUP_BACKOFF_MS.length
298
+ ) {
299
+ throw error;
300
+ }
301
+ writeShadowHomeCleanupRetryMarker(destinationPath, attempt);
302
+ sleepSync(SHADOW_HOME_CLEANUP_BACKOFF_MS[attempt]);
303
+ }
304
+ }
305
+ }
306
+
307
+ function seedRequestedModelAliases() {
308
+ addRequestedModelReasoningAliases("gpt-5.4", "gpt-5.4");
309
+ addRequestedModelReasoningAliases("gpt-5.4-pro", "gpt-5.4-pro");
310
+ addRequestedModelReasoningAliases("gpt-5.2-pro", "gpt-5.2-pro");
311
+ addRequestedModelReasoningAliases("gpt-5-pro", "gpt-5-pro");
312
+ addRequestedModelReasoningAliases("gpt-5.2", "gpt-5.2");
313
+ addRequestedModelReasoningAliases("gpt-5.1", "gpt-5.1");
314
+ addRequestedModelReasoningAliases("gpt-5", "gpt-5");
315
+ addRequestedModelReasoningAliases("gpt-5-mini", "gpt-5-mini");
316
+ addRequestedModelReasoningAliases("gpt-5-nano", "gpt-5-nano");
317
+ addRequestedModelReasoningAliases("gpt-5.1-chat-latest", "gpt-5.1");
318
+ addRequestedModelReasoningAliases("gpt-5-chat-latest", "gpt-5");
319
+ addRequestedModelReasoningAliases("gpt-5.4-mini", "gpt-5-mini");
320
+ addRequestedModelReasoningAliases("gpt-5.4-nano", "gpt-5-nano");
321
+ addRequestedModelReasoningAliases("gpt-5-codex", "gpt-5-codex");
322
+ addRequestedModelReasoningAliases("gpt-5.3-codex-spark", "gpt-5-codex");
323
+ addRequestedModelReasoningAliases("gpt-5.3-codex", "gpt-5-codex");
324
+ addRequestedModelReasoningAliases("gpt-5.2-codex", "gpt-5-codex");
325
+ addRequestedModelReasoningAliases("gpt-5.1-codex", "gpt-5-codex");
326
+ addRequestedModelAlias("gpt_5_codex", "gpt-5-codex");
327
+ addRequestedModelReasoningAliases("codex-max", "gpt-5.1-codex-max");
328
+ addRequestedModelReasoningAliases("gpt-5.1-codex-max", "gpt-5.1-codex-max");
329
+ addRequestedModelAlias("codex-mini-latest", "gpt-5.1-codex-mini");
330
+ addRequestedModelReasoningAliases("gpt-5-codex-mini", "gpt-5.1-codex-mini");
331
+ addRequestedModelReasoningAliases("gpt-5.1-codex-mini", "gpt-5.1-codex-mini");
332
+ }
333
+
334
+ seedRequestedModelAliases();
335
+
336
+ function stripProviderPrefix(model) {
337
+ return typeof model === "string" && model.includes("/")
338
+ ? model.split("/").pop() ?? model
339
+ : model;
340
+ }
341
+
342
+ function normalizeRequestedModel(model) {
343
+ const stripped = stripProviderPrefix(model ?? "");
344
+ const normalized = stripped.trim().toLowerCase();
345
+ if (normalized.length === 0) return "";
346
+ const exactMatch = REQUESTED_MODEL_ALIASES.get(normalized);
347
+ if (exactMatch) {
348
+ return exactMatch;
349
+ }
350
+
351
+ if (
352
+ normalized.includes("gpt-5.1-codex-max") ||
353
+ normalized.includes("gpt 5.1 codex max") ||
354
+ normalized.includes("codex-max")
355
+ ) {
356
+ return "gpt-5.1-codex-max";
357
+ }
358
+ if (
359
+ normalized.includes("gpt-5.1-codex-mini") ||
360
+ normalized.includes("gpt 5.1 codex mini") ||
361
+ normalized.includes("gpt-5-codex-mini") ||
362
+ normalized.includes("gpt 5 codex mini") ||
363
+ normalized.includes("codex-mini-latest")
364
+ ) {
365
+ return "gpt-5.1-codex-mini";
366
+ }
367
+ if (
368
+ normalized.includes("gpt-5.3-codex-spark") ||
369
+ normalized.includes("gpt 5.3 codex spark") ||
370
+ normalized.includes("gpt-5.3-codex") ||
371
+ normalized.includes("gpt 5.3 codex") ||
372
+ normalized.includes("gpt-5.2-codex") ||
373
+ normalized.includes("gpt 5.2 codex") ||
374
+ normalized.includes("gpt-5.1-codex") ||
375
+ normalized.includes("gpt 5.1 codex") ||
376
+ normalized.includes("gpt-5-codex") ||
377
+ normalized.includes("gpt 5 codex") ||
378
+ normalized === "codex"
379
+ ) {
380
+ return "gpt-5-codex";
381
+ }
382
+ if (normalized.includes("gpt-5.4-pro") || normalized.includes("gpt 5.4 pro")) {
383
+ return "gpt-5.4-pro";
384
+ }
385
+ if (normalized.includes("gpt-5.2-pro") || normalized.includes("gpt 5.2 pro")) {
386
+ return "gpt-5.2-pro";
387
+ }
388
+ if (normalized.includes("gpt-5-pro") || normalized.includes("gpt 5 pro")) {
389
+ return "gpt-5-pro";
390
+ }
391
+ if (
392
+ normalized.includes("gpt-5.4-mini") ||
393
+ normalized.includes("gpt 5.4 mini") ||
394
+ normalized.includes("gpt-5-mini") ||
395
+ normalized.includes("gpt 5 mini")
396
+ ) {
397
+ return "gpt-5-mini";
398
+ }
399
+ if (
400
+ normalized.includes("gpt-5.4-nano") ||
401
+ normalized.includes("gpt 5.4 nano") ||
402
+ normalized.includes("gpt-5-nano") ||
403
+ normalized.includes("gpt 5 nano")
404
+ ) {
405
+ return "gpt-5-nano";
406
+ }
407
+ if (normalized.includes("gpt-5.4") || normalized.includes("gpt 5.4")) {
408
+ return "gpt-5.4";
409
+ }
410
+ if (normalized.includes("gpt-5.2") || normalized.includes("gpt 5.2")) {
411
+ return "gpt-5.2";
412
+ }
413
+ if (
414
+ normalized.includes("gpt-5.1-chat-latest") ||
415
+ normalized.includes("gpt-5.1") ||
416
+ normalized.includes("gpt 5.1")
417
+ ) {
418
+ return "gpt-5.1";
419
+ }
420
+ if (
421
+ normalized === "gpt-5-chat-latest" ||
422
+ normalized === "gpt-5" ||
423
+ normalized.includes("gpt 5")
424
+ ) {
425
+ return "gpt-5";
426
+ }
427
+ return "";
428
+ }
429
+
430
+ function coerceReasoningEffortForModel(model, effort) {
431
+ if (typeof effort !== "string") return effort;
432
+ const normalizedEffort = effort.trim().toLowerCase();
433
+ if (!KNOWN_REASONING_EFFORTS.has(normalizedEffort)) {
434
+ return effort;
435
+ }
436
+
437
+ const normalizedModel = normalizeRequestedModel(model);
438
+ const supportedEfforts =
439
+ SUPPORTED_REASONING_EFFORTS_BY_MODEL[normalizedModel] ?? null;
440
+ if (!supportedEfforts || supportedEfforts.includes(normalizedEffort)) {
441
+ return normalizedEffort;
442
+ }
443
+
444
+ const fallbackOrder = REASONING_FALLBACKS[normalizedEffort] ?? [normalizedEffort];
445
+ for (const candidate of fallbackOrder) {
446
+ if (supportedEfforts.includes(candidate)) {
447
+ return candidate;
448
+ }
449
+ }
450
+
451
+ return normalizedEffort;
452
+ }
453
+
454
+ function extractRequestedModel(args) {
455
+ for (let i = 0; i < args.length; i += 1) {
456
+ const arg = args[i];
457
+ if (arg === "--model") {
458
+ const next = args[i + 1];
459
+ if (typeof next === "string" && next.trim().length > 0) {
460
+ return next.trim();
461
+ }
462
+ continue;
463
+ }
464
+ if (typeof arg === "string" && arg.startsWith("--model=")) {
465
+ const value = arg.slice("--model=".length).trim();
466
+ if (value.length > 0) {
467
+ return value;
468
+ }
469
+ }
470
+ }
471
+ return null;
472
+ }
473
+
474
+ function parseConfigAssignment(value) {
475
+ if (typeof value !== "string") return null;
476
+ const separatorIndex = value.indexOf("=");
477
+ if (separatorIndex < 0) return null;
478
+ return {
479
+ key: value.slice(0, separatorIndex).trim(),
480
+ value: value.slice(separatorIndex + 1).trim(),
481
+ };
482
+ }
483
+
484
+ function parseQuotedValue(value) {
485
+ if (typeof value !== "string") {
486
+ return { quote: '"', inner: "" };
487
+ }
488
+ const trimmed = value.trim();
489
+ const first = trimmed[0];
490
+ const last = trimmed.at(-1);
491
+ if ((first === '"' || first === "'") && last === first) {
492
+ return {
493
+ quote: first,
494
+ inner: trimmed.slice(1, -1),
495
+ };
496
+ }
497
+ return { quote: '"', inner: trimmed };
498
+ }
499
+
500
+ function rewriteReasoningConfigAssignment(assignment, requestedModel) {
501
+ const parsed = parseConfigAssignment(assignment);
502
+ if (!parsed || parsed.key !== "model_reasoning_effort") {
503
+ return assignment;
504
+ }
505
+
506
+ const { quote, inner } = parseQuotedValue(parsed.value);
507
+ const coercedEffort = coerceReasoningEffortForModel(requestedModel, inner);
508
+ if (coercedEffort === inner) {
509
+ return assignment;
510
+ }
511
+
512
+ return `${parsed.key}=${quote}${coercedEffort}${quote}`;
513
+ }
514
+
515
+ function rewriteReasoningConfigArgs(rawArgs) {
516
+ const requestedModel = extractRequestedModel(rawArgs);
517
+ if (!requestedModel) {
518
+ return {
519
+ args: [...rawArgs],
520
+ requestedModel: null,
521
+ };
522
+ }
523
+
524
+ const nextArgs = [...rawArgs];
525
+ for (let i = 0; i < nextArgs.length; i += 1) {
526
+ const arg = nextArgs[i];
527
+ if ((arg === "-c" || arg === "--config") && typeof nextArgs[i + 1] === "string") {
528
+ nextArgs[i + 1] = rewriteReasoningConfigAssignment(
529
+ nextArgs[i + 1],
530
+ requestedModel,
531
+ );
532
+ i += 1;
533
+ continue;
534
+ }
535
+ if (typeof arg === "string" && arg.startsWith("--config=")) {
536
+ const assignment = arg.slice("--config=".length);
537
+ nextArgs[i] = `--config=${rewriteReasoningConfigAssignment(
538
+ assignment,
539
+ requestedModel,
540
+ )}`;
541
+ }
542
+ }
543
+
544
+ return {
545
+ args: nextArgs,
546
+ requestedModel,
547
+ };
548
+ }
549
+
550
+ function resolveCodexHomeDir(env = process.env) {
551
+ const override = (env.CODEX_HOME ?? "").trim();
552
+ if (override.length > 0) return override;
553
+ if (process.platform === "win32") {
554
+ const homeDir = resolveWindowsUserHomeDir();
555
+ if (homeDir) {
556
+ return join(homeDir, ".codex");
557
+ }
558
+ }
559
+ return join(env.HOME ?? homedir(), ".codex");
560
+ }
561
+
562
+ function ensureTrailingNewline(value) {
563
+ return value.endsWith("\n") ? value : `${value}\n`;
564
+ }
565
+
566
+ function captureShadowHomeState(filePath, options = {}) {
567
+ try {
568
+ if (!existsSync(filePath)) {
569
+ return { exists: false, content: null };
570
+ }
571
+ if (options.rethrowRetryableReadErrors) {
572
+ maybeThrowSimulatedShadowHomePreflightReadBusyError();
573
+ }
574
+ return {
575
+ exists: true,
576
+ content: readFileSync(filePath, "utf8"),
577
+ };
578
+ } catch (error) {
579
+ if (options.rethrowRetryableReadErrors && isRetryableShadowHomeCleanupError(error)) {
580
+ throw error;
581
+ }
582
+ return { exists: true, content: null, unreadable: true };
583
+ }
584
+ }
585
+
586
+ function shadowHomeStateMatches(left, right) {
587
+ return (
588
+ left.exists === right.exists &&
589
+ left.content === right.content &&
590
+ Boolean(left.unreadable) === Boolean(right.unreadable)
591
+ );
592
+ }
593
+
594
+ function syncShadowHomeStateFile(
595
+ sourcePath,
596
+ destinationPath,
597
+ expectedDestinationState,
598
+ ) {
599
+ const tempPath = join(
600
+ dirname(destinationPath),
601
+ `.${basename(destinationPath)}.codex-multi-auth-sync-${process.pid}.tmp`,
602
+ );
603
+ try {
604
+ mkdirSync(dirname(destinationPath), { recursive: true });
605
+ copyFileSync(sourcePath, tempPath);
606
+ renameFileWithRetry(tempPath, destinationPath, expectedDestinationState);
607
+ } catch (error) {
608
+ try {
609
+ rmSync(tempPath, { force: true });
610
+ } catch {
611
+ // Best-effort cleanup only.
612
+ }
613
+ throw error;
614
+ }
615
+ }
616
+
617
+ function rewriteConfigTomlReasoningEffort(rawConfig, requestedModel) {
618
+ const lineEnding = rawConfig.includes("\r\n") ? "\r\n" : "\n";
619
+ let changed = false;
620
+ const nextLines = rawConfig.split(/\r?\n/).map((line) => {
621
+ const quotedMatch = line.match(
622
+ /^(\s*model_reasoning_effort\s*=\s*)(["'])([^"']+)(\2.*)$/,
623
+ );
624
+ const bareMatch = quotedMatch
625
+ ? null
626
+ : line.match(
627
+ /^(\s*model_reasoning_effort\s*=\s*)([^\s#]+)(\s*(?:#.*)?)$/,
628
+ );
629
+ if (!quotedMatch && !bareMatch) return line;
630
+
631
+ const prefix = quotedMatch?.[1] ?? bareMatch?.[1] ?? "";
632
+ const openingQuote = quotedMatch?.[2] ?? "";
633
+ const currentEffort = quotedMatch?.[3] ?? bareMatch?.[2] ?? "";
634
+ const suffix = quotedMatch?.[4] ?? bareMatch?.[3] ?? "";
635
+ const coercedEffort = coerceReasoningEffortForModel(
636
+ requestedModel,
637
+ currentEffort,
638
+ );
639
+ if (coercedEffort === currentEffort) {
640
+ return line;
641
+ }
642
+
643
+ changed = true;
644
+ return quotedMatch
645
+ ? `${prefix}${openingQuote}${coercedEffort}${suffix}`
646
+ : `${prefix}${coercedEffort}${suffix}`;
647
+ });
648
+
649
+ if (!changed) {
650
+ return rawConfig;
651
+ }
652
+
653
+ return ensureTrailingNewline(nextLines.join(lineEnding));
654
+ }
655
+
656
+ function resolveOriginalMultiAuthDir(env) {
657
+ const explicit = (env.CODEX_MULTI_AUTH_DIR ?? "").trim();
658
+ if (explicit.length > 0) {
659
+ return explicit;
660
+ }
661
+ return undefined;
662
+ }
663
+
664
+ function createCompatibilityCodexHome(
665
+ processedArgs,
666
+ requestedModel,
667
+ baseEnv = process.env,
668
+ ) {
669
+ if (!requestedModel) {
670
+ return { args: processedArgs, env: baseEnv, cleanup: undefined };
671
+ }
672
+
673
+ const originalCodexHome = resolveCodexHomeDir(baseEnv);
674
+ const configPath = join(originalCodexHome, "config.toml");
675
+ if (!existsSync(configPath)) {
676
+ return { args: processedArgs, env: baseEnv, cleanup: undefined };
677
+ }
678
+ const originalShadowHomeState = new Map(
679
+ SHADOW_HOME_STATE_FILES.map((name) => [
680
+ name,
681
+ captureShadowHomeState(join(originalCodexHome, name)),
682
+ ]),
683
+ );
684
+
685
+ const rawConfig = readFileSync(configPath, "utf8");
686
+ const compatConfig = rewriteConfigTomlReasoningEffort(
687
+ rawConfig,
688
+ requestedModel,
689
+ );
690
+ if (compatConfig === rawConfig) {
691
+ return { args: processedArgs, env: baseEnv, cleanup: undefined };
692
+ }
693
+
694
+ const shadowCodexHome = mkdtempSync(join(tmpdir(), "codex-multi-auth-home-"));
695
+ const cleanup = () => {
696
+ try {
697
+ removeDirectoryWithRetry(shadowCodexHome);
698
+ } catch {
699
+ // Best-effort cleanup only.
700
+ }
701
+ };
702
+ const tightenShadowHomePermissions = (path) => {
703
+ try {
704
+ chmodSync(path, 0o600);
705
+ } catch {
706
+ // Best-effort only; permission semantics vary by platform.
707
+ }
708
+ };
709
+ const syncShadowHomeStateBack = () => {
710
+ for (const name of SHADOW_HOME_STATE_FILES) {
711
+ const shadowPath = join(shadowCodexHome, name);
712
+ const shadowState = captureShadowHomeState(shadowPath);
713
+ if (!shadowState.exists || shadowState.unreadable) {
714
+ continue;
715
+ }
716
+
717
+ try {
718
+ const originalPath = join(originalCodexHome, name);
719
+ const originalSnapshot =
720
+ originalShadowHomeState.get(name) ?? { exists: false, content: null };
721
+ const currentOriginalState = captureShadowHomeState(originalPath);
722
+ if (!shadowHomeStateMatches(currentOriginalState, originalSnapshot)) {
723
+ continue;
724
+ }
725
+ if (shadowHomeStateMatches(shadowState, originalSnapshot)) {
726
+ continue;
727
+ }
728
+ syncShadowHomeStateFile(shadowPath, originalPath, originalSnapshot);
729
+ tightenShadowHomePermissions(originalPath);
730
+ } catch {
731
+ // Best-effort only; runtime auth refreshes should not fail cleanup.
732
+ }
733
+ }
734
+ };
735
+ try {
736
+ const compatConfigPath = join(shadowCodexHome, "config.toml");
737
+ writeFileSync(compatConfigPath, compatConfig, "utf8");
738
+ tightenShadowHomePermissions(compatConfigPath);
739
+ for (const name of SHADOW_HOME_STATE_FILES) {
740
+ const sourcePath = join(originalCodexHome, name);
741
+ if (existsSync(sourcePath)) {
742
+ const destinationPath = join(shadowCodexHome, name);
743
+ copyFileSync(sourcePath, destinationPath);
744
+ tightenShadowHomePermissions(destinationPath);
745
+ }
746
+ }
747
+ } catch (error) {
748
+ cleanup();
749
+ throw error;
750
+ }
751
+ const cleanupWithSync = () => {
752
+ syncShadowHomeStateBack();
753
+ cleanup();
754
+ };
755
+
756
+ const forwardedEnv = {
757
+ ...baseEnv,
758
+ CODEX_HOME: shadowCodexHome,
759
+ };
760
+ const originalMultiAuthDir = resolveOriginalMultiAuthDir(baseEnv);
761
+ if (originalMultiAuthDir) {
762
+ forwardedEnv.CODEX_MULTI_AUTH_DIR = originalMultiAuthDir;
763
+ }
764
+
765
+ return {
766
+ args: processedArgs,
767
+ env: forwardedEnv,
768
+ cleanup: cleanupWithSync,
769
+ };
770
+ }
771
+
171
772
  function buildForwardArgs(rawArgs) {
773
+ const { args: compatibilityArgs, requestedModel } = rewriteReasoningConfigArgs(rawArgs);
172
774
  const forceFileAuthStore = (process.env.CODEX_MULTI_AUTH_FORCE_FILE_AUTH_STORE ?? "1").trim() !== "0";
173
- if (!forceFileAuthStore) return [...rawArgs];
174
- if (hasCliAuthCredentialsStoreOverride(rawArgs)) return [...rawArgs];
775
+ if (!forceFileAuthStore) {
776
+ return { args: compatibilityArgs, requestedModel };
777
+ }
778
+ if (hasCliAuthCredentialsStoreOverride(compatibilityArgs)) {
779
+ return { args: compatibilityArgs, requestedModel };
780
+ }
175
781
 
176
- return [
177
- ...rawArgs,
178
- "-c",
179
- 'cli_auth_credentials_store="file"',
180
- ];
782
+ return {
783
+ args: [
784
+ ...compatibilityArgs,
785
+ "-c",
786
+ 'cli_auth_credentials_store="file"',
787
+ ],
788
+ requestedModel,
789
+ };
181
790
  }
182
791
 
183
792
  function normalizeExitCode(value) {
@@ -197,7 +806,7 @@ const POWERSHELL_PROFILE_MARKER_END = "# <<< codex-multi-auth shell guard <<<";
197
806
 
198
807
  function shouldInstallWindowsBatchShimGuard() {
199
808
  if (process.platform !== "win32") return false;
200
- const override = (process.env.CODEX_MULTI_AUTH_WINDOWS_BATCH_SHIM_GUARD ?? "1").trim();
809
+ const override = (process.env.CODEX_MULTI_AUTH_WINDOWS_BATCH_SHIM_GUARD ?? "0").trim();
201
810
  return override !== "0";
202
811
  }
203
812
 
@@ -370,7 +979,7 @@ function ensureWindowsShellShim(filePath, desiredContent, options = {}) {
370
979
 
371
980
  function shouldInstallPowerShellProfileGuard() {
372
981
  if (process.platform !== "win32") return false;
373
- const override = (process.env.CODEX_MULTI_AUTH_PWSH_PROFILE_GUARD ?? "1").trim();
982
+ const override = (process.env.CODEX_MULTI_AUTH_PWSH_PROFILE_GUARD ?? "0").trim();
374
983
  return override !== "0";
375
984
  }
376
985
 
@@ -455,7 +1064,9 @@ function ensurePowerShellProfileGuard(shimDirectory) {
455
1064
  }
456
1065
 
457
1066
  function ensureWindowsShellShimGuards() {
458
- if (!shouldInstallWindowsBatchShimGuard()) return;
1067
+ const shouldInstallBatchGuard = shouldInstallWindowsBatchShimGuard();
1068
+ const shouldInstallProfileGuard = shouldInstallPowerShellProfileGuard();
1069
+ if (!shouldInstallBatchGuard && !shouldInstallProfileGuard) return;
459
1070
  const shimDirectory = resolveWindowsShimDirectoryFromPath();
460
1071
  if (!shimDirectory) return;
461
1072
 
@@ -464,23 +1075,31 @@ function ensureWindowsShellShimGuards() {
464
1075
 
465
1076
  const overwriteCustomShim =
466
1077
  (process.env.CODEX_MULTI_AUTH_OVERWRITE_CUSTOM_BATCH_SHIM ?? "0").trim() === "1";
467
- const installedBatch = ensureWindowsShellShim(
468
- join(shimDirectory, "codex.bat"),
469
- buildWindowsBatchShimContent(),
470
- { overwriteCustomShim },
471
- );
472
- const installedCmd = ensureWindowsShellShim(
473
- join(shimDirectory, "codex.cmd"),
474
- buildWindowsCmdShimContent(),
475
- { overwriteCustomShim },
476
- );
477
- const installedPs1 = ensureWindowsShellShim(
478
- join(shimDirectory, "codex.ps1"),
479
- buildWindowsPowerShellShimContent(),
480
- { overwriteCustomShim },
481
- );
1078
+ const installedBatch = shouldInstallBatchGuard
1079
+ ? ensureWindowsShellShim(
1080
+ join(shimDirectory, "codex.bat"),
1081
+ buildWindowsBatchShimContent(),
1082
+ { overwriteCustomShim },
1083
+ )
1084
+ : false;
1085
+ const installedCmd = shouldInstallBatchGuard
1086
+ ? ensureWindowsShellShim(
1087
+ join(shimDirectory, "codex.cmd"),
1088
+ buildWindowsCmdShimContent(),
1089
+ { overwriteCustomShim },
1090
+ )
1091
+ : false;
1092
+ const installedPs1 = shouldInstallBatchGuard
1093
+ ? ensureWindowsShellShim(
1094
+ join(shimDirectory, "codex.ps1"),
1095
+ buildWindowsPowerShellShimContent(),
1096
+ { overwriteCustomShim },
1097
+ )
1098
+ : false;
482
1099
  const installedAny = installedBatch || installedCmd || installedPs1;
483
- const installedProfileGuard = ensurePowerShellProfileGuard(shimDirectory);
1100
+ const installedProfileGuard = shouldInstallProfileGuard
1101
+ ? ensurePowerShellProfileGuard(shimDirectory)
1102
+ : false;
484
1103
  if (installedAny || installedProfileGuard) {
485
1104
  console.error(
486
1105
  "codex-multi-auth: installed Windows shell guards to keep multi-auth routing after codex npm updates.",
@@ -525,8 +1144,17 @@ async function main() {
525
1144
  }
526
1145
 
527
1146
  await autoSyncManagerActiveSelectionIfEnabled();
528
- const forwardArgs = buildForwardArgs(rawArgs);
529
- return forwardToRealCodex(realCodexBin, forwardArgs);
1147
+ const { args: forwardArgs, requestedModel } = buildForwardArgs(rawArgs);
1148
+ const compatibility = createCompatibilityCodexHome(
1149
+ forwardArgs,
1150
+ requestedModel,
1151
+ );
1152
+ return forwardToRealCodex(
1153
+ realCodexBin,
1154
+ compatibility.args,
1155
+ compatibility.env,
1156
+ compatibility.cleanup,
1157
+ );
530
1158
  }
531
1159
 
532
1160
  const exitCode = await main();