(server)
| 425 | return { |
| 426 | name: 'yaml-flow-editor-run-agent-api', |
| 427 | configureServer(server) { |
| 428 | server.middlewares.use(async (req, res, next) => { |
| 429 | try { |
| 430 | if (!req.url) return next() |
| 431 | |
| 432 | const url = new URL(req.url, 'http://localhost') |
| 433 | const isRunApi = url.pathname.startsWith('/api/run-agent') |
| 434 | const isDashboardApi = url.pathname.startsWith('/api/dashboard/') |
| 435 | if (!isRunApi && !isDashboardApi) return next() |
| 436 | |
| 437 | // CORS/preflight (mostly for safety; UI is same-origin) |
| 438 | res.setHeader('Access-Control-Allow-Origin', '*') |
| 439 | res.setHeader('Access-Control-Allow-Methods', 'GET,POST,OPTIONS') |
| 440 | res.setHeader('Access-Control-Allow-Headers', 'Content-Type') |
| 441 | if (req.method === 'OPTIONS') { |
| 442 | res.statusCode = 204 |
| 443 | return res.end() |
| 444 | } |
| 445 | |
| 446 | const projectRoot = getProjectRoot() |
| 447 | |
| 448 | // /api/dashboard/:id/* -> proxy to local dashboard server |
| 449 | { |
| 450 | const m = url.pathname.match(/^\/api\/dashboard\/([^/]+)(\/.*)?$/) |
| 451 | if (m) { |
| 452 | const id = m[1] |
| 453 | let run = runs.get(id) |
| 454 | if (!run) { |
| 455 | // Best-effort recovery for old runs after dev-server restart. |
| 456 | const recoveredPlanPath = path.join(projectRoot, 'outputs', '_ui_runs', `run_${id}.yaml`) |
| 457 | if (!fs.existsSync(recoveredPlanPath)) { |
| 458 | return writeJson(res, 404, { error: 'Run not found' }) |
| 459 | } |
| 460 | run = { |
| 461 | id, |
| 462 | createdAt: Date.now(), |
| 463 | state: 'exited', |
| 464 | planPath: recoveredPlanPath, |
| 465 | cmd: '', |
| 466 | log: '', |
| 467 | subscribers: new Set(), |
| 468 | } |
| 469 | const meta = readRunMeta(projectRoot, id) |
| 470 | if (meta?.dashboardPort) run.dashboardPort = meta.dashboardPort |
| 471 | if (meta?.dashboardPid) run.dashboardPid = meta.dashboardPid |
| 472 | runs.set(id, run) |
| 473 | } |
| 474 | |
| 475 | // Backfill from disk if needed (survives page refreshes / reconnects). |
| 476 | if (!run.dashboardPort) { |
| 477 | const meta = readRunMeta(projectRoot, id) |
| 478 | if (meta?.dashboardPort) run.dashboardPort = meta.dashboardPort |
| 479 | if (meta?.dashboardPid) run.dashboardPid = meta.dashboardPid |
| 480 | } |
| 481 | |
| 482 | // If the dashboard process is gone (port dead), restart it on demand. |
| 483 | try { |
| 484 | await ensureDashboard(projectRoot, run) |
nothing calls this directly
no test coverage detected