





























@@ -1370,6 +1370,77 @@ describe("scripts/openclaw-cross-os-release-checks", () => {
13701370}
13711371});
137213721373+it("exits promptly after externally signaled commands close", async () => {
1374+if (process.platform === "win32") {
1375+return;
1376+}
1377+1378+const dir = mkdtempSync(join(tmpdir(), "openclaw-cross-os-run-command-signal-exit-"));
1379+const childPidPath = join(dir, "child.pid");
1380+const logPath = join(dir, "signal.log");
1381+const scriptUrl = pathToFileURL(
1382+resolvePath("scripts/openclaw-cross-os-release-checks.ts"),
1383+).href;
1384+let childPid: number | undefined;
1385+let runnerPid: number | undefined;
1386+1387+try {
1388+const childScript = "setInterval(() => {}, 1000);";
1389+const parentScript = [
1390+"const { spawn } = require('node:child_process');",
1391+"const fs = require('node:fs');",
1392+`const child = spawn(process.execPath, ['-e', ${JSON.stringify(childScript)}], { stdio: 'ignore' });`,
1393+"process.stdout.write('signal cleanup log sentinel\\n', () => {",
1394+" fs.writeFileSync(process.env.OPENCLAW_TEST_CHILD_PID, String(child.pid));",
1395+"});",
1396+"setInterval(() => {}, 1000);",
1397+].join("");
1398+const runnerScript = [
1399+`import { runCommand } from ${JSON.stringify(scriptUrl)};`,
1400+`await runCommand(process.execPath, ['-e', ${JSON.stringify(parentScript)}], {`,
1401+` cwd: ${JSON.stringify(dir)},`,
1402+` env: process.env,`,
1403+` logPath: ${JSON.stringify(logPath)},`,
1404+` timeoutMs: 60000,`,
1405+`});`,
1406+].join("\n");
1407+const runner = spawn(
1408+process.execPath,
1409+["--import", "tsx", "--input-type=module", "-e", runnerScript],
1410+{
1411+cwd: process.cwd(),
1412+env: {
1413+ ...process.env,
1414+OPENCLAW_CROSS_OS_PROCESS_TREE_KILL_AFTER_MS: "3000",
1415+OPENCLAW_TEST_CHILD_PID: childPidPath,
1416+},
1417+stdio: ["ignore", "ignore", "pipe"],
1418+},
1419+);
1420+runnerPid = runner.pid;
1421+1422+await waitForFile(childPidPath, 2_000);
1423+childPid = Number.parseInt(readFileSync(childPidPath, "utf8"), 10);
1424+const signaledAt = Date.now();
1425+runner.kill("SIGTERM");
1426+const result = await waitForExit(runner, 5_000);
1427+const elapsedMs = Date.now() - signaledAt;
1428+1429+expect(result).toEqual({ signal: null, status: 143 });
1430+expect(elapsedMs).toBeLessThan(2_000);
1431+expect(readFileSync(logPath, "utf8")).toContain("signal cleanup log sentinel");
1432+await waitForDead(childPid, 2_000);
1433+} finally {
1434+if (runnerPid !== undefined && isProcessAlive(runnerPid)) {
1435+process.kill(runnerPid, "SIGKILL");
1436+}
1437+if (childPid !== undefined && isProcessAlive(childPid)) {
1438+process.kill(childPid, "SIGKILL");
1439+}
1440+rmSync(dir, { recursive: true, force: true });
1441+}
1442+});
1443+13731444it("derives the installed prefix from resolved CLI paths", () => {
13741445expect(
13751446resolveInstalledPrefixDirFromCliPath(
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。