remove the forge links from the hive dashboard and the agent page
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.
This commit is contained in:
parent
1b6d7f3c99
commit
d516bb890c
16 changed files with 49 additions and 148 deletions
|
|
@ -81,14 +81,11 @@ at a given viewport width. Two columns:
|
||||||
- **Links badge** (`🔗`): opens a popover listing `↑ dashboard`
|
- **Links badge** (`🔗`): opens a popover listing `↑ dashboard`
|
||||||
(back-link to the host dashboard, `${dashboardBase}`)
|
(back-link to the host dashboard, `${dashboardBase}`)
|
||||||
followed by this agent's `agent_links()`-sourced navigation (stats,
|
followed by this agent's `agent_links()`-sourced navigation (stats,
|
||||||
screen when VNC is enabled, forge profile + config-repo mirror when
|
screen when VNC is enabled, any `services.hyperhive.agent.dashboardLinks`
|
||||||
the agent has a forge account, any `services.hyperhive.agent.dashboardLinks`
|
|
||||||
extras) — all as real `<a>` elements, not a `Dropdown`-style
|
extras) — all as real `<a>` elements, not a `Dropdown`-style
|
||||||
command list, so ctrl/middle-click and "copy link address" keep
|
command list, so ctrl/middle-click and "copy link address" keep
|
||||||
working. Each `AgentLink.kind` resolves differently: `container` →
|
working. Each `AgentLink.kind` resolves differently: `container` →
|
||||||
same-origin path; `forge` → `state.forge_public_url + url`, and
|
same-origin path; `external` → already absolute. Same source
|
||||||
hive-agent drops the link entirely when that's unset (never guessed from
|
|
||||||
`<host>:3000`); `external` → already absolute. Same source
|
|
||||||
(`GET /api/state`'s `links` field) also feeds
|
(`GET /api/state`'s `links` field) also feeds
|
||||||
`DashboardState.links` for the dashboard card's icon strip —
|
`DashboardState.links` for the dashboard card's icon strip —
|
||||||
`agent_links()` in hive-agent is the single source of truth for
|
`agent_links()` in hive-agent is the single source of truth for
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,8 @@ covers it.
|
||||||
|
|
||||||
**Check on an agent.** 4G3NTS shows every container as a row: name, whether
|
**Check on an agent.** 4G3NTS shows every container as a row: name, whether
|
||||||
it's running, what it's doing (a live status pill such as `rebuilding…` or
|
it's running, what it's doing (a live status pill such as `rebuilding…` or
|
||||||
`starting…` while something's in flight), and quick links (stats, screen,
|
`starting…` while something's in flight), and quick links (stats, screen).
|
||||||
forge profile). Select the name to open its terminal and watch it work.
|
Select the name to open its terminal and watch it work.
|
||||||
|
|
||||||
**Review a config change.** Agent config changes (new packages, env vars, MCP
|
**Review a config change.** Agent config changes (new packages, env vars, MCP
|
||||||
servers) are pull requests on the agent's `agent-configs` repo. Review and
|
servers) are pull requests on the agent's `agent-configs` repo. Review and
|
||||||
|
|
|
||||||
|
|
@ -197,9 +197,8 @@ fetch entirely.
|
||||||
**icon-only nav strip** plus live agent-owned state, all populated
|
**icon-only nav strip** plus live agent-owned state, all populated
|
||||||
async from a single `GET /api/dashboard-state` call to the agent's
|
async from a single `GET /api/dashboard-state` call to the agent's
|
||||||
own hive-agent. The response (`DashboardState`) carries: `links` (nav
|
own hive-agent. The response (`DashboardState`) carries: `links` (nav
|
||||||
strip entries — `📊 stats`, `🖥 screen` when GUI is enabled, `⬡ forge
|
strip entries — `📊 stats`, `🖥 screen` when GUI is enabled, plus any
|
||||||
profile`, `↳ agent-configs mirror`, plus any agent-declared
|
agent-declared `dashboardLinks` extras), `status_text` / `status_set_at` (agent
|
||||||
`dashboardLinks` extras), `status_text` / `status_set_at` (agent
|
|
||||||
self-reported status — the `(set N ago)` chip carries a `data-set-at`
|
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
|
stamp and ticks every 30s to stay fresh across the long-lived keyed row
|
||||||
cache), `rate_limited`, `ctx_tokens` / `context_window_tokens`
|
cache), `rate_limited`, `ctx_tokens` / `context_window_tokens`
|
||||||
|
|
@ -216,11 +215,9 @@ per-agent harness — TCP or unix-domain depending on the agent's
|
||||||
`docs/4-implementation/networking/gateway.md::Per-agent unix-socket upstream`).
|
`docs/4-implementation/networking/gateway.md::Per-agent unix-socket upstream`).
|
||||||
Gateway-off is the flag-absent fallback (a dashboard served outside a
|
Gateway-off is the flag-absent fallback (a dashboard served outside a
|
||||||
hyperhive deploy): base URL is
|
hyperhive deploy): base URL is
|
||||||
`http://<host>:<container.port>` (direct TCP). Forge
|
`http://<host>:<container.port>` (direct TCP). External links are
|
||||||
links resolve against `state.forge_public_url` (sourced from
|
already absolute. The row has no forge links: an agent's forge profile
|
||||||
`services.hyperhive.swarm.forge.publicUrl`) and disappear entirely when
|
and config repo are on the forge itself. The same base URL drives the primary agent-name
|
||||||
that's unset — never guessed from `<host>:3000`. External links are
|
|
||||||
already absolute. The same base URL drives the primary agent-name
|
|
||||||
link + favicon fetch, so the whole row routes through the gateway
|
link + favicon fetch, so the whole row routes through the gateway
|
||||||
as a unit.
|
as a unit.
|
||||||
<!-- vale write-good.Passive = YES -->
|
<!-- vale write-good.Passive = YES -->
|
||||||
|
|
|
||||||
|
|
@ -431,9 +431,7 @@ when `StateSnapshot.gateway_enabled` is true, and direct
|
||||||
every hive, so the direct shape is a fallback for the variable being unset.
|
every hive, so the direct shape is a fallback for the variable being unset.
|
||||||
Three render sites follow it: the agent-name link, the favicon fetch
|
Three render sites follow it: the agent-name link, the favicon fetch
|
||||||
(`<url>/icon`), and the nav-strip `container`-kind links from
|
(`<url>/icon`), and the nav-strip `container`-kind links from
|
||||||
`DashboardState.links` (`GET /api/dashboard-state`). Forge links come from
|
`DashboardState.links` (`GET /api/dashboard-state`). See `docs/2-handbook/hives/dashboard.md::Container row` for the frontend side.
|
||||||
`swarm.forge.publicUrl`; with it unset the dashboard hides them rather than
|
|
||||||
guess. See `docs/2-handbook/hives/dashboard.md::Container row` for the frontend side.
|
|
||||||
|
|
||||||
### Dialing another vhost by name (`verifiedProxyTo`)
|
### Dialing another vhost by name (`verifiedProxyTo`)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,7 @@ export function Root() {
|
||||||
count={todos.length}
|
count={todos.length}
|
||||||
onClick={() => setOpenPanel("todos")}
|
onClick={() => setOpenPanel("todos")}
|
||||||
/>
|
/>
|
||||||
{/* Needs a loaded snapshot for its data (links/forge_public_url,
|
{/* Needs a loaded snapshot for its data (links,
|
||||||
* dashboard_port) — omitted until the first /api/state resolves,
|
* dashboard_port) — omitted until the first /api/state resolves,
|
||||||
* same as the old page's populateOverflowMenu (now deleted —
|
* same as the old page's populateOverflowMenu (now deleted —
|
||||||
* dashboard-link moved here, rebuild removed outright, see
|
* dashboard-link moved here, rebuild removed outright, see
|
||||||
|
|
@ -149,7 +149,6 @@ export function Root() {
|
||||||
{state ? (
|
{state ? (
|
||||||
<MetaNav
|
<MetaNav
|
||||||
links={state.links}
|
links={state.links}
|
||||||
forgePublicUrl={state.forge_public_url}
|
|
||||||
dashboardBase={resolveDashboardBase(state.dashboard_port)}
|
dashboardBase={resolveDashboardBase(state.dashboard_port)}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
|
||||||
|
|
@ -196,7 +196,7 @@ h3 {
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Meta-nav links (stats / screen / forge / dashboard / extras) —
|
/* Meta-nav links (stats / screen / dashboard / extras) —
|
||||||
no underline; hover lights with cyan glow + subtle background
|
no underline; hover lights with cyan glow + subtle background
|
||||||
tint. Reads as a row of soft tabs rather than default-styled
|
tint. Reads as a row of soft tabs rather than default-styled
|
||||||
inline anchors. */
|
inline anchors. */
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
// (`@hive/shared/icons.js`'s `LinkIcon` — an emoji glyph can't match a
|
// (`@hive/shared/icons.js`'s `LinkIcon` — an emoji glyph can't match a
|
||||||
// neighbouring icon's size/weight on any platform, see that file's
|
// neighbouring icon's size/weight on any platform, see that file's
|
||||||
// comment) in the pills cluster that opens a popover listing
|
// comment) in the pills cluster that opens a popover listing
|
||||||
// stats/screen/forge/config + any `services.hyperhive.agent.dashboardLinks` extras,
|
// stats/screen + any `services.hyperhive.agent.dashboardLinks` extras,
|
||||||
// sourced from the backend's `agent_links()` (the single source of
|
// sourced from the backend's `agent_links()` (the single source of
|
||||||
// truth — same list also feeds the dashboard card's icon strip) — plus
|
// truth — same list also feeds the dashboard card's icon strip) — plus
|
||||||
// a `🧭 dashboard` back-link.
|
// a `🧭 dashboard` back-link.
|
||||||
|
|
@ -28,17 +28,12 @@ import "./MetaNav.css";
|
||||||
|
|
||||||
export interface MetaNavProps {
|
export interface MetaNavProps {
|
||||||
links: AgentLink[];
|
links: AgentLink[];
|
||||||
forgePublicUrl: string | null;
|
|
||||||
/** Absolute base URL of the host dashboard (`resolveDashboardBase`)
|
/** Absolute base URL of the host dashboard (`resolveDashboardBase`)
|
||||||
* — the `🧭 dashboard` item links to `${dashboardBase}`. */
|
* — the `🧭 dashboard` item links to `${dashboardBase}`. */
|
||||||
dashboardBase: string;
|
dashboardBase: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MetaNav({
|
export function MetaNav({ links, dashboardBase }: MetaNavProps) {
|
||||||
links,
|
|
||||||
forgePublicUrl,
|
|
||||||
dashboardBase,
|
|
||||||
}: MetaNavProps) {
|
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const rootRef = useRef<HTMLDivElement>(null);
|
const rootRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
|
@ -64,11 +59,7 @@ export function MetaNav({
|
||||||
};
|
};
|
||||||
}, [open]);
|
}, [open]);
|
||||||
|
|
||||||
// Same kind -> URL resolution as app.js's old refreshState meta-links
|
// `external` is already absolute; `container` is a same-origin path.
|
||||||
// loop: `forge` needs `forgePublicUrl` set or the link is dropped
|
|
||||||
// entirely (never guessed from `<host>:3000`); `external` is already
|
|
||||||
// absolute; `container` is a same-origin path.
|
|
||||||
const visible = links.filter((lnk) => lnk.kind !== "forge" || forgePublicUrl);
|
|
||||||
// No early-return-on-empty: the dashboard link below is always
|
// No early-return-on-empty: the dashboard link below is always
|
||||||
// present, so the trigger always has at least one item.
|
// present, so the trigger always has at least one item.
|
||||||
|
|
||||||
|
|
@ -93,24 +84,20 @@ export function MetaNav({
|
||||||
>
|
>
|
||||||
🧭 dashboard
|
🧭 dashboard
|
||||||
</a>
|
</a>
|
||||||
{visible.map((lnk) => {
|
{links.map((lnk) => (
|
||||||
const href =
|
<a
|
||||||
lnk.kind === "forge" ? `${forgePublicUrl}${lnk.url}` : lnk.url;
|
key={lnk.url}
|
||||||
return (
|
class="meta-nav-item"
|
||||||
<a
|
href={lnk.url}
|
||||||
key={lnk.url}
|
target="_blank"
|
||||||
class="meta-nav-item"
|
rel="noopener"
|
||||||
href={href}
|
role="menuitem"
|
||||||
target="_blank"
|
onClick={() => setOpen(false)}
|
||||||
rel="noopener"
|
>
|
||||||
role="menuitem"
|
{lnk.icon ? <span aria-hidden="true">{lnk.icon}</span> : null}
|
||||||
onClick={() => setOpen(false)}
|
{lnk.label}
|
||||||
>
|
</a>
|
||||||
{lnk.icon ? <span aria-hidden="true">{lnk.icon}</span> : null}
|
))}
|
||||||
{lnk.label}
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -28,11 +28,10 @@ export interface AgentState {
|
||||||
paused: boolean;
|
paused: boolean;
|
||||||
inbox: InboxRow[];
|
inbox: InboxRow[];
|
||||||
links: AgentLink[];
|
links: AgentLink[];
|
||||||
forge_public_url: string | null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mirrors `hive_agent::web_ui::state::AgentLink` — one meta-nav entry
|
// Mirrors `hive_agent::web_ui::state::AgentLink` — one meta-nav entry
|
||||||
// in the agent page header (stats/screen/forge/config + any
|
// in the agent page header (stats/screen + any
|
||||||
// `services.hyperhive.agent.dashboardLinks` extras). `agent_links()` on the backend
|
// `services.hyperhive.agent.dashboardLinks` extras). `agent_links()` on the backend
|
||||||
// is the single source of truth for what links an agent exposes; see
|
// is the single source of truth for what links an agent exposes; see
|
||||||
// docs/2-handbook/agents/web-page.md::Header for the kind → URL resolution table.
|
// docs/2-handbook/agents/web-page.md::Header for the kind → URL resolution table.
|
||||||
|
|
@ -40,7 +39,7 @@ export interface AgentLink {
|
||||||
url: string;
|
url: string;
|
||||||
icon: string;
|
icon: string;
|
||||||
label: string;
|
label: string;
|
||||||
kind: "container" | "forge" | "external";
|
kind: "container" | "external";
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TokenUsage {
|
export interface TokenUsage {
|
||||||
|
|
|
||||||
|
|
@ -270,8 +270,7 @@ function containerRowFingerprint(
|
||||||
// changed (keyed cache), skipping the build + async dashboard-state
|
// changed (keyed cache), skipping the build + async dashboard-state
|
||||||
// fetch for stable rows.
|
// fetch for stable rows.
|
||||||
function buildContainerLi(c, opts) {
|
function buildContainerLi(c, opts) {
|
||||||
const { pending, opRunning, selected, url, containerBase, forgeBase, s } =
|
const { pending, opRunning, selected, url, containerBase, s } = opts;
|
||||||
opts;
|
|
||||||
// A single `pending-running` class covers any active transient
|
// A single `pending-running` class covers any active transient
|
||||||
// badge state (see the pending-badge derivation comment in
|
// badge state (see the pending-badge derivation comment in
|
||||||
// renderContainers).
|
// renderContainers).
|
||||||
|
|
@ -342,8 +341,7 @@ function buildContainerLi(c, opts) {
|
||||||
// `GET /api/dashboard-state` (via gateway when enabled, direct
|
// `GET /api/dashboard-state` (via gateway when enabled, direct
|
||||||
// TCP otherwise). The agent is the single source of truth for its
|
// TCP otherwise). The agent is the single source of truth for its
|
||||||
// link list: stats / screen (GUI agents only — c0re's disk-based
|
// link list: stats / screen (GUI agents only — c0re's disk-based
|
||||||
// fallback cannot detect this) / forge profile / agent-configs /
|
// fallback cannot detect this) / extras. DOM-built — link strings come from the agent's process
|
||||||
// extras. DOM-built — link strings come from the agent's process
|
|
||||||
// and must never reach the HTML parser.
|
// and must never reach the HTML parser.
|
||||||
const navStrip = el("span", { class: "nav-strip" });
|
const navStrip = el("span", { class: "nav-strip" });
|
||||||
head.append(navStrip);
|
head.append(navStrip);
|
||||||
|
|
@ -363,15 +361,13 @@ function buildContainerLi(c, opts) {
|
||||||
// -- nav strip -----------------------------------------------
|
// -- nav strip -----------------------------------------------
|
||||||
if (Array.isArray(ds.links)) {
|
if (Array.isArray(ds.links)) {
|
||||||
for (const lnk of ds.links) {
|
for (const lnk of ds.links) {
|
||||||
// No public forge URL to link to — hide rather than build
|
// An agent not yet rebuilt still sends forge links, which no
|
||||||
// a broken/guessed href (see forgeBase above).
|
// page links to any more.
|
||||||
if (lnk.kind === "forge" && !forgeBase) continue;
|
if (lnk.kind === "forge") continue;
|
||||||
const href =
|
const href =
|
||||||
lnk.kind === "forge"
|
lnk.kind === "external"
|
||||||
? forgeBase + (lnk.url || "")
|
? lnk.url || ""
|
||||||
: lnk.kind === "external"
|
: /* container */ containerBase + "/" + (lnk.url || "");
|
||||||
? lnk.url || ""
|
|
||||||
: /* container */ containerBase + "/" + (lnk.url || "");
|
|
||||||
const a = el("a", {
|
const a = el("a", {
|
||||||
class: "nav-link",
|
class: "nav-link",
|
||||||
href,
|
href,
|
||||||
|
|
@ -634,12 +630,6 @@ export function renderContainers(s) {
|
||||||
// loopback port while the socket marker is absent). See
|
// loopback port while the socket marker is absent). See
|
||||||
// `docs/2-handbook/hives/dashboard.md::Container row` + `docs/4-implementation/networking/gateway.md::Vhost map`.
|
// `docs/2-handbook/hives/dashboard.md::Container row` + `docs/4-implementation/networking/gateway.md::Vhost map`.
|
||||||
const gatewayLinks = !!(s && s.gateway_enabled);
|
const gatewayLinks = !!(s && s.gateway_enabled);
|
||||||
// Forge public URL: state.forge_public_url (set by the NixOS module
|
|
||||||
// from services.hyperhive.swarm.forge.publicUrl) or null — never guessed
|
|
||||||
// from "<hostname>:3000". `null` propagates down to
|
|
||||||
// buildContainerLi/buildAgentMenu, which already hide forge links
|
|
||||||
// rather than emit a possibly-wrong one.
|
|
||||||
const forgeBase = (s && s.forge_public_url) || null;
|
|
||||||
const ul = existingUl ?? el("ul", { class: "containers" });
|
const ul = existingUl ?? el("ul", { class: "containers" });
|
||||||
const rows = sortedContainerRows(containers);
|
const rows = sortedContainerRows(containers);
|
||||||
|
|
||||||
|
|
@ -691,7 +681,6 @@ export function renderContainers(s) {
|
||||||
selected,
|
selected,
|
||||||
url,
|
url,
|
||||||
containerBase,
|
containerBase,
|
||||||
forgeBase,
|
|
||||||
s,
|
s,
|
||||||
});
|
});
|
||||||
containerRowCache.set(c.name, { el: li, fingerprint: fp });
|
containerRowCache.set(c.name, { el: li, fingerprint: fp });
|
||||||
|
|
|
||||||
|
|
@ -47,10 +47,7 @@ pub(super) async fn api_state(State(state): State<AppState>) -> axum::Json<State
|
||||||
context_window_tokens,
|
context_window_tokens,
|
||||||
ctx_usage,
|
ctx_usage,
|
||||||
cost_usage,
|
cost_usage,
|
||||||
links: agent_links(&state.label, state.gui_vnc_port.is_some()),
|
links: agent_links(state.gui_vnc_port.is_some()),
|
||||||
forge_public_url: std::env::var("HIVE_FORGE_PUBLIC_URL")
|
|
||||||
.ok()
|
|
||||||
.filter(|s| !s.is_empty()),
|
|
||||||
hive_name: crate::identity::hive_name(),
|
hive_name: crate::identity::hive_name(),
|
||||||
swarm_name: crate::identity::swarm_name(),
|
swarm_name: crate::identity::swarm_name(),
|
||||||
effort: shown.effort,
|
effort: shown.effort,
|
||||||
|
|
@ -87,7 +84,7 @@ pub(super) async fn api_dashboard_state(
|
||||||
ctx_tokens,
|
ctx_tokens,
|
||||||
context_window_tokens,
|
context_window_tokens,
|
||||||
rate_limited,
|
rate_limited,
|
||||||
links: agent_links(&state.label, state.gui_vnc_port.is_some()),
|
links: agent_links(state.gui_vnc_port.is_some()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -148,13 +145,6 @@ pub(super) struct StateSnapshot {
|
||||||
/// — single source of truth. See [`docs/2-handbook/hives/dashboard.md::Container row`]
|
/// — single source of truth. See [`docs/2-handbook/hives/dashboard.md::Container row`]
|
||||||
/// for the frontend resolver + which links appear in which conditions.
|
/// for the frontend resolver + which links appear in which conditions.
|
||||||
links: Vec<AgentLink>,
|
links: Vec<AgentLink>,
|
||||||
/// Public URL of the forge (e.g.
|
|
||||||
/// `"https://forge.pr1ma.darkest.space"`). Sourced from
|
|
||||||
/// `HIVE_FORGE_PUBLIC_URL` (set from
|
|
||||||
/// `services.hyperhive.swarm.forge.publicUrl`); `None` when unset. The frontend uses this to build
|
|
||||||
/// forge nav-strip links, and **hides** the forge link entirely
|
|
||||||
/// when absent rather than guessing `<hostname>:3000`.
|
|
||||||
forge_public_url: Option<String>,
|
|
||||||
/// Human name of this hive instance (e.g. `"pr1ma"`). Sourced
|
/// Human name of this hive instance (e.g. `"pr1ma"`). Sourced
|
||||||
/// from `HYPERHIVE_HIVE_NAME`; `None` when unset. The frontend
|
/// from `HYPERHIVE_HIVE_NAME`; `None` when unset. The frontend
|
||||||
/// uses this for the page `<title>` and header label so browser
|
/// uses this for the page `<title>` and header label so browser
|
||||||
|
|
@ -190,7 +180,7 @@ pub(super) struct StateSnapshot {
|
||||||
/// of truth for what links an agent exposes.
|
/// of truth for what links an agent exposes.
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct AgentLink {
|
struct AgentLink {
|
||||||
/// `kind = Container | Forge` → path; `kind = External` → full URL.
|
/// `kind = Container` → path; `kind = External` → full URL.
|
||||||
/// The frontend prepends the right base before rendering.
|
/// The frontend prepends the right base before rendering.
|
||||||
url: String,
|
url: String,
|
||||||
icon: String,
|
icon: String,
|
||||||
|
|
@ -209,10 +199,6 @@ enum AgentLinkKind {
|
||||||
/// `/screen`). Agent page: same-origin path. Dashboard:
|
/// `/screen`). Agent page: same-origin path. Dashboard:
|
||||||
/// `http://<host>:<container.port><url>`.
|
/// `http://<host>:<container.port><url>`.
|
||||||
Container,
|
Container,
|
||||||
/// `url` is a path on the local Forgejo (`/<label>`,
|
|
||||||
/// `/agent-configs/<label>`). Both surfaces:
|
|
||||||
/// `http://<host>:3000<url>`.
|
|
||||||
Forge,
|
|
||||||
/// `url` is already a fully-qualified absolute URL — use as-is.
|
/// `url` is already a fully-qualified absolute URL — use as-is.
|
||||||
/// Agent-declared `services.hyperhive.agent.dashboardLinks` extras arrive here.
|
/// Agent-declared `services.hyperhive.agent.dashboardLinks` extras arrive here.
|
||||||
External,
|
External,
|
||||||
|
|
@ -272,7 +258,7 @@ fn read_own_status() -> (Option<String>, Option<i64>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build the navigation link list for the agent page header. URLs
|
/// Build the navigation link list for the agent page header. URLs
|
||||||
/// are paths (relative) for `Container`/`Forge` targets and absolute
|
/// are paths (relative) for `Container` targets and absolute
|
||||||
/// for `External`; the frontend resolves each against its `kind`
|
/// for `External`; the frontend resolves each against its `kind`
|
||||||
/// against the right base so the backend never has to guess the
|
/// against the right base so the backend never has to guess the
|
||||||
/// operator's browser host. See
|
/// operator's browser host. See
|
||||||
|
|
@ -282,16 +268,9 @@ fn read_own_status() -> (Option<String>, Option<i64>) {
|
||||||
/// Icons are real emoji, not unicode arrow/geometric glyphs — mara:
|
/// Icons are real emoji, not unicode arrow/geometric glyphs — mara:
|
||||||
/// operator-declared `dashboardLinks` extras are already free-choice
|
/// operator-declared `dashboardLinks` extras are already free-choice
|
||||||
/// emoji, so the built-ins should read as the same family rather than
|
/// emoji, so the built-ins should read as the same family rather than
|
||||||
/// mixing in plain text glyphs (which also don't reliably render as a
|
/// mixing in plain text glyphs (which also don't reliably render across
|
||||||
/// hexagon/arrow across fonts to begin with). `config` went through two
|
/// fonts to begin with).
|
||||||
/// rounds on this: gear (`⚙`, with and without a variation selector)
|
fn agent_links(gui_enabled: bool) -> Vec<AgentLink> {
|
||||||
/// still rendered broken in mara's browser either way — same
|
|
||||||
/// font-unreliable codepoint `icons.tsx` already documents avoiding.
|
|
||||||
/// Landed on the wrench (`🔧`) instead: a supplementary-plane pictograph
|
|
||||||
/// with `Emoji_Presentation=Yes`, same category as every other icon
|
|
||||||
/// here (`📊`/`🖥`/`🔨`/`🧭`), so it doesn't need a variation selector to
|
|
||||||
/// render as color emoji in the first place.
|
|
||||||
fn agent_links(label: &str, gui_enabled: bool) -> Vec<AgentLink> {
|
|
||||||
let mut links = Vec::new();
|
let mut links = Vec::new();
|
||||||
|
|
||||||
links.push(AgentLink {
|
links.push(AgentLink {
|
||||||
|
|
@ -310,26 +289,6 @@ fn agent_links(label: &str, gui_enabled: bool) -> Vec<AgentLink> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Either copy of the forge token: the one fetched from the swarm secret
|
|
||||||
// store (`HIVE_FORGE_TOKEN_FILE`), or the state-dir file the hive used
|
|
||||||
// to write.
|
|
||||||
let fetched_token = std::env::var_os("HIVE_FORGE_TOKEN_FILE")
|
|
||||||
.is_some_and(|p| std::path::Path::new(&p).is_file());
|
|
||||||
if fetched_token || crate::paths::state_dir().join("forge-token").is_file() {
|
|
||||||
links.push(AgentLink {
|
|
||||||
url: format!("/{label}"),
|
|
||||||
icon: "🔨".to_owned(),
|
|
||||||
label: "forge".to_owned(),
|
|
||||||
kind: AgentLinkKind::Forge,
|
|
||||||
});
|
|
||||||
links.push(AgentLink {
|
|
||||||
url: format!("/agent-configs/{label}"),
|
|
||||||
icon: "🔧".to_owned(),
|
|
||||||
label: "config".to_owned(),
|
|
||||||
kind: AgentLinkKind::Forge,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Agent-declared extras (`services.hyperhive.agent.dashboardLinks` → the
|
// Agent-declared extras (`services.hyperhive.agent.dashboardLinks` → the
|
||||||
// `hive-dashboard-links` NixOS oneshot writes them to
|
// `hive-dashboard-links` NixOS oneshot writes them to
|
||||||
// `{state_dir}/hyperhive-dashboard-links.json`). Shape on disk
|
// `{state_dir}/hyperhive-dashboard-links.json`). Shape on disk
|
||||||
|
|
|
||||||
|
|
@ -66,15 +66,6 @@ pub(super) struct StateSnapshot {
|
||||||
/// `http://<hostname>:<port>/` TCP links) is retained as a defensive
|
/// `http://<hostname>:<port>/` TCP links) is retained as a defensive
|
||||||
/// fallback for the env being unset. See `docs/4-implementation/networking/gateway.md::Vhost map`.
|
/// fallback for the env being unset. See `docs/4-implementation/networking/gateway.md::Vhost map`.
|
||||||
gateway_enabled: bool,
|
gateway_enabled: bool,
|
||||||
/// Public URL of the forge (e.g.
|
|
||||||
/// `"https://forge.pr1ma.darkest.space"`). Sourced from the
|
|
||||||
/// `HIVE_FORGE_PUBLIC_URL` env var, which the c0re NixOS module
|
|
||||||
/// sets from `services.hyperhive.swarm.forge.publicUrl` (defaults to
|
|
||||||
/// the gateway vhost URL). `None` when absent — the frontend **hides**
|
|
||||||
/// forge links rather than guessing `http://<hostname>:3000`,
|
|
||||||
/// which is only right by accident on deployments that aren't
|
|
||||||
/// plain localhost.
|
|
||||||
forge_public_url: Option<String>,
|
|
||||||
/// Browser URL of the swarm UI (the swarm apex domain). Sourced from
|
/// Browser URL of the swarm UI (the swarm apex domain). Sourced from
|
||||||
/// `HIVE_SWARM_PUBLIC_URL`, which the c0re NixOS module sets from
|
/// `HIVE_SWARM_PUBLIC_URL`, which the c0re NixOS module sets from
|
||||||
/// `services.hyperhive.swarm.domain`. `None` when the domain is unset —
|
/// `services.hyperhive.swarm.domain`. `None` when the domain is unset —
|
||||||
|
|
@ -197,9 +188,6 @@ pub(super) async fn api_state(
|
||||||
let s = v.to_string_lossy().to_ascii_lowercase();
|
let s = v.to_string_lossy().to_ascii_lowercase();
|
||||||
matches!(s.as_str(), "1" | "true" | "yes")
|
matches!(s.as_str(), "1" | "true" | "yes")
|
||||||
}),
|
}),
|
||||||
forge_public_url: std::env::var("HIVE_FORGE_PUBLIC_URL")
|
|
||||||
.ok()
|
|
||||||
.filter(|s| !s.is_empty()),
|
|
||||||
swarm_public_url: std::env::var("HIVE_SWARM_PUBLIC_URL")
|
swarm_public_url: std::env::var("HIVE_SWARM_PUBLIC_URL")
|
||||||
.ok()
|
.ok()
|
||||||
.filter(|s| !s.is_empty()),
|
.filter(|s| !s.is_empty()),
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,6 @@ pub const FORWARDED_VARS: &[&str] = &[
|
||||||
// The metrics store's origin, for `swarm-metrics`, for the same reason.
|
// The metrics store's origin, for `swarm-metrics`, for the same reason.
|
||||||
"HIVE_AGENT_METRICS_URL",
|
"HIVE_AGENT_METRICS_URL",
|
||||||
"HIVE_FORGE_URL",
|
"HIVE_FORGE_URL",
|
||||||
"HIVE_FORGE_PUBLIC_URL",
|
|
||||||
"HIVE_MATRIX_URL",
|
"HIVE_MATRIX_URL",
|
||||||
"HYPERHIVE_SWARM_NAME",
|
"HYPERHIVE_SWARM_NAME",
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@
|
||||||
HIVE_AGENT_LOGS_QUERY_URL = "@HIVE_AGENT_LOGS_QUERY_URL@";
|
HIVE_AGENT_LOGS_QUERY_URL = "@HIVE_AGENT_LOGS_QUERY_URL@";
|
||||||
HIVE_AGENT_METRICS_URL = "@HIVE_AGENT_METRICS_URL@";
|
HIVE_AGENT_METRICS_URL = "@HIVE_AGENT_METRICS_URL@";
|
||||||
HIVE_FORGE_URL = "@HIVE_FORGE_URL@";
|
HIVE_FORGE_URL = "@HIVE_FORGE_URL@";
|
||||||
HIVE_FORGE_PUBLIC_URL = "@HIVE_FORGE_PUBLIC_URL@";
|
|
||||||
HIVE_MATRIX_URL = "@HIVE_MATRIX_URL@";
|
HIVE_MATRIX_URL = "@HIVE_MATRIX_URL@";
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
|
|
|
||||||
|
|
@ -78,17 +78,11 @@ in
|
||||||
defaultText = lib.literalExpression ''"https://''${domain}"'';
|
defaultText = lib.literalExpression ''"https://''${domain}"'';
|
||||||
example = "https://forge.example.com";
|
example = "https://forge.example.com";
|
||||||
description = ''
|
description = ''
|
||||||
Browser-facing forge URL the dashboard uses to build clickable
|
Browser-facing forge URL, sourced into hive-c0re as
|
||||||
forge links (the per-agent-row forge links) —
|
`HIVE_FORGE_PUBLIC_URL` and published with the hive's URLs.
|
||||||
sourced into every agent container + hive-c0re as
|
|
||||||
`HIVE_FORGE_PUBLIC_URL`.
|
|
||||||
|
|
||||||
Defaults to `https://''${cfg.domain}`, the gateway vhost. When
|
Defaults to `https://''${cfg.domain}`, the gateway vhost. When
|
||||||
`null`, the dashboard **hides** forge links rather than
|
`null`, the hive publishes no forge URL rather than guessing one.
|
||||||
guessing one — see `docs/2-handbook/hives/dashboard.md::Container row` for
|
|
||||||
the rationale (a link built from the operator's own browser
|
|
||||||
hostname + a container port is only an accident away from
|
|
||||||
wrong on any deployment that isn't plain localhost).
|
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -267,9 +267,6 @@ let
|
||||||
vars = {
|
vars = {
|
||||||
HIVE_FORGE_URL = "http://${forgeCfg.domain}";
|
HIVE_FORGE_URL = "http://${forgeCfg.domain}";
|
||||||
}
|
}
|
||||||
// lib.optionalAttrs (forgeCfg.publicUrl != null) {
|
|
||||||
HIVE_FORGE_PUBLIC_URL = forgeCfg.publicUrl;
|
|
||||||
}
|
|
||||||
// lib.optionalAttrs (matrixCfg.gatewayHost != null) {
|
// lib.optionalAttrs (matrixCfg.gatewayHost != null) {
|
||||||
HIVE_MATRIX_URL = "http://${matrixCfg.gatewayHost}";
|
HIVE_MATRIX_URL = "http://${matrixCfg.gatewayHost}";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,6 @@ let
|
||||||
"HIVE_AGENT_BAO_SERVICES_PKI_MOUNT"
|
"HIVE_AGENT_BAO_SERVICES_PKI_MOUNT"
|
||||||
"HIVE_AGENT_LOGS_QUERY_URL"
|
"HIVE_AGENT_LOGS_QUERY_URL"
|
||||||
"HIVE_AGENT_METRICS_URL"
|
"HIVE_AGENT_METRICS_URL"
|
||||||
"HIVE_FORGE_PUBLIC_URL"
|
|
||||||
"HIVE_FORGE_URL"
|
"HIVE_FORGE_URL"
|
||||||
"HIVE_MATRIX_URL"
|
"HIVE_MATRIX_URL"
|
||||||
];
|
];
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue