()
| 353 | // Ensure that when the Vite dev server process exits (e.g. Ctrl+C), |
| 354 | // we also best-effort stop any agent runs and dashboards it started. |
| 355 | function installProcessExitHooks() { |
| 356 | const projectRoot = getProjectRoot() |
| 357 | const uiRunsDir = path.join(projectRoot, 'outputs', '_ui_runs') |
| 358 | |
| 359 | const cleanup = (signal: NodeJS.Signals | 'exit') => { |
| 360 | // Stop all tracked runs (agent + dashboard processes). |
| 361 | for (const run of runs.values()) { |
| 362 | try { |
| 363 | if (run.proc?.pid) { |
| 364 | // Kill the whole process group for the agent. |
| 365 | try { |
| 366 | process.kill(-run.proc.pid, 'SIGTERM') |
| 367 | } catch { |
| 368 | try { |
| 369 | run.proc.kill('SIGTERM') |
| 370 | } catch { |
| 371 | // ignore |
| 372 | } |
| 373 | } |
| 374 | } |
| 375 | } catch { |
| 376 | // ignore |
| 377 | } |
| 378 | |
| 379 | // Kill any dashboards we started and recorded a PID for. |
| 380 | if (run.dashboardPid) { |
| 381 | try { |
| 382 | process.kill(run.dashboardPid, 'SIGTERM') |
| 383 | } catch { |
| 384 | // ignore (already gone) |
| 385 | } |
| 386 | } |
| 387 | } |
| 388 | |
| 389 | // Also kill the "last UI dashboard" recorded in .dashboard_pid, if present. |
| 390 | try { |
| 391 | const pidFile = path.join(uiRunsDir, '.dashboard_pid') |
| 392 | if (fs.existsSync(pidFile)) { |
| 393 | const raw = fs.readFileSync(pidFile, 'utf8').trim() |
| 394 | const pid = Number(raw) |
| 395 | if (Number.isFinite(pid) && pid > 0) { |
| 396 | try { |
| 397 | process.kill(pid, 'SIGTERM') |
| 398 | } catch { |
| 399 | // ignore |
| 400 | } |
| 401 | } |
| 402 | } |
| 403 | } catch { |
| 404 | // best-effort only |
| 405 | } |
| 406 | |
| 407 | if (signal !== 'exit') { |
| 408 | // Let default handlers run after cleanup. |
| 409 | process.exit(0) |
| 410 | } |
| 411 | } |
| 412 |
no test coverage detected