diff --git a/scripts/install-codex-cloud-command-shims.sh b/scripts/install-codex-cloud-command-shims.sh index 1aad4edb4f..5acf0ca43a 100644 --- a/scripts/install-codex-cloud-command-shims.sh +++ b/scripts/install-codex-cloud-command-shims.sh @@ -56,11 +56,11 @@ if [[ -n "$resolved_node_version" && "$resolved_node_version" != "N/A" ]]; then else fallback_node="$(PATH="$fallback_path" command -v node 2>/dev/null || true)" [[ -n "$fallback_node" ]] || fail "Node ${expected_node_major} is unavailable through nvm or PATH." - fallback_node_version="$($fallback_node -p 'process.versions.node' 2>/dev/null || true)" + fallback_node_version="$("$fallback_node" -p 'process.versions.node' 2>/dev/null || true)" node_version_supported "$fallback_node_version" || fail "Node ${expected_node_major}.x >=24.15.0 and <25 is required; detected ${fallback_node_version:-unavailable}." node_bin="$(cd "$(dirname "$fallback_node")" && pwd)" fi -node_version="$($node_bin/node -p 'process.versions.node' 2>/dev/null || true)" +node_version="$("$node_bin/node" -p 'process.versions.node' 2>/dev/null || true)" node_version_supported "$node_version" || fail "Node ${expected_node_major}.x >=24.15.0 and <25 is required; detected ${node_version:-unavailable}." mkdir -p "$HOME/.local/bin" for command_name in node npm npx; do diff --git a/tests/codex-cloud-setup.test.ts b/tests/codex-cloud-setup.test.ts index d16e69d29e..1671482ffa 100644 --- a/tests/codex-cloud-setup.test.ts +++ b/tests/codex-cloud-setup.test.ts @@ -815,6 +815,37 @@ describe("Codex Cloud environment contract", () => { expect(resolvedPath).toEqual(expect.arrayContaining(["/usr/bin", "/bin"])); }, 120_000); + it("probes a supported Node installed under a directory containing spaces", () => { + const homeRoot = temporaryDirectory("codex-cloud-shim-space-"); + const home = path.join(homeRoot, "home with spaces"); + mkdirSync(home, { recursive: true }); + const bashHome = bashPathEntry(home); + const nodeVersion = "v24.19.0"; + const nodeBin = path.join(home, ".nvm", "versions", "node", nodeVersion, "bin"); + mkdirSync(nodeBin, { recursive: true }); + for (const command of ["node", "npm", "npx"]) { + const executable = path.join(nodeBin, command); + writeFileSync( + executable, + command === "node" + ? `#!/usr/bin/env bash\nif [[ "\${1:-}" = "-p" ]]; then printf "%s\\n" "${nodeVersion.slice(1)}"; fi\n` + : '#!/usr/bin/env bash\nprintf "%s\\n" "$PATH"\n', + ); + chmodSync(executable, 0o755); + } + writeFileSync( + path.join(home, ".clinical-kb-codex-cloud.sh"), + [`export NVM_DIR="${bashHome}/.nvm"`, `nvm() { printf '${nodeVersion}\\n'; }`, ""].join("\n"), + ); + + const install = spawnSync(bashCommand, ["scripts/install-codex-cloud-command-shims.sh"], { + cwd: repoRoot, + encoding: "utf8", + env: { ...process.env, HOME: bashHome, PATH: bashPathList(process.env.PATH || "") }, + }); + expect(install.status, install.stderr || install.stdout).toBe(0); + }, 120_000); + it("only accepts a PATH fallback npm and npx that meet the repository npm range", () => { const home = temporaryDirectory("codex-cloud-shim-npm-home-"); const bashHome = bashPathEntry(home);