fix(runtime-smoke): kill bundled child trees on windows

This commit is contained in:
Vincent Koc
2026-06-20 13:24:14 +02:00
parent 6b82d4ecb7
commit 4514b5a387
2 changed files with 46 additions and 2 deletions

View File

@@ -641,8 +641,16 @@ function processTreeIsAlive(child) {
}
}
function signalChildProcessTree(child, signal) {
if (process.platform !== "win32" && typeof child.pid === "number") {
function defaultRunTaskkill(command, args, options) {
return childProcess.spawnSync(command, args, options);
}
export function signalChildProcessTree(
child,
signal,
{ platform = process.platform, runTaskkill = defaultRunTaskkill } = {},
) {
if (platform !== "win32" && typeof child.pid === "number") {
try {
process.kill(-child.pid, signal);
return;
@@ -651,6 +659,16 @@ function signalChildProcessTree(child, signal) {
// the legacy direct-child kill path as the fallback.
}
}
if (platform === "win32" && typeof child.pid === "number") {
const args = ["/PID", String(child.pid), "/T"];
if (signal === "SIGKILL") {
args.push("/F");
}
const result = runTaskkill("taskkill", args, { stdio: "ignore" });
if (!result?.error && result?.status === 0) {
return;
}
}
try {
child.kill(signal);
} catch (error) {

View File

@@ -456,6 +456,32 @@ describe("bundled plugin install/uninstall probe", () => {
expect(child.kill).not.toHaveBeenCalled();
});
it("signals Windows runtime child process trees with taskkill", async () => {
const runtimeSmoke = await import(pathToFileURL(runtimeSmokePath).href);
const child = {
kill: vi.fn(),
pid: 12345,
};
const runTaskkill = vi.fn(() => ({ error: undefined, status: 0 }));
runtimeSmoke.signalChildProcessTree(child, "SIGTERM", {
platform: "win32",
runTaskkill,
});
expect(runTaskkill).toHaveBeenNthCalledWith(1, "taskkill", ["/PID", "12345", "/T"], {
stdio: "ignore",
});
runtimeSmoke.signalChildProcessTree(child, "SIGKILL", {
platform: "win32",
runTaskkill,
});
expect(runTaskkill).toHaveBeenNthCalledWith(2, "taskkill", ["/PID", "12345", "/T", "/F"], {
stdio: "ignore",
});
expect(child.kill).not.toHaveBeenCalled();
});
it.runIf(process.platform !== "win32")("stops runtime gateway process groups", async () => {
const runtimeSmoke = await importRuntimeSmokeWithEnv({
OPENCLAW_BUNDLED_PLUGIN_RUNTIME_TEARDOWN_GRACE_MS: "50",