e9e4960714
- Verify createWorkflow in runtime has zero I/O imports - Migrate agent-cursor, agent-hermes to pure workflow-runtime dependency - Migrate agent-llm, util-agent, templates to dual dependency (runtime for types, engine for CAS/merkle/buildDescriptor) - All 377 tests passing Refs #121, relates #123 #124
71 lines
1.8 KiB
TypeScript
71 lines
1.8 KiB
TypeScript
import { spawn } from "node:child_process";
|
|
|
|
import { err, ok, type Result } from "@uncaged/workflow-runtime";
|
|
|
|
export type SpawnCliError =
|
|
| { kind: "non_zero_exit"; exitCode: number | null; stdout: string; stderr: string }
|
|
| { kind: "timeout" }
|
|
| { kind: "spawn_failed"; message: string };
|
|
|
|
export type SpawnCliConfig = {
|
|
cwd: string | null;
|
|
timeoutMs: number | null;
|
|
};
|
|
|
|
export type SpawnCliResult = Result<string, SpawnCliError>;
|
|
|
|
export function spawnCli(
|
|
command: string,
|
|
args: string[],
|
|
options: SpawnCliConfig,
|
|
): Promise<SpawnCliResult> {
|
|
return new Promise((resolve) => {
|
|
const child = spawn(command, args, {
|
|
cwd: options.cwd === null ? undefined : options.cwd,
|
|
shell: false,
|
|
stdio: ["ignore", "pipe", "pipe"],
|
|
});
|
|
|
|
let stdout = "";
|
|
let stderr = "";
|
|
child.stdout?.on("data", (chunk: Buffer) => {
|
|
stdout += chunk.toString();
|
|
});
|
|
child.stderr?.on("data", (chunk: Buffer) => {
|
|
stderr += chunk.toString();
|
|
});
|
|
|
|
let timedOut = false;
|
|
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
if (options.timeoutMs !== null) {
|
|
timeoutId = setTimeout(() => {
|
|
timedOut = true;
|
|
child.kill("SIGTERM");
|
|
}, options.timeoutMs);
|
|
}
|
|
|
|
child.on("error", (cause) => {
|
|
if (timeoutId !== null) {
|
|
clearTimeout(timeoutId);
|
|
}
|
|
const message = cause instanceof Error ? cause.message : String(cause);
|
|
resolve(err({ kind: "spawn_failed", message }));
|
|
});
|
|
|
|
child.on("close", (code) => {
|
|
if (timeoutId !== null) {
|
|
clearTimeout(timeoutId);
|
|
}
|
|
if (timedOut) {
|
|
resolve(err({ kind: "timeout" }));
|
|
return;
|
|
}
|
|
if (code === 0) {
|
|
resolve(ok(stdout));
|
|
return;
|
|
}
|
|
resolve(err({ kind: "non_zero_exit", exitCode: code, stdout, stderr }));
|
|
});
|
|
});
|
|
}
|