




























@@ -42,6 +42,7 @@ const buildScript = "scripts/tsdown-build.mjs";
4242const bundledPluginAssetsScript = "scripts/bundled-plugin-assets.mjs";
4343const compilerArgs = [buildScript, "--no-clean"];
4444const bundledPluginAssetBuildArgs = [bundledPluginAssetsScript, "--phase", "build"];
45+const RUN_NODE_SIGNAL_FORCE_KILL_AFTER_MS = 5_000;
45464647const runtimePostBuildWatchedPaths = [
4748"scripts/copy-bundled-plugin-metadata.mjs",
@@ -929,10 +930,37 @@ const resolveRunNodeDiagnosticArgs = (deps) => {
929930return args;
930931};
931932933+const shouldUseRunNodeChildProcessGroup = (deps) =>
934+deps.platform !== "win32" && deps.process.stdin?.isTTY !== true;
935+936+const signalSpawnedProcess = (childProcess, signal, useProcessGroup, deps) => {
937+if (useProcessGroup && typeof childProcess.pid === "number") {
938+try {
939+deps.signalProcess(-childProcess.pid, signal);
940+return;
941+} catch (error) {
942+if (error?.code === "ESRCH" || error?.code === "EPERM") {
943+return;
944+}
945+}
946+}
947+try {
948+childProcess.kill?.(signal);
949+} catch {
950+// Best-effort only. Exit handling still happens via the child "exit" event.
951+}
952+};
953+932954const waitForSpawnedProcess = async (childProcess, deps) => {
933955let forwardedSignal = null;
956+let forceKillTimer = null;
957+let cleanedForwardedSignalGroup = false;
958+const useProcessGroup = shouldUseRunNodeChildProcessGroup(deps);
934959935960const cleanupSignals = () => {
961+if (forceKillTimer) {
962+clearTimeout(forceKillTimer);
963+}
936964if (onSigInt) {
937965deps.process.off("SIGINT", onSigInt);
938966}
@@ -946,11 +974,11 @@ const waitForSpawnedProcess = async (childProcess, deps) => {
946974return;
947975}
948976forwardedSignal = signal;
949-try {
950- childProcess.kill?.(signal);
951-} catch {
952-// Best-effort only. Exit handling still happens via the child "exit" event.
953-}
977+signalSpawnedProcess(childProcess, signal, useProcessGroup, deps);
978+forceKillTimer = setTimeout(() => {
979+ forceKillTimer = null;
980+signalSpawnedProcess(childProcess, "SIGKILL", useProcessGroup, deps);
981+}, RUN_NODE_SIGNAL_FORCE_KILL_AFTER_MS);
954982};
955983956984const onSigInt = () => {
@@ -978,6 +1006,10 @@ const waitForSpawnedProcess = async (childProcess, deps) => {
9781006settle({ exitCode: 1, exitSignal: null, forwardedSignal });
9791007});
9801008childProcess.on("exit", (exitCode, exitSignal) => {
1009+if (forwardedSignal && !cleanedForwardedSignalGroup) {
1010+cleanedForwardedSignalGroup = true;
1011+signalSpawnedProcess(childProcess, "SIGKILL", useProcessGroup, deps);
1012+}
9811013settle({ exitCode, exitSignal, forwardedSignal });
9821014});
9831015});
@@ -998,8 +1030,10 @@ const getInterruptedSpawnExitCode = (res) => {
99810309991031const runOpenClaw = async (deps) => {
10001032const diagnosticArgs = resolveRunNodeDiagnosticArgs(deps);
1033+const useProcessGroup = shouldUseRunNodeChildProcessGroup(deps);
10011034const nodeProcess = deps.spawn(deps.execPath, [...diagnosticArgs, "openclaw.mjs", ...deps.args], {
10021035cwd: deps.cwd,
1036+detached: useProcessGroup,
10031037env: deps.env,
10041038stdio: deps.outputTee ? ["inherit", "pipe", "pipe"] : "inherit",
10051039});
@@ -1353,11 +1387,13 @@ const shouldRunQaCoverageReportFromSource = (deps, buildRequirement) =>
1353138713541388const runQaParityReportFromSource = async (deps) => {
13551389const sourceEntrypoint = path.join(deps.cwd, "scripts", "qa-parity-report.ts");
1390+const useProcessGroup = shouldUseRunNodeChildProcessGroup(deps);
13561391const nodeProcess = deps.spawn(
13571392deps.execPath,
13581393["--import", "tsx", sourceEntrypoint, ...deps.args.slice(2)],
13591394{
13601395cwd: deps.cwd,
1396+detached: useProcessGroup,
13611397env: deps.env,
13621398stdio: deps.outputTee ? ["inherit", "pipe", "pipe"] : "inherit",
13631399},
@@ -1373,11 +1409,13 @@ const runQaParityReportFromSource = async (deps) => {
1373140913741410const runQaCoverageReportFromSource = async (deps) => {
13751411const sourceEntrypoint = path.join(deps.cwd, "scripts", "qa-coverage-report.ts");
1412+const useProcessGroup = shouldUseRunNodeChildProcessGroup(deps);
13761413const nodeProcess = deps.spawn(
13771414deps.execPath,
13781415["--import", "tsx", sourceEntrypoint, ...deps.args.slice(2)],
13791416{
13801417cwd: deps.cwd,
1418+detached: useProcessGroup,
13811419env: deps.env,
13821420stdio: deps.outputTee ? ["inherit", "pipe", "pipe"] : "inherit",
13831421},
@@ -1406,6 +1444,8 @@ export async function runNodeMain(params = {}) {
14061444cwd: params.cwd ?? process.cwd(),
14071445args: params.args ?? process.argv.slice(2),
14081446env: params.env ? { ...params.env } : { ...process.env },
1447+platform: params.platform ?? process.platform,
1448+signalProcess: params.signalProcess ?? process.kill,
14091449runRuntimePostBuild: params.runRuntimePostBuild ?? runRuntimePostBuild,
14101450};
14111451@@ -1496,6 +1536,7 @@ export async function runNodeMain(params = {}) {
14961536async () => {
14971537const assetBuild = deps.spawn(buildCmd, bundledPluginAssetBuildArgs, {
14981538cwd: deps.cwd,
1539+detached: shouldUseRunNodeChildProcessGroup(deps),
14991540env: deps.env,
15001541stdio: ["inherit", "pipe", "pipe"],
15011542});
@@ -1511,6 +1552,7 @@ export async function runNodeMain(params = {}) {
1511155215121553const build = deps.spawn(buildCmd, compilerArgs, {
15131554cwd: deps.cwd,
1555+detached: shouldUseRunNodeChildProcessGroup(deps),
15141556env: {
15151557 ...deps.env,
15161558[RUN_NODE_SKIP_DTS_BUILD_ENV]: deps.env[RUN_NODE_SKIP_DTS_BUILD_ENV] ?? "1",
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。