add per-agent url to host.sock agent status rows

This commit is contained in:
damocles 2026-09-07 20:10:16 +02:00 committed by mara
commit 4fae3f13cb
2 changed files with 53 additions and 1 deletions

View file

@ -109,9 +109,11 @@ impl From<ContainerView> for hive_sh4re::container::AgentStatusRow {
/// syntactically, just always empty, until iris's frontend follow-up
/// decides whether to drop the column entirely. `port`, `cpu_quota`,
/// `memory_max` have no equivalent on the row at all and are simply
/// not carried over.
/// not carried over. `url` has no source on `ContainerView` at all —
/// see [`agent_url`].
fn from(v: ContainerView) -> Self {
Self {
url: agent_url(&v.name),
name: v.name,
running: v.running,
failed: v.failed,
@ -128,6 +130,30 @@ impl From<ContainerView> for hive_sh4re::container::AgentStatusRow {
}
}
/// This agent's own web UI, behind the gateway. `None` when the hive's
/// domain isn't configured — same env var, same "empty string counts as
/// unset" filter `server.rs::hive_urls()` uses for `HiveUrls::home`, kept
/// in sync with it by reading the one env var directly rather than
/// threading a resolved domain through `HiveEnv` for a value nothing else
/// needs pre-resolved.
fn agent_url(name: &str) -> Option<String> {
let domain = std::env::var("HYPERHIVE_HIVE_DOMAIN")
.ok()
.filter(|v| !v.is_empty())?;
Some(agent_url_for_domain(&domain, name))
}
/// The pure half of [`agent_url`], split out so the path-scheme formatting
/// is testable without an env var in the loop. `/agent/<name>/` is the
/// gateway's real path scheme
/// (`nix/host-modules/hive-gateway/vhosts.nix`'s per-agent `agentLocations`
/// block) — computed once here so a client (trollshell's sidebar, the
/// dashboard) never has to derive it and silently drift if that scheme
/// ever changes.
fn agent_url_for_domain(domain: &str, name: &str) -> String {
format!("https://{domain}/agent/{name}/")
}
/// Build the full container list. Wraps `lifecycle::list()` and
/// resolves every per-agent attribute the dashboard surfaces.
///
@ -548,5 +574,21 @@ mod tests {
// No source for reminders on this side (see the `From` impl's doc
// comment) — always the stub, never left uninitialised.
assert_eq!(row.pending_reminders, 0);
// `url` depends on process-global env (`HYPERHIVE_HIVE_DOMAIN`), so
// it isn't asserted here — mutating env vars in a test race with
// every other test in this binary. `agent_url_for_domain` below
// covers the actual path-formatting logic without touching env.
}
/// The pure half of the per-agent URL: given a domain, is the path
/// exactly `/agent/<name>/`? (`agent_url` itself, the thin env-reading
/// wrapper, is intentionally untested — see the note on the test
/// above.)
#[test]
fn agent_url_for_domain_uses_the_gateways_real_path_scheme() {
assert_eq!(
super::agent_url_for_domain("hive.example.com", "alice"),
"https://hive.example.com/agent/alice/"
);
}
}

View file

@ -83,6 +83,16 @@ pub struct AgentStatusRow {
/// `None`.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub status_set_at: Option<DateTime<Utc>>,
/// This agent's own web UI, behind the gateway
/// (`https://<hive-domain>/agent/<name>/`) — the same URL the operator
/// dashboard's "open" action uses. `None` when the hive's domain isn't
/// configured (`HYPERHIVE_HIVE_DOMAIN` unset), same condition
/// `HostRequest::Urls`'s `HiveUrls::home` is `None` under. Present here
/// (not just derivable from `HiveUrls`) so a client never has to build
/// this path itself — see `hive-c0re/container_view.rs`'s `agent_url`
/// for why a client deriving it is the wrong shape to depend on.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
}
/// One matrix identity an agent can act as, surfaced in `GetAgentMeta`'s