(opts: {
command: string;
args: string[];
cwd: string;
stdin?: string;
stdoutPath: string;
stderrPath: string;
env?: NodeJS.ProcessEnv;
terminalJsonResultGraceMs?: number;
timeoutMs?: number;
})
| 288 | } |
| 289 | |
| 290 | function runCommand(opts: { |
| 291 | command: string; |
| 292 | args: string[]; |
| 293 | cwd: string; |
| 294 | stdin?: string; |
| 295 | stdoutPath: string; |
| 296 | stderrPath: string; |
| 297 | env?: NodeJS.ProcessEnv; |
| 298 | terminalJsonResultGraceMs?: number; |
| 299 | timeoutMs?: number; |
| 300 | }): Promise<CommandResult> { |
| 301 | return new Promise((resolve, reject) => { |
| 302 | const stdout = createWriteStream(opts.stdoutPath); |
| 303 | const stderr = createWriteStream(opts.stderrPath); |
| 304 | const started = process.hrtime.bigint(); |
| 305 | let stdoutBuffer = ''; |
| 306 | let terminalResultExitCode: number | undefined; |
| 307 | let terminalResultRequestedTermination = false; |
| 308 | let terminalResultTimer: NodeJS.Timeout | undefined; |
| 309 | let timeoutTimer: NodeJS.Timeout | undefined; |
| 310 | let hardKillTimer: NodeJS.Timeout | undefined; |
| 311 | let timedOut = false; |
| 312 | let settled = false; |
| 313 | const child = spawn(opts.command, opts.args, { |
| 314 | cwd: opts.cwd, |
| 315 | env: opts.env ?? process.env, |
| 316 | stdio: ['pipe', 'pipe', 'pipe'], |
| 317 | detached: opts.terminalJsonResultGraceMs !== undefined || opts.timeoutMs !== undefined, |
| 318 | }); |
| 319 | |
| 320 | const clearTerminalResultTimer = (): void => { |
| 321 | if (terminalResultTimer) clearTimeout(terminalResultTimer); |
| 322 | terminalResultTimer = undefined; |
| 323 | }; |
| 324 | |
| 325 | const clearTimeoutTimer = (): void => { |
| 326 | if (timeoutTimer) clearTimeout(timeoutTimer); |
| 327 | timeoutTimer = undefined; |
| 328 | }; |
| 329 | |
| 330 | const clearHardKillTimer = (): void => { |
| 331 | if (hardKillTimer) clearTimeout(hardKillTimer); |
| 332 | hardKillTimer = undefined; |
| 333 | }; |
| 334 | |
| 335 | const killChild = (signal: NodeJS.Signals): void => { |
| 336 | if (child.exitCode !== null || child.killed || child.pid === undefined) return; |
| 337 | try { |
| 338 | process.kill(-child.pid, signal); |
| 339 | } catch { |
| 340 | try { |
| 341 | child.kill(signal); |
| 342 | } catch { |
| 343 | // Ignore termination races; the close handler will resolve once stdio closes. |
| 344 | } |
| 345 | } |
| 346 | }; |
| 347 |
no test coverage detected