fix(dev): forward run-node wrapper signals

This commit is contained in:
Peter Steinberger
2026-04-05 17:05:12 +01:00
parent 9e8151f347
commit 17521116db
3 changed files with 138 additions and 11 deletions

View File

@@ -1,8 +1,9 @@
import { EventEmitter } from "node:events";
import fsSync from "node:fs";
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { describe, expect, it } from "vitest";
import { describe, expect, it, vi } from "vitest";
import { resolveBuildRequirement, runNodeMain } from "../../scripts/run-node.mjs";
import {
bundledDistPluginFile,
@@ -54,6 +55,13 @@ function createExitedProcess(code: number | null, signal: string | null = null)
};
}
function createFakeProcess() {
return Object.assign(new EventEmitter(), {
pid: 4242,
execPath: process.execPath,
}) as unknown as NodeJS.Process;
}
async function writeRuntimePostBuildScaffold(tmp: string): Promise<void> {
const pluginSdkAliasPath = path.join(tmp, "src", "plugin-sdk", "root-alias.cjs");
await fs.mkdir(path.dirname(pluginSdkAliasPath), { recursive: true });
@@ -341,6 +349,66 @@ describe("run-node script", () => {
});
});
it("forwards wrapper SIGTERM to the active openclaw child and returns 143", async () => {
await withTempDir(async (tmp) => {
await setupTrackedProject(tmp, {
files: {
[ROOT_SRC]: "export const value = 1;\n",
},
oldPaths: [ROOT_SRC, ROOT_TSCONFIG, ROOT_PACKAGE],
buildPaths: [DIST_ENTRY, BUILD_STAMP],
});
const fakeProcess = createFakeProcess();
const child = Object.assign(new EventEmitter(), {
kill: vi.fn((signal: string) => {
queueMicrotask(() => child.emit("exit", 0, null));
return signal;
}),
});
const spawn = vi.fn<
(
cmd: string,
args: string[],
options: unknown,
) => {
kill: (signal?: string) => string;
on: (event: "exit", cb: (code: number | null, signal: string | null) => void) => void;
}
>(() => ({
kill: (signal) => child.kill(String(signal ?? "SIGTERM")),
on: (event, cb) => {
child.on(event, cb);
},
}));
const exitCodePromise = runNodeMain({
cwd: tmp,
args: ["status"],
env: {
...process.env,
OPENCLAW_RUNNER_LOG: "0",
},
process: fakeProcess,
spawn,
execPath: process.execPath,
});
fakeProcess.emit("SIGTERM");
const exitCode = await exitCodePromise;
expect(exitCode).toBe(143);
expect(spawn).toHaveBeenCalledWith(
process.execPath,
["openclaw.mjs", "status"],
expect.objectContaining({ stdio: "inherit" }),
);
expect(child.kill).toHaveBeenCalledWith("SIGTERM");
expect(fakeProcess.listenerCount("SIGINT")).toBe(0);
expect(fakeProcess.listenerCount("SIGTERM")).toBe(0);
});
});
it("rebuilds when extension sources are newer than the build stamp", async () => {
await withTempDir(async (tmp) => {
await setupTrackedProject(tmp, {