vinta-ai-maestro 0.7.0-alpha21 → 0.7.0-alpha23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -22
- package/dist/cli/halt.js +185 -0
- package/dist/cli/halt.js.map +1 -0
- package/dist/cli/index.js +32 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/logs.js +133 -0
- package/dist/cli/logs.js.map +1 -0
- package/dist/cli/policy.js +191 -0
- package/dist/cli/policy.js.map +1 -0
- package/dist/cli/purge.js +15 -3
- package/dist/cli/purge.js.map +1 -1
- package/dist/cli/run.js +231 -105
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/serve.js +104 -262
- package/dist/cli/serve.js.map +1 -1
- package/dist/cli/status.js +189 -0
- package/dist/cli/status.js.map +1 -0
- package/dist/daemon/api.js +46 -0
- package/dist/daemon/api.js.map +1 -1
- package/dist/daemon/control.js.map +1 -1
- package/dist/daemon/proxy.js +130 -0
- package/dist/daemon/proxy.js.map +1 -0
- package/dist/daemon/schemas.js +1 -1
- package/dist/daemon/schemas.js.map +1 -1
- package/dist/daemon/server.js +37 -1
- package/dist/daemon/server.js.map +1 -1
- package/dist/doctor/index.js +36 -6
- package/dist/doctor/index.js.map +1 -1
- package/dist/gates/runner.js +17 -0
- package/dist/gates/runner.js.map +1 -1
- package/dist/job/job.js +178 -0
- package/dist/job/job.js.map +1 -0
- package/dist/journal/journal.js +4 -2
- package/dist/journal/journal.js.map +1 -1
- package/dist/postmortem/postmortem.js +5 -1
- package/dist/postmortem/postmortem.js.map +1 -1
- package/dist/run/start.js +8 -0
- package/dist/run/start.js.map +1 -1
- package/dist/scheduler/scheduler.js +128 -4
- package/dist/scheduler/scheduler.js.map +1 -1
- package/dist/system-one/adapter.js +10 -0
- package/dist/system-one/adapter.js.map +1 -1
- package/dist/system-one/config.js +7 -1
- package/dist/system-one/config.js.map +1 -1
- package/dist/system-one/http.js +8 -0
- package/dist/system-one/http.js.map +1 -1
- package/dist/system-one/index.js +1 -1
- package/dist/system-one/index.js.map +1 -1
- package/dist/ui/assets/{angular-html-CRxtcu8T.js → angular-html--rod_uFH.js} +1 -1
- package/dist/ui/assets/{angular-ts-YNySLBa9.js → angular-ts-CH4AhDr9.js} +1 -1
- package/dist/ui/assets/{apl-C5XxFwl2.js → apl-CaWMi3_O.js} +1 -1
- package/dist/ui/assets/{astro-Cl27tLte.js → astro-65q_izSI.js} +1 -1
- package/dist/ui/assets/{blade-4DvMBPB7.js → blade-BwQnvQGM.js} +1 -1
- package/dist/ui/assets/{c-MKR_TARB.js → c-Ym8Nhq5w.js} +1 -1
- package/dist/ui/assets/{chapel-D1cEsFmo.js → chapel-DEt1k_nb.js} +1 -1
- package/dist/ui/assets/{cobol-FqyqpV-Z.js → cobol-C7uzupvl.js} +1 -1
- package/dist/ui/assets/{coffee-DaQH_cnd.js → coffee-DOZQfojQ.js} +1 -1
- package/dist/ui/assets/{cpp-CDg7r5Lm.js → cpp-BtyhwYIb.js} +1 -1
- package/dist/ui/assets/{crystal-DdeNtPaD.js → crystal-CrqkYiNi.js} +1 -1
- package/dist/ui/assets/{css-7_Zq6chi.js → css-C8oBROxa.js} +1 -1
- package/dist/ui/assets/{edge-BY-k2s7F.js → edge-sFmhfyIP.js} +1 -1
- package/dist/ui/assets/{elixir-3ED3OKOH.js → elixir-BM9jh3e3.js} +1 -1
- package/dist/ui/assets/{elm-DVdSluoB.js → elm-BETVX7DL.js} +1 -1
- package/dist/ui/assets/{erb-Bq5Dc-e6.js → erb-BDyKRm-4.js} +1 -1
- package/dist/ui/assets/{git-rebase-BpW9Ws96.js → git-rebase-DAcGcSet.js} +1 -1
- package/dist/ui/assets/{glimmer-js-Bp-SmLHE.js → glimmer-js-BatXXwpe.js} +1 -1
- package/dist/ui/assets/{glimmer-ts-M5xKg4QH.js → glimmer-ts-Bo1DlScE.js} +1 -1
- package/dist/ui/assets/{glsl-DmXsA1Di.js → glsl-BqkyX5Iz.js} +1 -1
- package/dist/ui/assets/{graphql-LX-8XoeY.js → graphql-BkAH8Crt.js} +1 -1
- package/dist/ui/assets/{hack-DrC6kjZ-.js → hack-BAy_rDSy.js} +1 -1
- package/dist/ui/assets/{haml-Cs9eCa_K.js → haml-NAdeHrqL.js} +1 -1
- package/dist/ui/assets/{handlebars-knh5cKv7.js → handlebars-DP0nKrnR.js} +1 -1
- package/dist/ui/assets/{html-Bj-aBVNp.js → html-C-Hz7c0Z.js} +1 -1
- package/dist/ui/assets/{html-derivative-CiiCAeKi.js → html-derivative-CJndNfHU.js} +1 -1
- package/dist/ui/assets/{http-DF47-qRx.js → http-CoSnU61Z.js} +1 -1
- package/dist/ui/assets/{hurl-Cai6Oj1b.js → hurl-BKpQxBZx.js} +1 -1
- package/dist/ui/assets/{index-LzKXLqvN.js → index-DVILQrp_.js} +85 -85
- package/dist/ui/assets/{java-CgXVugkp.js → java-ClhzGlNg.js} +1 -1
- package/dist/ui/assets/{javascript-Br7K4LwF.js → javascript-DQKc5wHM.js} +1 -1
- package/dist/ui/assets/{jinja-CYOzT9zf.js → jinja-CM6c-Rc_.js} +1 -1
- package/dist/ui/assets/{jison-DYMbT0wb.js → jison-DEvrAQvy.js} +1 -1
- package/dist/ui/assets/{json-Vju3cCl7.js → json-COx1UCVY.js} +1 -1
- package/dist/ui/assets/{json-text-editor-9yQRL-wq.js → json-text-editor-CgieRwCY.js} +1 -1
- package/dist/ui/assets/{jsx-BJ2SfNc2.js → jsx-Dq4g_gGM.js} +1 -1
- package/dist/ui/assets/{julia-Bl5QJcj1.js → julia-_M8UpEBR.js} +1 -1
- package/dist/ui/assets/{just-BSgRkB_b.js → just-SEENIkfF.js} +1 -1
- package/dist/ui/assets/{latex-9nJBPEWl.js → latex-z11sRY1e.js} +1 -1
- package/dist/ui/assets/{liquid-BB_V_UwS.js → liquid-7qBozupt.js} +1 -1
- package/dist/ui/assets/{lua-DklLSFpq.js → lua-efJ4NR0B.js} +1 -1
- package/dist/ui/assets/{marko-9qUza2Cy.js → marko-CfHkKZQs.js} +1 -1
- package/dist/ui/assets/{mdc-CyuS771N.js → mdc-BFzMhqRK.js} +1 -1
- package/dist/ui/assets/{nginx-4PCMUjXv.js → nginx-Ba1yATRU.js} +1 -1
- package/dist/ui/assets/{nim-BGtJj58_.js → nim-D9tR0l59.js} +1 -1
- package/dist/ui/assets/{org-BZTxzmqq.js → org-BOYaWmQ6.js} +1 -1
- package/dist/ui/assets/{perl-DnYlfNV7.js → perl-Bhdbf0pR.js} +1 -1
- package/dist/ui/assets/{php-BjK_5VzK.js → php-DGidydcD.js} +1 -1
- package/dist/ui/assets/{pug-BxtgFz0R.js → pug-A8eoEM0H.js} +1 -1
- package/dist/ui/assets/{qml-D2-GrqTo.js → qml-C2Blb4M7.js} +1 -1
- package/dist/ui/assets/{r-DYmTOMm-.js → r-kHJ9J7di.js} +1 -1
- package/dist/ui/assets/{razor-D-GkAGbA.js → razor-DRWMcd86.js} +1 -1
- package/dist/ui/assets/{regexp-BqEDcLm3.js → regexp-DSa9yWx_.js} +1 -1
- package/dist/ui/assets/{rst-CZdKMzIl.js → rst-DFFsPn-_.js} +1 -1
- package/dist/ui/assets/{ruby-DVnXlHSi.js → ruby-QmGD3U24.js} +1 -1
- package/dist/ui/assets/{sas-DaOpt0VL.js → sas-Cu9PDsjp.js} +1 -1
- package/dist/ui/assets/{scss-KndlTnGC.js → scss-BT4OjSCC.js} +1 -1
- package/dist/ui/assets/{shellscript-vpjNsx9D.js → shellscript-CLSCFAsj.js} +1 -1
- package/dist/ui/assets/{shellsession-BiAZj2V1.js → shellsession-CwZ_5n4W.js} +1 -1
- package/dist/ui/assets/{soy-DHlNGgnB.js → soy-ofP5hQkZ.js} +1 -1
- package/dist/ui/assets/{sql-DGabU7xY.js → sql-j0ru169V.js} +1 -1
- package/dist/ui/assets/{stata-kbiiK5op.js → stata-v3x2Y_5m.js} +1 -1
- package/dist/ui/assets/{surrealql-CImshJdi.js → surrealql-Bmbpksc8.js} +1 -1
- package/dist/ui/assets/{svelte-DoeuXk_R.js → svelte-BuXIEx69.js} +1 -1
- package/dist/ui/assets/{templ-5tZza6px.js → templ-F72RdV4h.js} +1 -1
- package/dist/ui/assets/{tex-DO9W0uTa.js → tex-BdlHb9hn.js} +1 -1
- package/dist/ui/assets/{ts-tags-BRigjIub.js → ts-tags-DPrkt62A.js} +1 -1
- package/dist/ui/assets/{tsx-Ddc_hGks.js → tsx-DZLkwBdm.js} +1 -1
- package/dist/ui/assets/{twig-BJPQXv7X.js → twig-DTmppqKc.js} +1 -1
- package/dist/ui/assets/{typescript-BO9qTW9s.js → typescript-DJcPZKIN.js} +1 -1
- package/dist/ui/assets/{typst-BKKO1Q3c.js → typst-BiL5yhyo.js} +1 -1
- package/dist/ui/assets/{vue-Bem1c9GL.js → vue-Bzrf-J5S.js} +1 -1
- package/dist/ui/assets/{vue-html-Ss5ojtBB.js → vue-html-DbFRWAKl.js} +1 -1
- package/dist/ui/assets/{vue-vine-BNczxZ-v.js → vue-vine-C4Oz1MOg.js} +1 -1
- package/dist/ui/assets/{xml-C64yarKu.js → xml-mRmLJidt.js} +1 -1
- package/dist/ui/assets/{xsl-BTsmbzwy.js → xsl-DjiVjdxI.js} +1 -1
- package/dist/ui/assets/{yaml-D1GFx-Qg.js → yaml-Yth9CZzj.js} +1 -1
- package/dist/ui/index.html +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -60,7 +60,7 @@ pnpm install
|
|
|
60
60
|
That is the development setup. To *use* it, there is no clone at all — the package is published:
|
|
61
61
|
|
|
62
62
|
```bash
|
|
63
|
-
npx vinta-ai-maestro@alpha
|
|
63
|
+
npx vinta-ai-maestro@alpha ui
|
|
64
64
|
```
|
|
65
65
|
|
|
66
66
|
`@alpha` while the only published versions are pre-releases. Plain `npx vinta-ai-maestro` resolves the `latest` dist-tag, which is correct once a stable exists.
|
|
@@ -80,15 +80,20 @@ The published binary is a different file — `dist/cli/bin.js`, plain JavaScript
|
|
|
80
80
|
|
|
81
81
|
Every command runs against a project checkout — your project, not this one. `--repo <dir>` names it; with no flag it is the current directory.
|
|
82
82
|
|
|
83
|
-
## The
|
|
83
|
+
## The commands
|
|
84
84
|
|
|
85
85
|
| Command | What it does |
|
|
86
86
|
|---|---|
|
|
87
87
|
| `doctor <workflow.json> [--repo <dir>] [--resume <run-id>]` | Preflights every check a run depends on and exits non-zero if a run cannot start. `--resume` asks the question for `run --resume <run-id>`: that run's own lanes are holding its phase branches on purpose, and are not leftovers to clear. |
|
|
88
88
|
| `simulate <workflow.json>` | Projects the schedule without running it — wall clock, critical path, pool contention. Spawns no agent. |
|
|
89
|
-
| `
|
|
90
|
-
| `run
|
|
91
|
-
| `
|
|
89
|
+
| `run <workflow.json> [--repo <dir>]` | Starts the workflow as a **background job** and returns once it is under way. The run does not need the terminal: close it and the run carries on. `--foreground` hosts it in this process instead and exits when it ends, for CI. |
|
|
90
|
+
| `run --resume <run-id>` | Picks an interrupted or paused run back up, as a background job. |
|
|
91
|
+
| `status [run-id] [--json]` | Every run in the project and what it is doing — or one run, phase by phase. A run whose job died without ending it shows as `interrupted`. |
|
|
92
|
+
| `logs <run-id> [-f] [-n <lines>]` | The run's job log: what `run` used to print to the terminal, plus its daemon's records. `-f` follows it until the job exits. |
|
|
93
|
+
| `pause <run-id> [--wait]` | Nothing new starts; each running phase finishes the step it is in, then the job exits. The run is `paused`, its lanes intact, and `run --resume` continues it. |
|
|
94
|
+
| `stop <run-id> [--wait]` | Kills every live agent turn and gate now. The run is `cancelled` and cannot be resumed. |
|
|
95
|
+
| `ui [--repo <dir>] [--host <host>] [--port <n>]` | Serves the browser UI for every run in the project and prints the URL to open. Runs are not hosted here — close it whenever you like. A run started from its editor is launched as a background job. `serve` is the same command. |
|
|
96
|
+
| `purge [run-id] [--repo <dir>] [--yes] [--dry-run]` | Deletes run state under `.vinta-ai-maestro/runs/`. A run whose job is still running is kept. |
|
|
92
97
|
| `with <resource> -- <cmd>` | Inside an agent turn, waits for a semaphore resource, runs the command, and releases it. The live run supplies its daemon connection through the lane environment. |
|
|
93
98
|
| `gate <gate-id>` | Inside an agent turn, asks the daemon to run one of the plan's declared gates against this turn's lane. The daemon holds the gate's resources and caches the result; the CLI exits with the gate's exit code. |
|
|
94
99
|
|
|
@@ -96,6 +101,14 @@ There is also `judge-hook`, which is internal: it is the hook `--permission judg
|
|
|
96
101
|
|
|
97
102
|
`--port` defaults to `0`, an OS-assigned port printed with the URL. `--host` defaults to `127.0.0.1` — see [The URL is the credential](#the-url-is-the-credential). `vinta-ai-maestro <command> --help` prints the command's own options.
|
|
98
103
|
|
|
104
|
+
### A run is a background job
|
|
105
|
+
|
|
106
|
+
`run` hands the run to a detached copy of itself — the run's *job* — and returns as soon as the job says the run has started. The job is the run's only host: it holds the scheduler, the agents, and a loopback API the agents call back into (`with`, `gate`). It writes `.vinta-ai-maestro/runs/<run-id>/job.json` — its pid, that API's address and its token — and every other command finds the run through that file. It serves no UI.
|
|
107
|
+
|
|
108
|
+
`ui` is the browser's way in. It reads every run's history from the journal, and for a run whose job is alive it forwards that run's requests and socket to the job, so the live graph, steering, answering a question and taking over a terminal all work as if the two were one process. Close `ui` and the runs carry on; open it tomorrow and it shows them where they got to.
|
|
109
|
+
|
|
110
|
+
A run ends one of four ways, and the journal says which: it settles (`done` or `failed`), it is paused (`paused`, resumable), it is stopped (`cancelled`, final), or its job is killed by a signal (`failed`, resumable — exactly what closing the terminal used to do). `status` shows a run whose job vanished without writing any of those — a reboot, a `kill -9` — as `interrupted`, and `run --resume` picks it up.
|
|
111
|
+
|
|
99
112
|
The daemon-facing commands use three exit codes, so a script can tell the cases
|
|
100
113
|
apart: `0` success, `1` the command ran and the answer was no, `2` the command
|
|
101
114
|
line was wrong. `with` and `gate` pass through the exit code of the thing they
|
|
@@ -304,10 +317,10 @@ A repository with two phases that depend on nothing, so both belong to wave 1 an
|
|
|
304
317
|
|
|
305
318
|
Scope it to what your phases actually need; this is the permissive end.
|
|
306
319
|
|
|
307
|
-
**3. Review and approve it in the editor.** `
|
|
320
|
+
**3. Review and approve it in the editor.** `ui` serves the browser UI for the project. Its Editor lists every `ai-plans/*.workflow.json` in the project — the file you just wrote, or the one `plan-feature` wrote — and saving writes back to that same file:
|
|
308
321
|
|
|
309
322
|
```console
|
|
310
|
-
$ vinta-ai-maestro
|
|
323
|
+
$ vinta-ai-maestro ui
|
|
311
324
|
vinta-ai-maestro: daemon listening on http://127.0.0.1:52218
|
|
312
325
|
Open this URL. It carries the access token, so treat it as a secret:
|
|
313
326
|
http://127.0.0.1:52218/?token=<the-token-printed-here>
|
|
@@ -370,13 +383,16 @@ Both phases start at `0s` — that is the parallelism the plan claimed, confirme
|
|
|
370
383
|
|
|
371
384
|
```console
|
|
372
385
|
$ vinta-ai-maestro run ai-plans/widget-tags.workflow.json
|
|
373
|
-
vinta-ai-maestro:
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
vinta-ai-maestro
|
|
386
|
+
vinta-ai-maestro: starting run widget-tags-mtvodosx in the background…
|
|
387
|
+
vinta-ai-maestro: run widget-tags-mtvodosx started (job pid 48213).
|
|
388
|
+
status vinta-ai-maestro status widget-tags-mtvodosx
|
|
389
|
+
logs vinta-ai-maestro logs widget-tags-mtvodosx --follow
|
|
390
|
+
ui vinta-ai-maestro ui
|
|
391
|
+
pause vinta-ai-maestro pause widget-tags-mtvodosx
|
|
392
|
+
stop vinta-ai-maestro stop widget-tags-mtvodosx
|
|
377
393
|
```
|
|
378
394
|
|
|
379
|
-
The
|
|
395
|
+
The command returns; the run does not need this terminal. The preflight is the job's — if it refuses, `run` prints the report and says the run did not start. Watch it from the terminal with `status` and `logs -f`, or keep `ui` open in another one and follow it in the browser. Both phases are assigned a lane immediately and implement concurrently, each in its own worktree under `.vinta-ai-maestro/lanes/`, on its own branch cut from `base_branch`:
|
|
380
396
|
|
|
381
397
|
```console
|
|
382
398
|
$ git branch
|
|
@@ -388,7 +404,7 @@ $ git branch
|
|
|
388
404
|
wt/widget-tags-mtvodosx-lane-2
|
|
389
405
|
```
|
|
390
406
|
|
|
391
|
-
`plan/…/phase-<id>` is the phase's own branch; `wt/…` are the branches the lane and integration worktrees are checked out on. When the run ends, read [What this walkthrough has and has not been run against](#what-this-walkthrough-has-and-has-not-been-run-against) before you read the last two lines it prints.
|
|
407
|
+
`plan/…/phase-<id>` is the phase's own branch; `wt/…` are the branches the lane and integration worktrees are checked out on. Need the machine back for an hour? `vinta-ai-maestro pause <run-id>` lets the phases finish the step they are in and ends the job; `vinta-ai-maestro run --resume <run-id>` carries on later. When the run ends, read [What this walkthrough has and has not been run against](#what-this-walkthrough-has-and-has-not-been-run-against) before you read the last two lines it prints.
|
|
392
408
|
|
|
393
409
|
**7. Read what happened, then clean up.** A finished run leaves its worktrees, branches and databases in place on purpose — they are the evidence. The post-mortem is written at the end and is what `plan-feature` reads before drawing the next feature's graph:
|
|
394
410
|
|
|
@@ -420,6 +436,8 @@ Everything a run writes lives inside the project, never in a global cache direct
|
|
|
420
436
|
logs/daemon.<n>.ndjson # rotations, oldest pruned past five
|
|
421
437
|
lanes/<lane>/ # the lane worktrees, and .templates/ for forked DBs
|
|
422
438
|
runs/<run-id>/
|
|
439
|
+
job.json # while the job runs: pid, API address, token (0600)
|
|
440
|
+
job.log # the job's console, every attempt appended
|
|
423
441
|
workflow.json # the snapshot frozen at run start
|
|
424
442
|
postmortem.json # written once the run has ended
|
|
425
443
|
nodes/<node-id>/
|
|
@@ -448,10 +466,10 @@ It removes the run *directory* — snapshot, transcripts, raw streams, gate logs
|
|
|
448
466
|
|
|
449
467
|
Transcripts say what the *agents* did. This says what the *daemon* did — which is the half you need when the answer is "nothing happened", "it stopped", or "the process is gone".
|
|
450
468
|
|
|
451
|
-
It is a file and a view, and they show the same records:
|
|
469
|
+
It is a file and a view, and they show the same records. Every process writes to the same file — each run's job and `ui` — and every record carries its `pid`. For one run's job alone, `vinta-ai-maestro logs <run-id>` is shorter:
|
|
452
470
|
|
|
453
471
|
```console
|
|
454
|
-
$ vinta-ai-maestro
|
|
472
|
+
$ vinta-ai-maestro ui
|
|
455
473
|
vinta-ai-maestro: daemon listening on http://127.0.0.1:52765
|
|
456
474
|
Open this URL. It carries the access token, so treat it as a secret:
|
|
457
475
|
http://127.0.0.1:52765/?token=<the-token-printed-here>
|
|
@@ -505,9 +523,9 @@ Everything other than `message` is **identifiers only** — the rule `flow.db` f
|
|
|
505
523
|
Flags:
|
|
506
524
|
|
|
507
525
|
```console
|
|
508
|
-
$ vinta-ai-maestro
|
|
509
|
-
$ vinta-ai-maestro
|
|
510
|
-
$ vinta-ai-maestro
|
|
526
|
+
$ vinta-ai-maestro ui --log-level debug # adds a line per request and per socket
|
|
527
|
+
$ vinta-ai-maestro ui --log-stderr # also print to the terminal, one line each
|
|
528
|
+
$ vinta-ai-maestro ui --log-detail kind # drop error messages, keep kinds and frames
|
|
511
529
|
```
|
|
512
530
|
|
|
513
531
|
`run` accepts the same three, and writes to the same file.
|
|
@@ -526,6 +544,8 @@ Open this URL. It carries the access token, so treat it as a secret:
|
|
|
526
544
|
|
|
527
545
|
That line is the only place in this package's output where the token ever appears. It is not in the "listening on" line, not in warnings, not in errors — so pasting a log into a ticket is safe, and pasting *that* URL into a ticket publishes the run.
|
|
528
546
|
|
|
547
|
+
Each run's job has a token of its own, which it is never printed: it is written to that run's `job.json`, readable by your user only, and removed when the job ends. `ui` reads it to forward a live run's traffic, and the browser never sees it — its requests carry `ui`'s token, which the job would refuse. Treat `job.json` like the URL: it is access to the run.
|
|
548
|
+
|
|
529
549
|
**`--host` is explicit and warned about.** The default bind is `127.0.0.1`. Any other value makes the daemon reachable from other machines, and the daemon prints a warning naming the host — on stderr, where it cannot be mistaken for part of the URL. Anyone who can reach the daemon and holds the token can drive the run: there is no per-user access control, by design. Prefer an SSH port-forward to `--host` for a daemon on a bigger box.
|
|
530
550
|
|
|
531
551
|
## Harnesses
|
|
@@ -544,7 +564,7 @@ Point the adapter at a specific binary with an environment variable, which overr
|
|
|
544
564
|
|
|
545
565
|
### What an agent may do in its lane
|
|
546
566
|
|
|
547
|
-
`--permission <ask|auto|full|judged>` on `run` and `
|
|
567
|
+
`--permission <ask|auto|full|judged>` on `run` and `ui` (for the runs it starts), defaulting to **`auto`**: the agent works unattended inside its own lane, which is what a lane is for.
|
|
548
568
|
|
|
549
569
|
**The operator sets this, never the workflow document.** It is an argument to the command rather than a field in the JSON, and deliberately so — the document is committed and shared, and a file in a repository should not be able to tell someone else's machine to run agents without approvals. A plan may say which model writes a phase; it may not say how much of a stranger's filesystem that model gets.
|
|
550
570
|
|
|
@@ -555,7 +575,7 @@ Point the adapter at a specific binary with an environment variable, which overr
|
|
|
555
575
|
| `full` | `--allow-dangerously-skip-permissions --permission-mode bypassPermissions` | `--dangerously-bypass-approvals-and-sandbox` |
|
|
556
576
|
| `judged` | as `full`, plus a `PreToolUse` hook that asks a System One classifier about each judged call | refused by `doctor` |
|
|
557
577
|
|
|
558
|
-
`ask` is the CLIs' own default and the one to avoid headlessly: nothing answers a permission prompt in a `run`. The request surfaces as a `permission_request` event and the transcript renders it, but no reply is ever sent — so the agent reports a blocked working directory and the phase fails having written nothing. Use it with `
|
|
578
|
+
`ask` is the CLIs' own default and the one to avoid headlessly: nothing answers a permission prompt in a `run`. The request surfaces as a `permission_request` event and the transcript renders it, but no reply is ever sent — so the agent reports a blocked working directory and the phase fails having written nothing. Use it with `ui` open and a human watching, or not at all.
|
|
559
579
|
|
|
560
580
|
`full` is available and is not the default. Both vendors describe their equivalent as being for sandboxes with no internet access, and a lane is not that — it has the network and whatever credentials the machine holds.
|
|
561
581
|
|
|
@@ -567,7 +587,7 @@ A lane is still a worktree of your repository, so committed settings travel into
|
|
|
567
587
|
|
|
568
588
|
## System One classifiers
|
|
569
589
|
|
|
570
|
-
`--system-one <config.json>` on `run`, `
|
|
590
|
+
`--system-one <config.json>` on `run`, `ui` and `doctor` points a run at a fast classifier — one that answers yes/no or scores a fixed set of labels, and writes nothing. The file is the operator's and never part of the plan:
|
|
571
591
|
|
|
572
592
|
```jsonc
|
|
573
593
|
{
|
|
@@ -579,7 +599,7 @@ A lane is still a worktree of your repository, so committed settings travel into
|
|
|
579
599
|
}
|
|
580
600
|
```
|
|
581
601
|
|
|
582
|
-
`http` POSTs `{ kind, question, labels, input }` and reads `{ scores }` or `{ yes }` back, with the key from the environment variable you name (removed from the daemon's environment once read, so agents never see it). `command` runs a local program with the same JSON on stdin and stdout — the choice when repository content must not leave the machine, because a hosted classifier receives diffs, gate logs and shell commands.
|
|
602
|
+
`http` POSTs `{ kind, question, labels, input }` and reads `{ scores }` or `{ yes }` back, with the key from the environment variable you name (removed from the daemon's environment once read, so agents never see it). Before a run starts, the preflight asks the classifier one synthetic question, so a missing or rejected key, or an unreachable URL, stops the run at minute zero instead of at the first gate. Set `"probe": false` to skip it. `doctor` also warns about an `http` adapter with no `api_key_env`, since that sends no `Authorization` header. `command` runs a local program with the same JSON on stdin and stdout — the choice when repository content must not leave the machine, because a hosted classifier receives diffs, gate logs and shell commands.
|
|
583
603
|
|
|
584
604
|
A plan can then declare **judge gates** next to its command gates:
|
|
585
605
|
|
package/dist/cli/halt.js
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `vinta-ai-maestro pause <runId>` and `vinta-ai-maestro stop <runId>`.
|
|
3
|
+
*
|
|
4
|
+
* Both end a run before its DAG does, and both reach it the same way: through
|
|
5
|
+
* the run's own job, found by its `job.json` and asked over the API its agents
|
|
6
|
+
* already use. Not by signal — a signal means "interrupted" to the job, and on
|
|
7
|
+
* Windows there is no signal to send that the job could catch at all.
|
|
8
|
+
*
|
|
9
|
+
* - **pause** drains. No new phase starts, each running phase finishes the step
|
|
10
|
+
* it is in, and the job exits with the run `paused` and every lane in place.
|
|
11
|
+
* `run --resume` picks it up.
|
|
12
|
+
* - **stop** kills: every live agent turn and every running gate ends now, and
|
|
13
|
+
* the run is `cancelled`, which `--resume` refuses.
|
|
14
|
+
*
|
|
15
|
+
* Both return as soon as the job has the request; `--wait` stays until it has
|
|
16
|
+
* exited. A pause can take as long as the longest step in flight.
|
|
17
|
+
*
|
|
18
|
+
* **When there is no job to ask.** A run whose job died is already as
|
|
19
|
+
* resumable as a pause would make it, so `pause` says so and changes nothing.
|
|
20
|
+
* `stop` still has something to do — make the run final — and does it
|
|
21
|
+
* directly in the journal. A job that is alive and not answering is the one
|
|
22
|
+
* case `stop` ends by force: its process tree is signalled, and the
|
|
23
|
+
* cancellation is written once it is gone.
|
|
24
|
+
*/
|
|
25
|
+
import { existsSync } from 'node:fs';
|
|
26
|
+
import { resolve } from 'node:path';
|
|
27
|
+
import { parseArgs } from 'node:util';
|
|
28
|
+
import { isAlive, liveJob } from '../job/job.js';
|
|
29
|
+
import { openJournal } from '../journal/journal.js';
|
|
30
|
+
import { killTree } from '../platform/platform.js';
|
|
31
|
+
import { FAILED, OK, USAGE } from './io.js';
|
|
32
|
+
import { storeFor } from './paths.js';
|
|
33
|
+
import { isResumable, runState } from './status.js';
|
|
34
|
+
export const PAUSE_USAGE = `usage: vinta-ai-maestro pause <runId> [--repo <dir>] [--wait]
|
|
35
|
+
|
|
36
|
+
Pauses a running run. Nothing new starts; each running phase finishes the
|
|
37
|
+
step it is in — the agent turn or gate under way — and then the run's job
|
|
38
|
+
exits, leaving every lane as it was. Resume it with
|
|
39
|
+
\`vinta-ai-maestro run --resume <runId>\`.
|
|
40
|
+
|
|
41
|
+
--repo <dir> The project. Defaults to the current directory.
|
|
42
|
+
--wait Return once the job has exited, not once it has the request.`;
|
|
43
|
+
export const STOP_USAGE = `usage: vinta-ai-maestro stop <runId> [--repo <dir>] [--wait]
|
|
44
|
+
|
|
45
|
+
Stops a run for good. Every live agent turn and every running gate is
|
|
46
|
+
killed, and the run ends "cancelled": it cannot be resumed. Lanes and
|
|
47
|
+
branches are left in place, as for any finished run. A paused or
|
|
48
|
+
interrupted run can be stopped too, which makes it final.
|
|
49
|
+
|
|
50
|
+
--repo <dir> The project. Defaults to the current directory.
|
|
51
|
+
--wait Return once the job has exited, not once it has the request.`;
|
|
52
|
+
export const pauseCommand = (argv, io, deps = {}) => haltCommand('pause', argv, io, deps);
|
|
53
|
+
export const stopCommand = (argv, io, deps = {}) => haltCommand('stop', argv, io, deps);
|
|
54
|
+
async function haltCommand(mode, argv, io, deps) {
|
|
55
|
+
const usage = mode === 'pause' ? PAUSE_USAGE : STOP_USAGE;
|
|
56
|
+
let parsed;
|
|
57
|
+
try {
|
|
58
|
+
parsed = parseArgs({
|
|
59
|
+
args: [...argv],
|
|
60
|
+
options: { repo: { type: 'string' }, wait: { type: 'boolean' } },
|
|
61
|
+
allowPositionals: true,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
io.err(usage);
|
|
66
|
+
return USAGE;
|
|
67
|
+
}
|
|
68
|
+
const runId = parsed.positionals[0];
|
|
69
|
+
if (runId === undefined || parsed.positionals.length > 1) {
|
|
70
|
+
io.err(usage);
|
|
71
|
+
return USAGE;
|
|
72
|
+
}
|
|
73
|
+
const repoPath = resolve(parsed.values.repo ?? process.cwd());
|
|
74
|
+
if (!existsSync(storeFor(repoPath))) {
|
|
75
|
+
io.err(`vinta-ai-maestro: no run "${runId}" in ${repoPath}`);
|
|
76
|
+
return FAILED;
|
|
77
|
+
}
|
|
78
|
+
const alive = deps.alive ?? isAlive;
|
|
79
|
+
const sleep = deps.sleep ?? ((ms) => new Promise((done) => setTimeout(done, ms)));
|
|
80
|
+
const journal = openJournal(repoPath);
|
|
81
|
+
try {
|
|
82
|
+
const row = journal.run(runId);
|
|
83
|
+
if (row === undefined) {
|
|
84
|
+
io.err(`vinta-ai-maestro: no run "${runId}" in ${repoPath}`);
|
|
85
|
+
return FAILED;
|
|
86
|
+
}
|
|
87
|
+
const job = liveJob(repoPath, runId, alive);
|
|
88
|
+
const state = runState(row, job);
|
|
89
|
+
if (job === null)
|
|
90
|
+
return haltUnhosted(mode, journal, runId, state, io);
|
|
91
|
+
const asked = await ask(job, mode, deps.fetch ?? fetch);
|
|
92
|
+
if (asked === 'unreachable') {
|
|
93
|
+
if (mode === 'pause') {
|
|
94
|
+
io.err(`vinta-ai-maestro: run ${runId}'s job (pid ${job.pid}) is not answering. ` +
|
|
95
|
+
`\`vinta-ai-maestro stop ${runId}\` ends it regardless.`);
|
|
96
|
+
return FAILED;
|
|
97
|
+
}
|
|
98
|
+
return await forceStop(journal, runId, job, io, { alive, sleep, kill: deps.kill ?? signalTree });
|
|
99
|
+
}
|
|
100
|
+
if (asked === 'refused') {
|
|
101
|
+
io.err(`vinta-ai-maestro: run ${runId} is ${state}; there is nothing to ${mode}.`);
|
|
102
|
+
return FAILED;
|
|
103
|
+
}
|
|
104
|
+
io.out(mode === 'pause'
|
|
105
|
+
? `vinta-ai-maestro: pausing run ${runId}. Running phases finish their current step, then the job exits.`
|
|
106
|
+
: `vinta-ai-maestro: stopping run ${runId}.`);
|
|
107
|
+
if (parsed.values.wait !== true) {
|
|
108
|
+
io.out(`vinta-ai-maestro: follow it with: vinta-ai-maestro logs ${runId} --follow`);
|
|
109
|
+
return OK;
|
|
110
|
+
}
|
|
111
|
+
while (alive(job.pid))
|
|
112
|
+
await sleep(250);
|
|
113
|
+
const ended = journal.run(runId)?.status ?? 'unknown';
|
|
114
|
+
io.out(`vinta-ai-maestro: run ${runId} is ${ended}.`);
|
|
115
|
+
return OK;
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
journal.close();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/** The run's own API, asked. `refused` is a 409: the run is not running. */
|
|
122
|
+
async function ask(job, mode, fetchImpl) {
|
|
123
|
+
try {
|
|
124
|
+
const response = await fetchImpl(`${job.url}/api/runs/${encodeURIComponent(job.runId)}/${mode}`, {
|
|
125
|
+
method: 'POST',
|
|
126
|
+
headers: { authorization: `Bearer ${job.token}`, 'content-type': 'application/json' },
|
|
127
|
+
body: '{}',
|
|
128
|
+
signal: AbortSignal.timeout(10_000),
|
|
129
|
+
});
|
|
130
|
+
if (response.ok)
|
|
131
|
+
return 'accepted';
|
|
132
|
+
return response.status === 409 ? 'refused' : 'unreachable';
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
return 'unreachable';
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function haltUnhosted(mode, journal, runId, state, io) {
|
|
139
|
+
if (state === 'done' || state === 'cancelled') {
|
|
140
|
+
io.err(`vinta-ai-maestro: run ${runId} already ended (${state}).`);
|
|
141
|
+
return FAILED;
|
|
142
|
+
}
|
|
143
|
+
if (mode === 'pause') {
|
|
144
|
+
// Nothing is running it, so there is nothing to drain — and it is already
|
|
145
|
+
// exactly as resumable as a pause would leave it.
|
|
146
|
+
io.err(`vinta-ai-maestro: run ${runId} is ${state} — nothing is running it, so there is nothing to pause.`);
|
|
147
|
+
if (isResumable(state))
|
|
148
|
+
io.err(`vinta-ai-maestro: resume it with: vinta-ai-maestro run --resume ${runId}`);
|
|
149
|
+
return FAILED;
|
|
150
|
+
}
|
|
151
|
+
journal.append({ runId, type: 'run_ended', payload: { status: 'cancelled' } });
|
|
152
|
+
io.out(`vinta-ai-maestro: run ${runId} cancelled (it was ${state}). It can no longer be resumed.`);
|
|
153
|
+
return OK;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* A job that is alive and will not answer: ended from outside. SIGTERM first —
|
|
157
|
+
* the job's own handler records the run as interrupted — then SIGKILL if it
|
|
158
|
+
* will not go, and the cancellation written over whatever it managed to say.
|
|
159
|
+
*/
|
|
160
|
+
async function forceStop(journal, runId, job, io, deps) {
|
|
161
|
+
io.err(`vinta-ai-maestro: run ${runId}'s job (pid ${job.pid}) is not answering; ending it.`);
|
|
162
|
+
deps.kill(job.pid, 'SIGTERM');
|
|
163
|
+
for (let waited = 0; waited < 10_000 && deps.alive(job.pid); waited += 250)
|
|
164
|
+
await deps.sleep(250);
|
|
165
|
+
if (deps.alive(job.pid))
|
|
166
|
+
deps.kill(job.pid, 'SIGKILL');
|
|
167
|
+
journal.append({ runId, type: 'run_ended', payload: { status: 'cancelled' } });
|
|
168
|
+
io.out(`vinta-ai-maestro: run ${runId} cancelled.`);
|
|
169
|
+
// Agents and gates lead process groups of their own, which a job killed from
|
|
170
|
+
// outside never got the chance to end.
|
|
171
|
+
io.err('vinta-ai-maestro: agent or gate processes it started may still be running.');
|
|
172
|
+
return OK;
|
|
173
|
+
}
|
|
174
|
+
/** The job leads its own session (it was spawned detached), so its group is its tree. */
|
|
175
|
+
function signalTree(pid, signal) {
|
|
176
|
+
if (!killTree(pid, signal)) {
|
|
177
|
+
try {
|
|
178
|
+
process.kill(pid, signal);
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
// Already gone.
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=halt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"halt.js","sourceRoot":"","sources":["../../src/cli/halt.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The `vinta-ai-maestro` command line.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* A dozen subcommands, dispatched by hand. `node:util`'s `parseArgs` does the flag
|
|
5
5
|
* parsing inside each one, and nothing else does any: a framework here would be
|
|
6
6
|
* a dependency, a plugin lifecycle and a help renderer bought to replace a
|
|
7
|
-
* switch statement over
|
|
7
|
+
* switch statement over a dozen strings.
|
|
8
8
|
*
|
|
9
9
|
* `main` returns an exit code rather than calling `process.exit`. That is what
|
|
10
10
|
* makes the commands testable without a built binary, and it keeps the one
|
|
@@ -18,10 +18,13 @@
|
|
|
18
18
|
import { OK, USAGE, processIo } from './io.js';
|
|
19
19
|
import { DOCTOR_USAGE, doctorCommand } from './doctor.js';
|
|
20
20
|
import { GATE_USAGE, gateCommand } from './gate.js';
|
|
21
|
+
import { PAUSE_USAGE, STOP_USAGE, pauseCommand, stopCommand } from './halt.js';
|
|
22
|
+
import { LOGS_USAGE, logsCommand } from './logs.js';
|
|
21
23
|
import { PURGE_USAGE, purgeCommand } from './purge.js';
|
|
22
24
|
import { RUN_USAGE, runCommand } from './run.js';
|
|
23
25
|
import { SERVE_USAGE, serveCommand } from './serve.js';
|
|
24
26
|
import { SIMULATE_USAGE, simulateCommand } from './simulate.js';
|
|
27
|
+
import { STATUS_USAGE, statusCommand } from './status.js';
|
|
25
28
|
import { WITH_USAGE, withCommand } from './with.js';
|
|
26
29
|
import { judgeHookCommandMain } from '../system-one/hook.js';
|
|
27
30
|
export const HELP = `vinta-ai-maestro — code-orchestrated parallel execution of a plan.
|
|
@@ -32,11 +35,16 @@ usage: vinta-ai-maestro <command> [options]
|
|
|
32
35
|
non-zero if a run cannot start.
|
|
33
36
|
simulate <workflow.json> Project the schedule without running it: wall
|
|
34
37
|
clock, critical path and pool contention.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
run
|
|
39
|
-
|
|
38
|
+
run <workflow.json> Start the workflow as a background job and
|
|
39
|
+
return. The run outlives this terminal.
|
|
40
|
+
run --resume <run-id> Pick an interrupted or paused run back up.
|
|
41
|
+
status [run-id] What every run — or one run — is doing.
|
|
42
|
+
logs <run-id> [-f] A run's job log; -f follows it to the end.
|
|
43
|
+
pause <run-id> Let running steps finish, then stop. Resumable.
|
|
44
|
+
stop <run-id> Kill the run now. Final.
|
|
45
|
+
ui Serve the browser UI for every run and print the
|
|
46
|
+
URL to open. Closing it leaves runs running.
|
|
47
|
+
(\`serve\` is the same command.)
|
|
40
48
|
purge [run-id] Delete run state under .vinta-ai-maestro/ — transcripts
|
|
41
49
|
and gate logs hold repository contents verbatim.
|
|
42
50
|
with <resource> -- <cmd> Run a command while holding a live run's resource.
|
|
@@ -58,7 +66,12 @@ const USAGES = {
|
|
|
58
66
|
doctor: DOCTOR_USAGE,
|
|
59
67
|
simulate: SIMULATE_USAGE,
|
|
60
68
|
serve: SERVE_USAGE,
|
|
69
|
+
ui: SERVE_USAGE,
|
|
61
70
|
run: RUN_USAGE,
|
|
71
|
+
status: STATUS_USAGE,
|
|
72
|
+
logs: LOGS_USAGE,
|
|
73
|
+
pause: PAUSE_USAGE,
|
|
74
|
+
stop: STOP_USAGE,
|
|
62
75
|
purge: PURGE_USAGE,
|
|
63
76
|
with: WITH_USAGE,
|
|
64
77
|
gate: GATE_USAGE,
|
|
@@ -86,9 +99,18 @@ export async function main(argv, io = processIo()) {
|
|
|
86
99
|
case 'simulate':
|
|
87
100
|
return await simulateCommand(rest, io);
|
|
88
101
|
case 'serve':
|
|
102
|
+
case 'ui':
|
|
89
103
|
return await serveCommand(rest, io);
|
|
90
104
|
case 'run':
|
|
91
105
|
return await runCommand(rest, io);
|
|
106
|
+
case 'status':
|
|
107
|
+
return await statusCommand(rest, io);
|
|
108
|
+
case 'logs':
|
|
109
|
+
return await logsCommand(rest, io);
|
|
110
|
+
case 'pause':
|
|
111
|
+
return await pauseCommand(rest, io);
|
|
112
|
+
case 'stop':
|
|
113
|
+
return await stopCommand(rest, io);
|
|
92
114
|
case 'purge':
|
|
93
115
|
return await purgeCommand(rest, io);
|
|
94
116
|
case 'with':
|
|
@@ -123,6 +145,9 @@ export { doctorCommand } from './doctor.js';
|
|
|
123
145
|
export { simulateCommand } from './simulate.js';
|
|
124
146
|
export { serveCommand, announce } from './serve.js';
|
|
125
147
|
export { runCommand } from './run.js';
|
|
148
|
+
export { statusCommand } from './status.js';
|
|
149
|
+
export { logsCommand } from './logs.js';
|
|
150
|
+
export { pauseCommand, stopCommand } from './halt.js';
|
|
126
151
|
export { purgeCommand } from './purge.js';
|
|
127
152
|
export { withCommand } from './with.js';
|
|
128
153
|
export { gateCommand } from './gate.js';
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,EAAW,MAAM,SAAS,CAAA;AACvD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AACzD,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AACnD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AACtD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAChD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AACtD,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAC/D,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AACnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AAE5D,MAAM,CAAC,MAAM,IAAI,GAAG
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/logs.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `vinta-ai-maestro logs <runId>` — what a run's job has said.
|
|
3
|
+
*
|
|
4
|
+
* The job's console, from `runs/<id>/job.log`: everything `run` used to print
|
|
5
|
+
* to the terminal it no longer holds — the preflight report, warnings, the
|
|
6
|
+
* start and end lines — and the daemon's own records for that process, one per
|
|
7
|
+
* line. Every attempt at the run appends to the same file, so a resumed run
|
|
8
|
+
* reads as one history.
|
|
9
|
+
*
|
|
10
|
+
* `--follow` tails it until the job exits, which is how a pause or a stop is
|
|
11
|
+
* watched to its end. It is a poll of the file's size, because a follow that
|
|
12
|
+
* depended on a filesystem watcher would behave differently on every platform
|
|
13
|
+
* this package runs on, and a quarter-second is fast enough for a human.
|
|
14
|
+
*
|
|
15
|
+
* The file is read and printed as the job wrote it, and nothing here adds to
|
|
16
|
+
* it. What is in it is what §11 allows on a stream — identifiers and the
|
|
17
|
+
* operator's own command lines — and it sits under `runs/`, so `purge` takes
|
|
18
|
+
* it with the run.
|
|
19
|
+
*/
|
|
20
|
+
import { closeSync, existsSync, openSync, readSync, statSync } from 'node:fs';
|
|
21
|
+
import { resolve } from 'node:path';
|
|
22
|
+
import { parseArgs } from 'node:util';
|
|
23
|
+
import { isAlive, jobLogPathFor, readJob } from '../job/job.js';
|
|
24
|
+
import { FAILED, OK, USAGE } from './io.js';
|
|
25
|
+
export const LOGS_USAGE = `usage: vinta-ai-maestro logs <runId> [--repo <dir>] [--follow] [--lines <n>]
|
|
26
|
+
|
|
27
|
+
Prints the run's job log: what the run would have printed to a terminal,
|
|
28
|
+
and its daemon's records, one per line. Every attempt — the original and
|
|
29
|
+
each resume — is in the same file.
|
|
30
|
+
|
|
31
|
+
--repo <dir> The project. Defaults to the current directory.
|
|
32
|
+
-f, --follow Keep printing as the job writes, until it exits.
|
|
33
|
+
-n, --lines <n> Only the last n lines. Defaults to all of them.`;
|
|
34
|
+
export async function logsCommand(argv, io, deps = {}) {
|
|
35
|
+
let parsed;
|
|
36
|
+
try {
|
|
37
|
+
parsed = parseArgs({
|
|
38
|
+
args: [...argv],
|
|
39
|
+
options: {
|
|
40
|
+
repo: { type: 'string' },
|
|
41
|
+
follow: { type: 'boolean', short: 'f' },
|
|
42
|
+
lines: { type: 'string', short: 'n' },
|
|
43
|
+
},
|
|
44
|
+
allowPositionals: true,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
io.err(LOGS_USAGE);
|
|
49
|
+
return USAGE;
|
|
50
|
+
}
|
|
51
|
+
const runId = parsed.positionals[0];
|
|
52
|
+
if (runId === undefined || parsed.positionals.length > 1) {
|
|
53
|
+
io.err(LOGS_USAGE);
|
|
54
|
+
return USAGE;
|
|
55
|
+
}
|
|
56
|
+
const rawLines = parsed.values.lines;
|
|
57
|
+
const lines = rawLines === undefined ? undefined : Number(rawLines);
|
|
58
|
+
if (lines !== undefined && (!Number.isInteger(lines) || lines < 0)) {
|
|
59
|
+
io.err('vinta-ai-maestro: --lines must be a whole number');
|
|
60
|
+
return USAGE;
|
|
61
|
+
}
|
|
62
|
+
const repoPath = resolve(parsed.values.repo ?? process.cwd());
|
|
63
|
+
const path = jobLogPathFor(repoPath, runId);
|
|
64
|
+
if (!existsSync(path)) {
|
|
65
|
+
io.err(`vinta-ai-maestro: run "${runId}" has no job log in ${repoPath}.`);
|
|
66
|
+
return FAILED;
|
|
67
|
+
}
|
|
68
|
+
let offset = 0;
|
|
69
|
+
const initial = readFrom(path, 0);
|
|
70
|
+
offset = initial.length;
|
|
71
|
+
let text = initial.toString('utf8');
|
|
72
|
+
if (lines !== undefined) {
|
|
73
|
+
const all = text.split('\n');
|
|
74
|
+
if (all.at(-1) === '')
|
|
75
|
+
all.pop();
|
|
76
|
+
text = all.slice(Math.max(0, all.length - lines)).join('\n');
|
|
77
|
+
}
|
|
78
|
+
// A partial last line waits for the rest of itself before it is printed.
|
|
79
|
+
let pending = '';
|
|
80
|
+
const emit = (chunk) => {
|
|
81
|
+
const parts = (pending + chunk).split('\n');
|
|
82
|
+
pending = parts.pop() ?? '';
|
|
83
|
+
for (const line of parts)
|
|
84
|
+
io.out(line);
|
|
85
|
+
};
|
|
86
|
+
emit(lines === undefined || text === '' ? text : `${text}\n`);
|
|
87
|
+
if (parsed.values.follow !== true) {
|
|
88
|
+
if (pending !== '')
|
|
89
|
+
io.out(pending);
|
|
90
|
+
return OK;
|
|
91
|
+
}
|
|
92
|
+
const alive = deps.alive ?? isAlive;
|
|
93
|
+
const sleep = deps.sleep ?? ((ms) => new Promise((done) => setTimeout(done, ms)));
|
|
94
|
+
for (;;) {
|
|
95
|
+
const job = readJob(repoPath, runId);
|
|
96
|
+
const chunk = readFrom(path, offset);
|
|
97
|
+
offset += chunk.length;
|
|
98
|
+
if (chunk.length > 0)
|
|
99
|
+
emit(chunk.toString('utf8'));
|
|
100
|
+
// Read once more after the job is gone, so its last words are not lost to
|
|
101
|
+
// the race between its final write and its exit.
|
|
102
|
+
if (job === null || !alive(job.pid)) {
|
|
103
|
+
const last = readFrom(path, offset);
|
|
104
|
+
if (last.length > 0)
|
|
105
|
+
emit(last.toString('utf8'));
|
|
106
|
+
if (pending !== '')
|
|
107
|
+
io.out(pending);
|
|
108
|
+
return OK;
|
|
109
|
+
}
|
|
110
|
+
await sleep(250);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function readFrom(path, offset) {
|
|
114
|
+
let size;
|
|
115
|
+
try {
|
|
116
|
+
size = statSync(path).size;
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return Buffer.alloc(0);
|
|
120
|
+
}
|
|
121
|
+
if (size <= offset)
|
|
122
|
+
return Buffer.alloc(0);
|
|
123
|
+
const fd = openSync(path, 'r');
|
|
124
|
+
try {
|
|
125
|
+
const buffer = Buffer.alloc(size - offset);
|
|
126
|
+
const read = readSync(fd, buffer, 0, buffer.length, offset);
|
|
127
|
+
return buffer.subarray(0, read);
|
|
128
|
+
}
|
|
129
|
+
finally {
|
|
130
|
+
closeSync(fd);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=logs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logs.js","sourceRoot":"","sources":["../../src/cli/logs.ts"],"names":[],"mappings":"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"}
|