docs: dashboard event channel, hive-fr0nt crate, mutation events, seq dedupe
This commit is contained in:
parent
62784d4933
commit
d8d393da6d
3 changed files with 167 additions and 30 deletions
|
|
@ -24,9 +24,12 @@ admin socket.
|
|||
## Wire protocol
|
||||
|
||||
JSON line-delimited over unix sockets in both directions (host admin
|
||||
/ manager / agent). SSE streams (`/messages/stream`,
|
||||
`/events/stream`) are `text/event-stream`. Request/response types
|
||||
live in `hive-sh4re` — change them in one place.
|
||||
/ manager / agent). SSE streams (`/dashboard/stream` on hive-c0re,
|
||||
`/events/stream` on the per-agent web UIs) are `text/event-stream`;
|
||||
each frame carries a `seq` field for the snapshot-dedupe dance
|
||||
(see `docs/web-ui.md`). Request/response types live in `hive-sh4re`
|
||||
— change them in one place. The dashboard event vocabulary lives
|
||||
in `hive-c0re::dashboard_events::DashboardEvent`.
|
||||
|
||||
## Async forms
|
||||
|
||||
|
|
|
|||
123
docs/web-ui.md
123
docs/web-ui.md
|
|
@ -10,10 +10,31 @@ and the per-agent UIs (manager on :8000, sub-agents on a hashed
|
|||
- `GET /` → `assets/index.html` (placeholders for state-driven
|
||||
sections, shipped via `include_str!` so the binary has no runtime
|
||||
file dependency).
|
||||
- `GET /static/*.css` + `GET /static/*.js` → static assets.
|
||||
- `GET /api/state` → JSON snapshot the JS app renders into the DOM.
|
||||
- `GET /events/stream` (per-agent) / `GET /messages/stream`
|
||||
(dashboard) → `text/event-stream` SSE for live updates.
|
||||
- `GET /static/*.css` + `GET /static/*.js` → static assets. Both
|
||||
pages prepend `hive_fr0nt::BASE_CSS` + `TERMINAL_CSS` to their
|
||||
per-page stylesheet, and `GET /static/hive-fr0nt.js` serves the
|
||||
shared `window.HiveTerminal.create` runtime. The dashboard's
|
||||
`#msgflow` and the per-agent `#live` log are both backed by
|
||||
this terminal — sticky-bottom auto-scroll, "↓ N new" pill,
|
||||
history backfill, SSE plumbing all live there. Each page
|
||||
registers a kind→renderer map; unknown kinds fall through to
|
||||
a JSON-dump note row.
|
||||
- `GET /api/state` → JSON snapshot the JS app renders into the
|
||||
DOM. Includes a top-level `seq` (the dashboard event channel's
|
||||
high-water mark at the moment the snapshot was assembled);
|
||||
clients use it to dedupe their buffered SSE traffic against
|
||||
the snapshot (drop frames with `seq <= snapshot.seq`).
|
||||
- `GET /dashboard/stream` (dashboard) / `GET /events/stream`
|
||||
(per-agent) → `text/event-stream` SSE for live updates. The
|
||||
dashboard stream carries broker `Sent` / `Delivered` (mirrored
|
||||
by a forwarder task from the broker's intra-process channel)
|
||||
plus mutation events (`approval_added` / `approval_resolved`,
|
||||
`question_added` / `question_resolved`, `transient_set` /
|
||||
`transient_cleared`). Each frame carries a `seq`. The
|
||||
matching backfill endpoint is `GET /dashboard/history` (last
|
||||
~200 broker messages wrapped in `{ seq, events }`) on the
|
||||
dashboard and `GET /events/history` (last 2000 `LiveEvent`s
|
||||
also wrapped in `{ seq, events }`) on the agent.
|
||||
|
||||
The JS app handles all `form[data-async]` submissions via a delegated
|
||||
listener: read `data-confirm`, swap the button to a spinner, POST
|
||||
|
|
@ -71,26 +92,37 @@ the previous process's socket release resolves itself.
|
|||
with `[cancelled]`. Questions with a `ttl_seconds` show a
|
||||
`⏳ MM:SS` chip; the host-side watchdog auto-cancels with
|
||||
`[expired]` when the deadline fires.
|
||||
5. **0PER4T0R 1NB0X** — recent messages addressed to `operator`
|
||||
(last 50, from the broker).
|
||||
5. **0PER4T0R 1NB0X** — recent messages addressed to `operator`,
|
||||
derived client-side from the dashboard event stream (no longer
|
||||
a snapshot field). Cold load seeds from
|
||||
`/dashboard/history`'s 200-message backfill; subsequent
|
||||
`sent` events with `to == "operator"` are appended live. Cap
|
||||
50, newest-first.
|
||||
6. **P3NDING APPR0VALS** — the queue. The R3QU3ST SP4WN form
|
||||
lives at the top of this section since submitting it
|
||||
immediately queues an approval that lands directly below.
|
||||
7. **MESS4GE FL0W** — live broker SSE tail (newest-first).
|
||||
Each row is one broker event — `sent` or `delivered` — with
|
||||
`from → to: body`; per-agent thinking / tool calls / claude
|
||||
chatter stay out of this view, only what passes through
|
||||
hive-c0re's broker. Below the stream sits a terminal-style
|
||||
compose box: `@name` picks the recipient (sticky across
|
||||
sends via localStorage; auto-complete from the live
|
||||
container list, Tab/Enter to confirm), starting a message
|
||||
with `@<name> body` retargets in one stroke, plain text
|
||||
sends to the sticky recipient. `POST /op-send` drops
|
||||
`{from:"operator", to, body}` into the broker — same shape
|
||||
any sub-agent sees as a regular inbox message. Manager is
|
||||
addressed as `@manager` (the broker recipient string), not
|
||||
`@hm1nd` (the container name); the auto-complete swaps
|
||||
automatically.
|
||||
7. **MESS4GE FL0W** — live broker tail wrapped in a
|
||||
`.terminal-wrap` (same chrome as the per-agent terminal).
|
||||
Cold load backfills the last ~200 messages from
|
||||
`/dashboard/history`; live frames arrive on
|
||||
`/dashboard/stream` and dispatch through
|
||||
`HiveTerminal.create`. Each row is one broker event —
|
||||
`sent` or `delivered` — with `from → to: body`; per-agent
|
||||
thinking / tool calls / claude chatter stay out of this
|
||||
view, only what passes through hive-c0re's broker. Sticky-
|
||||
bottom auto-scroll + "↓ N new" pill match the per-agent
|
||||
page. Below the stream sits a terminal-style compose box:
|
||||
`@name` picks the recipient (sticky across sends via
|
||||
localStorage; auto-complete from the live container list,
|
||||
Tab/Enter to confirm; `@*` broadcasts to every registered
|
||||
agent), starting a message with `@<name> body` retargets
|
||||
in one stroke, plain text sends to the sticky recipient.
|
||||
`POST /op-send` drops `{from:"operator", to, body}` into
|
||||
the broker and returns 200; the resulting SSE frame
|
||||
re-renders both the terminal row and the inbox section
|
||||
(no `/api/state` refetch). Manager is addressed as
|
||||
`@manager` (the broker recipient string), not `@hm1nd`
|
||||
(the container name); the auto-complete swaps automatically.
|
||||
|
||||
### Container row
|
||||
|
||||
|
|
@ -143,10 +175,13 @@ not ours.
|
|||
|
||||
### Dashboard endpoints
|
||||
|
||||
- `POST /approve/{id}` — approve a pending approval.
|
||||
- `POST /approve/{id}` — approve a pending approval. Fires
|
||||
`ApprovalResolved` on the dashboard event channel; client
|
||||
updates derived approvals state from the event.
|
||||
- `POST /deny/{id}` (`note=<reason>`, optional) — deny a pending
|
||||
approval with an optional operator-supplied reason. The reason
|
||||
travels to the manager as `HelperEvent::ApprovalResolved.note`.
|
||||
travels to the manager as `HelperEvent::ApprovalResolved.note`
|
||||
and also rides on the dashboard's `ApprovalResolved` event.
|
||||
Dashboard prompts via `window.prompt()` on click.
|
||||
- `POST /{rebuild,kill,restart,start,destroy}/{name}` — lifecycle.
|
||||
`destroy` accepts `purge=on` to also wipe state dirs.
|
||||
|
|
@ -157,12 +192,52 @@ not ours.
|
|||
- `POST /request-spawn` — queue a Spawn approval.
|
||||
- `POST /update-all` — rebuild every stale container.
|
||||
- `POST /op-send` (`to=<name>`, `body=<text>`) — drop an
|
||||
operator-authored message into `<name>`'s inbox. Used by the
|
||||
operator-authored message into `<name>`'s inbox. `to=*` fans
|
||||
out to every registered agent. Returns 200; the broker
|
||||
`Sent` event re-renders both the message-flow terminal and
|
||||
the operator inbox without a snapshot refetch. Used by the
|
||||
compose textbox under MESS4GE FL0W.
|
||||
- `GET /api/journal/{name}?unit=&lines=` — journalctl viewer for
|
||||
a managed container.
|
||||
- `GET /api/agent-config/{name}` — read-only view of the applied
|
||||
`agent.nix`.
|
||||
- `GET /dashboard/stream` — unified live event channel:
|
||||
broker `sent` / `delivered`, plus the mutation events listed
|
||||
below. Each frame carries `seq`.
|
||||
- `GET /dashboard/history` — last ~200 broker messages
|
||||
(wrapped as `{ seq, events }`) for the message-flow
|
||||
terminal's backfill on page load.
|
||||
|
||||
### Dashboard event channel
|
||||
|
||||
Wire vocabulary on `/dashboard/stream` (kind tag is in the JSON
|
||||
payload):
|
||||
|
||||
- `sent` / `delivered` — broker traffic, mirrored from the
|
||||
intra-process channel by a forwarder task. Used by the
|
||||
message-flow terminal renderer and the operator-inbox
|
||||
derived state.
|
||||
- `approval_added` (id, agent, approval_kind, sha_short, diff,
|
||||
description) / `approval_resolved` (id, agent, approval_kind,
|
||||
sha_short, status, resolved_at, note, description) — pending
|
||||
queue + history mutations. Client mutates a derived store and
|
||||
re-renders only the approvals section.
|
||||
- `question_added` (id, asker, question, options, multi,
|
||||
asked_at, deadline_at) / `question_resolved` (id, answer,
|
||||
answerer, answered_at, cancelled) — operator-targeted
|
||||
questions only (peer-to-peer questions never fire these). The
|
||||
ttl watchdog fires `question_resolved` with
|
||||
`answerer = "ttl-watchdog"` on expiry.
|
||||
- `transient_set` (name, transient_kind, since_unix) /
|
||||
`transient_cleared` (name) — lifecycle action spinners. The
|
||||
client ticks the elapsed-seconds badge off `since_unix`
|
||||
client-side, no polling.
|
||||
|
||||
`/api/state` still serves `approvals` / `approval_history` /
|
||||
`questions` / `question_history` / `transients` for cold-start
|
||||
on first page load and as a safety-net resync from the 5s poll;
|
||||
the client maintains the same arrays in derived stores and
|
||||
applies the events on top.
|
||||
|
||||
Generalised form helpers: `form[data-confirm="…"]` pops
|
||||
`confirm()` before submit; `form[data-prompt="…"]` pops
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue