dashboard+agent: agent backend owns its nav links; dashboard proxies

The previous take put a shared NavLink wire type in hive-sh4re and
duplicated the link-building logic across crates. Per @mara on #326:
that doesn't fit the eventual frontend/backend split goal (#273).
The agent backend is the natural source of truth for what links its
own page exposes; hive-c0re just passes the list through to the
dashboard.

* hive-ag3nt/src/web_ui.rs: agent_links now also serves the
  config-repo link + reads agent-declared dashboardLinks extras
  from {state_dir}/hyperhive-dashboard-links.json. AgentLink gains a
  kind enum (Container | Forge | External) so the frontend can build
  the right href no matter which surface is rendering. The host
  header is no longer used — URLs are paths for Container/Forge,
  absolute for External.

* hive-c0re/src/dashboard.rs: new GET /api/agent/{name}/links route,
  a same-origin proxy that fetches the agent's /api/state and
  forwards just the links field. No shared wire type — hive-c0re
  treats the payload as opaque JSON (serde_json::Value). All failure
  modes degrade to an empty list so the dashboard still renders.

* hive-c0re/assets/app.js: container card head row gets an async-
  populated icon-only nav strip from the proxy. The hardcoded stats
  link, the standalone config-repo trigger, and the extras block are
  gone. The deployed:<sha> chip stays — the agent harness can't know
  its own deployed sha, so this chip is how the operator sees what's
  live alongside the agent's (root-only) config link.

* hive-ag3nt/assets/app.js: agent page meta-links rendered via
  el() / textContent (DOM build) so agent-declared icon / label / url
  strings never reach innerHTML. kind-based href resolution mirrors
  the dashboard side.

* docs/web-ui.md: dashboard + per-agent sections updated for the new
  architecture.

Closes #262.
This commit is contained in:
iris 2026-05-23 01:28:46 +02:00 committed by Mara
parent e70ae7776c
commit 222a5b4dc6
5 changed files with 243 additions and 71 deletions

View file

@ -184,9 +184,19 @@ falls it back to the dimmed hyperhive mark (`/favicon.svg`)
instead of an empty box — a real load-failure fallback, not a
guess from container state.
- Line 1: agent name (link → new tab), m1nd/ag3nt chip, status
badges — `⊘ rate limited` (red, while the harness is parked
after a 429), `needs login`, `needs update` — in-flight `◐
- Line 1: agent name (link → new tab), m1nd/ag3nt chip, an
**icon-only nav strip** populated async from the agent backend
(`📊 stats`, `🖥 screen` when GUI is enabled, `⬡ forge profile`,
`↳ agent-configs mirror`, plus any agent-declared
`dashboardLinks` extras — issue #262). The dashboard JS fetches
`GET /api/agent/{name}/links`, a same-origin passthrough proxy
that forwards the agent's own link list; the agent backend is
the single source of truth. The frontend resolves each
`AgentLink.kind` (`container``http://host:<container.port>`,
`forge``http://host:3000`, `external` → already absolute).
Status badges follow — `⊘ rate limited` (red, while the harness
is parked after a 429), `needs login`, `needs update` — in-flight
`◐
pending-state…` pill (replaces buttons during start / stop /
restart / rebuild / destroy), container name + port, and a
`ctx · Nk` chip showing the agent's last-turn context size
@ -209,12 +219,12 @@ guess from container state.
(`journalctl -M <container> -b --no-pager --output=short-iso`).
A unit dropdown (harness service / full machine journal) and
a refresh button live in the panel.
- `↳ config repo ↗` — link to the agent's applied config repo
on the bundled forge (`agent-configs/<name>`), opened in a
new tab. Shown only when `forge_present`. Replaces the old
one-file `agent.nix` viewer — the forge shows the full repo
with history. Mutating config still requires
`request_apply_commit` + approval.
- Plain navigation links (config repo, forge profile,
`dashboardLinks` extras) now live in the icon-only nav strip
on Line 1 — see above (issue #262). The agent's `config` link
goes to the repo root; the deployed sha shows separately on
Line 1 as the `deployed:<sha>` chip, since the agent harness
can't know its own deployed commit.
`↻ UPD4TE 4LL` button appears above the containers list when any
agent is stale. Banner pulses on each broker SSE event
@ -399,10 +409,21 @@ Layout, top to bottom:
- Banner (gradient shimmer while state=thinking).
- Title with `↑ DASHB04RD` back-link (new tab) + `↻ R3BU1LD`.
- Meta links row: `📊 stats →`, `🖥 screen →` (shown when
`gui_enabled`), and `⬡ forge ↗` (shown when `/api/state`
supplies a non-null `forge_url` — the agent's Forgejo profile,
assembled server-side from the request `Host` header, new tab).
- Meta links row: backend-supplied `StateSnapshot.links` (issue
#262) rendered as `<icon> label →`. Always includes `📊 stats`
(`kind = Container`); `🖥 screen` when the VNC compositor is
enabled; `⬡ forge` (profile) + `↳ config` (agent-configs
mirror, repo root since the agent doesn't know its own deployed
sha) when the agent has a forge account, both `kind = Forge`;
followed by any `hyperhive.dashboardLinks` extras
(`kind = External`) read from
`{state_dir}/hyperhive-dashboard-links.json`. The same list
feeds the dashboard card's icon strip via the host's
`GET /api/agent/{name}/links` passthrough proxy — agent backend
is the single source of truth for what links it exposes.
Frontend resolves each `kind` (`container` → same-origin path,
`forge``http://host:3000`, `external` → absolute) via DOM
building, so agent-declared strings never reach `innerHTML`.
- Status section: empty when online (alive-badge in the state
row carries the signal), populated with the login form /
OAuth URL when `status` is `needs_login_*`.