diff --git a/scripts/npm-runner.mjs b/scripts/npm-runner.mjs index be4be3339993..597e41cf6efc 100644 --- a/scripts/npm-runner.mjs +++ b/scripts/npm-runner.mjs @@ -11,10 +11,7 @@ function resolveToolchainNpmRunner(params) { const npmCliPath = npmCliCandidates.find((candidate) => params.existsSync(candidate)); if (npmCliPath) { return { - command: - params.platform === "win32" - ? params.pathImpl.join(params.nodeDir, "node.exe") - : params.pathImpl.join(params.nodeDir, "node"), + command: params.execPath, args: [npmCliPath, ...params.npmArgs], shell: false, }; @@ -57,6 +54,7 @@ export function resolveNpmRunner(params = {}) { const npmToolchain = resolveToolchainNpmRunner({ comSpec, existsSync, + execPath, nodeDir, npmArgs, pathImpl, diff --git a/test/scripts/npm-runner.test.ts b/test/scripts/npm-runner.test.ts index 7378ad767a79..258ac6bd9ff5 100644 --- a/test/scripts/npm-runner.test.ts +++ b/test/scripts/npm-runner.test.ts @@ -25,6 +25,28 @@ describe("resolveNpmRunner", () => { }); }); + it("uses the active node executable when its basename is not node", () => { + const execPath = "/Users/test/.toolchains/node-24/bin/node24"; + const expectedNpmCliPath = path.posix.resolve( + path.posix.dirname(execPath), + "../lib/node_modules/npm/bin/npm-cli.js", + ); + + const runner = resolveNpmRunner({ + execPath, + env: {}, + existsSync: (candidate) => candidate === expectedNpmCliPath, + npmArgs: ["pack", "openclaw@beta"], + platform: "darwin", + }); + + expect(runner).toEqual({ + command: execPath, + args: [expectedNpmCliPath, "pack", "openclaw@beta"], + shell: false, + }); + }); + it("anchors Windows npm staging to the adjacent npm-cli.js without a shell", () => { const execPath = "C:\\nodejs\\node.exe"; const expectedNpmCliPath = path.win32.resolve(