diff --git a/hive-c0re/assets/app.js b/hive-c0re/assets/app.js index 44785ace..42309267 100644 --- a/hive-c0re/assets/app.js +++ b/hive-c0re/assets/app.js @@ -723,21 +723,6 @@ } body.append(drill); - // ── extra agent-declared links ─────────────────────────────── - if (c.extra_links && c.extra_links.length > 0) { - const extras = el('div', { class: 'drill-ins drill-ins-extra' }); - for (const lnk of c.extra_links) { - extras.append(el('a', { - class: 'panel-trigger', - href: lnk.url, - target: '_blank', - rel: 'noopener', - title: lnk.url, - }, (lnk.icon ? lnk.icon + ' ' : '') + lnk.label + ' ↗')); - } - body.append(extras); - } - li.append(icon, body); ul.append(li); } diff --git a/hive-c0re/src/container_view.rs b/hive-c0re/src/container_view.rs index 25248922..00273661 100644 --- a/hive-c0re/src/container_view.rs +++ b/hive-c0re/src/container_view.rs @@ -9,22 +9,11 @@ use std::collections::HashMap; use std::path::Path; use rusqlite::Connection; -use serde::{Deserialize, Serialize}; +use serde::Serialize; use crate::coordinator::Coordinator; use crate::lifecycle::{self, AGENT_PREFIX, MANAGER_NAME}; -/// An agent-declared extra navigation link surfaced on the dashboard card. -/// Written by the `hive-dashboard-links` NixOS oneshot into -/// `{state_dir}/hyperhive-dashboard-links.json` and read by `build_all`. -#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, Default)] -pub struct DashboardLink { - pub label: String, - #[serde(default)] - pub icon: String, - pub url: String, -} - #[derive(Serialize, Clone, PartialEq, Eq, Debug)] #[allow(clippy::struct_excessive_bools)] pub struct ContainerView { @@ -61,13 +50,6 @@ pub struct ContainerView { /// removes when it resumes. Stale by up to one crash-watch cycle. #[serde(default)] pub rate_limited: bool, - /// Extra navigation links declared by the agent via - /// `hyperhive.dashboardLinks` in `agent.nix`. Written to - /// `{state_dir}/hyperhive-dashboard-links.json` by the - /// `hive-dashboard-links` oneshot at container boot. Empty when - /// the file is absent or the agent declares no links. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub extra_links: Vec, } /// Build the full container list. Wraps `lifecycle::list()` and @@ -105,7 +87,6 @@ pub async fn build_all(coord: &Coordinator) -> Vec { .unwrap_or(0); let ctx_tokens = read_last_ctx_tokens(&logical); let rate_limited = is_rate_limited(&logical); - let extra_links = read_dashboard_links(&logical); out.push(ContainerView { port: lifecycle::agent_web_port(&logical), running: lifecycle::is_running(&logical).await, @@ -118,7 +99,6 @@ pub async fn build_all(coord: &Coordinator) -> Vec { pending_reminders, ctx_tokens, rate_limited, - extra_links, }); } out @@ -137,18 +117,6 @@ pub fn claude_has_session(dir: &Path) -> bool { .any(|e| e.file_type().is_ok_and(|t| t.is_file())) } -/// Read agent-declared extra dashboard links from -/// `{state_dir}/hyperhive-dashboard-links.json`. Returns an empty vec when -/// the file is absent, empty, or unparseable — best-effort, never panics. -fn read_dashboard_links(name: &str) -> Vec { - let path = Coordinator::agent_notes_dir(name).join("hyperhive-dashboard-links.json"); - let text = match std::fs::read_to_string(&path) { - Ok(t) if !t.trim().is_empty() => t, - _ => return Vec::new(), - }; - serde_json::from_str::>(&text).unwrap_or_default() -} - /// Returns true if the agent's harness is currently parked after an API /// rate-limit response. Detected via the sentinel file written by /// `hive_ag3nt::events::Bus::emit_status("rate_limited")`. diff --git a/nix/templates/harness-base.nix b/nix/templates/harness-base.nix index 359adc3e..01ec86b2 100644 --- a/nix/templates/harness-base.nix +++ b/nix/templates/harness-base.nix @@ -173,39 +173,6 @@ ''; }; - options.hyperhive.dashboardLinks = lib.mkOption { - type = lib.types.listOf (lib.types.submodule { - options = { - label = lib.mkOption { - type = lib.types.str; - description = "Display label for the link."; - }; - icon = lib.mkOption { - type = lib.types.str; - default = ""; - description = "Optional icon emoji or short glyph."; - }; - url = lib.mkOption { - type = lib.types.str; - description = "Full URL (may include a different port, e.g. http://localhost:9001/stats)."; - }; - }; - }); - default = [ ]; - example = lib.literalExpression '' - [ - { label = "Stats"; icon = "📊"; url = "http://localhost:9001/stats"; } - ] - ''; - description = '' - Extra navigation links surfaced on the hive-c0re dashboard card for - this agent. Declare any additional web UI pages the agent exposes — - stats pages, custom UIs, etc. hive-c0re reads the JSON file this - option produces at each container-view snapshot and attaches the - links to the agent card without any code changes. - ''; - }; - options.hyperhive.claudeMarketplaces = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ "anthropics/claude-plugins-official" ]; @@ -513,30 +480,6 @@ ''; }; - # Write declared dashboardLinks to the state dir so hive-c0re can read - # them without accessing the container's /etc/ from the host. - # Runs every boot; idempotent (overwrite). Always exits 0. - systemd.services.hive-dashboard-links = lib.mkIf (config.hyperhive.dashboardLinks != [ ]) { - description = "write declarative dashboardLinks to agent state dir"; - wantedBy = [ "multi-user.target" ]; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - }; - environment.LINKS_JSON = builtins.toJSON config.hyperhive.dashboardLinks; - script = '' - # Sub-agents have their state dir bind-mounted at /agents//state. - # Use a glob — exactly one match per container at runtime. - STATE_DIR=$(echo /agents/*/state) - if [ ! -d "$STATE_DIR" ]; then - echo "hive-dashboard-links: no state dir found at /agents/*/state; skipping" - exit 0 - fi - printf '%s' "$LINKS_JSON" > "$STATE_DIR/hyperhive-dashboard-links.json" - echo "hive-dashboard-links: wrote $(printf '%s' "$LINKS_JSON" | wc -c) bytes to $STATE_DIR/hyperhive-dashboard-links.json" - ''; - }; - # Git is needed by claude's Bash tool (for the agent <-> manager config # request flow) and by hive-c0re's own setup_applied / setup_proposed. # The per-agent `applied//flake.nix` overrides `user.name` and