codex-multi-auth 0.1.4 → 0.1.7

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/scripts/codex.js CHANGED
@@ -1,13 +1,26 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { spawn, spawnSync } from "node:child_process";
4
- import { existsSync } from "node:fs";
4
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
5
5
  import { createRequire } from "node:module";
6
- import { dirname, join } from "node:path";
6
+ import { basename, delimiter, dirname, join, resolve as resolvePath } from "node:path";
7
7
  import process from "node:process";
8
8
  import { fileURLToPath } from "node:url";
9
9
  import { normalizeAuthAlias, shouldHandleMultiAuthAuth } from "./codex-routing.js";
10
10
 
11
+ function hydrateCliVersionEnv() {
12
+ try {
13
+ const require = createRequire(import.meta.url);
14
+ const pkg = require("../package.json");
15
+ const version = typeof pkg?.version === "string" ? pkg.version.trim() : "";
16
+ if (version.length > 0) {
17
+ process.env.CODEX_MULTI_AUTH_CLI_VERSION = version;
18
+ }
19
+ } catch {
20
+ // Best effort only.
21
+ }
22
+ }
23
+
11
24
  async function loadRunCodexMultiAuthCli() {
12
25
  try {
13
26
  const mod = await import("../dist/lib/codex-manager.js");
@@ -178,39 +191,343 @@ function normalizeExitCode(value) {
178
191
  return 1;
179
192
  }
180
193
 
181
- const rawArgs = process.argv.slice(2);
182
- const normalizedArgs = normalizeAuthAlias(rawArgs);
183
- const bypass = (process.env.CODEX_MULTI_AUTH_BYPASS ?? "").trim() === "1";
194
+ const WINDOWS_SHIM_MARKER = "codex-multi-auth windows shim guardian v1";
195
+ const POWERSHELL_PROFILE_MARKER_START = "# >>> codex-multi-auth shell guard >>>";
196
+ const POWERSHELL_PROFILE_MARKER_END = "# <<< codex-multi-auth shell guard <<<";
197
+
198
+ function shouldInstallWindowsBatchShimGuard() {
199
+ if (process.platform !== "win32") return false;
200
+ const override = (process.env.CODEX_MULTI_AUTH_WINDOWS_BATCH_SHIM_GUARD ?? "1").trim();
201
+ return override !== "0";
202
+ }
203
+
204
+ function splitPathEntries(pathValue) {
205
+ if (typeof pathValue !== "string" || pathValue.trim().length === 0) {
206
+ return [];
207
+ }
208
+ return pathValue
209
+ .split(delimiter)
210
+ .map((entry) => entry.trim())
211
+ .filter((entry) => entry.length > 0);
212
+ }
213
+
214
+ function resolveWindowsShimDirectoryFromInvocation() {
215
+ const invokedScript = (process.argv[1] ?? "").trim();
216
+ if (invokedScript.length === 0) return null;
217
+ const resolvedScript = resolvePath(invokedScript);
218
+ const scriptDir = dirname(resolvedScript);
219
+ const packageRoot = dirname(scriptDir);
220
+ const nodeModulesDir = dirname(packageRoot);
221
+ if (basename(nodeModulesDir).toLowerCase() !== "node_modules") {
222
+ return null;
223
+ }
224
+ const shimDir = dirname(nodeModulesDir);
225
+ if (existsSync(join(shimDir, "codex-multi-auth.cmd"))) {
226
+ return shimDir;
227
+ }
228
+ return null;
229
+ }
230
+
231
+ function resolveWindowsShimDirectoryFromPath() {
232
+ const fromInvocation = resolveWindowsShimDirectoryFromInvocation();
233
+ if (fromInvocation) {
234
+ return fromInvocation;
235
+ }
236
+ const pathEntries = splitPathEntries(process.env.PATH ?? process.env.Path ?? "");
237
+ for (const entry of pathEntries) {
238
+ if (existsSync(join(entry, "codex-multi-auth.cmd"))) {
239
+ return entry;
240
+ }
241
+ }
242
+ return null;
243
+ }
244
+
245
+ function buildWindowsBatchShimContent() {
246
+ return [
247
+ "@ECHO off",
248
+ `:: ${WINDOWS_SHIM_MARKER}`,
249
+ "GOTO start",
250
+ ":find_dp0",
251
+ "SET dp0=%~dp0",
252
+ "EXIT /b",
253
+ ":start",
254
+ "SETLOCAL",
255
+ "CALL :find_dp0",
256
+ "",
257
+ 'IF EXIST "%dp0%\\node.exe" (',
258
+ ' SET "_prog=%dp0%\\node.exe"',
259
+ ") ELSE (",
260
+ ' SET "_prog=node"',
261
+ ' SET PATHEXT=%PATHEXT:;.JS;=%',
262
+ ")",
263
+ "",
264
+ 'endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\\node_modules\\codex-multi-auth\\scripts\\codex.js" %*',
265
+ ].join("\r\n");
266
+ }
267
+
268
+ function buildWindowsCmdShimContent() {
269
+ return [
270
+ "@ECHO off",
271
+ `:: ${WINDOWS_SHIM_MARKER}`,
272
+ "GOTO start",
273
+ ":find_dp0",
274
+ "SET dp0=%~dp0",
275
+ "EXIT /b",
276
+ ":start",
277
+ "SETLOCAL",
278
+ "CALL :find_dp0",
279
+ "",
280
+ 'IF EXIST "%dp0%\\node.exe" (',
281
+ ' SET "_prog=%dp0%\\node.exe"',
282
+ ") ELSE (",
283
+ ' SET "_prog=node"',
284
+ ' SET PATHEXT=%PATHEXT:;.JS;=%',
285
+ ")",
286
+ "",
287
+ 'endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\\node_modules\\codex-multi-auth\\scripts\\codex.js" %*',
288
+ ].join("\r\n");
289
+ }
290
+
291
+ function buildWindowsPowerShellShimContent() {
292
+ return [
293
+ `# ${WINDOWS_SHIM_MARKER}`,
294
+ "$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent",
295
+ "",
296
+ '$exe=""',
297
+ 'if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {',
298
+ ' $exe=".exe"',
299
+ "}",
300
+ "$ret=0",
301
+ 'if (Test-Path "$basedir/node$exe") {',
302
+ " if ($MyInvocation.ExpectingInput) {",
303
+ ' $input | & "$basedir/node$exe" "$basedir/node_modules/codex-multi-auth/scripts/codex.js" $args',
304
+ " } else {",
305
+ ' & "$basedir/node$exe" "$basedir/node_modules/codex-multi-auth/scripts/codex.js" $args',
306
+ " }",
307
+ " $ret=$LASTEXITCODE",
308
+ "} else {",
309
+ " if ($MyInvocation.ExpectingInput) {",
310
+ ' $input | & "node$exe" "$basedir/node_modules/codex-multi-auth/scripts/codex.js" $args',
311
+ " } else {",
312
+ ' & "node$exe" "$basedir/node_modules/codex-multi-auth/scripts/codex.js" $args',
313
+ " }",
314
+ " $ret=$LASTEXITCODE",
315
+ "}",
316
+ "if ($null -eq $ret) {",
317
+ " exit 0",
318
+ "}",
319
+ "exit $ret",
320
+ ].join("\r\n");
321
+ }
322
+
323
+ function ensureWindowsShellShim(filePath, desiredContent, options = {}) {
324
+ const {
325
+ overwriteCustomShim = false,
326
+ shimMarker = WINDOWS_SHIM_MARKER,
327
+ } = options;
328
+
329
+ let currentContent = "";
330
+ if (existsSync(filePath)) {
331
+ try {
332
+ currentContent = readFileSync(filePath, "utf8");
333
+ } catch {
334
+ return false;
335
+ }
336
+ if (currentContent === desiredContent || currentContent.includes(shimMarker)) {
337
+ if (currentContent !== desiredContent) {
338
+ try {
339
+ writeFileSync(filePath, desiredContent, { encoding: "utf8", mode: 0o755 });
340
+ return true;
341
+ } catch {
342
+ return false;
343
+ }
344
+ }
345
+ return false;
346
+ }
347
+ const looksLikeStockOpenAiShim =
348
+ currentContent.includes("node_modules\\@openai\\codex\\bin\\codex.js") ||
349
+ currentContent.includes("node_modules/@openai/codex/bin/codex.js");
350
+ if (looksLikeStockOpenAiShim) {
351
+ try {
352
+ writeFileSync(filePath, desiredContent, { encoding: "utf8", mode: 0o755 });
353
+ return true;
354
+ } catch {
355
+ return false;
356
+ }
357
+ }
358
+ if (!overwriteCustomShim) {
359
+ return false;
360
+ }
361
+ }
184
362
 
185
- if (!bypass && shouldHandleMultiAuthAuth(normalizedArgs)) {
186
363
  try {
187
- const runCodexMultiAuthCli = await loadRunCodexMultiAuthCli();
188
- if (!runCodexMultiAuthCli) {
189
- process.exit(1);
364
+ writeFileSync(filePath, desiredContent, { encoding: "utf8", mode: 0o755 });
365
+ return true;
366
+ } catch {
367
+ return false;
368
+ }
369
+ }
370
+
371
+ function shouldInstallPowerShellProfileGuard() {
372
+ if (process.platform !== "win32") return false;
373
+ const override = (process.env.CODEX_MULTI_AUTH_PWSH_PROFILE_GUARD ?? "1").trim();
374
+ return override !== "0";
375
+ }
376
+
377
+ function resolveWindowsUserHomeDir() {
378
+ const userProfile = (process.env.USERPROFILE ?? "").trim();
379
+ if (userProfile.length > 0) return userProfile;
380
+ const homeDrive = (process.env.HOMEDRIVE ?? "").trim();
381
+ const homePath = (process.env.HOMEPATH ?? "").trim();
382
+ if (homeDrive.length > 0 && homePath.length > 0) {
383
+ return `${homeDrive}${homePath}`;
384
+ }
385
+ const home = (process.env.HOME ?? "").trim();
386
+ return home;
387
+ }
388
+
389
+ function buildPowerShellProfileGuardBlock(shimDirectory) {
390
+ const codexBatchPath = join(shimDirectory, "codex.bat").replace(/\\/g, "\\\\");
391
+ return [
392
+ POWERSHELL_PROFILE_MARKER_START,
393
+ `$CodexMultiAuthShim = "${codexBatchPath}"`,
394
+ "if (Test-Path $CodexMultiAuthShim) {",
395
+ " function global:codex {",
396
+ " & $CodexMultiAuthShim @args",
397
+ " }",
398
+ "}",
399
+ POWERSHELL_PROFILE_MARKER_END,
400
+ ].join("\r\n");
401
+ }
402
+
403
+ function upsertPowerShellProfileGuard(profilePath, guardBlock) {
404
+ let content = "";
405
+ if (existsSync(profilePath)) {
406
+ try {
407
+ content = readFileSync(profilePath, "utf8");
408
+ } catch {
409
+ return false;
190
410
  }
191
- const exitCode = await runCodexMultiAuthCli(normalizedArgs);
192
- process.exit(normalizeExitCode(exitCode));
193
- } catch (error) {
411
+ }
412
+ const normalizedCurrentContent = content.replace(/\r?\n$/, "");
413
+
414
+ const startIndex = content.indexOf(POWERSHELL_PROFILE_MARKER_START);
415
+ const endIndex = content.indexOf(POWERSHELL_PROFILE_MARKER_END);
416
+ let nextContent;
417
+ if (startIndex >= 0 && endIndex >= startIndex) {
418
+ const endWithMarker = endIndex + POWERSHELL_PROFILE_MARKER_END.length;
419
+ const prefix = content.slice(0, startIndex).replace(/\s*$/, "");
420
+ const suffix = content.slice(endWithMarker).replace(/^\s*/, "");
421
+ nextContent = `${prefix}\r\n\r\n${guardBlock}\r\n\r\n${suffix}`.trimEnd();
422
+ } else if (normalizedCurrentContent.trim().length === 0) {
423
+ nextContent = guardBlock;
424
+ } else {
425
+ nextContent = `${normalizedCurrentContent.replace(/\s*$/, "")}\r\n\r\n${guardBlock}`;
426
+ }
427
+
428
+ if (nextContent === normalizedCurrentContent) {
429
+ return false;
430
+ }
431
+
432
+ try {
433
+ mkdirSync(dirname(profilePath), { recursive: true });
434
+ writeFileSync(profilePath, `${nextContent}\r\n`, { encoding: "utf8", mode: 0o644 });
435
+ return true;
436
+ } catch {
437
+ return false;
438
+ }
439
+ }
440
+
441
+ function ensurePowerShellProfileGuard(shimDirectory) {
442
+ if (!shouldInstallPowerShellProfileGuard()) return false;
443
+ const homeDir = resolveWindowsUserHomeDir();
444
+ if (!homeDir) return false;
445
+ const guardBlock = buildPowerShellProfileGuardBlock(shimDirectory);
446
+ const profilePaths = [
447
+ join(homeDir, "Documents", "PowerShell", "Microsoft.PowerShell_profile.ps1"),
448
+ join(homeDir, "Documents", "WindowsPowerShell", "Microsoft.PowerShell_profile.ps1"),
449
+ ];
450
+ let changed = false;
451
+ for (const profilePath of profilePaths) {
452
+ changed = upsertPowerShellProfileGuard(profilePath, guardBlock) || changed;
453
+ }
454
+ return changed;
455
+ }
456
+
457
+ function ensureWindowsShellShimGuards() {
458
+ if (!shouldInstallWindowsBatchShimGuard()) return;
459
+ const shimDirectory = resolveWindowsShimDirectoryFromPath();
460
+ if (!shimDirectory) return;
461
+
462
+ const codexMultiAuthShimPath = join(shimDirectory, "codex-multi-auth.cmd");
463
+ if (!existsSync(codexMultiAuthShimPath)) return;
464
+
465
+ const overwriteCustomShim =
466
+ (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
+ );
482
+ const installedAny = installedBatch || installedCmd || installedPs1;
483
+ const installedProfileGuard = ensurePowerShellProfileGuard(shimDirectory);
484
+ if (installedAny || installedProfileGuard) {
194
485
  console.error(
195
- `codex-multi-auth runner failed: ${error instanceof Error ? error.message : String(error)}`,
486
+ "codex-multi-auth: installed Windows shell guards to keep multi-auth routing after codex npm updates.",
196
487
  );
197
- process.exit(1);
198
488
  }
199
489
  }
200
490
 
201
- const realCodexBin = resolveRealCodexBin();
202
- if (!realCodexBin) {
203
- console.error(
204
- [
205
- "Could not locate the official Codex CLI binary (@openai/codex).",
206
- "Install it globally: npm install -g @openai/codex",
207
- "Or set CODEX_MULTI_AUTH_REAL_CODEX_BIN to a full bin/codex.js path.",
208
- ].join("\n"),
209
- );
210
- process.exit(1);
491
+ async function main() {
492
+ hydrateCliVersionEnv();
493
+ ensureWindowsShellShimGuards();
494
+
495
+ const rawArgs = process.argv.slice(2);
496
+ const normalizedArgs = normalizeAuthAlias(rawArgs);
497
+ const bypass = (process.env.CODEX_MULTI_AUTH_BYPASS ?? "").trim() === "1";
498
+
499
+ if (!bypass && shouldHandleMultiAuthAuth(normalizedArgs)) {
500
+ try {
501
+ const runCodexMultiAuthCli = await loadRunCodexMultiAuthCli();
502
+ if (!runCodexMultiAuthCli) {
503
+ return 1;
504
+ }
505
+ const exitCode = await runCodexMultiAuthCli(normalizedArgs);
506
+ return normalizeExitCode(exitCode);
507
+ } catch (error) {
508
+ console.error(
509
+ `codex-multi-auth runner failed: ${error instanceof Error ? error.message : String(error)}`,
510
+ );
511
+ return 1;
512
+ }
513
+ }
514
+
515
+ const realCodexBin = resolveRealCodexBin();
516
+ if (!realCodexBin) {
517
+ console.error(
518
+ [
519
+ "Could not locate the official Codex CLI binary (@openai/codex).",
520
+ "Install it globally: npm install -g @openai/codex",
521
+ "Or set CODEX_MULTI_AUTH_REAL_CODEX_BIN to a full bin/codex.js path.",
522
+ ].join("\n"),
523
+ );
524
+ return 1;
525
+ }
526
+
527
+ await autoSyncManagerActiveSelectionIfEnabled();
528
+ const forwardArgs = buildForwardArgs(rawArgs);
529
+ return forwardToRealCodex(realCodexBin, forwardArgs);
211
530
  }
212
531
 
213
- await autoSyncManagerActiveSelectionIfEnabled();
214
- const forwardArgs = buildForwardArgs(rawArgs);
215
- const forwardExitCode = await forwardToRealCodex(realCodexBin, forwardArgs);
216
- process.exit(forwardExitCode);
532
+ const exitCode = await main();
533
+ process.exitCode = normalizeExitCode(exitCode);
@@ -143,15 +143,15 @@ async function clearCache() {
143
143
  if (dryRun) {
144
144
  log(`[dry-run] Would remove ${cacheNodeModules}`);
145
145
  log(`[dry-run] Would remove ${cacheBunLock}`);
146
- } else {
147
- try {
148
- await withFileOperationRetry(() => rm(cacheNodeModules, { recursive: true, force: true }));
149
- await withFileOperationRetry(() => rm(cacheBunLock, { force: true }));
150
- } catch (error) {
151
- log(
152
- `Warning: Could not fully clear cache (${error instanceof Error ? error.message : String(error)}). You may need to restart Codex.`,
153
- );
154
- }
146
+ } else {
147
+ try {
148
+ await withFileOperationRetry(() => rm(cacheNodeModules, { recursive: true, force: true }));
149
+ await withFileOperationRetry(() => rm(cacheBunLock, { force: true }));
150
+ } catch (error) {
151
+ log(
152
+ `Warning: Could not fully clear cache (${error instanceof Error ? error.message : String(error)}). You may need to restart Codex.`,
153
+ );
154
+ }
155
155
  }
156
156
 
157
157
  await removePluginFromCachePackage();
@@ -1,7 +1,7 @@
1
- #!/bin/bash
2
-
3
- set -euo pipefail
4
-
5
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
- node "${SCRIPT_DIR}/test-model-matrix.js" "$@"
7
-
1
+ #!/bin/bash
2
+
3
+ set -euo pipefail
4
+
5
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
+ node "${SCRIPT_DIR}/test-model-matrix.js" "$@"
7
+
@@ -1,4 +1,4 @@
1
- import { existsSync } from "node:fs";
1
+ import { existsSync, readFileSync } from "node:fs";
2
2
  import { readFile, writeFile, rm, mkdir } from "node:fs/promises";
3
3
  import { spawnSync } from "node:child_process";
4
4
  import { dirname, join, resolve } from "node:path";
@@ -13,16 +13,45 @@ const scenarioTemplates = {
13
13
  modern: join(repoRoot, "config", "codex-modern.json"),
14
14
  };
15
15
 
16
- const defaultPromptPrefix = "Reply exactly:";
17
- const modelProviderId = "openai";
18
16
  const pluginPackageName = "codex-multi-auth";
19
17
  const DEFAULT_MATRIX_TIMEOUT_MS = 120000;
20
18
 
19
+ function resolveCmdScriptEntry(commandPath) {
20
+ if (!/\.cmd$/i.test(commandPath)) {
21
+ return null;
22
+ }
23
+ try {
24
+ const raw = readFileSync(commandPath, "utf8");
25
+ const match = raw.match(/"%dp0%\\([^"\r\n]+\.js)"/i);
26
+ if (!match || typeof match[1] !== "string") {
27
+ return null;
28
+ }
29
+ const relScriptPath = match[1].replace(/[\\/]+/g, "/");
30
+ const scriptPath = resolve(dirname(commandPath), relScriptPath);
31
+ return existsSync(scriptPath) ? scriptPath : null;
32
+ } catch {
33
+ return null;
34
+ }
35
+ }
36
+
37
+ function buildExecutable(command) {
38
+ const scriptEntry = resolveCmdScriptEntry(command);
39
+ if (scriptEntry) {
40
+ return {
41
+ command: process.execPath,
42
+ shell: false,
43
+ prefixArgs: [scriptEntry],
44
+ displayCommand: command,
45
+ };
46
+ }
47
+ return { command, shell: /\.cmd$/i.test(command) };
48
+ }
49
+
21
50
  export function resolveCodexExecutable() {
22
51
  const envOverride = process.env.CODEX_BIN;
23
52
  if (envOverride && envOverride.trim().length > 0) {
24
53
  const command = envOverride.trim();
25
- return { command, shell: /\.cmd$/i.test(command) };
54
+ return buildExecutable(command);
26
55
  }
27
56
 
28
57
  if (process.platform !== "win32") {
@@ -53,12 +82,12 @@ export function resolveCodexExecutable() {
53
82
  /npm\\Codex\.cmd$/i.test(candidate),
54
83
  );
55
84
  if (exactCmd) {
56
- return { command: exactCmd, shell: true };
85
+ return buildExecutable(exactCmd);
57
86
  }
58
87
 
59
88
  const anyCmd = candidates.find((candidate) => /\.cmd$/i.test(candidate));
60
89
  if (anyCmd) {
61
- return { command: anyCmd, shell: true };
90
+ return buildExecutable(anyCmd);
62
91
  }
63
92
 
64
93
  return { command: candidates[0], shell: false };
@@ -101,6 +130,9 @@ function toFileUri(pathValue) {
101
130
  return `file:///${normalized}`;
102
131
  }
103
132
 
133
+ let stopCodexServersQueue = Promise.resolve();
134
+ const spawnedCodexPids = new Set();
135
+
104
136
  function runQuiet(command, commandArgs) {
105
137
  try {
106
138
  spawnSync(command, commandArgs, {
@@ -112,9 +144,6 @@ function runQuiet(command, commandArgs) {
112
144
  }
113
145
  }
114
146
 
115
- let stopCodexServersQueue = Promise.resolve();
116
- const spawnedCodexPids = new Set();
117
-
118
147
  export function registerSpawnedCodex(pid) {
119
148
  if (!Number.isInteger(pid) || pid <= 0) return;
120
149
  spawnedCodexPids.add(pid);
@@ -215,23 +244,32 @@ function enumerateCases(models, smoke, maxCases) {
215
244
  return selected;
216
245
  }
217
246
 
218
- function executeModelCase(caseInfo, index, port) {
247
+ function buildModelCaseArgs(caseInfo, index) {
219
248
  const token = `MODEL_MATRIX_OK_${index}`;
220
- const message = `${defaultPromptPrefix} ${token}`;
221
249
  const args = [
222
- "run",
223
- message,
250
+ "exec",
251
+ token,
224
252
  "--model",
225
- `${modelProviderId}/${caseInfo.model}`,
226
- "--port",
227
- String(port),
253
+ caseInfo.model,
254
+ "--json",
255
+ "--skip-git-repo-check",
228
256
  ];
229
257
  if (caseInfo.variant) {
230
- args.push("--variant", caseInfo.variant);
258
+ args.push("-c", `model_reasoning_effort="${caseInfo.variant}"`);
231
259
  }
260
+ return { token, args };
261
+ }
262
+
263
+ export function __buildModelCaseArgsForTests(caseInfo, index) {
264
+ return buildModelCaseArgs(caseInfo, index);
265
+ }
266
+
267
+ function executeModelCase(caseInfo, index) {
268
+ const { token, args } = buildModelCaseArgs(caseInfo, index);
232
269
 
233
270
  const timeoutMs = resolveMatrixTimeoutMs();
234
- const finalized = spawnSync(CodexExecutable.command, args, {
271
+ const commandArgs = [...(CodexExecutable.prefixArgs ?? []), ...args];
272
+ const finalized = spawnSync(CodexExecutable.command, commandArgs, {
235
273
  cwd: repoRoot,
236
274
  encoding: "utf8",
237
275
  windowsHide: true,
@@ -258,9 +296,8 @@ function executeModelCase(caseInfo, index, port) {
258
296
 
259
297
  const combinedOutput = `${finalized.stdout ?? ""}\n${finalized.stderr ?? ""}`.trim();
260
298
  const hasToken = combinedOutput.includes(token);
261
- const hasFatalError = /ProviderModelNotFoundError|Model not found/i.test(combinedOutput);
262
299
  const exitCode = finalized.status ?? 1;
263
- const ok = exitCode === 0 && hasToken && !hasFatalError;
300
+ const ok = exitCode === 0 && hasToken;
264
301
 
265
302
  return {
266
303
  ...caseInfo,
@@ -346,7 +383,6 @@ async function runScenario(scenario, options) {
346
383
  const result = executeModelCase(
347
384
  caseInfo,
348
385
  i + 1,
349
- options.portStart + i,
350
386
  );
351
387
  results.push(result);
352
388
  const variantLabel = result.variant ? ` [variant=${result.variant}]` : "";
@@ -401,7 +437,7 @@ async function main() {
401
437
  console.log(`Scenarios: ${scenarios.join(", ")}`);
402
438
  console.log(`Mode: ${smoke ? "smoke" : "full"}`);
403
439
  console.log(`Plugin: ${pluginRef}`);
404
- console.log(`Codex command: ${CodexExecutable.command}`);
440
+ console.log(`Codex command: ${CodexExecutable.displayCommand ?? CodexExecutable.command}`);
405
441
 
406
442
  const backups = await backupLocalConfigs();
407
443
  const allResults = [];
@@ -413,7 +449,6 @@ async function main() {
413
449
  smoke,
414
450
  maxCases,
415
451
  pluginRef,
416
- portStart: 47000 + i * 500,
417
452
  });
418
453
  allResults.push(...scenarioResults.map((item) => ({ ...item, scenario })));
419
454
  }
@@ -437,7 +472,7 @@ async function main() {
437
472
  scenarios,
438
473
  mode: smoke ? "smoke" : "full",
439
474
  plugin: pluginRef,
440
- CodexCommand: CodexExecutable.command,
475
+ CodexCommand: CodexExecutable.displayCommand ?? CodexExecutable.command,
441
476
  totals: {
442
477
  total: allResults.length,
443
478
  passed,
@@ -1,7 +1,7 @@
1
- #!/bin/bash
2
-
3
- set -euo pipefail
4
-
5
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
- node "${SCRIPT_DIR}/test-model-matrix.js" --smoke "$@"
7
-
1
+ #!/bin/bash
2
+
3
+ set -euo pipefail
4
+
5
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
+ node "${SCRIPT_DIR}/test-model-matrix.js" --smoke "$@"
7
+