agent icon: 404 when unconfigured, client-side fallback
hive_sh4re::assets::branding_svg() resolved a server-side default icon at runtime from HIVE_ASSETS_DIR — the only consumer was serve_icon(), which fell back to it whenever the agent had no `hyperhive.icon` override. Removed both the fallback and the function: serve_icon() now 404s when /etc/hyperhive/icon.svg is absent, and the per-agent web UI (app.js) picks up the existing dashboard swarm.js pattern — swap the <img> src to the frontend-bundled /favicon.svg on load failure, guarded against looping if the fallback itself 404s. Updated the doc/comment claims that said the server always returns an image (docs/web-ui/agent.md, nix/agent-modules/default.nix, the hive-c0re/forge/users.rs comment referencing the old shared-asset set). forge-avatar-sync and the matrix avatar sync are unaffected — both are gated on hyperhive.icon != null and never depended on the removed fallback.
This commit is contained in:
parent
609035961f
commit
e525dcb6d4
6 changed files with 49 additions and 38 deletions
|
|
@ -383,10 +383,10 @@ shaped).
|
||||||
earliest recorded turn with an adaptive bucket width.
|
earliest recorded turn with an adaptive bucket width.
|
||||||
- `GET /icon` — agent's icon as `image/svg+xml`. Returns
|
- `GET /icon` — agent's icon as `image/svg+xml`. Returns
|
||||||
`/etc/hyperhive/icon.svg` (set via `hyperhive.icon` in `agent.nix`)
|
`/etc/hyperhive/icon.svg` (set via `hyperhive.icon` in `agent.nix`)
|
||||||
when present, otherwise the bundled default hyperhive logo. Always
|
when present, otherwise **404** — there is no server-side default.
|
||||||
returns an image — consumers (dashboard container row, per-agent
|
Consumers (dashboard container row, this page's own header icon)
|
||||||
favicon) can hit `/icon` unconditionally without probing for a
|
hit `/icon` optimistically and fall back client-side on load failure
|
||||||
custom config.
|
to the frontend-bundled `/favicon.svg` rather than probing first.
|
||||||
- `GET /events/history` — replay buffer for the terminal.
|
- `GET /events/history` — replay buffer for the terminal.
|
||||||
- `GET /screen` — VNC viewer page (minimal RFB-over-WebSocket
|
- `GET /screen` — VNC viewer page (minimal RFB-over-WebSocket
|
||||||
renderer — deliberately thin, just enough to display the
|
renderer — deliberately thin, just enough to display the
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,22 @@ window.marked = marked;
|
||||||
// native confirm()/alert() dialogs that broke out of the page theme.
|
// native confirm()/alert() dialogs that broke out of the page theme.
|
||||||
bindAsyncForms(() => refreshState());
|
bindAsyncForms(() => refreshState());
|
||||||
|
|
||||||
|
// ─── header icon fallback ───────────────────────────────────────────────
|
||||||
|
// `/icon` 404s when this agent has no `hyperhive.icon` override (see
|
||||||
|
// hive-agent::web_ui::screen::serve_icon — no bundled server-side
|
||||||
|
// default any more). Mirrors the dashboard's `swarm.js` `/favicon.svg`
|
||||||
|
// fallback: fire-and-forget load, swap on failure, guarded so a 404 on
|
||||||
|
// the fallback itself can't loop.
|
||||||
|
(function bindHeaderIconFallback() {
|
||||||
|
const iconImg = document.querySelector('.agent-icon');
|
||||||
|
if (!iconImg) return;
|
||||||
|
iconImg.addEventListener('error', () => {
|
||||||
|
if (iconImg.dataset.fallback) return;
|
||||||
|
iconImg.dataset.fallback = '1';
|
||||||
|
iconImg.src = '/favicon.svg';
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
// ─── side panel (singleton drawer for inbox + loose-ends flyouts) ──────
|
// ─── side panel (singleton drawer for inbox + loose-ends flyouts) ──────
|
||||||
// The shared `<hive-side-panel>` element (see @hive/shared/side-panel.js
|
// The shared `<hive-side-panel>` element (see @hive/shared/side-panel.js
|
||||||
// for the chrome/behavior it owns), created once, eagerly, when this
|
// for the chrome/behavior it owns), created once, eagerly, when this
|
||||||
|
|
|
||||||
|
|
@ -11,20 +11,18 @@ use super::AppState;
|
||||||
|
|
||||||
/// This agent's icon. Serves the operator-configured SVG from
|
/// This agent's icon. Serves the operator-configured SVG from
|
||||||
/// `/etc/hyperhive/icon.svg` (set via the `hyperhive.icon` agent.nix
|
/// `/etc/hyperhive/icon.svg` (set via the `hyperhive.icon` agent.nix
|
||||||
/// option) when present, otherwise the bundled default hyperhive logo.
|
/// option) when present, otherwise **404** — there is no bundled
|
||||||
/// Always returns an image, so consumers (dashboard, favicon) can hit
|
/// server-side default any more (that was `hive_sh4re::assets::
|
||||||
/// `/icon` unconditionally without probing whether one is configured.
|
/// branding_svg`, now removed). Consumers fall back client-side: the
|
||||||
pub(super) async fn serve_icon() -> impl IntoResponse {
|
/// dashboard's `swarm.js` and this agent's own `app.js` both swap an
|
||||||
// Per-agent icon overrides go through `/etc/hyperhive/icon.svg`
|
/// `/icon` load failure to the frontend-bundled `/favicon.svg` rather
|
||||||
// (set via the `hyperhive.icon` agent.nix option); the bundled
|
/// than probing first, so a 404 here is the expected "unconfigured"
|
||||||
// default is resolved at runtime from
|
/// signal, not an error case to work around.
|
||||||
// `$HIVE_ASSETS_DIR/branding/hyperhive.svg`. If neither file can
|
pub(super) async fn serve_icon() -> Response {
|
||||||
// be read we serve an empty body — keeps the response a valid SVG
|
match std::fs::read_to_string("/etc/hyperhive/icon.svg") {
|
||||||
// content-type without a panic on a misconfigured container.
|
Ok(body) => ([("content-type", "image/svg+xml")], body).into_response(),
|
||||||
let body = std::fs::read_to_string("/etc/hyperhive/icon.svg").unwrap_or_else(|_| {
|
Err(_) => (StatusCode::NOT_FOUND, "no icon configured").into_response(),
|
||||||
std::fs::read_to_string(hive_sh4re::assets::branding_svg()).unwrap_or_default()
|
}
|
||||||
});
|
|
||||||
([("content-type", "image/svg+xml")], body)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// WebSocket handler: upgrade then pump bytes between the WS client and
|
/// WebSocket handler: upgrade then pump bytes between the WS client and
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,9 @@ use crate::paths::FORGE_CORE_TOKEN as CORE_TOKEN_PATH;
|
||||||
// Avatar PNG paths are resolved by `core_avatar_png_path` /
|
// Avatar PNG paths are resolved by `core_avatar_png_path` /
|
||||||
// `config_org_avatar_png_path` below — only-used-here, so they live
|
// `config_org_avatar_png_path` below — only-used-here, so they live
|
||||||
// in this module rather than the shared `hive_sh4re::assets` helpers
|
// in this module rather than the shared `hive_sh4re::assets` helpers
|
||||||
// (which stay for paths every crate needs, e.g. `branding_svg`).
|
// (which now hold only `prompt_template`, the one asset path every
|
||||||
|
// crate needs — the agent icon, the last other one, moved off the
|
||||||
|
// shared-asset model entirely; see `hive-agent::web_ui::screen`).
|
||||||
|
|
||||||
/// `$HIVE_ASSETS_DIR/branding/hyperhive.png` — the core mark,
|
/// `$HIVE_ASSETS_DIR/branding/hyperhive.png` — the core mark,
|
||||||
/// rasterised. Not independently configurable (unlike the org avatar
|
/// rasterised. Not independently configurable (unlike the org avatar
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,13 @@
|
||||||
//! layout.
|
//! layout.
|
||||||
//!
|
//!
|
||||||
//! Each crate that wants a specific asset goes through one of the
|
//! Each crate that wants a specific asset goes through one of the
|
||||||
//! typed helpers (`branding_svg()`, `prompt_template()`, …) so the
|
//! typed helpers (currently just `prompt_template()` — the branding
|
||||||
//! lookup contract is centralised. Missing files panic at first
|
//! SVG used to live here too; the per-agent icon now 404s server-side
|
||||||
//! call with a clear "set `HIVE_ASSETS_DIR` + put the file at …"
|
//! when unconfigured and falls back to a frontend-bundled default
|
||||||
//! message.
|
//! client-side instead of a server-resolved runtime path, see
|
||||||
|
//! `hive-agent::web_ui::screen::serve_icon`) so the lookup contract is
|
||||||
|
//! centralised. Missing files panic at first call with a clear "set
|
||||||
|
//! `HIVE_ASSETS_DIR` + put the file at …" message.
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
|
@ -41,15 +44,6 @@ fn dir() -> PathBuf {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `$HIVE_ASSETS_DIR/branding/hyperhive.svg` — the project's primary
|
|
||||||
/// mark. Loaded by the per-agent web UI as its default icon
|
|
||||||
/// (`/agents/<n>/icon.svg` falls through here when the agent didn't
|
|
||||||
/// override `hyperhive.icon` in its `agent.nix`).
|
|
||||||
#[must_use]
|
|
||||||
pub fn branding_svg() -> PathBuf {
|
|
||||||
dir().join("branding/hyperhive.svg")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `$HIVE_ASSETS_DIR/prompts/system.md` — the claude system prompt
|
/// `$HIVE_ASSETS_DIR/prompts/system.md` — the claude system prompt
|
||||||
/// template. `hive-agent::prompt::render` filters the role markers
|
/// template. `hive-agent::prompt::render` filters the role markers
|
||||||
/// inside it per agent / manager flavor.
|
/// inside it per agent / manager flavor.
|
||||||
|
|
|
||||||
|
|
@ -55,9 +55,10 @@
|
||||||
the SVG into the agent's config repo next to `agent.nix` and
|
the SVG into the agent's config repo next to `agent.nix` and
|
||||||
reference it as a relative path (`./icon.svg`).
|
reference it as a relative path (`./icon.svg`).
|
||||||
|
|
||||||
When null (the default) the agent falls back to the shared
|
When null (the default), `GET /icon` on the per-agent web port
|
||||||
hyperhive logo. The harness serves the icon (configured or
|
404s and consumers (dashboard, this agent's own page header)
|
||||||
default) at `GET /icon` on the per-agent web port.
|
fall back client-side to the frontend-bundled `/favicon.svg`
|
||||||
|
rather than a server-resolved default.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -113,9 +114,9 @@
|
||||||
|
|
||||||
# Operator-set per-agent icon (hyperhive.icon). When configured, the
|
# Operator-set per-agent icon (hyperhive.icon). When configured, the
|
||||||
# SVG lands at /etc/hyperhive/icon.svg; the harness serves it at
|
# SVG lands at /etc/hyperhive/icon.svg; the harness serves it at
|
||||||
# GET /icon, falling back to the bundled hyperhive logo when absent.
|
# GET /icon, 404ing when absent (client-side fallback, no
|
||||||
# Consumed by forge-avatar-sync (./forge.nix) and the matrix avatar
|
# server-side default). Consumed by forge-avatar-sync (./forge.nix)
|
||||||
# sync (./matrix.nix) too.
|
# and the matrix avatar sync (./matrix.nix) too.
|
||||||
environment.etc."hyperhive/icon.svg" = lib.mkIf (config.hyperhive.icon != null) {
|
environment.etc."hyperhive/icon.svg" = lib.mkIf (config.hyperhive.icon != null) {
|
||||||
source = config.hyperhive.icon;
|
source = config.hyperhive.icon;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue