codex-multi-auth 2.19.1 → 2.21.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.
Files changed (79) hide show
  1. package/.codex-plugin/plugin.json +9 -9
  2. package/LICENSE +21 -21
  3. package/README.md +149 -148
  4. package/assets/codex-multi-auth-icon.svg +39 -39
  5. package/assets/readme-hero.svg +31 -31
  6. package/config/README.md +31 -31
  7. package/config/codex-legacy.json +1021 -1021
  8. package/config/codex-modern.json +347 -347
  9. package/config/minimal-codex.json +15 -15
  10. package/dist/index.js +1 -1
  11. package/dist/lib/accounts/rate-limits.js +5 -0
  12. package/dist/lib/accounts.js +5 -4
  13. package/dist/lib/auth/account-access.js +2 -2
  14. package/dist/lib/codex-cli/version.d.ts +22 -0
  15. package/dist/lib/codex-cli/version.js +78 -0
  16. package/dist/lib/codex-manager/commands/limits.d.ts +2 -0
  17. package/dist/lib/codex-manager/commands/limits.js +31 -0
  18. package/dist/lib/codex-manager/commands/status.js +1 -1
  19. package/dist/lib/codex-manager.js +2 -0
  20. package/dist/lib/constants.d.ts +2 -0
  21. package/dist/lib/constants.js +2 -0
  22. package/dist/lib/context-budget/model-context-windows.d.ts +1 -1
  23. package/dist/lib/context-budget/model-context-windows.js +4 -0
  24. package/dist/lib/context-budget-response.js +8 -8
  25. package/dist/lib/context-overflow.js +10 -10
  26. package/dist/lib/luna-reserve.d.ts +26 -0
  27. package/dist/lib/luna-reserve.js +68 -0
  28. package/dist/lib/oauth-success.html +125 -125
  29. package/dist/lib/policy/runtime-policy.js +14 -0
  30. package/dist/lib/prompts/codex-host-bridge.js +138 -138
  31. package/dist/lib/prompts/codex.js +79 -79
  32. package/dist/lib/request/helpers/model-map.d.ts +1 -1
  33. package/dist/lib/request/helpers/model-map.js +12 -0
  34. package/dist/lib/request/response-outcome.d.ts +1 -1
  35. package/dist/lib/request/response-outcome.js +4 -1
  36. package/dist/lib/runtime/account-status.js +2 -1
  37. package/dist/lib/runtime/luna-reserve-usage.d.ts +9 -0
  38. package/dist/lib/runtime/luna-reserve-usage.js +68 -0
  39. package/dist/lib/runtime/model-discovery-status.d.ts +6 -2
  40. package/dist/lib/runtime/model-discovery-status.js +13 -5
  41. package/dist/lib/runtime/reset-credit-routing.js +4 -1
  42. package/dist/lib/runtime/rotation-server-types.d.ts +2 -0
  43. package/dist/lib/runtime/runtime-observability.d.ts +2 -0
  44. package/dist/lib/runtime/runtime-observability.js +1 -0
  45. package/dist/lib/runtime-rotation-proxy.js +200 -32
  46. package/dist/lib/usage/pricing.d.ts +1 -1
  47. package/dist/lib/usage/pricing.js +4 -0
  48. package/dist/lib/usage/usage-extraction.d.ts +2 -0
  49. package/dist/lib/usage/usage-extraction.js +25 -1
  50. package/dist/lib/utils.d.ts +1 -1
  51. package/dist/lib/utils.js +1 -1
  52. package/node_modules/@codex-ai/plugin/dist/index.d.ts +2 -2
  53. package/node_modules/@codex-ai/plugin/dist/index.js +2 -2
  54. package/node_modules/@codex-ai/plugin/dist/tool.d.ts +42 -42
  55. package/node_modules/@codex-ai/plugin/dist/tool.js +29 -29
  56. package/node_modules/@codex-ai/plugin/package.json +9 -9
  57. package/node_modules/@codex-ai/sdk/dist/index.d.ts +4 -4
  58. package/node_modules/@codex-ai/sdk/dist/index.js +2 -2
  59. package/node_modules/@codex-ai/sdk/package.json +8 -8
  60. package/package.json +207 -207
  61. package/scripts/codex-app-launcher.js +653 -653
  62. package/scripts/codex-app-router.js +374 -373
  63. package/scripts/codex-bin-resolver.js +332 -332
  64. package/scripts/codex-multi-auth.js +65 -65
  65. package/scripts/codex-routing.js +62 -62
  66. package/scripts/codex.js +7006 -7005
  67. package/scripts/install-codex-auth-utils.js +213 -213
  68. package/scripts/mcodex.js +367 -367
  69. package/scripts/postinstall.js +114 -114
  70. package/scripts/preuninstall.js +265 -265
  71. package/skills/codex-auth-setup/SKILL.md +73 -73
  72. package/vendor/codex-ai-plugin/dist/index.d.ts +2 -2
  73. package/vendor/codex-ai-plugin/dist/index.js +2 -2
  74. package/vendor/codex-ai-plugin/dist/tool.d.ts +42 -42
  75. package/vendor/codex-ai-plugin/dist/tool.js +29 -29
  76. package/vendor/codex-ai-plugin/package.json +9 -9
  77. package/vendor/codex-ai-sdk/dist/index.d.ts +4 -4
  78. package/vendor/codex-ai-sdk/dist/index.js +2 -2
  79. package/vendor/codex-ai-sdk/package.json +8 -8
@@ -1,332 +1,332 @@
1
- import { spawnSync } from "node:child_process";
2
- import { existsSync, realpathSync } from "node:fs";
3
- import { createRequire } from "node:module";
4
- import { basename, dirname, extname, isAbsolute, join, posix, relative, win32 } from "node:path";
5
- import process from "node:process";
6
- import { fileURLToPath } from "node:url";
7
-
8
- function isJavaScriptEntryPath(candidate) {
9
- const extension = extname(candidate).toLowerCase();
10
- return extension === ".js" || extension === ".mjs" || extension === ".cjs";
11
- }
12
-
13
- function createResolvedCodexBin(path) {
14
- return {
15
- path,
16
- launchWithNode: isJavaScriptEntryPath(path),
17
- };
18
- }
19
-
20
- function normalizeResolvedPath(candidatePath, realpathSyncImpl) {
21
- try {
22
- return realpathSyncImpl(candidatePath);
23
- } catch {
24
- return candidatePath;
25
- }
26
- }
27
-
28
- // Defense-in-depth self-recursion guard. The exact-realpath check only blocks
29
- // the wrapper's own codex.js (the POSIX symlink case). If the wrapper were ever
30
- // exposed as a native codex/codex.exe sitting alongside it, that exact-path
31
- // check would miss it. Skipping any candidate that resolves inside the
32
- // wrapper's own directory closes that latent self-loop.
33
- function isWithinDirectory(candidatePath, directoryPath, realpathSyncImpl) {
34
- if (!directoryPath) return false;
35
- const resolvedCandidate = normalizeResolvedPath(candidatePath, realpathSyncImpl);
36
- const relativePath = relative(directoryPath, resolvedCandidate);
37
- return (
38
- relativePath === "" ||
39
- (!relativePath.startsWith("..") && !isAbsolute(relativePath))
40
- );
41
- }
42
-
43
- function resolveWrapperScriptPath(moduleUrl, realpathSyncImpl) {
44
- return normalizeResolvedPath(fileURLToPath(moduleUrl), realpathSyncImpl);
45
- }
46
-
47
- const DEFAULT_WRAPPER_MODULE_URL = new URL("./codex.js", import.meta.url).href;
48
-
49
- function defaultResolvePackageBin(moduleUrl) {
50
- try {
51
- const require = createRequire(moduleUrl);
52
- return require.resolve("@openai/codex/bin/codex.js");
53
- } catch {
54
- return null;
55
- }
56
- }
57
-
58
- // spawn() without a shell cannot run these on Windows: `.cmd`/`.bat` are
59
- // EINVAL, `.ps1` is not an executable image, and an extensionless file
60
- // (npm's `#!/bin/sh` shim) is ENOENT even when it is a real PE image. Going through cmd.exe instead would put every
61
- // forwarded argument through cmd's quoting rules.
62
- export function isWindowsShimPath(candidatePath) {
63
- const extension = win32.extname(candidatePath).toLowerCase();
64
- return extension === "" || extension === ".cmd" || extension === ".bat" || extension === ".ps1";
65
- }
66
-
67
- // npm's Windows shims sit in the prefix dir and run
68
- // `<prefix>\node_modules\@openai\codex\bin\codex.js`.
69
- export function resolveWindowsShimPackageEntry(shimPath, existsSyncImpl = existsSync) {
70
- const entry = win32.join(
71
- win32.dirname(shimPath),
72
- "node_modules",
73
- "@openai",
74
- "codex",
75
- "bin",
76
- "codex.js",
77
- );
78
- return existsSyncImpl(entry) ? entry : null;
79
- }
80
-
81
- function resolveWindowsCmdPath(env) {
82
- const comSpec = (env.ComSpec ?? env.COMSPEC ?? "").trim();
83
- if (comSpec.length > 0) return comSpec;
84
-
85
- const systemRoot = (env.SystemRoot ?? env.SYSTEMROOT ?? "").trim();
86
- if (systemRoot.length > 0) {
87
- return `${systemRoot.replace(/[\\/]+$/, "")}\\System32\\cmd.exe`;
88
- }
89
-
90
- return "cmd.exe";
91
- }
92
-
93
- // win32.isAbsolute("\bin\codex.exe") is true for a root-relative path with no
94
- // drive — it resolves against the CURRENT drive, so the existence check would
95
- // probe (and a forwarder would exec) whatever file lives at "<cwd's
96
- // drive>\bin\codex.exe". CWE-426: a binary override must be drive-qualified
97
- // (C:\...) or a UNC/device path (\\server\share\..., \\?\...), never a bare
98
- // "\" or "/" root. POSIX keeps plain absolute-path semantics.
99
- export function isFullyQualifiedBinOverride(candidatePath, platform) {
100
- if (platform === "win32") {
101
- if (!win32.isAbsolute(candidatePath)) return false;
102
- const root = win32.parse(candidatePath).root;
103
- return root !== "\\" && root !== "/";
104
- }
105
- return posix.isAbsolute(candidatePath);
106
- }
107
-
108
- export function splitPathEntries(pathValue, platform = process.platform) {
109
- if (typeof pathValue !== "string" || pathValue.trim().length === 0) {
110
- return [];
111
- }
112
- return pathValue
113
- .split(platform === "win32" ? ";" : ":")
114
- .map((entry) => entry.trim())
115
- .filter((entry) => entry.length > 0);
116
- }
117
-
118
- function resolvePathExecutableName(platform) {
119
- return platform === "win32" ? "codex.exe" : "codex";
120
- }
121
-
122
- function resolveCandidateExecutableNames(platform) {
123
- if (platform !== "win32") {
124
- return [resolvePathExecutableName(platform)];
125
- }
126
- // npm on Windows puts `codex.cmd` plus an extensionless `#!/bin/sh` shim in
127
- // the prefix dir and the package under `<prefix>\node_modules`. Neither shim
128
- // can be spawned without a shell (`.cmd` is EINVAL, the sh script ENOENT),
129
- // so launch the package entry the shims point at before falling back to an
130
- // extensionless `codex`.
131
- return ["codex.exe", win32.join("node_modules", "@openai", "codex", "bin", "codex.js"), "codex"];
132
- }
133
-
134
- function resolveCodexExecutableFromPath(
135
- pathEntries,
136
- platform,
137
- existsSyncImpl,
138
- selfScriptPath,
139
- realpathSyncImpl,
140
- ) {
141
- for (const entry of pathEntries) {
142
- for (const executableName of resolveCandidateExecutableNames(platform)) {
143
- const candidate = (platform === "win32" ? win32 : posix).join(entry, executableName);
144
- if (!existsSyncImpl(candidate)) {
145
- continue;
146
- }
147
- if (
148
- selfScriptPath &&
149
- normalizeResolvedPath(candidate, realpathSyncImpl) === selfScriptPath
150
- ) {
151
- continue;
152
- }
153
- if (
154
- selfScriptPath &&
155
- isWithinDirectory(candidate, dirname(selfScriptPath), realpathSyncImpl)
156
- ) {
157
- continue;
158
- }
159
- return candidate;
160
- }
161
- }
162
- return null;
163
- }
164
-
165
- function normalizeWhereOutput(stdout) {
166
- return stdout
167
- .split(/\r?\n/)
168
- .map((line) => line.trim())
169
- .filter((line) => line.length > 0);
170
- }
171
-
172
- function resolveCodexExecutableFromSystemPath(
173
- env,
174
- platform,
175
- spawnSyncImpl,
176
- existsSyncImpl,
177
- selfScriptPath,
178
- realpathSyncImpl,
179
- ) {
180
- const pathEntries = splitPathEntries(env.PATH ?? env.Path ?? "", platform);
181
- const fromEnvPath = resolveCodexExecutableFromPath(
182
- pathEntries,
183
- platform,
184
- existsSyncImpl,
185
- selfScriptPath,
186
- realpathSyncImpl,
187
- );
188
- if (fromEnvPath) {
189
- return fromEnvPath;
190
- }
191
-
192
- try {
193
- const lookupResult =
194
- platform === "win32"
195
- ? spawnSyncImpl(resolveWindowsCmdPath(env), ["/d", "/s", "/c", "where codex"], {
196
- encoding: "utf8",
197
- env,
198
- stdio: ["ignore", "pipe", "ignore"],
199
- timeout: 5000,
200
- windowsHide: true,
201
- })
202
- : spawnSyncImpl("which", ["codex"], {
203
- encoding: "utf8",
204
- env,
205
- stdio: ["ignore", "pipe", "ignore"],
206
- timeout: 5000,
207
- });
208
- if (lookupResult.status !== 0) {
209
- return null;
210
- }
211
- for (const candidate of normalizeWhereOutput(lookupResult.stdout)) {
212
- if (!existsSyncImpl(candidate)) {
213
- continue;
214
- }
215
- if (
216
- selfScriptPath &&
217
- normalizeResolvedPath(candidate, realpathSyncImpl) === selfScriptPath
218
- ) {
219
- continue;
220
- }
221
- if (
222
- selfScriptPath &&
223
- isWithinDirectory(candidate, dirname(selfScriptPath), realpathSyncImpl)
224
- ) {
225
- continue;
226
- }
227
- const fileName = basename(candidate).toLowerCase();
228
- if (fileName === "codex" || fileName === "codex.exe") {
229
- return candidate;
230
- }
231
- }
232
- } catch {
233
- // Ignore and fall through.
234
- }
235
-
236
- return null;
237
- }
238
-
239
- export function resolveRealCodexBin(options = {}) {
240
- const {
241
- env = process.env,
242
- argv = process.argv,
243
- platform = process.platform,
244
- moduleUrl = DEFAULT_WRAPPER_MODULE_URL,
245
- existsSyncImpl = existsSync,
246
- realpathSyncImpl = realpathSync,
247
- spawnSyncImpl = spawnSync,
248
- resolvePackageBin = defaultResolvePackageBin,
249
- } = options;
250
-
251
- const override = (env.CODEX_MULTI_AUTH_REAL_CODEX_BIN ?? "").trim();
252
- if (override.length > 0) {
253
- // The override is the binary every forwarded command execs, so require a
254
- // fully-qualified absolute path: a relative one resolves against the
255
- // caller's cwd, and on Windows a root-relative one resolves against the
256
- // cwd's DRIVE — either could silently exec a planted file.
257
- if (!isFullyQualifiedBinOverride(override, platform) || !existsSyncImpl(override)) return null;
258
- if (platform === "win32" && isWindowsShimPath(override)) {
259
- const entry = resolveWindowsShimPackageEntry(override, existsSyncImpl);
260
- return entry ? createResolvedCodexBin(entry) : null;
261
- }
262
- return createResolvedCodexBin(override);
263
- }
264
-
265
- const resolved = resolvePackageBin(moduleUrl);
266
- if (typeof resolved === "string" && resolved.length > 0 && existsSyncImpl(resolved)) {
267
- return createResolvedCodexBin(resolved);
268
- }
269
-
270
- const searchRoots = [];
271
- const scriptDir = dirname(fileURLToPath(moduleUrl));
272
- searchRoots.push(join(scriptDir, "..", ".."));
273
-
274
- const invokedScript = argv[1];
275
- if (typeof invokedScript === "string" && invokedScript.length > 0) {
276
- searchRoots.push(join(dirname(invokedScript), "..", ".."));
277
- }
278
-
279
- const npmPrefix = (env.npm_config_prefix ?? env.PREFIX ?? "").trim();
280
- if (npmPrefix.length > 0) {
281
- searchRoots.push(join(npmPrefix, "node_modules"));
282
- searchRoots.push(join(npmPrefix, "lib", "node_modules"));
283
- }
284
-
285
- for (const root of searchRoots) {
286
- const candidate = join(root, "@openai", "codex", "bin", "codex.js");
287
- if (existsSyncImpl(candidate)) return createResolvedCodexBin(candidate);
288
- }
289
-
290
- try {
291
- const rootResult =
292
- platform === "win32"
293
- ? spawnSyncImpl(resolveWindowsCmdPath(env), ["/d", "/s", "/c", "npm root -g"], {
294
- encoding: "utf8",
295
- env,
296
- stdio: ["ignore", "pipe", "ignore"],
297
- timeout: 5000,
298
- windowsHide: true,
299
- })
300
- : spawnSyncImpl("npm", ["root", "-g"], {
301
- encoding: "utf8",
302
- env,
303
- stdio: ["ignore", "pipe", "ignore"],
304
- timeout: 5000,
305
- });
306
- if (rootResult.status === 0) {
307
- const globalRoot = rootResult.stdout.trim();
308
- if (globalRoot.length > 0) {
309
- const globalBin = join(globalRoot, "@openai", "codex", "bin", "codex.js");
310
- if (existsSyncImpl(globalBin)) return createResolvedCodexBin(globalBin);
311
- }
312
- }
313
- } catch {
314
- // Ignore and fall through to null.
315
- }
316
-
317
- const selfScriptPath = resolveWrapperScriptPath(moduleUrl, realpathSyncImpl);
318
-
319
- const nativeCodexBin = resolveCodexExecutableFromSystemPath(
320
- env,
321
- platform,
322
- spawnSyncImpl,
323
- existsSyncImpl,
324
- selfScriptPath,
325
- realpathSyncImpl,
326
- );
327
- if (nativeCodexBin) {
328
- return createResolvedCodexBin(nativeCodexBin);
329
- }
330
-
331
- return null;
332
- }
1
+ import { spawnSync } from "node:child_process";
2
+ import { existsSync, realpathSync } from "node:fs";
3
+ import { createRequire } from "node:module";
4
+ import { basename, dirname, extname, isAbsolute, join, posix, relative, win32 } from "node:path";
5
+ import process from "node:process";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ function isJavaScriptEntryPath(candidate) {
9
+ const extension = extname(candidate).toLowerCase();
10
+ return extension === ".js" || extension === ".mjs" || extension === ".cjs";
11
+ }
12
+
13
+ function createResolvedCodexBin(path) {
14
+ return {
15
+ path,
16
+ launchWithNode: isJavaScriptEntryPath(path),
17
+ };
18
+ }
19
+
20
+ function normalizeResolvedPath(candidatePath, realpathSyncImpl) {
21
+ try {
22
+ return realpathSyncImpl(candidatePath);
23
+ } catch {
24
+ return candidatePath;
25
+ }
26
+ }
27
+
28
+ // Defense-in-depth self-recursion guard. The exact-realpath check only blocks
29
+ // the wrapper's own codex.js (the POSIX symlink case). If the wrapper were ever
30
+ // exposed as a native codex/codex.exe sitting alongside it, that exact-path
31
+ // check would miss it. Skipping any candidate that resolves inside the
32
+ // wrapper's own directory closes that latent self-loop.
33
+ function isWithinDirectory(candidatePath, directoryPath, realpathSyncImpl) {
34
+ if (!directoryPath) return false;
35
+ const resolvedCandidate = normalizeResolvedPath(candidatePath, realpathSyncImpl);
36
+ const relativePath = relative(directoryPath, resolvedCandidate);
37
+ return (
38
+ relativePath === "" ||
39
+ (!relativePath.startsWith("..") && !isAbsolute(relativePath))
40
+ );
41
+ }
42
+
43
+ function resolveWrapperScriptPath(moduleUrl, realpathSyncImpl) {
44
+ return normalizeResolvedPath(fileURLToPath(moduleUrl), realpathSyncImpl);
45
+ }
46
+
47
+ const DEFAULT_WRAPPER_MODULE_URL = new URL("./codex.js", import.meta.url).href;
48
+
49
+ function defaultResolvePackageBin(moduleUrl) {
50
+ try {
51
+ const require = createRequire(moduleUrl);
52
+ return require.resolve("@openai/codex/bin/codex.js");
53
+ } catch {
54
+ return null;
55
+ }
56
+ }
57
+
58
+ // spawn() without a shell cannot run these on Windows: `.cmd`/`.bat` are
59
+ // EINVAL, `.ps1` is not an executable image, and an extensionless file
60
+ // (npm's `#!/bin/sh` shim) is ENOENT even when it is a real PE image. Going through cmd.exe instead would put every
61
+ // forwarded argument through cmd's quoting rules.
62
+ export function isWindowsShimPath(candidatePath) {
63
+ const extension = win32.extname(candidatePath).toLowerCase();
64
+ return extension === "" || extension === ".cmd" || extension === ".bat" || extension === ".ps1";
65
+ }
66
+
67
+ // npm's Windows shims sit in the prefix dir and run
68
+ // `<prefix>\node_modules\@openai\codex\bin\codex.js`.
69
+ export function resolveWindowsShimPackageEntry(shimPath, existsSyncImpl = existsSync) {
70
+ const entry = win32.join(
71
+ win32.dirname(shimPath),
72
+ "node_modules",
73
+ "@openai",
74
+ "codex",
75
+ "bin",
76
+ "codex.js",
77
+ );
78
+ return existsSyncImpl(entry) ? entry : null;
79
+ }
80
+
81
+ function resolveWindowsCmdPath(env) {
82
+ const comSpec = (env.ComSpec ?? env.COMSPEC ?? "").trim();
83
+ if (comSpec.length > 0) return comSpec;
84
+
85
+ const systemRoot = (env.SystemRoot ?? env.SYSTEMROOT ?? "").trim();
86
+ if (systemRoot.length > 0) {
87
+ return `${systemRoot.replace(/[\\/]+$/, "")}\\System32\\cmd.exe`;
88
+ }
89
+
90
+ return "cmd.exe";
91
+ }
92
+
93
+ // win32.isAbsolute("\bin\codex.exe") is true for a root-relative path with no
94
+ // drive — it resolves against the CURRENT drive, so the existence check would
95
+ // probe (and a forwarder would exec) whatever file lives at "<cwd's
96
+ // drive>\bin\codex.exe". CWE-426: a binary override must be drive-qualified
97
+ // (C:\...) or a UNC/device path (\\server\share\..., \\?\...), never a bare
98
+ // "\" or "/" root. POSIX keeps plain absolute-path semantics.
99
+ export function isFullyQualifiedBinOverride(candidatePath, platform) {
100
+ if (platform === "win32") {
101
+ if (!win32.isAbsolute(candidatePath)) return false;
102
+ const root = win32.parse(candidatePath).root;
103
+ return root !== "\\" && root !== "/";
104
+ }
105
+ return posix.isAbsolute(candidatePath);
106
+ }
107
+
108
+ export function splitPathEntries(pathValue, platform = process.platform) {
109
+ if (typeof pathValue !== "string" || pathValue.trim().length === 0) {
110
+ return [];
111
+ }
112
+ return pathValue
113
+ .split(platform === "win32" ? ";" : ":")
114
+ .map((entry) => entry.trim())
115
+ .filter((entry) => entry.length > 0);
116
+ }
117
+
118
+ function resolvePathExecutableName(platform) {
119
+ return platform === "win32" ? "codex.exe" : "codex";
120
+ }
121
+
122
+ function resolveCandidateExecutableNames(platform) {
123
+ if (platform !== "win32") {
124
+ return [resolvePathExecutableName(platform)];
125
+ }
126
+ // npm on Windows puts `codex.cmd` plus an extensionless `#!/bin/sh` shim in
127
+ // the prefix dir and the package under `<prefix>\node_modules`. Neither shim
128
+ // can be spawned without a shell (`.cmd` is EINVAL, the sh script ENOENT),
129
+ // so launch the package entry the shims point at before falling back to an
130
+ // extensionless `codex`.
131
+ return ["codex.exe", win32.join("node_modules", "@openai", "codex", "bin", "codex.js"), "codex"];
132
+ }
133
+
134
+ function resolveCodexExecutableFromPath(
135
+ pathEntries,
136
+ platform,
137
+ existsSyncImpl,
138
+ selfScriptPath,
139
+ realpathSyncImpl,
140
+ ) {
141
+ for (const entry of pathEntries) {
142
+ for (const executableName of resolveCandidateExecutableNames(platform)) {
143
+ const candidate = (platform === "win32" ? win32 : posix).join(entry, executableName);
144
+ if (!existsSyncImpl(candidate)) {
145
+ continue;
146
+ }
147
+ if (
148
+ selfScriptPath &&
149
+ normalizeResolvedPath(candidate, realpathSyncImpl) === selfScriptPath
150
+ ) {
151
+ continue;
152
+ }
153
+ if (
154
+ selfScriptPath &&
155
+ isWithinDirectory(candidate, dirname(selfScriptPath), realpathSyncImpl)
156
+ ) {
157
+ continue;
158
+ }
159
+ return candidate;
160
+ }
161
+ }
162
+ return null;
163
+ }
164
+
165
+ function normalizeWhereOutput(stdout) {
166
+ return stdout
167
+ .split(/\r?\n/)
168
+ .map((line) => line.trim())
169
+ .filter((line) => line.length > 0);
170
+ }
171
+
172
+ function resolveCodexExecutableFromSystemPath(
173
+ env,
174
+ platform,
175
+ spawnSyncImpl,
176
+ existsSyncImpl,
177
+ selfScriptPath,
178
+ realpathSyncImpl,
179
+ ) {
180
+ const pathEntries = splitPathEntries(env.PATH ?? env.Path ?? "", platform);
181
+ const fromEnvPath = resolveCodexExecutableFromPath(
182
+ pathEntries,
183
+ platform,
184
+ existsSyncImpl,
185
+ selfScriptPath,
186
+ realpathSyncImpl,
187
+ );
188
+ if (fromEnvPath) {
189
+ return fromEnvPath;
190
+ }
191
+
192
+ try {
193
+ const lookupResult =
194
+ platform === "win32"
195
+ ? spawnSyncImpl(resolveWindowsCmdPath(env), ["/d", "/s", "/c", "where codex"], {
196
+ encoding: "utf8",
197
+ env,
198
+ stdio: ["ignore", "pipe", "ignore"],
199
+ timeout: 5000,
200
+ windowsHide: true,
201
+ })
202
+ : spawnSyncImpl("which", ["codex"], {
203
+ encoding: "utf8",
204
+ env,
205
+ stdio: ["ignore", "pipe", "ignore"],
206
+ timeout: 5000,
207
+ });
208
+ if (lookupResult.status !== 0) {
209
+ return null;
210
+ }
211
+ for (const candidate of normalizeWhereOutput(lookupResult.stdout)) {
212
+ if (!existsSyncImpl(candidate)) {
213
+ continue;
214
+ }
215
+ if (
216
+ selfScriptPath &&
217
+ normalizeResolvedPath(candidate, realpathSyncImpl) === selfScriptPath
218
+ ) {
219
+ continue;
220
+ }
221
+ if (
222
+ selfScriptPath &&
223
+ isWithinDirectory(candidate, dirname(selfScriptPath), realpathSyncImpl)
224
+ ) {
225
+ continue;
226
+ }
227
+ const fileName = basename(candidate).toLowerCase();
228
+ if (fileName === "codex" || fileName === "codex.exe") {
229
+ return candidate;
230
+ }
231
+ }
232
+ } catch {
233
+ // Ignore and fall through.
234
+ }
235
+
236
+ return null;
237
+ }
238
+
239
+ export function resolveRealCodexBin(options = {}) {
240
+ const {
241
+ env = process.env,
242
+ argv = process.argv,
243
+ platform = process.platform,
244
+ moduleUrl = DEFAULT_WRAPPER_MODULE_URL,
245
+ existsSyncImpl = existsSync,
246
+ realpathSyncImpl = realpathSync,
247
+ spawnSyncImpl = spawnSync,
248
+ resolvePackageBin = defaultResolvePackageBin,
249
+ } = options;
250
+
251
+ const override = (env.CODEX_MULTI_AUTH_REAL_CODEX_BIN ?? "").trim();
252
+ if (override.length > 0) {
253
+ // The override is the binary every forwarded command execs, so require a
254
+ // fully-qualified absolute path: a relative one resolves against the
255
+ // caller's cwd, and on Windows a root-relative one resolves against the
256
+ // cwd's DRIVE — either could silently exec a planted file.
257
+ if (!isFullyQualifiedBinOverride(override, platform) || !existsSyncImpl(override)) return null;
258
+ if (platform === "win32" && isWindowsShimPath(override)) {
259
+ const entry = resolveWindowsShimPackageEntry(override, existsSyncImpl);
260
+ return entry ? createResolvedCodexBin(entry) : null;
261
+ }
262
+ return createResolvedCodexBin(override);
263
+ }
264
+
265
+ const resolved = resolvePackageBin(moduleUrl);
266
+ if (typeof resolved === "string" && resolved.length > 0 && existsSyncImpl(resolved)) {
267
+ return createResolvedCodexBin(resolved);
268
+ }
269
+
270
+ const searchRoots = [];
271
+ const scriptDir = dirname(fileURLToPath(moduleUrl));
272
+ searchRoots.push(join(scriptDir, "..", ".."));
273
+
274
+ const invokedScript = argv[1];
275
+ if (typeof invokedScript === "string" && invokedScript.length > 0) {
276
+ searchRoots.push(join(dirname(invokedScript), "..", ".."));
277
+ }
278
+
279
+ const npmPrefix = (env.npm_config_prefix ?? env.PREFIX ?? "").trim();
280
+ if (npmPrefix.length > 0) {
281
+ searchRoots.push(join(npmPrefix, "node_modules"));
282
+ searchRoots.push(join(npmPrefix, "lib", "node_modules"));
283
+ }
284
+
285
+ for (const root of searchRoots) {
286
+ const candidate = join(root, "@openai", "codex", "bin", "codex.js");
287
+ if (existsSyncImpl(candidate)) return createResolvedCodexBin(candidate);
288
+ }
289
+
290
+ try {
291
+ const rootResult =
292
+ platform === "win32"
293
+ ? spawnSyncImpl(resolveWindowsCmdPath(env), ["/d", "/s", "/c", "npm root -g"], {
294
+ encoding: "utf8",
295
+ env,
296
+ stdio: ["ignore", "pipe", "ignore"],
297
+ timeout: 5000,
298
+ windowsHide: true,
299
+ })
300
+ : spawnSyncImpl("npm", ["root", "-g"], {
301
+ encoding: "utf8",
302
+ env,
303
+ stdio: ["ignore", "pipe", "ignore"],
304
+ timeout: 5000,
305
+ });
306
+ if (rootResult.status === 0) {
307
+ const globalRoot = rootResult.stdout.trim();
308
+ if (globalRoot.length > 0) {
309
+ const globalBin = join(globalRoot, "@openai", "codex", "bin", "codex.js");
310
+ if (existsSyncImpl(globalBin)) return createResolvedCodexBin(globalBin);
311
+ }
312
+ }
313
+ } catch {
314
+ // Ignore and fall through to null.
315
+ }
316
+
317
+ const selfScriptPath = resolveWrapperScriptPath(moduleUrl, realpathSyncImpl);
318
+
319
+ const nativeCodexBin = resolveCodexExecutableFromSystemPath(
320
+ env,
321
+ platform,
322
+ spawnSyncImpl,
323
+ existsSyncImpl,
324
+ selfScriptPath,
325
+ realpathSyncImpl,
326
+ );
327
+ if (nativeCodexBin) {
328
+ return createResolvedCodexBin(nativeCodexBin);
329
+ }
330
+
331
+ return null;
332
+ }