()
| 58 | } |
| 59 | |
| 60 | function runAgentApiPlugin(): Plugin { |
| 61 | const runs = new Map<string, RunInfo>() |
| 62 | const thisDir = path.dirname(fileURLToPath(import.meta.url)) |
| 63 | const DASHBOARD_PORT_RE = /Dashboard:\s*https?:\/\/127\.0\.0\.1:(\d+)/i |
| 64 | |
| 65 | function getProjectRoot() { |
| 66 | // vite.config.ts lives in <repo>/yaml-flow-editor |
| 67 | return path.resolve(thisDir, '..') |
| 68 | } |
| 69 | |
| 70 | function dashboardProxyPath(run: RunInfo) { |
| 71 | return run.dashboardPort ? `/api/dashboard/${run.id}/` : null |
| 72 | } |
| 73 | |
| 74 | function runMetaPath(projectRoot: string, runId: string) { |
| 75 | return path.join(projectRoot, 'outputs', '_ui_runs', `run_${runId}.meta.json`) |
| 76 | } |
| 77 | |
| 78 | function writeRunMeta(projectRoot: string, run: RunInfo) { |
| 79 | try { |
| 80 | const p = runMetaPath(projectRoot, run.id) |
| 81 | fs.mkdirSync(path.dirname(p), { recursive: true }) |
| 82 | fs.writeFileSync( |
| 83 | p, |
| 84 | JSON.stringify( |
| 85 | { |
| 86 | id: run.id, |
| 87 | planPath: run.planPath, |
| 88 | dashboardPort: run.dashboardPort ?? null, |
| 89 | dashboardPid: run.dashboardPid ?? null, |
| 90 | updatedAt: Date.now(), |
| 91 | }, |
| 92 | null, |
| 93 | 2 |
| 94 | ), |
| 95 | 'utf8' |
| 96 | ) |
| 97 | } catch { |
| 98 | // best-effort |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | function readRunMeta(projectRoot: string, runId: string): { dashboardPort?: number; dashboardPid?: number } | null { |
| 103 | try { |
| 104 | const p = runMetaPath(projectRoot, runId) |
| 105 | if (!fs.existsSync(p)) return null |
| 106 | const raw = fs.readFileSync(p, 'utf8') |
| 107 | const j = JSON.parse(raw) |
| 108 | const port = typeof j?.dashboardPort === 'number' ? j.dashboardPort : undefined |
| 109 | const pid = typeof j?.dashboardPid === 'number' ? j.dashboardPid : undefined |
| 110 | return { dashboardPort: port, dashboardPid: pid } |
| 111 | } catch { |
| 112 | return null |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | function parseTaskNameFromYamlText(yamlText: string): string | null { |
| 117 | // Very small heuristic: the editor always writes `name: ...` at the top. |
no test coverage detected