* Setup process event handlers
(processId: string, childProcess: ChildProcess, timeoutMs: number)
| 303 | * Setup process event handlers |
| 304 | */ |
| 305 | function setupProcessHandlers(processId: string, childProcess: ChildProcess, timeoutMs: number): void { |
| 306 | // Timeout handler |
| 307 | const timeoutId = setTimeout(() => { |
| 308 | logger.warn("[Process] Process timed out", JSON.stringify({ processId, timeoutMs })) |
| 309 | // Kill the entire process group (negative PID) |
| 310 | if (childProcess.pid) { |
| 311 | try { |
| 312 | process.kill(-childProcess.pid, "SIGTERM") |
| 313 | } catch (err) { |
| 314 | logger.debug('[Process] Error killing process group, falling back to direct kill:', err) |
| 315 | childProcess.kill("SIGTERM") |
| 316 | } |
| 317 | } |
| 318 | // Force kill after 5 seconds if still running |
| 319 | setTimeout(() => { |
| 320 | if (!childProcess.killed && childProcess.pid) { |
| 321 | try { |
| 322 | process.kill(-childProcess.pid, "SIGKILL") |
| 323 | } catch (err) { |
| 324 | logger.debug('[Process] Error force killing process group, falling back to direct kill:', err) |
| 325 | childProcess.kill("SIGKILL") |
| 326 | } |
| 327 | } |
| 328 | }, 5000) |
| 329 | sendError(processId, `Process timed out after ${timeoutMs}ms`) |
| 330 | }, timeoutMs) |
| 331 | |
| 332 | // stdout handler |
| 333 | childProcess.stdout?.on("data", (data: Buffer) => { |
| 334 | sendOutput(processId, "stdout", data.toString("utf8")) |
| 335 | }) |
| 336 | |
| 337 | // stderr handler |
| 338 | childProcess.stderr?.on("data", (data: Buffer) => { |
| 339 | sendOutput(processId, "stderr", data.toString("utf8")) |
| 340 | }) |
| 341 | |
| 342 | // Exit handler |
| 343 | childProcess.on("exit", (code, signal) => { |
| 344 | clearTimeout(timeoutId) |
| 345 | logger.info("[Process] Process exited", JSON.stringify({ processId, code, signal })) |
| 346 | sendExit(processId, code, signal?.toString() || null) |
| 347 | }) |
| 348 | |
| 349 | // Error handler (spawn errors) |
| 350 | childProcess.on("error", (err) => { |
| 351 | clearTimeout(timeoutId) |
| 352 | logger.error("[Process] Process error", JSON.stringify({ processId, error: err.message })) |
| 353 | sendError(processId, err.message) |
| 354 | }) |
| 355 | } |
| 356 | |
| 357 | // ============================================================================ |
| 358 | // IPC Handlers |