






















@@ -23,6 +23,7 @@ import { tmpdir } from "node:os";
2323import { dirname, join, relative, resolve, win32 as pathWin32 } from "node:path";
2424import { fileURLToPath, pathToFileURL } from "node:url";
2525import { isLocalBuildMetadataDistPath } from "./lib/local-build-metadata-paths.mjs";
26+import { buildCmdExeCommandLine } from "./windows-cmd-helpers.mjs";
26272728const SCRIPT_PATH = fileURLToPath(import.meta.url);
2829const PUBLISHED_INSTALLER_BASE_URL = "https://openclaw.ai";
@@ -1674,25 +1675,56 @@ function readInstalledMetadataFromCliPath(cliPath, platform = process.platform)
16741675);
16751676}
167616771677-function resolveInstalledCliInvocation(cliPath, platform = process.platform) {
1678+export function resolveCommandSpawnInvocation(
1679+command,
1680+args,
1681+options = {
1682+platform: process.platform,
1683+comSpec: process.env.ComSpec,
1684+},
1685+) {
1686+const platform = options.platform ?? process.platform;
1687+if (platform === "win32" && /\.(cmd|bat)$/iu.test(command)) {
1688+return {
1689+command: options.comSpec ?? process.env.ComSpec ?? "cmd.exe",
1690+args: ["/d", "/s", "/c", buildCmdExeCommandLine(command, args)],
1691+shell: false,
1692+windowsVerbatimArguments: true,
1693+};
1694+}
1695+return { command, args, shell: false };
1696+}
1697+1698+export function resolveInstalledCliInvocation(
1699+cliPath,
1700+args = [],
1701+options = {
1702+platform: process.platform,
1703+comSpec: process.env.ComSpec,
1704+},
1705+) {
1706+const platform = options.platform ?? process.platform;
16781707if (platform !== "win32") {
1679-return { command: cliPath, argsPrefix: [], shell: false };
1708+return { command: cliPath, args, shell: false };
16801709}
16811710const normalizedCliPath = normalizeWindowsInstalledCliPath(cliPath);
16821711if (!/\.cmd$/iu.test(normalizedCliPath)) {
1683-return { command: normalizedCliPath, argsPrefix: [], shell: false };
1712+return { command: normalizedCliPath, args, shell: false };
16841713}
16851714const entryPath = installedEntryPath(
16861715resolveInstalledPrefixDirFromCliPath(normalizedCliPath, platform),
16871716);
16881717if (existsSync(entryPath)) {
16891718return {
16901719command: process.execPath,
1691-argsPrefix: [entryPath],
1720+args: [entryPath, ...args],
16921721shell: false,
16931722};
16941723}
1695-return { command: normalizedCliPath, argsPrefix: [], shell: true };
1724+return resolveCommandSpawnInvocation(normalizedCliPath, args, {
1725+comSpec: options.comSpec,
1726+ platform,
1727+});
16961728}
1697172916981730async function runPosixShellScript(script, options) {
@@ -1896,8 +1928,11 @@ async function verifyFreshShellCommand(params) {
18961928}
1897192918981930async function runInstalledCli(params) {
1899-const invocation = resolveInstalledCliInvocation(params.cliPath);
1900-return runCommand(invocation.command, [...invocation.argsPrefix, ...params.args], {
1931+const invocation = resolveInstalledCliInvocation(params.cliPath, params.args, {
1932+comSpec: params.env?.ComSpec ?? params.env?.COMSPEC,
1933+platform: process.platform,
1934+});
1935+return runCommandInvocation(invocation, {
19011936cwd: params.cwd,
19021937env: params.env,
19031938logPath: params.logPath,
@@ -1981,11 +2016,9 @@ export function buildReleaseOnboardArgs(params) {
19812016async function startManualGatewayFromInstalledCli(params) {
19822017mkdirSync(dirname(params.logPath), { recursive: true });
19832018const gatewayLog = createWriteStream(params.logPath, { flags: "a" });
1984-const invocation = resolveInstalledCliInvocation(params.cliPath);
1985-const child = spawn(
1986-invocation.command,
2019+const invocation = resolveInstalledCliInvocation(
2020+params.cliPath,
19872021[
1988- ...invocation.argsPrefix,
19892022"gateway",
19902023"run",
19912024"--bind",
@@ -1995,13 +2028,18 @@ async function startManualGatewayFromInstalledCli(params) {
19952028"--force",
19962029],
19972030{
1998-cwd: params.lane.homeDir,
1999-env: params.env,
2000-shell: invocation.shell,
2001-stdio: ["ignore", "pipe", "pipe"],
2002-windowsHide: true,
2031+comSpec: params.env?.ComSpec ?? params.env?.COMSPEC,
2032+platform: process.platform,
20032033},
20042034);
2035+const child = spawn(invocation.command, invocation.args, {
2036+cwd: params.lane.homeDir,
2037+env: params.env,
2038+shell: invocation.shell,
2039+stdio: ["ignore", "pipe", "pipe"],
2040+windowsVerbatimArguments: invocation.windowsVerbatimArguments,
2041+windowsHide: true,
2042+});
20052043child.stdout?.on("data", (chunk) => {
20062044gatewayLog.write(chunk);
20072045});
@@ -3481,14 +3519,23 @@ function gitCommand() {
34813519return process.platform === "win32" ? "git.exe" : "git";
34823520}
348335213484-async function runCommand(command, args, options) {
3522+export async function runCommand(command, args, options) {
3523+const invocation = resolveCommandSpawnInvocation(command, args, {
3524+comSpec: options.env?.ComSpec ?? options.env?.COMSPEC,
3525+platform: process.platform,
3526+});
3527+return runCommandInvocation(invocation, options);
3528+}
3529+3530+async function runCommandInvocation(invocation, options) {
34853531return new Promise((resolvePromise, rejectPromise) => {
3486-const useWindowsShell = process.platform === "win32" && /\.(cmd|bat)$/iu.test(command);
3487-const child = spawn(command, args, {
3532+const commandLabel = `${invocation.command} ${invocation.args.join(" ")}`;
3533+const child = spawn(invocation.command, invocation.args, {
34883534cwd: options.cwd,
34893535env: options.env,
3490-shell: useWindowsShell,
3536+shell: invocation.shell,
34913537stdio: ["ignore", "pipe", "pipe"],
3538+windowsVerbatimArguments: invocation.windowsVerbatimArguments,
34923539windowsHide: true,
34933540});
34943541const logStream = createWriteStream(options.logPath, { flags: "a" });
@@ -3546,15 +3593,13 @@ async function runCommand(command, args, options) {
35463593options.timeoutMs && Number.isFinite(options.timeoutMs)
35473594 ? setTimeout(() => {
35483595timedOut = true;
3549-logStream.write(
3550-`${new Date().toISOString()} timeout command=${command} args=${args.join(" ")}\n`,
3551-);
3596+logStream.write(`${new Date().toISOString()} timeout command=${commandLabel}\n`);
35523597requestKill();
35533598killWaitTimer = setTimeout(() => {
35543599finalize(() => {
35553600rejectPromise(
35563601new Error(
3557-`Command timed out and could not be terminated cleanly: ${command} ${args.join(" ")}`,
3602+`Command timed out and could not be terminated cleanly: ${commandLabel}`,
35583603),
35593604);
35603605});
@@ -3565,16 +3610,14 @@ async function runCommand(command, args, options) {
35653610CROSS_OS_COMMAND_HEARTBEAT_SECONDS > 0
35663611 ? setInterval(() => {
35673612const elapsedSeconds = Math.floor((Date.now() - startedAt) / 1000);
3568-const message = `${new Date().toISOString()} still running after ${elapsedSeconds}s: ${command} ${args.join(" ")}\n`;
3613+const message = `${new Date().toISOString()} still running after ${elapsedSeconds}s: ${commandLabel}\n`;
35693614logStream.write(message);
35703615process.stdout.write(`[release-checks] ${message}`);
35713616}, CROSS_OS_COMMAND_HEARTBEAT_SECONDS * 1000)
35723617 : null;
35733618heartbeatTimer?.unref?.();
357436193575-logStream.write(
3576-`${new Date().toISOString()} start command=${command} args=${args.join(" ")}\n`,
3577-);
3620+logStream.write(`${new Date().toISOString()} start command=${commandLabel}\n`);
3578362135793622child.stdout?.on("data", (chunk) => {
35803623const text = chunk.toString();
@@ -3599,13 +3642,13 @@ async function runCommand(command, args, options) {
35993642 stderr,
36003643};
36013644if (timedOut) {
3602-rejectPromise(new Error(`Command timed out: ${command} ${args.join(" ")}`));
3645+rejectPromise(new Error(`Command timed out: ${commandLabel}`));
36033646return;
36043647}
36053648if ((options.check ?? true) && result.exitCode !== 0) {
36063649rejectPromise(
36073650new Error(
3608-`Command failed (${result.exitCode}): ${command} ${args.join(" ")}\n${trimForSummary(
3651+`Command failed (${result.exitCode}): ${commandLabel}\n${trimForSummary(
36093652 `${stdout}\n${stderr}`,
36103653 )}`,
36113654),
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。