omp-conductor 0.18.2 → 0.19.1
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 +105 -40
- package/REFERENCE.md +865 -30
- package/package.json +1 -1
- package/schema/config.schema.json +26 -0
- package/src/admission.ts +212 -26
- package/src/ask.ts +288 -1
- package/src/briefs/orchestrator.md +6 -5
- package/src/cli.ts +5 -1
- package/src/command-help.ts +9 -1
- package/src/command-manifest.ts +36 -3
- package/src/commands/arm.ts +5 -1
- package/src/commands/context.ts +2 -0
- package/src/commands/message.ts +26 -2
- package/src/commands/reconcile-units.ts +104 -0
- package/src/commands/release-composition.ts +232 -0
- package/src/commands/resume.ts +2 -27
- package/src/commands/setup.ts +101 -16
- package/src/commands/stats.ts +11 -30
- package/src/commands/tail.ts +31 -1
- package/src/commands/upgrade.ts +20 -3
- package/src/commands/verb.ts +2 -1
- package/src/config-schema.ts +19 -0
- package/src/config.ts +80 -0
- package/src/credential-class.ts +366 -0
- package/src/daemon.ts +1218 -288
- package/src/dashboard/app.js +504 -2
- package/src/dashboard/controls.ts +336 -0
- package/src/dashboard/index.html +30 -0
- package/src/dashboard/server.ts +271 -30
- package/src/dashboard/style.css +116 -0
- package/src/dashboard/transcript.ts +173 -0
- package/src/doctor.ts +379 -22
- package/src/failure-class.ts +59 -0
- package/src/fleet.ts +511 -101
- package/src/host.ts +6 -130
- package/src/omp.ts +29 -0
- package/src/orchestrator-tick.ts +343 -88
- package/src/pause.ts +233 -0
- package/src/settlement.ts +159 -2
- package/src/setup-answers.ts +97 -0
- package/src/setup-host.ts +325 -1159
- package/src/setup-install.ts +204 -27
- package/src/setup-wizard.ts +111 -50
- package/src/setup.ts +33 -0
- package/src/spend-telemetry.ts +117 -0
- package/src/stats.ts +35 -0
- package/src/status-render.ts +348 -19
- package/src/store.ts +1229 -55
- package/src/telegram-freshness.ts +269 -0
- package/src/to-spec.ts +27 -0
- package/src/types.ts +697 -4
- package/src/unblock.ts +22 -0
- package/src/unit-reconcile.ts +303 -0
- package/src/upgrade-verify.ts +8 -1
- package/src/upgrade.ts +326 -47
- package/src/verbs/actions.ts +124 -10
- package/src/verbs/protocol.ts +70 -2
- package/src/verbs/server.ts +447 -8
- package/src/wake.ts +48 -0
- package/src/worker.ts +403 -3
package/README.md
CHANGED
|
@@ -271,39 +271,39 @@ The Herdr half owns recovery, not dispatch or policy: it restores the exact
|
|
|
271
271
|
session identity, requests an immediate heartbeat, or reports through Telegram
|
|
272
272
|
and a Herdr notification that the fleet is down.
|
|
273
273
|
|
|
274
|
-
###
|
|
275
|
-
|
|
276
|
-
Worker sessions
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
its
|
|
288
|
-
`
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
274
|
+
### What confines a worker session
|
|
275
|
+
|
|
276
|
+
Worker sessions run as the fleet account — the same account the daemon runs as.
|
|
277
|
+
An earlier release ran them under a dedicated `omp-worker` UID with your install
|
|
278
|
+
bind-mounted read-only for them to resolve; that design did not survive contact
|
|
279
|
+
with Bun's install, cache and native-addon resolution (a worker could not load
|
|
280
|
+
the harness at all), and it was withdrawn. Exposing package files is not a
|
|
281
|
+
self-contained runnable runtime.
|
|
282
|
+
|
|
283
|
+
What confines a worker is unchanged, and none of it needed a separate account:
|
|
284
|
+
|
|
285
|
+
- **its worktree.** Dispatch hands each run an isolated git worktree and session
|
|
286
|
+
directory; that is its writable world for the attempt.
|
|
287
|
+
- **its verbs.** A worker session may push and open a pull request through the
|
|
288
|
+
mediated verbs and nothing else: `conductor_pr_merge`, `conductor_label` and
|
|
289
|
+
`conductor_release` refuse a worker mechanically, whatever any config says.
|
|
290
|
+
- **its budget.** Turn, wall-clock, spend and attempt caps bound every run, and
|
|
291
|
+
a cap kill is recorded rather than retried silently.
|
|
292
|
+
- **its review.** A green pull request is where a worker stops; merging is the
|
|
293
|
+
orchestrator's, behind the exact-head, green-checks and single-flight gates.
|
|
294
|
+
|
|
295
|
+
`setup host` no longer provisions that account, its ACL projection or the mount
|
|
296
|
+
unit, and a host that carries them from an earlier release is converged the next
|
|
297
|
+
time `setup host` or `upgrade` runs: the mount is disabled, unmounted and
|
|
298
|
+
removed, before anything else in the transaction. The account itself is left
|
|
299
|
+
alone — an unused Unix account is inert, and deleting one to tidy up is
|
|
300
|
+
destructive.
|
|
301
|
+
|
|
302
|
+
OS-user isolation is a design worth revisiting, and the next attempt needs an
|
|
303
|
+
immutable self-contained worker runtime rather than a bind of the operator's
|
|
304
|
+
install, a real end-to-end session preflight before any switch, and atomic
|
|
305
|
+
rollback. Until then, `omp-conductor` still has to be *installed* (not run from a
|
|
306
|
+
source checkout) for a worker to resolve its harness peer.
|
|
307
307
|
|
|
308
308
|
### Stop the conductor (hold / stop)
|
|
309
309
|
|
|
@@ -407,12 +407,77 @@ Clear the pin with `omp-conductor resume` when you want recovery again.
|
|
|
407
407
|
### See the fleet in a browser: `dashboard`
|
|
408
408
|
|
|
409
409
|
`omp-conductor dashboard` serves the same fleet facts `status` and `board`
|
|
410
|
-
render, in a browser
|
|
411
|
-
|
|
412
|
-
(
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
410
|
+
render, in a browser, and — since #295 — lets you *act* on them without a shell
|
|
411
|
+
on the host. It is a separate process from the dispatch daemon: the read surface
|
|
412
|
+
(`GET /api/projects`, `/api/overview`, and per-project `board`, `status`,
|
|
413
|
+
`ledger`, `reports`, `runs/:issue`) reads fleet state exactly as `status` does,
|
|
414
|
+
and the control surface (`POST`) routes every action to the same code the CLI
|
|
415
|
+
verb runs.
|
|
416
|
+
|
|
417
|
+
| action | what it does |
|
|
418
|
+
|---|---|
|
|
419
|
+
| `pause` / `resume` | stop and restart new claims, **keeping the heartbeat** — the cheap, reversible control |
|
|
420
|
+
| `hold` | stop claims *and* disarm ticks; re-arming costs a Telegram challenge, so the UI confirms it by name |
|
|
421
|
+
| `disarm` | take the heartbeat down without touching dispatch |
|
|
422
|
+
| `runs/:issue/extend` | raise a run's turn ceiling, **proxied to the owning daemon** so its own bounds and refusals apply verbatim |
|
|
423
|
+
| `runs/:issue/worker/{pause,resume,stop}` | drive one live worker, proxied for the same reason — the daemon holds the session controller |
|
|
424
|
+
| `runs/:issue/unblock` | the same `unblock` the CLI runs, `force` included and confirmed by name |
|
|
425
|
+
| `decisions/:id/answer` | resolve one open decision; a second answer is a `409`, never a silent overwrite |
|
|
426
|
+
|
|
427
|
+
Three properties are worth knowing before you use it:
|
|
428
|
+
|
|
429
|
+
- **Nothing here is a second implementation.** Where an action already lives
|
|
430
|
+
behind the daemon's HTTP surface, the dashboard *calls that endpoint* rather
|
|
431
|
+
than reaching past it to the store. `extend` is the instructive case: the
|
|
432
|
+
daemon's `turn-limit` route owns the ceiling bound, the live-controller check
|
|
433
|
+
and the not-an-increase refusal, so its `409`/`422` answers pass through
|
|
434
|
+
unchanged and the browser cannot come to disagree with `omp-conductor extend`.
|
|
435
|
+
- **A dead daemon is a `502` naming it**, never a `200` that pretends a worker
|
|
436
|
+
was paused.
|
|
437
|
+
- **`arm` is deliberately absent.** It requires an inbound Telegram proof that a
|
|
438
|
+
human answered a challenge, and a browser button cannot supply one — a
|
|
439
|
+
dashboard `arm` would either weaken that proof or lie about having it. Arming
|
|
440
|
+
stays CLI/Telegram.
|
|
441
|
+
|
|
442
|
+
**Watching a run.** The run detail view streams the transcript live
|
|
443
|
+
(`GET .../runs/:issue/transcript/stream`, Server-Sent Events). It is the same
|
|
444
|
+
follow `omp-conductor tail` runs — the same 1s re-stat cadence, reading from byte
|
|
445
|
+
zero so attaching to a run ten turns in shows all ten, and the same advisor
|
|
446
|
+
transcripts alongside. Nothing here touches the daemon: two browsers on one run
|
|
447
|
+
are two read-only file follows, and a fleet with no daemon running at all still
|
|
448
|
+
streams. A run with no transcript yet gets one explicit `error` event saying so
|
|
449
|
+
rather than an empty stream that reads as a quiet worker; a finished run streams
|
|
450
|
+
its tail, writes `run ended: <state>`, and closes.
|
|
451
|
+
|
|
452
|
+
One wrinkle worth knowing: `EventSource` cannot set an `Authorization` header, so
|
|
453
|
+
this endpoint — and only this endpoint — also accepts `?token=`. A URL can reach
|
|
454
|
+
a server log or a browser history entry, which is why the acceptance is scoped to
|
|
455
|
+
that exact path and stated rather than buried.
|
|
456
|
+
|
|
457
|
+
**Analytics.** The Analytics tab answers *what did the fleet accomplish, and at
|
|
458
|
+
what cost* — merges per week, USD per merged PR, lead-time p50/p90, attempts per
|
|
459
|
+
merge, and a failure-class histogram, over a 7d/30d/custom window. It is the
|
|
460
|
+
same aggregation `omp-conductor stats` prints, computed by the same module: one
|
|
461
|
+
implementation, two consumers, so the page and the CLI cannot disagree about a
|
|
462
|
+
number. Store-only — rendering it makes no GitHub API calls.
|
|
463
|
+
|
|
464
|
+
Two things it deliberately will not do. A window in which nothing settled
|
|
465
|
+
renders an explicit *nothing settled* rather than a chart of zeros, because a
|
|
466
|
+
zero drawn on an axis reads as a measurement. And runs whose harness reported no
|
|
467
|
+
spend (`spendUsd = 0`) are counted as **unmetered**, never averaged in as free:
|
|
468
|
+
cost-per-merge divides by the merged issues that actually metered, and the
|
|
469
|
+
unmetered remainder is named beside it.
|
|
470
|
+
|
|
471
|
+
**Answering a question.** Open decisions render on the Reports tab with an
|
|
472
|
+
answer box, resolving through the same `PUT /decisions/:id/answer` the CLI's
|
|
473
|
+
`decision resolve` uses — so a second answer to the same question is a 409, not
|
|
474
|
+
a silent overwrite. Watches are listed but never offered an answer box: a watch
|
|
475
|
+
is the orchestrator's own condition, not a question anyone asked you.
|
|
476
|
+
|
|
477
|
+
Every mutation attributes itself with source `dashboard`, so `status` can say
|
|
478
|
+
*who* paused the fleet rather than only that it is paused. The operator's own
|
|
479
|
+
sentence rides in the sentinel's `reason=` field, because `source=` is a
|
|
480
|
+
space-free token the pause fence matches on.
|
|
416
481
|
|
|
417
482
|
Binds `127.0.0.1:8788` by default. First start mints a bearer token at
|
|
418
483
|
`<stateDir()>/dashboard-token` (mode `0600`, next to `config.json` under
|
|
@@ -620,7 +685,7 @@ reference — usage strings, flags and per-command behaviour — is in the
|
|
|
620
685
|
| `doctor [--json] [--probe-telegram]` | Read-only deployment health — gh auth, exact-case labels, systemd drift, config backup, sqlite integrity, spend telemetry, timezones, Telegram. Run after install and after every upgrade; exit 0 only when nothing failed. |
|
|
621
686
|
| `ledger [--json]` | The action audit — every mediated verb, refusal and turn budget. |
|
|
622
687
|
| `board` | Live terminal kanban from Queue to Settled. |
|
|
623
|
-
| `dashboard [--port N] [--host ADDR]` | Browser UI plus bearer-authenticated `/api/projects
|
|
688
|
+
| `dashboard [--port N] [--host ADDR]` | Browser UI plus a bearer-authenticated API: read the fleet (`/api/projects`, `overview`, `board`, `status`, `ledger`, `reports`, `runs`, `stats`) and drive it (`pause`/`resume`/`hold`/`disarm`, `extend`, `unblock`, worker control, decision answering), plus a live SSE transcript stream per run and an analytics view over the same aggregation `stats` prints. `arm` is deliberately absent. Loopback by default; the token lives at `<state>/dashboard-token`. |
|
|
624
689
|
| `hold [--keep-ticks]` | Pause claims and disarm ticks — the soft stop. |
|
|
625
690
|
| `arm` / `disarm` | Gate / clear the orchestrator tick's arm marker. |
|
|
626
691
|
| `tail <issue>` | Follow a live worker's transcript. |
|