copilot-hub 0.1.25 → 0.1.26
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/package.json +1 -1
- package/scripts/dist/cli.mjs +8 -19
- package/scripts/dist/codex-runtime.mjs +4 -25
- package/scripts/dist/codex-spawn.mjs +42 -0
- package/scripts/src/cli.mts +8 -24
- package/scripts/src/codex-runtime.mts +4 -28
- package/scripts/src/codex-spawn.mts +84 -0
- package/scripts/test/codex-spawn.test.mjs +60 -0
package/package.json
CHANGED
package/scripts/dist/cli.mjs
CHANGED
|
@@ -5,6 +5,7 @@ import process, { stdin as input, stdout as output } from "node:process";
|
|
|
5
5
|
import { spawnSync } from "node:child_process";
|
|
6
6
|
import { createInterface } from "node:readline/promises";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { spawnCodexSync } from "./codex-spawn.mjs";
|
|
8
9
|
import { codexInstallPackageSpec } from "./codex-version.mjs";
|
|
9
10
|
import { initializeCopilotHubLayout, resolveCopilotHubLayout } from "./install-layout.mjs";
|
|
10
11
|
import { buildCodexCompatibilityError, buildCodexCompatibilityNotice, probeCodexVersion, resolveCodexBinForStart, resolveCompatibleInstalledCodexBin, } from "./codex-runtime.mjs";
|
|
@@ -367,22 +368,13 @@ async function ensureCompatibleCodexBinary({ autoInstall, purpose, }) {
|
|
|
367
368
|
}
|
|
368
369
|
function runCodex(codexBin, args, stdioMode) {
|
|
369
370
|
const stdio = stdioMode === "inherit" ? "inherit" : ["ignore", "pipe", "pipe"];
|
|
370
|
-
const result =
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
shell: true,
|
|
378
|
-
encoding: "utf8",
|
|
379
|
-
})
|
|
380
|
-
: spawnSync(codexBin, args, {
|
|
381
|
-
cwd: repoRoot,
|
|
382
|
-
stdio,
|
|
383
|
-
shell: false,
|
|
384
|
-
encoding: "utf8",
|
|
385
|
-
});
|
|
371
|
+
const result = spawnCodexSync({
|
|
372
|
+
codexBin,
|
|
373
|
+
args,
|
|
374
|
+
cwd: repoRoot,
|
|
375
|
+
stdio,
|
|
376
|
+
encoding: "utf8",
|
|
377
|
+
});
|
|
386
378
|
if (result.error) {
|
|
387
379
|
return {
|
|
388
380
|
ok: false,
|
|
@@ -401,9 +393,6 @@ function runCodex(codexBin, args, stdioMode) {
|
|
|
401
393
|
errorCode: "",
|
|
402
394
|
};
|
|
403
395
|
}
|
|
404
|
-
function quoteWindowsShellValue(value) {
|
|
405
|
-
return `"${String(value ?? "").replace(/"/g, '\\"')}"`;
|
|
406
|
-
}
|
|
407
396
|
function runNpm(args, stdioMode) {
|
|
408
397
|
const stdio = stdioMode === "inherit" ? "inherit" : ["ignore", "pipe", "pipe"];
|
|
409
398
|
const result = spawnNpm(args, {
|
|
@@ -2,6 +2,7 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import process from "node:process";
|
|
4
4
|
import { spawnSync } from "node:child_process";
|
|
5
|
+
import { spawnCodexSync } from "./codex-spawn.mjs";
|
|
5
6
|
import { compareSemver, codexVersionRequirementLabel, extractSemver, isCodexVersionCompatible, } from "./codex-version.mjs";
|
|
6
7
|
export function resolveCodexBinForStart({ repoRoot, agentEngineEnvPath, controlPlaneEnvPath, env = process.env, }) {
|
|
7
8
|
const fromEnv = nonEmpty(env.CODEX_BIN);
|
|
@@ -262,30 +263,11 @@ function runCodex({ codexBin, args, repoRoot, }) {
|
|
|
262
263
|
};
|
|
263
264
|
}
|
|
264
265
|
function spawnCodex(codexBin, args, repoRoot) {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
269
|
-
shell: false,
|
|
270
|
-
encoding: "utf8",
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
if (process.platform === "win32" && /\.(cmd|bat)$/i.test(codexBin)) {
|
|
274
|
-
const commandLine = [
|
|
275
|
-
quoteWindowsShellValue(codexBin),
|
|
276
|
-
...args.map(quoteWindowsShellValue),
|
|
277
|
-
].join(" ");
|
|
278
|
-
return spawnSync(commandLine, {
|
|
279
|
-
cwd: repoRoot,
|
|
280
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
281
|
-
shell: true,
|
|
282
|
-
encoding: "utf8",
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
return spawnSync(codexBin, args, {
|
|
266
|
+
return spawnCodexSync({
|
|
267
|
+
codexBin,
|
|
268
|
+
args,
|
|
286
269
|
cwd: repoRoot,
|
|
287
270
|
stdio: ["ignore", "pipe", "pipe"],
|
|
288
|
-
shell: false,
|
|
289
271
|
encoding: "utf8",
|
|
290
272
|
});
|
|
291
273
|
}
|
|
@@ -346,9 +328,6 @@ function normalizeErrorCode(error) {
|
|
|
346
328
|
function dedupe(values) {
|
|
347
329
|
return [...new Set(values.map((value) => String(value ?? "").trim()).filter(Boolean))];
|
|
348
330
|
}
|
|
349
|
-
function quoteWindowsShellValue(value) {
|
|
350
|
-
return `"${String(value ?? "").replace(/"/g, '\\"')}"`;
|
|
351
|
-
}
|
|
352
331
|
function spawnNpm(args, repoRoot) {
|
|
353
332
|
if (process.platform === "win32") {
|
|
354
333
|
const comspec = process.env.ComSpec || "cmd.exe";
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
export function requiresNodeScriptCodexBin(command) {
|
|
4
|
+
return /\.(cjs|mjs|js)$/i.test(String(command ?? "").trim());
|
|
5
|
+
}
|
|
6
|
+
export function requiresShellWrappedCodexBin(command, platform = process.platform) {
|
|
7
|
+
return platform === "win32" && /\.(cmd|bat)$/i.test(String(command ?? "").trim());
|
|
8
|
+
}
|
|
9
|
+
export function buildCodexSpawnSpec({ codexBin, args, platform = process.platform, nodeBin = process.execPath, }) {
|
|
10
|
+
if (requiresNodeScriptCodexBin(codexBin)) {
|
|
11
|
+
return {
|
|
12
|
+
command: nodeBin,
|
|
13
|
+
args: [codexBin, ...args],
|
|
14
|
+
shell: false,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
if (requiresShellWrappedCodexBin(codexBin, platform)) {
|
|
18
|
+
return {
|
|
19
|
+
command: [quoteWindowsShellValue(codexBin), ...args.map(quoteWindowsShellValue)].join(" "),
|
|
20
|
+
args: [],
|
|
21
|
+
shell: true,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
command: codexBin,
|
|
26
|
+
args: [...args],
|
|
27
|
+
shell: false,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export function spawnCodexSync({ codexBin, args, cwd, stdio, encoding = "utf8", input, }) {
|
|
31
|
+
const spec = buildCodexSpawnSpec({ codexBin, args });
|
|
32
|
+
return spawnSync(spec.command, spec.args, {
|
|
33
|
+
cwd,
|
|
34
|
+
stdio,
|
|
35
|
+
shell: spec.shell,
|
|
36
|
+
encoding,
|
|
37
|
+
...(input !== undefined ? { input } : {}),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function quoteWindowsShellValue(value) {
|
|
41
|
+
return `"${String(value ?? "").replace(/"/g, '\\"')}"`;
|
|
42
|
+
}
|
package/scripts/src/cli.mts
CHANGED
|
@@ -5,6 +5,7 @@ import process, { stdin as input, stdout as output } from "node:process";
|
|
|
5
5
|
import { spawnSync } from "node:child_process";
|
|
6
6
|
import { createInterface } from "node:readline/promises";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { spawnCodexSync } from "./codex-spawn.mjs";
|
|
8
9
|
import { codexInstallPackageSpec } from "./codex-version.mjs";
|
|
9
10
|
import { initializeCopilotHubLayout, resolveCopilotHubLayout } from "./install-layout.mjs";
|
|
10
11
|
import {
|
|
@@ -443,26 +444,13 @@ async function ensureCompatibleCodexBinary({
|
|
|
443
444
|
|
|
444
445
|
function runCodex(codexBin, args, stdioMode) {
|
|
445
446
|
const stdio: any = stdioMode === "inherit" ? "inherit" : ["ignore", "pipe", "pipe"];
|
|
446
|
-
const result =
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
{
|
|
454
|
-
cwd: repoRoot,
|
|
455
|
-
stdio,
|
|
456
|
-
shell: true,
|
|
457
|
-
encoding: "utf8",
|
|
458
|
-
},
|
|
459
|
-
)
|
|
460
|
-
: spawnSync(codexBin, args, {
|
|
461
|
-
cwd: repoRoot,
|
|
462
|
-
stdio,
|
|
463
|
-
shell: false,
|
|
464
|
-
encoding: "utf8",
|
|
465
|
-
});
|
|
447
|
+
const result = spawnCodexSync({
|
|
448
|
+
codexBin,
|
|
449
|
+
args,
|
|
450
|
+
cwd: repoRoot,
|
|
451
|
+
stdio,
|
|
452
|
+
encoding: "utf8",
|
|
453
|
+
});
|
|
466
454
|
|
|
467
455
|
if (result.error) {
|
|
468
456
|
return {
|
|
@@ -484,10 +472,6 @@ function runCodex(codexBin, args, stdioMode) {
|
|
|
484
472
|
};
|
|
485
473
|
}
|
|
486
474
|
|
|
487
|
-
function quoteWindowsShellValue(value) {
|
|
488
|
-
return `"${String(value ?? "").replace(/"/g, '\\"')}"`;
|
|
489
|
-
}
|
|
490
|
-
|
|
491
475
|
function runNpm(args, stdioMode) {
|
|
492
476
|
const stdio = stdioMode === "inherit" ? "inherit" : ["ignore", "pipe", "pipe"];
|
|
493
477
|
const result = spawnNpm(args, {
|
|
@@ -2,6 +2,7 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import process from "node:process";
|
|
4
4
|
import { spawnSync } from "node:child_process";
|
|
5
|
+
import { spawnCodexSync } from "./codex-spawn.mjs";
|
|
5
6
|
import {
|
|
6
7
|
compareSemver,
|
|
7
8
|
codexVersionRequirementLabel,
|
|
@@ -395,32 +396,11 @@ function runCodex({
|
|
|
395
396
|
}
|
|
396
397
|
|
|
397
398
|
function spawnCodex(codexBin: string, args: string[], repoRoot: string) {
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
402
|
-
shell: false,
|
|
403
|
-
encoding: "utf8",
|
|
404
|
-
});
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
if (process.platform === "win32" && /\.(cmd|bat)$/i.test(codexBin)) {
|
|
408
|
-
const commandLine = [
|
|
409
|
-
quoteWindowsShellValue(codexBin),
|
|
410
|
-
...args.map(quoteWindowsShellValue),
|
|
411
|
-
].join(" ");
|
|
412
|
-
return spawnSync(commandLine, {
|
|
413
|
-
cwd: repoRoot,
|
|
414
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
415
|
-
shell: true,
|
|
416
|
-
encoding: "utf8",
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
return spawnSync(codexBin, args, {
|
|
399
|
+
return spawnCodexSync({
|
|
400
|
+
codexBin,
|
|
401
|
+
args,
|
|
421
402
|
cwd: repoRoot,
|
|
422
403
|
stdio: ["ignore", "pipe", "pipe"],
|
|
423
|
-
shell: false,
|
|
424
404
|
encoding: "utf8",
|
|
425
405
|
});
|
|
426
406
|
}
|
|
@@ -493,10 +473,6 @@ function dedupe(values: Array<string | null | undefined>): string[] {
|
|
|
493
473
|
return [...new Set(values.map((value) => String(value ?? "").trim()).filter(Boolean))];
|
|
494
474
|
}
|
|
495
475
|
|
|
496
|
-
function quoteWindowsShellValue(value: string): string {
|
|
497
|
-
return `"${String(value ?? "").replace(/"/g, '\\"')}"`;
|
|
498
|
-
}
|
|
499
|
-
|
|
500
476
|
function spawnNpm(args: string[], repoRoot: string) {
|
|
501
477
|
if (process.platform === "win32") {
|
|
502
478
|
const comspec = process.env.ComSpec || "cmd.exe";
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
|
|
4
|
+
type StdioMode = "pipe" | "inherit" | ["ignore", "pipe", "pipe"];
|
|
5
|
+
|
|
6
|
+
type CodexSpawnSpec = {
|
|
7
|
+
command: string;
|
|
8
|
+
args: string[];
|
|
9
|
+
shell: boolean;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function requiresNodeScriptCodexBin(command: string): boolean {
|
|
13
|
+
return /\.(cjs|mjs|js)$/i.test(String(command ?? "").trim());
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function requiresShellWrappedCodexBin(
|
|
17
|
+
command: string,
|
|
18
|
+
platform: NodeJS.Platform = process.platform,
|
|
19
|
+
): boolean {
|
|
20
|
+
return platform === "win32" && /\.(cmd|bat)$/i.test(String(command ?? "").trim());
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function buildCodexSpawnSpec({
|
|
24
|
+
codexBin,
|
|
25
|
+
args,
|
|
26
|
+
platform = process.platform,
|
|
27
|
+
nodeBin = process.execPath,
|
|
28
|
+
}: {
|
|
29
|
+
codexBin: string;
|
|
30
|
+
args: string[];
|
|
31
|
+
platform?: NodeJS.Platform;
|
|
32
|
+
nodeBin?: string;
|
|
33
|
+
}): CodexSpawnSpec {
|
|
34
|
+
if (requiresNodeScriptCodexBin(codexBin)) {
|
|
35
|
+
return {
|
|
36
|
+
command: nodeBin,
|
|
37
|
+
args: [codexBin, ...args],
|
|
38
|
+
shell: false,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (requiresShellWrappedCodexBin(codexBin, platform)) {
|
|
43
|
+
return {
|
|
44
|
+
command: [quoteWindowsShellValue(codexBin), ...args.map(quoteWindowsShellValue)].join(" "),
|
|
45
|
+
args: [],
|
|
46
|
+
shell: true,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
command: codexBin,
|
|
52
|
+
args: [...args],
|
|
53
|
+
shell: false,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function spawnCodexSync({
|
|
58
|
+
codexBin,
|
|
59
|
+
args,
|
|
60
|
+
cwd,
|
|
61
|
+
stdio,
|
|
62
|
+
encoding = "utf8",
|
|
63
|
+
input,
|
|
64
|
+
}: {
|
|
65
|
+
codexBin: string;
|
|
66
|
+
args: string[];
|
|
67
|
+
cwd: string;
|
|
68
|
+
stdio: StdioMode;
|
|
69
|
+
encoding?: BufferEncoding;
|
|
70
|
+
input?: string;
|
|
71
|
+
}) {
|
|
72
|
+
const spec = buildCodexSpawnSpec({ codexBin, args });
|
|
73
|
+
return spawnSync(spec.command, spec.args, {
|
|
74
|
+
cwd,
|
|
75
|
+
stdio,
|
|
76
|
+
shell: spec.shell,
|
|
77
|
+
encoding,
|
|
78
|
+
...(input !== undefined ? { input } : {}),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function quoteWindowsShellValue(value: string): string {
|
|
83
|
+
return `"${String(value ?? "").replace(/"/g, '\\"')}"`;
|
|
84
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import process from "node:process";
|
|
3
|
+
import test from "node:test";
|
|
4
|
+
import {
|
|
5
|
+
buildCodexSpawnSpec,
|
|
6
|
+
requiresNodeScriptCodexBin,
|
|
7
|
+
requiresShellWrappedCodexBin,
|
|
8
|
+
} from "../dist/codex-spawn.mjs";
|
|
9
|
+
|
|
10
|
+
test("requiresNodeScriptCodexBin matches js entrypoints only", () => {
|
|
11
|
+
assert.equal(requiresNodeScriptCodexBin("C:/tools/codex.js"), true);
|
|
12
|
+
assert.equal(requiresNodeScriptCodexBin("C:/tools/codex.mjs"), true);
|
|
13
|
+
assert.equal(requiresNodeScriptCodexBin("C:/tools/codex.cjs"), true);
|
|
14
|
+
assert.equal(requiresNodeScriptCodexBin("C:/tools/codex.exe"), false);
|
|
15
|
+
assert.equal(requiresNodeScriptCodexBin("C:/tools/codex.cmd"), false);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("requiresShellWrappedCodexBin matches Windows batch launchers only", () => {
|
|
19
|
+
assert.equal(requiresShellWrappedCodexBin("C:/tools/codex.cmd", "win32"), true);
|
|
20
|
+
assert.equal(requiresShellWrappedCodexBin("C:/tools/codex.bat", "win32"), true);
|
|
21
|
+
assert.equal(requiresShellWrappedCodexBin("C:/tools/codex.exe", "win32"), false);
|
|
22
|
+
assert.equal(requiresShellWrappedCodexBin("C:/tools/codex.js", "win32"), false);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("buildCodexSpawnSpec routes js entrypoints through node", () => {
|
|
26
|
+
const spec = buildCodexSpawnSpec({
|
|
27
|
+
codexBin: "C:/Users/amine/AppData/Roaming/npm/node_modules/@openai/codex/bin/codex.js",
|
|
28
|
+
args: ["login", "status"],
|
|
29
|
+
platform: "win32",
|
|
30
|
+
nodeBin: process.execPath,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
assert.equal(spec.command, process.execPath);
|
|
34
|
+
assert.deepEqual(spec.args, [
|
|
35
|
+
"C:/Users/amine/AppData/Roaming/npm/node_modules/@openai/codex/bin/codex.js",
|
|
36
|
+
"login",
|
|
37
|
+
"status",
|
|
38
|
+
]);
|
|
39
|
+
assert.equal(spec.shell, false);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("buildCodexSpawnSpec keeps exe direct and cmd shell-wrapped", () => {
|
|
43
|
+
const exeSpec = buildCodexSpawnSpec({
|
|
44
|
+
codexBin: "C:/tools/codex.exe",
|
|
45
|
+
args: ["--version"],
|
|
46
|
+
platform: "win32",
|
|
47
|
+
});
|
|
48
|
+
assert.equal(exeSpec.command, "C:/tools/codex.exe");
|
|
49
|
+
assert.deepEqual(exeSpec.args, ["--version"]);
|
|
50
|
+
assert.equal(exeSpec.shell, false);
|
|
51
|
+
|
|
52
|
+
const cmdSpec = buildCodexSpawnSpec({
|
|
53
|
+
codexBin: "C:/tools/codex.cmd",
|
|
54
|
+
args: ["login", "status"],
|
|
55
|
+
platform: "win32",
|
|
56
|
+
});
|
|
57
|
+
assert.equal(cmdSpec.shell, true);
|
|
58
|
+
assert.equal(cmdSpec.args.length, 0);
|
|
59
|
+
assert.match(cmdSpec.command, /^"C:\/tools\/codex\.cmd" "login" "status"$/);
|
|
60
|
+
});
|