diff --git a/docs/help/debugging.md b/docs/help/debugging.md index 70220c6d76c..073cfed9ce2 100644 --- a/docs/help/debugging.md +++ b/docs/help/debugging.md @@ -96,6 +96,10 @@ add Node's sync I/O trace flag through the source runner: OPENCLAW_TRACE_SYNC_IO=1 pnpm openclaw gateway --force ``` +`pnpm gateway:watch` enables this flag by default for the watched Gateway child. +Set `OPENCLAW_TRACE_SYNC_IO=0` to suppress Node sync I/O trace output in watch +mode. + ## Gateway watch mode For fast iteration, run the gateway under the file watcher: diff --git a/scripts/watch-node.mjs b/scripts/watch-node.mjs index 6cb65d6dbc9..da64b7cff0a 100644 --- a/scripts/watch-node.mjs +++ b/scripts/watch-node.mjs @@ -277,6 +277,9 @@ export async function runWatchMain(params = {}) { // The watcher owns process restarts; keep SIGUSR1/config reloads in-process // so inherited launchd/systemd markers do not make the child exit and stall. childEnv.OPENCLAW_NO_RESPAWN = "1"; + if (isGatewayWatchCommand(deps.args) && childEnv.OPENCLAW_TRACE_SYNC_IO === undefined) { + childEnv.OPENCLAW_TRACE_SYNC_IO = "1"; + } if (deps.args.length > 0) { childEnv.OPENCLAW_WATCH_COMMAND = deps.args.join(" "); } diff --git a/src/infra/watch-node.test.ts b/src/infra/watch-node.test.ts index 3e6f787e3c9..84fff891353 100644 --- a/src/infra/watch-node.test.ts +++ b/src/infra/watch-node.test.ts @@ -143,6 +143,7 @@ describe("watch-node script", () => { OPENCLAW_WATCH_MODE: "1", OPENCLAW_WATCH_SESSION: "1700000000000-4242", OPENCLAW_NO_RESPAWN: "1", + OPENCLAW_TRACE_SYNC_IO: "1", OPENCLAW_WATCH_COMMAND: "gateway --force", }), }), @@ -155,6 +156,35 @@ describe("watch-node script", () => { }); }); + it("preserves explicit sync I/O trace overrides for gateway watch", async () => { + const { child, spawn, createWatcher, fakeProcess } = createWatchHarness(); + await withTempDir({ prefix: "openclaw-watch-node-" }, async (cwd) => { + const runPromise = runWatch({ + args: ["gateway", "--force"], + cwd, + createWatcher, + env: { OPENCLAW_TRACE_SYNC_IO: "0" }, + lockDisabled: true, + process: fakeProcess, + spawn, + }); + + expect(spawn).toHaveBeenCalledWith( + "/usr/local/bin/node", + ["scripts/run-node.mjs", "gateway", "--force"], + expect.objectContaining({ + env: expect.objectContaining({ + OPENCLAW_TRACE_SYNC_IO: "0", + }), + }), + ); + + fakeProcess.emit("SIGINT"); + await runPromise; + expect(child.kill).toHaveBeenCalledWith("SIGTERM"); + }); + }); + it("starts the runner before loading chokidar", async () => { const child = Object.assign(new EventEmitter(), { kill: vi.fn(() => {}),