| 12 | }; |
| 13 | |
| 14 | export function run(cmd: string, args: string[], opts: RunOptions = {}): Promise<RunResult> { |
| 15 | return new Promise((resolve, reject) => { |
| 16 | const child = spawn(cmd, args, { |
| 17 | cwd: opts.cwd, |
| 18 | env: process.env, |
| 19 | stdio: ["ignore", "pipe", "pipe"], |
| 20 | }); |
| 21 | |
| 22 | const timeoutMs = opts.timeoutMs; |
| 23 | const timeout = |
| 24 | typeof timeoutMs === "number" && timeoutMs > 0 |
| 25 | ? setTimeout(() => { |
| 26 | child.kill("SIGKILL"); |
| 27 | }, timeoutMs) |
| 28 | : undefined; |
| 29 | |
| 30 | let stdout = ""; |
| 31 | let stderr = ""; |
| 32 | |
| 33 | child.stdout.setEncoding("utf8"); |
| 34 | child.stderr.setEncoding("utf8"); |
| 35 | |
| 36 | child.stdout.on("data", (chunk) => { |
| 37 | stdout += chunk; |
| 38 | }); |
| 39 | |
| 40 | child.stderr.on("data", (chunk) => { |
| 41 | stderr += chunk; |
| 42 | }); |
| 43 | |
| 44 | child.on("error", (err: NodeJS.ErrnoException) => { |
| 45 | if (timeout) clearTimeout(timeout); |
| 46 | if (err?.code === "ENOENT") { |
| 47 | reject( |
| 48 | new Error( |
| 49 | `Command not found: ${cmd}. Install Cursor CLI (agent) or set CURSOR_AGENT_BIN to its path.`, |
| 50 | ), |
| 51 | ); |
| 52 | return; |
| 53 | } |
| 54 | reject(err); |
| 55 | }); |
| 56 | |
| 57 | child.on("close", (code) => { |
| 58 | if (timeout) clearTimeout(timeout); |
| 59 | resolve({ code: code ?? 0, stdout, stderr }); |
| 60 | }); |
| 61 | }); |
| 62 | } |