fix(cli): handle Volta shim respawns

This commit is contained in:
Peter Steinberger
2026-04-26 06:48:50 +01:00
parent e4e69c5bc6
commit e69c2853b2
4 changed files with 60 additions and 2 deletions

View File

@@ -74,6 +74,7 @@ Docs: https://docs.openclaw.ai
- Installer/macOS: rerun Homebrew install steps without the gum spinner when raw-mode ioctl failures occur, and avoid claiming `node@24` was installed when the Homebrew keg binary is missing. Fixes #70411. Thanks @1fanwang and @dad-io.
- Installer: load nvm before Node.js detection so `curl | bash` installs respect nvm-managed Node instead of stale system Node. Fixes #49556. Thanks @heavenlxj.
- CLI/Volta: respawn raw `openclaw` CLI runs through the named `node` shim when the current Node executable resolves to `volta-shim`, avoiding direct shim execution failures in non-interactive shells. Fixes #68672. Thanks @sanchezm86.
- Docker: copy patched dependency files into runtime images so downstream `pnpm install` layers keep working. Fixes #69224. Thanks @gucasbrg.
- Agents/runtime: submit heartbeat, cron, and exec wakeups as transient runtime context instead of visible user prompts, keeping synthetic system work out of chat transcripts. Fixes #66496 and #66814. Thanks @jeades and @mandomaker.
- Telegram: include native quote excerpts automatically for threaded replies and reply tags when the original Telegram text is available, without adding another config knob. Fixes #6975. Thanks @rex05ai.

View File

@@ -4,6 +4,7 @@ import {
EXPERIMENTAL_WARNING_FLAG,
OPENCLAW_NODE_EXTRA_CA_CERTS_READY,
OPENCLAW_NODE_OPTIONS_READY,
resolveCliRespawnCommand,
} from "./entry.respawn.js";
const shouldSkipRespawnForArgvMock = vi.hoisted(() => vi.fn(() => false));
@@ -42,6 +43,7 @@ describe("buildCliRespawnPlan", () => {
});
expect(plan).not.toBeNull();
expect(plan?.command).toBe(process.execPath);
expect(plan?.argv[0]).toBe(EXPERIMENTAL_WARNING_FLAG);
expect(plan?.env.NODE_EXTRA_CA_CERTS).toBe("/etc/ssl/certs/ca-certificates.crt");
expect(plan?.env[OPENCLAW_NODE_EXTRA_CA_CERTS_READY]).toBe("1");
@@ -88,4 +90,35 @@ describe("buildCliRespawnPlan", () => {
}),
).toBeNull();
});
it("respawns Volta shims through node so the shim is not called directly", () => {
const plan = buildCliRespawnPlan({
argv: ["/home/alice/.volta/bin/volta-shim", "/usr/local/bin/openclaw", "status"],
env: { PATH: "/home/alice/.volta/bin:/usr/bin:/bin" },
execArgv: [],
execPath: "/home/alice/.volta/bin/volta-shim",
autoNodeExtraCaCerts: undefined,
platform: "linux",
});
expect(plan?.command).toBe("node");
expect(plan?.argv).toEqual([EXPERIMENTAL_WARNING_FLAG, "/usr/local/bin/openclaw", "status"]);
});
});
describe("resolveCliRespawnCommand", () => {
it("keeps normal node paths absolute", () => {
expect(resolveCliRespawnCommand({ execPath: "/usr/bin/node", platform: "linux" })).toBe(
"/usr/bin/node",
);
});
it("maps Volta's Unix shim target back to the named node shim", () => {
expect(
resolveCliRespawnCommand({
execPath: "/home/alice/.volta/bin/volta-shim",
platform: "linux",
}),
).toBe("node");
});
});

View File

@@ -1,3 +1,4 @@
import path from "node:path";
import { resolveNodeStartupTlsEnvironment } from "./bootstrap/node-startup-env.js";
import { shouldSkipRespawnForArgv } from "./cli/respawn-policy.js";
import { isTruthyEnvValue } from "./infra/env.js";
@@ -6,6 +7,28 @@ export const EXPERIMENTAL_WARNING_FLAG = "--disable-warning=ExperimentalWarning"
export const OPENCLAW_NODE_OPTIONS_READY = "OPENCLAW_NODE_OPTIONS_READY";
export const OPENCLAW_NODE_EXTRA_CA_CERTS_READY = "OPENCLAW_NODE_EXTRA_CA_CERTS_READY";
export type CliRespawnPlan = {
command: string;
argv: string[];
env: NodeJS.ProcessEnv;
};
function pathModuleForPlatform(platform: NodeJS.Platform): typeof path.posix {
return platform === "win32" ? path.win32 : path.posix;
}
export function resolveCliRespawnCommand(params: {
execPath: string;
platform?: NodeJS.Platform;
}): string {
const platform = params.platform ?? process.platform;
const basename = pathModuleForPlatform(platform).basename(params.execPath).toLowerCase();
if (basename === "volta-shim" || basename === "volta-shim.exe") {
return "node";
}
return params.execPath;
}
export function hasExperimentalWarningSuppressed(
params: {
env?: NodeJS.ProcessEnv;
@@ -30,7 +53,7 @@ export function buildCliRespawnPlan(
autoNodeExtraCaCerts?: string | undefined;
platform?: NodeJS.Platform;
} = {},
): { argv: string[]; env: NodeJS.ProcessEnv } | null {
): CliRespawnPlan | null {
const argv = params.argv ?? process.argv;
const env = params.env ?? process.env;
const execArgv = params.execArgv ?? process.execArgv;
@@ -80,6 +103,7 @@ export function buildCliRespawnPlan(
}
return {
command: resolveCliRespawnCommand({ execPath, platform }),
argv: [...childExecArgv, ...argv.slice(1)],
env: childEnv,
};

View File

@@ -70,7 +70,7 @@ if (
return false;
}
const child = spawn(process.execPath, plan.argv, {
const child = spawn(plan.command, plan.argv, {
stdio: "inherit",
env: plan.env,
});