Each agent row on the hive dashboard, and the agent page's link menu, linked to the agent's forge profile and its config repo. Both are gone, with what only they used: - hive-agent no longer builds the two links, the `Forge` link kind, or the `forge_public_url` field of its state, and no longer reads `HIVE_FORGE_PUBLIC_URL`. The agent page's link menu and the dashboard row's icon strip (which skips a forge link an agent not yet rebuilt still sends) drop their forge handling. - hive-c0re's dashboard state no longer carries `forge_public_url`. - `HIVE_FORGE_PUBLIC_URL` is no longer forwarded to agents through the meta flake (the forwarded-variable list, its test fixture, the controller's settings and the VM test), so the next meta flake render differs by that one line. hive-c0re still reads it for the forge URL a hive publishes, so `swarm.forge.publicUrl` stays, with its description corrected. - The docs stop describing the links.
525 lines
27 KiB
Markdown
525 lines
27 KiB
Markdown
# Dashboard layout
|
|
|
|
> Part of [Hives](README.md). See also:
|
|
> [Shape (shared)](../../4-implementation/web-ui/shape.md) · [Per-agent page](../agents/web-page.md)
|
|
|
|
This page covers the per-hive UI that hive-c0re serves. Swarm-wide
|
|
surfaces — the agent roster, agent creation, linking external forge and
|
|
matrix accounts, the controller's job graph — are on the
|
|
[swarm UI](../swarm/ui.md).
|
|
|
|
hive-c0re serves the dashboard at `/`.
|
|
It has a fixed chrome header at the top and a `<main>` that shows exactly
|
|
one tab pane at a time. The URL hash (`#agents`, `#rebuild`, `#meta`,
|
|
`#kept`) drives which pane is
|
|
active; hash changes don't reload the page.
|
|
|
|
**Chrome header** (fixed, overlays the active tab pane):
|
|
- **Pages menu**: a link icon at the right end of the chrome, beside the
|
|
profile avatar (`links-menu.js`, `@hive/shared`'s `LinksMenu`, the same
|
|
component the swarm UI uses for its service links), opening a popover with links to **api docs**
|
|
(`/api/docs/`) and **swarm** (the swarm
|
|
UI at the swarm apex domain, opened in a new tab). The swarm entry
|
|
comes from `state.swarm_public_url`, which hive-c0re reads from
|
|
`HIVE_SWARM_PUBLIC_URL` (set from `services.hyperhive.swarm.domain`);
|
|
with no domain set there is no swarm entry. The standalone pages link back to `/`.
|
|
- **Profile menu**: the signed-in user's avatar at the right end of the
|
|
chrome (`user-menu.js`, the same component as the swarm UI's and the agent
|
|
page's). It opens a popover with the user's name and links to authelia's
|
|
settings and sign-out pages. The name comes from `/api/whoami`, which the
|
|
dashboard's nginx vhost proxies to authelia, and the links from
|
|
`state.authelia_public_url`, which hive-c0re reads from
|
|
`HIVE_AUTHELIA_PUBLIC_URL` (set from `services.hyperhive.swarm.authelia.url`).
|
|
If the name doesn't load the avatar shows a question mark, and without the URL the
|
|
popover has no links.
|
|
- **Tab strip**: `◆ 4G3NTS ◆`, `◆ R3BU1LD QU3U3 ◆`,
|
|
`◆ K3PT ST4T3 ◆`.
|
|
- **Banner-thin** (`░▒▓█▓▒░ HYPERHIVE / HIVE-C0RE / WE ARE THE WIRED ░▒▓█▓▒░`)
|
|
— sits below the tab strip.
|
|
- **No warning banner.** No page shows hive-level warnings (host disk
|
|
pressure, failing background sweeps, forge boot failures, agents that
|
|
need a claude login). They live in a push-based registry
|
|
(`hive-c0re`'s `warnings.rs`): any subsystem raises or clears its own
|
|
entry through an RAII guard, and each raise logs one
|
|
`hive warning raised` line. The operator sees the registry as metrics and
|
|
Grafana alerts: see
|
|
[Hive-scoped metrics](../../4-implementation/scheduler/observability.md#hive-scoped-metrics-hive-c0re).
|
|
A container crash is a swarm-level Grafana alert on the crash watcher's
|
|
own log line, so a hive with no otel export has no operator-visible
|
|
warning signal beyond `/health/ready`.
|
|
- **Browser tab title** — `hyperhive // h1ve-c0re` by default; updated
|
|
to `<swarm> / <hive> // h1ve-c0re` once `hive_name` / `swarm_name`
|
|
arrive in the state snapshot.
|
|
|
|
## 4G3NTS tab
|
|
|
|
**C0NTAINERS** — live containers as a flat list, one row per
|
|
container, sorted alphabetically by agent name; every container the
|
|
rescan reports gets a row. Pulsing red banner at the top
|
|
of this section if any two sub-agents hash to the same port
|
|
(`port_conflicts` from `/api/state`): the operator must rename
|
|
one of them and rebuild. `lifecycle::{spawn,rebuild}` also
|
|
preflight this and refuse with a clear error message naming the
|
|
conflicting agent.
|
|
|
|
## K3PT ST4T3 tab
|
|
|
|
Passive / rare-interaction state. `kept.js` renders it from the
|
|
`/api/state` snapshot and keeps it live from the `tombstones_changed`
|
|
stream event. (The rebuild queue and build logs are dashboard tabs too —
|
|
see below.)
|
|
|
|
Per-container CPU, memory and disk load isn't shown in the hive UI. The
|
|
same figures go out as OTEL metrics and appear in Grafana (see
|
|
[observability](../../4-implementation/scheduler/observability.md)). An agent's CPU and memory
|
|
caps live per agent in the swarm UI.
|
|
|
|
**K3PT ST4T3** — agents with kept state and **no container** (size + age +
|
|
claude-creds badge). One action: `PURG3` (wipes the state
|
|
dir; `POST /api/purge-tombstone/{name}`).
|
|
|
|
⚠️ **Not only *destroyed* agents.** Nothing records a destroy: `Provision`
|
|
writes every definition-side artifact (state subvolume,
|
|
meta registration, topology entry) *before* the container exists,
|
|
and that artifact set survives `lifecycle::destroy`. An agent
|
|
part-way through a spawn is byte-identical on disk to a tombstone, and
|
|
the pane lists both. The pane carries a standing warning to that effect, and
|
|
the row badge says `offline` rather than `destroyed` — the absence of a
|
|
container is all hive-c0re can actually prove. Fixing it properly
|
|
needs a recorded destroy — **deferred** to the swarm-controller /
|
|
snapshot-storage rework where the problem changes shape.
|
|
|
|
## Build tab (`#rebuild`)
|
|
|
|
The rebuild queue is the **R3BU1LD QU3U3** tab (`#rebuild`). It lives in `builds.js`, which `tabs.js` imports: the
|
|
dashboard's one tab strip, `/api/state` snapshot and
|
|
`/api/dashboard/stream` subscription serve them, with
|
|
`rebuild_queue_changed` on the stream alongside the dashboard's other
|
|
kinds.
|
|
The 4G3NTS tab does **not** read the graph endpoint, by design — the swarm
|
|
view stays independent of job-queue internals. Its per-agent pending
|
|
badges are transient-only, and its queue-summary banner reads the much
|
|
narrower `GET /api/jobq/rollup` instead (see Container row, below) — a
|
|
handful of pre-tallied counts, not the graph. The banner's "view queue →"
|
|
link goes to the `#rebuild` tab.
|
|
|
|
**R3BU1LD QU3U3** — pending, in-flight, and recently settled container
|
|
operations: rebuilds and first-spawns. One
|
|
operation runs at a time; the worker drains FIFO. **Is a mounted
|
|
`JobqGraph`** (the shared generic graph-viewer Preact component,
|
|
`@hive/shared/jobq-graph.js` — the same one swarm-ui uses) —
|
|
`builds.js` doesn't render the queue itself; it just mounts the component
|
|
(`mountJobqGraph(container, props)`, since this page has no JSX
|
|
pipeline) with an `onUpdate` callback to drive the two things below it
|
|
that the generic view doesn't show. The component owns fetching, cold
|
|
and live: `GET /api/jobq/graph` on mount, and a refetch via the mount
|
|
handle's `.refresh()` on every `rebuild_queue_changed` SSE tick (that
|
|
event is a bare `{ seq }` trigger carrying no queue payload; both
|
|
dashboard pages treat the tick as a pure refetch trigger).
|
|
|
|
Each row is one root graph node (`parent: null`); a multi-step op's
|
|
per-agent subgraphs and sub-steps render as nodes within that one
|
|
entry (structural `parent` edges define the tree; sibling order is
|
|
array order, unchanged from the wire). A row shows a state glyph (`⏸`
|
|
pending / `▶` running / `◐` finishing — own work done, a sub-node
|
|
still running / `✔` done / `✖` failed / `⊘` cancelled / `·` skipped)
|
|
and each step's own label/agent. A `Node`-kind dep on a sibling shows
|
|
as a plain "waits on: `<label>`" text line under the row rather than a
|
|
gutter rail — a rail breaks visually whenever a nested subtree sits
|
|
between the two related rows, since a text line needs no continuous
|
|
vertical space to draw.
|
|
`builds.js` mounts the component with `cancellable` set, which turns on
|
|
a per-node cancel button (`✕`) on any non-terminal row — the button
|
|
calls the mount's `onCancel(id)` callback, and the page does the actual
|
|
`POST /api/rebuild-queue/{id}/cancel`, matching that callback to its
|
|
own domain concept (the component knows nothing about that endpoint).
|
|
**Rows carry no source chip, kind label, timing, or build-log
|
|
deep-link** — the generic graph wire doesn't carry those fields, and
|
|
rows deliberately present exactly what the endpoint provides rather
|
|
than reconstructing chrome hive-c0re doesn't send. Settled entries
|
|
render their **full step tree**, not just a bare summary — the wire
|
|
doesn't filter `Done` nodes out.
|
|
|
|
**State filter.** A row of per-state checkboxes above
|
|
the tree — one per lifecycle state, matching the row glyphs — lets the
|
|
operator narrow which root groups render; unchecking a state re-fetches
|
|
`GET /api/jobq/graph?states=<checked, comma-joined>` rather than
|
|
hiding rows client-side, so the `onUpdate` callback's node list (and
|
|
everything downstream of it) only
|
|
ever sees what's actually shown. Filtering is by a **root's own**
|
|
state, which is already its subtree's rolled-up answer, so the filter
|
|
keeps or drops a whole group, never splitting mid-tree. Default selection is
|
|
every state **except** `Done`/`Skipped` — a fresh queue view leads
|
|
with what's still moving or needs attention, not the settled tail;
|
|
builds.js omits the `states` param entirely (identical request to
|
|
before this filter existed) when the operator has checked every state. Server-side: the query
|
|
narrows [`Queue::graph_snapshot`]'s already-bounded (`MAX_HISTORY_DAGS`)
|
|
root set — the history cap and the state filter are independent
|
|
concerns, so a narrow filter never reaches further back in time to
|
|
compensate.
|
|
|
|
A build's output and history aren't on this page. hive-c0re logs every container build
|
|
to the journal, and the swarm's `builds` Grafana board reads those lines: the builds with
|
|
their outcome and runtime, and the output of the one you pick.
|
|
|
|
## Container row
|
|
|
|
A full-height **square agent icon** (5em, capped) on the left. The
|
|
icon is the **selection toggle**: select (or Enter/Space) adds/removes
|
|
the agent from the selection set; `aria-pressed` reflects the state;
|
|
the tooltip says "select … for bulk actions" or "deselect … (or press
|
|
Esc to clear all)." The `<img>` points at `<url>/icon`; load failure
|
|
falls back to the dimmed hyperhive mark (`/favicon.svg`). The card
|
|
body sits to the right with three stacked lines
|
|
(`assets/agents.js::renderContainers`).
|
|
|
|
<!-- vale write-good.Passive = NO -->
|
|
**Icon layout + load strategy:** the `<img>` is absolutely
|
|
positioned (`inset: 0`) inside the `.container-icon` wrapper —
|
|
the wrapper is the flex child and sizes itself via `width: 5em` +
|
|
`aspect-ratio: 1`, the `<img>` is out of flow so its load state
|
|
(pending, loaded, broken) can never contribute intrinsic size or
|
|
reflow the row. Without that, the row would briefly grow as the
|
|
image's natural dimensions arrived, then snap back on
|
|
`object-fit: contain`. The load itself is fire-and-forget: the
|
|
dashboard doesn't pre-check whether the agent is reachable, it
|
|
just lets the `<img>` try and listens for an `error` event. On
|
|
failure the handler swaps the `src` to `/favicon.svg` (served by
|
|
the dashboard itself, always reachable) and adds the
|
|
`icon-unreachable` class for the dimmed look. When the dashboard
|
|
already knows the container is stopped (`ContainerView.running = false`)
|
|
the fallback fires immediately, skipping the doomed `<url>/icon`
|
|
fetch entirely.
|
|
<!-- vale write-good.Passive = YES -->
|
|
|
|
<!-- vale write-good.Passive = NO -->
|
|
**Line 1** — agent name (link → new tab), m1nd/ag3nt chip, an
|
|
**icon-only nav strip** plus live agent-owned state, all populated
|
|
async from a single `GET /api/dashboard-state` call to the agent's
|
|
own hive-agent. The response (`DashboardState`) carries: `links` (nav
|
|
strip entries — `📊 stats`, `🖥 screen` when GUI is enabled, plus any
|
|
agent-declared `dashboardLinks` extras), `status_text` / `status_set_at` (agent
|
|
self-reported status — the `(set N ago)` chip carries a `data-set-at`
|
|
stamp and ticks every 30s to stay fresh across the long-lived keyed row
|
|
cache), `rate_limited`, `ctx_tokens` / `context_window_tokens`
|
|
(context-window badge data). hive-agent is the single source
|
|
of truth for all of these. The dashboard resolves each `AgentLink.kind`
|
|
against a per-agent base URL depending on whether hive-gateway is in
|
|
front (`StateSnapshot.gateway_enabled`, sourced from the
|
|
`HIVE_GATEWAY_ENABLED` env). The c0re NixOS module sets that env
|
|
unconditionally — the gateway always runs; the flag stays so the
|
|
dashboard doesn't have to learn that. Gateway-on: base URL is
|
|
`/agent/<name>` (same origin, gateway proxies to the
|
|
per-agent harness — TCP or unix-domain depending on the agent's
|
|
`HIVE_WEB_SOCKET` opt-in, see
|
|
`docs/4-implementation/networking/gateway.md::Per-agent unix-socket upstream`).
|
|
Gateway-off is the flag-absent fallback (a dashboard served outside a
|
|
hyperhive deploy): base URL is
|
|
`http://<host>:<container.port>` (direct TCP). External links are
|
|
already absolute. The row has no forge links: an agent's forge profile
|
|
and config repo are on the forge itself. The same base URL drives the primary agent-name
|
|
link + favicon fetch, so the whole row routes through the gateway
|
|
as a unit.
|
|
<!-- vale write-good.Passive = YES -->
|
|
|
|
<!-- vale write-good.Passive = NO -->
|
|
**When the container is stopped** (`ContainerView.running = false`),
|
|
the dashboard skips the async `dashboard-state` fetch entirely (the agent
|
|
web server is down), so a single badge replaces the badge chain,
|
|
the nav strip is empty, and status text / rate-limited / ctx
|
|
badges disappear. The agent icon goes straight to the dimmed
|
|
`/favicon.svg` fallback instead of attempting a doomed load from the
|
|
container's URL. Static fields — `pending_reminders`, `parent`, `config` link — remain visible
|
|
regardless of run state.
|
|
<!-- vale write-good.Passive = YES -->
|
|
|
|
**Line 2** — status badges only (no per-card action buttons — actions
|
|
live in the **selection bar** or the **per-agent `⋮` menu**, see
|
|
below).
|
|
|
|
### Status badges
|
|
|
|
**Which single badge:** `ContainerView.failed`
|
|
(systemd `ActiveState=failed` — the unit exhausted its bounded
|
|
restarts and gave up on its own) draws a red `✖ gave up` badge;
|
|
otherwise a plain muted `■ not running` — a container an operator
|
|
stopped deliberately. Both states read `running: false`; `failed` is
|
|
the orthogonal fact (a fourth one alongside `paused`/
|
|
`needs_login`, same "independent flags, no state machine" shape —
|
|
see `ContainerView`'s own doc comment) that tells them apart. An
|
|
older hive-c0re without the field serves `failed: undefined`, which
|
|
reads falsy — degrades cleanly to the single `not running` badge.
|
|
|
|
When the container is running, status badges follow — `⊘ rate
|
|
limited` (red, while the harness parks itself after a 429), `needs
|
|
login` — plus **one `◐ pending-state…` pill per
|
|
active transient** (replaces buttons during operator-initiated
|
|
start / stop / restart / rebuild / destroy). An agent can carry
|
|
**multiple transients at once** — for example a lease-exempt `prebuild`
|
|
running alongside a `stop_for_update` on the same agent — and each
|
|
renders as its own independent badge instead of collapsing
|
|
into one label, matching the existing multi-badge convention this
|
|
line already uses for `paused`/model/ctx.
|
|
|
|
Any pending badge means the row is actually **running** something
|
|
right now (see **Pending-badge derivation** below); every row
|
|
carrying ≥1 badge keeps the amber row tint AND draws a **rotating
|
|
amber ring** around the agent icon.
|
|
|
|
**Pending-badge derivation:** transients only (`transientsState`,
|
|
keyed `agent -> Map<kind, since_unix>`) — a transient is **derived
|
|
from a job-queue node currently `Running`** against that agent, not
|
|
declared per request, so its label follows the operation as it
|
|
progresses (a rebuild reads `stop_for_update`, then `swap`, then
|
|
`reconcile` rather than one constant `rebuilding` for its whole
|
|
life). Two consequences for anything rendering it:
|
|
|
|
- The label vocabulary is **open** — it's the node's own wire tag
|
|
(`NodeKind`'s `&str` conversion, the same strings the graph wire's node
|
|
labels carry), not a fixed set. Treat it as an opaque display
|
|
string; don't switch on specific values.
|
|
- It's **not** exclusively operator-initiated, and **not** limited
|
|
to rebuild-shaped work — `running_transients()` in hive-c0re is
|
|
a status-only test (any `Running` node whose payload names a
|
|
non-empty agent lights a pill), so work the operator never
|
|
triggered (a crash-recover rebuild, a boot-sweep rebuild, a
|
|
lease-exempt `prebuild`) lights the same mechanism.
|
|
|
|
Ops with no queue node behind them (destroy, migration) supply
|
|
their own label directly via `TransientSet`/`TransientCleared`
|
|
events carrying no backing node at all.
|
|
|
|
**Queued (not-yet-started) work shows nothing on the card.** Only
|
|
running work gets a per-agent pending badge, by design — the swarm
|
|
view stays independent of job-queue internals. The queue-summary
|
|
banner below is the only queued-work indicator on this tab, and it
|
|
reads the narrow `/api/jobq/rollup` endpoint rather than the full
|
|
graph.
|
|
|
|
**Active model badge** (`model · <name>`, blue) appears when the
|
|
container is running and the harness has persisted a model name (the
|
|
`active_model` field of `hyperhive-harness.json`, the consolidated
|
|
harness state file in the agent's state dir). Read by hive-c0re's
|
|
`ContainerView` (`read_active_model`); absent until the agent has
|
|
completed at least one turn, and hive-c0re suppresses stale values
|
|
for stopped containers.
|
|
|
|
**`ctx · Nk` chip** shows the agent's last-turn context size,
|
|
populated from `DashboardState.ctx_tokens` (absent until the
|
|
agent has completed at least one turn). The chip colour (green /
|
|
yellow / red) keys off `DashboardState.context_window_tokens`
|
|
(the real context window for the model the agent last ran on,
|
|
authoritative from the agent side); the badge goes yellow ≥ 50%
|
|
and red ≥ 75% of that window, matching the harness compaction
|
|
watermarks. When the window value is absent the badge falls back
|
|
to fixed 100k / 150k thresholds.
|
|
|
|
**Per-agent `⋮` overflow menu** — a `⋮` button appears on the right
|
|
edge of each container row. Selecting it opens a small dropdown with
|
|
per-agent actions and navigation links. Contents:
|
|
|
|
- `↺ R3ST4RT` (running agents only) / `■ ST0P` (running only) /
|
|
`▶ ST4RT` (stopped only) — single-agent run-state toggles.
|
|
Identical to the bulk actions on the selection bar but operate
|
|
on one agent without requiring a selection.
|
|
- `↻ R3BU1LD` — always available; queues a rebuild for this agent.
|
|
- `DESTR0Y` / `PURG3` — destructive, each prompts for confirmation
|
|
via the themed dialog (see **Themed dialogs** below).
|
|
|
|
**Build-queue summary banner** — when the job queue has any active
|
|
work, a compact amber banner sits above the container list: `◐ build
|
|
queue — N running · M queued — view queue →` (the link goes to the
|
|
R3BU1LD QU3U3 tab). The shared `JobqRollup` Preact component
|
|
(`@hive/shared/jobq-rollup.js` — the same one swarm-ui's `/jobs` page
|
|
mounts, pointed at swarm-controller's own rollup endpoint instead),
|
|
mounted once into `#jobq-rollup-section` by `agents.js::initJobqRollup`
|
|
and refreshed via its own handle rather than being re-rendered by
|
|
`renderContainers`. Reads `GET /api/jobq/rollup`
|
|
(`hive-jobq-wire::state_rollup`) — `Vec<{ state, nodes, roots }>`,
|
|
every lifecycle state present in a fixed order, zero counts included —
|
|
rather than the full `/api/jobq/graph` tree: `running` sums the
|
|
`Running` and `Finishing` entries' `roots` (`Finishing` = own work
|
|
done, subtree still going, still in flight), `queued` reads the
|
|
`Pending` entry's `roots`. `roots` specifically, not `nodes` — the
|
|
banner means *N whole operations*, not raw steps (one rebuild is ~7
|
|
nodes but 1 root); `nodes` exists on the same endpoint for a consumer
|
|
that wants step-level counts instead, unused here.
|
|
|
|
### Themed dialogs
|
|
|
|
All confirmations and transient error notices use an in-app themed
|
|
dialog system (`@hive/shared/modal.js`) rather than the browser's
|
|
native `confirm()` / `alert()` chrome, so they match the Catppuccin
|
|
palette and the OS can't style them away. Two primitives, both built
|
|
on the `openDialog` core:
|
|
|
|
- `themedConfirm({ message, danger, confirmLabel, checkboxes })`
|
|
— a modal confirm that resolves to `null` on cancel or an object
|
|
of checkbox states on confirm. Destructive actions pass
|
|
`danger: true` (the confirm button turns red and the cancel
|
|
button takes focus). Selecting the backdrop and `Esc` both cancel.
|
|
- `themedToast(message, { type, ... })` — a non-blocking toast (top-right,
|
|
`info` / `error` / `ok`) for transient validation + action
|
|
failures, so an error doesn't lock the operator behind a modal.
|
|
Single-action errors autodismiss; bulk / partial-failure
|
|
summaries are sticky (select to dismiss) so they aren't missed.
|
|
|
|
Every destructive run-state action (`ST0P`, `R3ST4RT`, `R3BU1LD`,
|
|
`DESTR0Y`, `PURG3`) routes through `themedConfirm`, on both
|
|
the per-agent `⋮` menu and the bulk selection bar.
|
|
|
|
**Graceful stop** — the `■ ST0P` confirm dialog (per-agent and
|
|
bulk) carries a `stop gracefully — let the agent finish its turn
|
|
and flush state before the container stops` checkbox. When ticked,
|
|
the action POSTs `/api/kill/<name>?graceful=true` (the bulk path
|
|
appends the flag per-agent); unticked is the instant hard stop
|
|
(`/api/kill/<name>` with no query). hive-c0re enqueues a
|
|
`Signal` → `Drain` job-queue node pair (`NodeKind::Signal` /
|
|
`NodeKind::Drain`): `Signal` sets the graceful-stop fence and kicks
|
|
the harness so it runs one stop-checkpoint turn (so the agent can
|
|
flush `/state`); `Drain` awaits the harness clearing that fence,
|
|
bounded by a 3-minute timeout (`GRACEFUL_STOP_TIMEOUT`) that
|
|
resolves either way and falls back to the downstream mechanical
|
|
stop. The quiescing progress surfaces through the same
|
|
rebuild-queue pending-badge mechanism the card already reads for a
|
|
rebuild, driven by the `Signal`/`Drain` pair rather than a nix
|
|
build. (The `hivectl stop --graceful` CLI flag enqueues the same
|
|
`Signal`/`Drain` pair, so the dashboard and CLI paths behave
|
|
identically.)
|
|
|
|
## Selection bar
|
|
|
|
Bulk actions (`R3ST4RT` / `ST0P` / `ST4RT` / `P4US3` / `R3SUM3` /
|
|
`R3BU1LD` / `DESTR0Y` / `PURG3`) live here rather than as per-card
|
|
buttons — see **Container row** above. Selecting an agent's icon
|
|
toggles its selection (an in-memory `Set<name>`); `Esc` or the bar's
|
|
`✕ clear` button drops everything. The selection persists across tab
|
|
switches in-memory — the bar just hides on non-4G3NTS tabs since other
|
|
tabs don't show the agent cards needed to cross-reference.
|
|
|
|
When the operator selects one or more agents (via the icon), a sticky
|
|
frosted-mauve bar slides up from the bottom of the viewport
|
|
(`#selection-bar`, `position: fixed; bottom: 0`). It shows:
|
|
|
|
<!-- vale write-good.Passive = NO -->
|
|
- **Count + names** — "N agents selected · name1, name2, …"
|
|
- **Bulk action buttons** — only enabled when ALL selected agents
|
|
support the action; greyed out with a tooltip naming the blockers
|
|
when the selection is mixed:
|
|
- `↺ R3ST4RT` — running agents only
|
|
- `■ ST0P` — running agents only
|
|
- `▶ ST4RT` — stopped agents only
|
|
- `⏸ P4US3` — unpaused agents only (orthogonal to running/stopped —
|
|
see **Container row**'s own pause/resume note)
|
|
- `▶ R3SUM3` — paused agents only
|
|
- `↻ R3BU1LD` — always available
|
|
- `DESTR0Y` / `PURG3` — always available
|
|
<!-- vale write-good.Passive = YES -->
|
|
|
|
That list is the whole set — the bar offers no other bulk action.
|
|
- **`✕ clear`** button + `Esc` key clear the entire selection.
|
|
|
|
Every render prunes stale selections (agents destroyed while
|
|
selected) before the bar appears.
|
|
|
|
## Dashboard endpoints
|
|
|
|
Also browsable interactively at `/api/docs` (a Swagger UI, linked from
|
|
the pages menu's **api docs** entry), with the raw spec at `/api/openapi.json`.
|
|
It's a growing supplement, not yet a full replacement for the list
|
|
below — some endpoints aren't in it yet.
|
|
|
|
- `POST /api/{rebuild,kill,restart,start,destroy}/{name}` — lifecycle.
|
|
`destroy` accepts `purge=on` to also wipe state dirs.
|
|
- `POST /api/purge-tombstone/{name}` — wipe a tombstone's state dirs.
|
|
- `POST /api/rebuild-queue/{id}/cancel` — drop a `Queued` entry.
|
|
Refuses `Running` / terminal-state entries (in-flight
|
|
rebuilds can't be safely interrupted). Always 200; body is
|
|
`{"cancelled": true}` on a successful flip or
|
|
`{"cancelled": false}` when the entry wasn't in `Queued` state.
|
|
- `POST /api/agent/{name}/mark-all-read` — ack all pending broker
|
|
messages for `{name}`. Backfills `delivered_at` for rows
|
|
not yet delivered and sets `acked_at = now`. Returns
|
|
`{ "marked": N }`. Agent name validated against
|
|
`[a-z0-9_-]`, 1-63 chars; 400 on bad input.
|
|
- `GET /api/build-logs/{agent}?limit=N` — most-recent build log
|
|
headers for one agent, newest first. Returns
|
|
`Vec<BuildLogHeader>` (JSON): `id`, `agent`, `kind`, `cmdline`,
|
|
`started_at`, `finished_at`, `status` (`"ok"` / `"fail"` /
|
|
`null` while in-progress). `limit` defaults to 10, server-side
|
|
cap at 50. Agent name validated (`[a-z0-9_-]`, 1-63 chars).
|
|
- `GET /api/build-logs/id/{id}` — full build log by id. Returns
|
|
`BuildLogFull` (JSON): all header fields plus `stdout` and
|
|
`stderr` as plain text (newline-terminated lines, utf-8). HTTP
|
|
404 when the row is missing (vacuum-reaped or stale id).
|
|
- `GET /api/build-logs/id/{id}/stream` — live SSE stream of
|
|
stdout+stderr for an in-progress build (newline-delimited JSON
|
|
frames). The
|
|
stream closes when the build finishes or errors.
|
|
- `GET /api/build-logs/id/{id}/raw` — raw concatenated
|
|
stdout+stderr as `text/plain`. Works on both finished and in-progress
|
|
build logs.
|
|
- `GET /api/reminders` — list pending reminders for the
|
|
dashboard's queued-reminders panel.
|
|
- `POST /api/cancel-reminder/{id}` — hard-delete a pending reminder.
|
|
- `POST /api/retry-reminder/{id}` — re-arm a reminder whose delivery
|
|
failed (clears the failure state so the scheduler retries).
|
|
|
|
- `GET /api/dashboard/stream` — unified live event channel:
|
|
broker `sent` / `delivered`, plus the mutation events listed
|
|
below. Each frame carries `seq`.
|
|
<!-- vale write-good.Passive = YES -->
|
|
|
|
## Dashboard event channel
|
|
|
|
Wire vocabulary on `/api/dashboard/stream` (kind tag is in the JSON
|
|
payload):
|
|
|
|
- `sent` / `delivered` — broker traffic, mirrored from the
|
|
intra-process channel by a forwarder task. Both carry `id: i64`
|
|
(the broker row id) and `in_reply_to: Option<i64>` for thread
|
|
rendering.
|
|
- `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. `transient_kind` is an **open**
|
|
display string (the running node's own tag), not a fixed
|
|
enum — render it, don't branch on it.
|
|
- `container_state_changed` (container: ContainerView) /
|
|
`container_removed` (name) — per-row container mutations,
|
|
emitted by `Coordinator::rescan_containers_and_emit` from
|
|
the job queue's own node execution (`job_queue::exec`, for example
|
|
after a rebuild's stop/swap/start steps or a destroy's teardown
|
|
step) — and from the 10s
|
|
`crash_watch` poll. Client upserts/removes by name and
|
|
reads the pending overlay from `transientsState` since the
|
|
payload doesn't carry it.
|
|
- `rebuild_queue_changed` (seq) — **payload-free trigger**, fired on
|
|
every queue mutation (enqueue, state transition, dedup collapse,
|
|
terminal-history trim). Unlike the snapshot events below it ships
|
|
no state at all: the client re-fetches `GET /api/jobq/graph`, which
|
|
is where it reads the queue from cold too.
|
|
- `reminders_changed` (seq, reminders: `Vec<PendingReminder>`) —
|
|
full snapshot of all pending reminders. Emitted after every
|
|
reminder mutation: agent `remind` calls (`agent_server`),
|
|
operator cancel / retry (`/api/system/reminders/*`), `cancel_loose_end`
|
|
with Reminder kind, and the scheduler tick after each delivery
|
|
batch (`reminder_scheduler`).
|
|
- `tombstones_changed` (seq, tombstones: `Vec<TombstoneView>`) —
|
|
full snapshot of destroyed-but-state-kept agents. Emitted on
|
|
destroy, purge, and revive. The K3PT ST4T3 tab subscribes.
|
|
|
|
|
|
<!-- vale write-good.Passive = NO -->
|
|
`/api/state` is **only fetched on cold-load and on the few
|
|
forms that mutate non-event-derived state** (PURG3, since
|
|
tombstones aren't event-shaped yet). Every other section — transients,
|
|
containers —
|
|
derives from `/api/dashboard/stream` after the initial snapshot,
|
|
maintaining its own client-side store and applying events on
|
|
top.
|
|
<!-- vale write-good.Passive = YES -->
|
|
|
|
Generalised form helper: `form[data-confirm="…"]` pops
|
|
`confirm()` before submit.
|
|
|