File size: 2,346 Bytes
fcd8223 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | import { spawn } from "node:child_process";
import { parseNodeWorkerDesktopLaunchInput } from "../worker/node-desktop-protocol.js";
const DESKTOP_LAUNCH_TIMEOUT_MS = 30_000;
function signalError(signal: AbortSignal): Error {
return signal.reason instanceof Error
? signal.reason
: new Error("node worker desktop launch aborted");
}
/** Directly runs one provider-attested zero-argument launcher without replay. */
export async function invokeNodeWorkerDesktopLaunch(params: {
paramsJSON?: string | null;
signal?: AbortSignal;
}): Promise<{ status: "ready" }> {
const app = parseNodeWorkerDesktopLaunchInput(params.paramsJSON);
const signal = params.signal;
signal?.throwIfAborted();
const child = spawn(app.executablePath, [], {
shell: false,
stdio: "ignore",
windowsHide: true,
});
await new Promise<void>((resolve, reject) => {
let settled = false;
const finish = (error?: Error) => {
if (settled) {
return;
}
settled = true;
clearTimeout(timer);
signal?.removeEventListener("abort", onAbort);
child.off("error", onError);
child.off("exit", onExit);
if (error) {
reject(error);
} else {
resolve();
}
};
const stop = (error: Error) => {
try {
child.kill("SIGKILL");
} catch {
// The terminal error still owns this one-shot launch result.
}
finish(error);
};
const onAbort = () => signal && stop(signalError(signal));
const onError = (error: Error) => finish(error);
const onExit = (code: number | null, terminationSignal: NodeJS.Signals | null) => {
if (code === 0) {
finish();
return;
}
finish(
new Error(
terminationSignal
? `node worker desktop launcher terminated by ${terminationSignal}`
: `node worker desktop launcher exited with code ${code ?? "unknown"}`,
),
);
};
const timer = setTimeout(
() => stop(new Error("node worker desktop launcher timed out")),
DESKTOP_LAUNCH_TIMEOUT_MS,
);
timer.unref?.();
child.once("error", onError);
child.once("exit", onExit);
signal?.addEventListener("abort", onAbort, { once: true });
if (signal?.aborted) {
onAbort();
}
});
return { status: "ready" };
}
|