fix(cli): preserve Windows user argv after script entry (#121064)

This commit is contained in:
Peter Steinberger
2026-08-09 07:56:15 -07:00
committed by GitHub
parent 623866a30e
commit f806abf43b
4 changed files with 72 additions and 22 deletions
+1 -1
View File
@@ -528,8 +528,8 @@ describe("registerChannelsCli", () => {
mockProcessPlatform("win32");
process.argv = [
"C:\\Program Files\\nodejs\\node.exe",
"C:\\repo\\openclaw.js",
"C:\\Program Files\\nodejs\\node.exe",
"C:\\repo\\openclaw.js",
"channels",
"add",
"--channel",
+48
View File
@@ -83,4 +83,52 @@ describe("normalizeWindowsArgv", () => {
platform.mockRestore();
}
});
it("preserves node.exe as the first user argument after the script entry", () => {
const platform = mockProcessPlatform("win32");
try {
expect(
normalizeWindowsArgv([
"C:\\Program Files\\nodejs\\node.exe",
"C:\\pkg\\openclaw.mjs",
"node.exe",
"--help",
]),
).toEqual([
"C:\\Program Files\\nodejs\\node.exe",
"C:\\pkg\\openclaw.mjs",
"node.exe",
"--help",
]);
} finally {
platform.mockRestore();
}
});
it("preserves a post-script node.exe argument after normalizing a duplicated prefix", () => {
const platform = mockProcessPlatform("win32");
try {
expect(
normalizeWindowsArgv([
"C:\\Program Files\\nodejs\\node.exe",
"C:\\Program Files\\nodejs\\node.exe",
"C:\\pkg\\openclaw.mjs",
"node.exe",
"--help",
]),
).toEqual([
"C:\\Program Files\\nodejs\\node.exe",
"C:\\pkg\\openclaw.mjs",
"node.exe",
"--help",
]);
} finally {
platform.mockRestore();
}
});
it("does not normalize POSIX argv", () => {
const argv = ["/usr/bin/node", "/opt/openclaw/openclaw.mjs", "node.exe", "--help"];
expect(normalizeWindowsArgv(argv, { platform: "linux" })).toBe(argv);
});
});
+1 -19
View File
@@ -58,31 +58,13 @@ export function normalizeWindowsArgv(
);
};
const argv0IsExecPath = isExecPath(argv[0]);
const next = [...argv];
let removedLauncherPrefix = false;
for (const i = 1; i < next.length;) {
if (isExecPath(next[i])) {
next.splice(i, 1);
removedLauncherPrefix = true;
continue;
}
break;
}
if (next.length < 3 || (!argv0IsExecPath && !removedLauncherPrefix)) {
return next;
}
const cleaned = [...next];
for (const i = 2; i < cleaned.length;) {
const arg = cleaned[i];
if (!arg || arg.startsWith("-")) {
break;
}
if (isExecPath(arg)) {
cleaned.splice(i, 1);
continue;
}
break;
}
return cleaned;
return next;
}
+22 -2
View File
@@ -216,7 +216,7 @@ describe("buildCliRespawnPlan", () => {
expect(respawnPlan.detachForProcessTree).toBe(false);
});
it("normalizes duplicated Windows node.exe argv before respawning", () => {
it("normalizes a duplicated Windows node.exe launcher prefix before respawning", () => {
const scriptPath =
"C:\\Users\\alice\\AppData\\Roaming\\npm\\node_modules\\openclaw\\openclaw.mjs";
const plan = buildCliRespawnPlan({
@@ -224,7 +224,6 @@ describe("buildCliRespawnPlan", () => {
"C:\\Program Files\\nodejs\\node.exe",
"C:\\Program Files\\nodejs\\node.exe",
scriptPath,
"node.exe",
"dashboard",
"--no-open",
],
@@ -238,6 +237,27 @@ describe("buildCliRespawnPlan", () => {
expect(respawnPlan.argv).toEqual(["--stack-size=8192", scriptPath, "dashboard", "--no-open"]);
});
it("preserves post-script node.exe arguments after normalizing the launcher prefix", () => {
const scriptPath =
"C:\\Users\\alice\\AppData\\Roaming\\npm\\node_modules\\openclaw\\openclaw.mjs";
const plan = buildCliRespawnPlan({
argv: [
"C:\\Program Files\\nodejs\\node.exe",
"C:\\Program Files\\nodejs\\node.exe",
scriptPath,
"node.exe",
"status",
],
env: {},
execArgv: [],
execPath: "C:\\Program Files\\nodejs\\node.exe",
platform: "win32",
});
const respawnPlan = expectCliRespawnPlan(plan);
expect(respawnPlan.argv).toEqual(["--stack-size=8192", scriptPath, "node.exe", "status"]);
});
it("does not respawn on Windows when stack size is already configured", () => {
expect(
buildCliRespawnPlan({