codex-multi-auth 2.19.1 → 2.20.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 +9 -9
- package/LICENSE +21 -21
- package/README.md +149 -148
- package/assets/codex-multi-auth-icon.svg +39 -39
- package/assets/readme-hero.svg +31 -31
- package/config/README.md +31 -31
- package/config/codex-legacy.json +1021 -1021
- package/config/codex-modern.json +347 -347
- package/config/minimal-codex.json +15 -15
- package/dist/index.js +1 -1
- package/dist/lib/accounts/rate-limits.js +5 -0
- package/dist/lib/accounts.js +5 -4
- package/dist/lib/auth/account-access.js +2 -2
- package/dist/lib/codex-cli/version.d.ts +22 -0
- package/dist/lib/codex-cli/version.js +78 -0
- package/dist/lib/codex-manager/commands/limits.d.ts +2 -0
- package/dist/lib/codex-manager/commands/limits.js +31 -0
- package/dist/lib/codex-manager/commands/status.js +1 -1
- package/dist/lib/codex-manager.js +2 -0
- package/dist/lib/constants.d.ts +2 -0
- package/dist/lib/constants.js +2 -0
- package/dist/lib/context-budget/model-context-windows.d.ts +1 -1
- package/dist/lib/context-budget/model-context-windows.js +4 -0
- package/dist/lib/context-budget-response.js +8 -8
- package/dist/lib/context-overflow.js +10 -10
- package/dist/lib/luna-reserve.d.ts +26 -0
- package/dist/lib/luna-reserve.js +68 -0
- package/dist/lib/oauth-success.html +125 -125
- package/dist/lib/policy/runtime-policy.js +14 -0
- package/dist/lib/prompts/codex-host-bridge.js +138 -138
- package/dist/lib/prompts/codex.js +79 -79
- package/dist/lib/request/helpers/model-map.d.ts +1 -1
- package/dist/lib/request/helpers/model-map.js +12 -0
- package/dist/lib/request/response-outcome.d.ts +1 -1
- package/dist/lib/request/response-outcome.js +4 -1
- package/dist/lib/runtime/account-status.js +2 -1
- package/dist/lib/runtime/luna-reserve-usage.d.ts +9 -0
- package/dist/lib/runtime/luna-reserve-usage.js +68 -0
- package/dist/lib/runtime/model-discovery-status.d.ts +6 -2
- package/dist/lib/runtime/model-discovery-status.js +13 -5
- package/dist/lib/runtime/reset-credit-routing.js +4 -1
- package/dist/lib/runtime/rotation-server-types.d.ts +2 -0
- package/dist/lib/runtime/runtime-observability.d.ts +2 -0
- package/dist/lib/runtime/runtime-observability.js +1 -0
- package/dist/lib/runtime-rotation-proxy.js +200 -32
- package/dist/lib/usage/pricing.d.ts +1 -1
- package/dist/lib/usage/pricing.js +4 -0
- package/dist/lib/usage/usage-extraction.d.ts +2 -0
- package/dist/lib/usage/usage-extraction.js +25 -1
- package/dist/lib/utils.d.ts +1 -1
- package/dist/lib/utils.js +1 -1
- package/node_modules/@codex-ai/plugin/dist/index.d.ts +2 -2
- package/node_modules/@codex-ai/plugin/dist/index.js +2 -2
- package/node_modules/@codex-ai/plugin/dist/tool.d.ts +42 -42
- package/node_modules/@codex-ai/plugin/dist/tool.js +29 -29
- package/node_modules/@codex-ai/plugin/package.json +9 -9
- package/node_modules/@codex-ai/sdk/dist/index.d.ts +4 -4
- package/node_modules/@codex-ai/sdk/dist/index.js +2 -2
- package/node_modules/@codex-ai/sdk/package.json +8 -8
- package/package.json +207 -207
- package/scripts/codex-app-launcher.js +653 -653
- package/scripts/codex-app-router.js +374 -373
- package/scripts/codex-bin-resolver.js +332 -332
- package/scripts/codex-multi-auth.js +65 -65
- package/scripts/codex-routing.js +62 -62
- package/scripts/codex.js +7006 -7005
- package/scripts/install-codex-auth-utils.js +213 -213
- package/scripts/mcodex.js +367 -367
- package/scripts/postinstall.js +114 -114
- package/scripts/preuninstall.js +265 -265
- package/skills/codex-auth-setup/SKILL.md +73 -73
- package/vendor/codex-ai-plugin/dist/index.d.ts +2 -2
- package/vendor/codex-ai-plugin/dist/index.js +2 -2
- package/vendor/codex-ai-plugin/dist/tool.d.ts +42 -42
- package/vendor/codex-ai-plugin/dist/tool.js +29 -29
- package/vendor/codex-ai-plugin/package.json +9 -9
- package/vendor/codex-ai-sdk/dist/index.d.ts +4 -4
- package/vendor/codex-ai-sdk/dist/index.js +2 -2
- 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
|
+
}
|