mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 03:31:23 +00:00
fix(process): disable detached spawn on Windows to fix empty exec output (#18035)
The supervisor's child adapter always spawned with `detached: true`,
which creates a new process group. On Windows Scheduled Tasks (headless,
no console), this prevents stdout/stderr pipes from properly connecting,
causing all exec tool output to silently disappear.
The old exec path (pre-supervisor refactor) never used `detached: true`.
The regression was introduced in cd44a0d01 (refactor process spawning).
Changes:
- child.ts: set `detached: false` on Windows, keep `detached: true` on
POSIX (where it's needed to survive parent exit). Skip the no-detach
fallback on Windows since it's already the default.
- child.test.ts: platform-aware assertions for detached behavior.
Fixes #18035
Fixes #17806
This commit is contained in:
committed by
Peter Steinberger
parent
d0a5ee0176
commit
a1a1f56841
@@ -60,8 +60,15 @@ describe("createChildAdapter", () => {
|
|||||||
options?: { detached?: boolean };
|
options?: { detached?: boolean };
|
||||||
fallbacks?: Array<{ options?: { detached?: boolean } }>;
|
fallbacks?: Array<{ options?: { detached?: boolean } }>;
|
||||||
};
|
};
|
||||||
expect(spawnArgs.options?.detached).toBe(true);
|
// On Windows, detached defaults to false (headless Scheduled Task compat);
|
||||||
expect(spawnArgs.fallbacks?.[0]?.options?.detached).toBe(false);
|
// on POSIX, detached is true with a no-detach fallback.
|
||||||
|
if (process.platform === "win32") {
|
||||||
|
expect(spawnArgs.options?.detached).toBe(false);
|
||||||
|
expect(spawnArgs.fallbacks).toEqual([]);
|
||||||
|
} else {
|
||||||
|
expect(spawnArgs.options?.detached).toBe(true);
|
||||||
|
expect(spawnArgs.fallbacks?.[0]?.options?.detached).toBe(false);
|
||||||
|
}
|
||||||
|
|
||||||
adapter.kill();
|
adapter.kill();
|
||||||
|
|
||||||
|
|||||||
@@ -42,11 +42,17 @@ export async function createChildAdapter(params: {
|
|||||||
|
|
||||||
const stdinMode = params.stdinMode ?? (params.input !== undefined ? "pipe-closed" : "inherit");
|
const stdinMode = params.stdinMode ?? (params.input !== undefined ? "pipe-closed" : "inherit");
|
||||||
|
|
||||||
|
// On Windows, `detached: true` creates a new process group and can prevent
|
||||||
|
// stdout/stderr pipes from connecting when running under a Scheduled Task
|
||||||
|
// (headless, no console). Default to `detached: false` on Windows; on
|
||||||
|
// POSIX systems keep `detached: true` so the child survives parent exit.
|
||||||
|
const useDetached = process.platform !== "win32";
|
||||||
|
|
||||||
const options: SpawnOptions = {
|
const options: SpawnOptions = {
|
||||||
cwd: params.cwd,
|
cwd: params.cwd,
|
||||||
env: params.env ? toStringEnv(params.env) : undefined,
|
env: params.env ? toStringEnv(params.env) : undefined,
|
||||||
stdio: ["pipe", "pipe", "pipe"],
|
stdio: ["pipe", "pipe", "pipe"],
|
||||||
detached: true,
|
detached: useDetached,
|
||||||
windowsHide: true,
|
windowsHide: true,
|
||||||
windowsVerbatimArguments: params.windowsVerbatimArguments,
|
windowsVerbatimArguments: params.windowsVerbatimArguments,
|
||||||
};
|
};
|
||||||
@@ -59,12 +65,14 @@ export async function createChildAdapter(params: {
|
|||||||
const spawned = await spawnWithFallback({
|
const spawned = await spawnWithFallback({
|
||||||
argv: resolvedArgv,
|
argv: resolvedArgv,
|
||||||
options,
|
options,
|
||||||
fallbacks: [
|
fallbacks: useDetached
|
||||||
{
|
? [
|
||||||
label: "no-detach",
|
{
|
||||||
options: { detached: false },
|
label: "no-detach",
|
||||||
},
|
options: { detached: false },
|
||||||
],
|
},
|
||||||
|
]
|
||||||
|
: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
const child = spawned.child as ChildProcessWithoutNullStreams;
|
const child = spawned.child as ChildProcessWithoutNullStreams;
|
||||||
|
|||||||
Reference in New Issue
Block a user