diff --git a/Cargo.lock b/Cargo.lock index ea942228..a1ab379c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1694,6 +1694,7 @@ dependencies = [ name = "hive-host-sock" version = "0.1.0" dependencies = [ + "chrono", "hive-sh4re", "hive-types", "serde", diff --git a/hive-c0re/src/job_queue/mod.rs b/hive-c0re/src/job_queue/mod.rs index 6ebcc9f1..4a96b858 100644 --- a/hive-c0re/src/job_queue/mod.rs +++ b/hive-c0re/src/job_queue/mod.rs @@ -39,10 +39,10 @@ use std::collections::HashMap; use std::sync::Mutex; use chrono::{DateTime, Utc}; +use hive_host_sock::jobs::NodeView; use hive_jobq::resources::ResourceTable; use hive_jobq::scheduler::{Outcome, Scheduler}; use hive_jobq::{Dep, DepWhen as JobDepWhen, Graph, NodeId, State as JobState}; -use hive_sh4re::jobs::NodeView; use hive_sh4re::wire_time::now_unix; use tokio::sync::Notify; diff --git a/hive-c0re/src/job_queue/model.rs b/hive-c0re/src/job_queue/model.rs index 5d353d31..63aae51f 100644 --- a/hive-c0re/src/job_queue/model.rs +++ b/hive-c0re/src/job_queue/model.rs @@ -1,9 +1,10 @@ //! Data model for the generic job-DAG queue: node kinds (the primitive //! operations), dependency edges, and the runtime `Dag` / `Node` store. -//! The serialized *views* — `DagView` / `NodeView` plus the `Template` -//! / `Source` / `State` / `PermPayload` wire enums — live in -//! `hive_sh4re::jobs` (wire types belong to the shared crate) and are -//! re-exported here for the queue's internal use. +//! The serialized *views* — `DagView` / `NodeView` plus the `Source` / +//! `State` / `PermPayload` wire enums — live in `hive_host_sock::jobs` +//! (they travel on the host admin socket and the dashboard channels +//! served off the same snapshot, and nowhere else) and are re-exported +//! here for the queue's internal use. //! //! Two levels: the **DAG** is the unit of cancel / approval-resolution //! and the dashboard group; the **node** is the unit of scheduling / @@ -11,7 +12,7 @@ //! DAG can span agents). See `docs/coordinator.md::Job queue` for the //! full design. -pub use hive_sh4re::jobs::{DagView, NodeId, PermPayload, Source, State}; +pub use hive_host_sock::jobs::{DagView, NodeId, PermPayload, Source, State}; use serde::Serialize; use crate::coordinator::TransientKind; diff --git a/hive-host-sock/Cargo.toml b/hive-host-sock/Cargo.toml index cdb83f53..902b2ab7 100644 --- a/hive-host-sock/Cargo.toml +++ b/hive-host-sock/Cargo.toml @@ -8,6 +8,7 @@ readme = "README.md" workspace = true [dependencies] +chrono.workspace = true hive-sh4re.workspace = true hive-types.workspace = true serde.workspace = true diff --git a/hive-sh4re/src/jobs.rs b/hive-host-sock/src/jobs.rs similarity index 100% rename from hive-sh4re/src/jobs.rs rename to hive-host-sock/src/jobs.rs diff --git a/hive-host-sock/src/lib.rs b/hive-host-sock/src/lib.rs index 0427c6ee..518a43b0 100644 --- a/hive-host-sock/src/lib.rs +++ b/hive-host-sock/src/lib.rs @@ -2,16 +2,22 @@ //! //! The host-control protocol spoken between `hivectl` and the `hive-c0re` //! daemon. Re-homed out of `hive-sh4re` so a standalone `hivectl` can depend -//! on just this protocol crate instead of the whole daemon crate. The shared -//! payload types it references (`Approval`, `AgentStatusRow`, `jobs::DagView`) -//! stay in `hive-sh4re`. +//! on just this protocol crate instead of the whole daemon crate. The +//! job-queue wire types ([`jobs`]) travel on this socket and the dashboard +//! channels `hive-c0re` serves off the same snapshot, and on nothing else — +//! their whole consumer set is `hive-c0re` + `hivectl` + this crate — so they +//! live here rather than in the shared crate. The payload types genuinely +//! shared with the *agent* sockets (`Approval`, `AgentStatusRow`) stay in +//! `hive-sh4re`. use std::path::PathBuf; -use hive_sh4re::{AgentStatusRow, Approval, jobs}; +use hive_sh4re::{AgentStatusRow, Approval}; use hive_types::Ident; use serde::{Deserialize, Serialize}; +pub mod jobs; + // ── Shared hive layout facts ────────────────────────────────────────────── // Paths + names both the `hive-c0re` daemon and the host-side `hivectl` CLI // must agree on. Homed here (the protocol crate both sides already depend on) diff --git a/hive-sh4re/src/lib.rs b/hive-sh4re/src/lib.rs index ac49b852..3be84fa7 100644 --- a/hive-sh4re/src/lib.rs +++ b/hive-sh4re/src/lib.rs @@ -5,7 +5,6 @@ use hive_types::Ident; use serde::{Deserialize, Serialize}; pub mod assets; -pub mod jobs; pub mod paths; pub mod wire_time; diff --git a/hivectl/src/dag_progress.rs b/hivectl/src/dag_progress.rs index 1b30b4bf..ec32b6d8 100644 --- a/hivectl/src/dag_progress.rs +++ b/hivectl/src/dag_progress.rs @@ -66,7 +66,7 @@ async fn wait_for_dags_plain(socket: &Path, ids: Vec) -> Result<()> { // may still be running — keep watching so the operator // sees whether the agent came back. if d.nodes.iter().all(|n| n.state.is_terminal()) { - if d.rollup_state() == hive_sh4re::jobs::State::Failed { + if d.rollup_state() == hive_host_sock::jobs::State::Failed { failed.push(format!("{} {}", d.source.as_str(), dag_agents(d))); } } else { @@ -104,9 +104,9 @@ async fn wait_for_dags_animated(socket: &Path, ids: Vec) -> Result<()> { // insertion order groups a DAG's nodes right under its header. let mut dag_bars: std::collections::HashMap = std::collections::HashMap::new(); - let mut node_bars: std::collections::HashMap<(u64, hive_sh4re::jobs::NodeId), ProgressBar> = + let mut node_bars: std::collections::HashMap<(u64, hive_host_sock::jobs::NodeId), ProgressBar> = std::collections::HashMap::new(); - let mut node_done: std::collections::HashSet<(u64, hive_sh4re::jobs::NodeId)> = + let mut node_done: std::collections::HashSet<(u64, hive_host_sock::jobs::NodeId)> = std::collections::HashSet::new(); let mut pending: std::collections::BTreeSet = ids.into_iter().collect(); @@ -163,7 +163,7 @@ async fn wait_for_dags_animated(socket: &Path, ids: Vec) -> Result<()> { } } if d.nodes.iter().all(|n| n.state.is_terminal()) { - if d.rollup_state() == hive_sh4re::jobs::State::Failed { + if d.rollup_state() == hive_host_sock::jobs::State::Failed { failed.push(format!("{} {}", d.source.as_str(), dag_agents(d))); } } else { @@ -205,7 +205,7 @@ fn finish_wait(mut failed: Vec) -> Result<()> { /// Distinct agents across a DAG's nodes, comma-joined for display — the /// per-node replacement for the old DAG-level `agent` field. Single-agent /// DAGs render one name; a hive-wide DAG lists each. -fn dag_agents(d: &hive_sh4re::jobs::DagView) -> String { +fn dag_agents(d: &hive_host_sock::jobs::DagView) -> String { let mut seen: Vec<&str> = Vec::new(); for n in &d.nodes { if !seen.contains(&n.agent.as_str()) { @@ -227,7 +227,7 @@ fn now_unix() -> i64 { /// Elapsed seconds for a DAG: `started_at` (falling back to `created_at`) /// through `finished_at` or `now`. The wire carries these as RFC3339 /// `DateTime`; compare in unix seconds against `now`. -fn dag_elapsed(d: &hive_sh4re::jobs::DagView, now: i64) -> i64 { +fn dag_elapsed(d: &hive_host_sock::jobs::DagView, now: i64) -> i64 { let start = d .started_at .map_or_else(|| d.created_at.timestamp(), |t| t.timestamp()); @@ -236,7 +236,7 @@ fn dag_elapsed(d: &hive_sh4re::jobs::DagView, now: i64) -> i64 { /// Elapsed seconds for a node: `started_at` → `finished_at`/`now`, or 0 /// when it hasn't started. -fn node_elapsed(n: &hive_sh4re::jobs::NodeView, now: i64) -> i64 { +fn node_elapsed(n: &hive_host_sock::jobs::NodeView, now: i64) -> i64 { match n.started_at { Some(start) => (n.finished_at.map_or(now, |t| t.timestamp()) - start.timestamp()).max(0), None => 0, @@ -255,7 +255,11 @@ fn fmt_dur(secs: i64) -> String { /// One animated node line: kind, an `(after …)` marker for a fan-in node /// (>1 dep), its elapsed timer, and a truncated error tail. -fn node_line(d: &hive_sh4re::jobs::DagView, n: &hive_sh4re::jobs::NodeView, now: i64) -> String { +fn node_line( + d: &hive_host_sock::jobs::DagView, + n: &hive_host_sock::jobs::NodeView, + now: i64, +) -> String { use std::fmt::Write as _; let mut s = n.kind.clone(); if n.deps.len() > 1 { @@ -280,13 +284,13 @@ fn node_line(d: &hive_sh4re::jobs::DagView, n: &hive_sh4re::jobs::NodeView, now: s } -fn state_glyph(state: hive_sh4re::jobs::State) -> &'static str { +fn state_glyph(state: hive_host_sock::jobs::State) -> &'static str { match state { - hive_sh4re::jobs::State::Queued => "⏸", - hive_sh4re::jobs::State::Running => "▶", - hive_sh4re::jobs::State::Done => "✔", - hive_sh4re::jobs::State::Failed => "✖", - hive_sh4re::jobs::State::Cancelled => "⊘", + hive_host_sock::jobs::State::Queued => "⏸", + hive_host_sock::jobs::State::Running => "▶", + hive_host_sock::jobs::State::Done => "✔", + hive_host_sock::jobs::State::Failed => "✖", + hive_host_sock::jobs::State::Cancelled => "⊘", } } @@ -294,7 +298,7 @@ fn state_glyph(state: hive_sh4re::jobs::State) -> &'static str { /// node chain — the CLI twin of the dashboard's queue card. The header shows /// what the backend sends (`source` + the raw node kinds); only the roll-up /// state glyph is derived from the node set. Used by the plain (non-TTY) path. -fn render_dag_line(d: &hive_sh4re::jobs::DagView) -> String { +fn render_dag_line(d: &hive_host_sock::jobs::DagView) -> String { use std::fmt::Write as _; let mut out = format!( "{} {} {:<12}", @@ -315,7 +319,7 @@ fn render_dag_line(d: &hive_sh4re::jobs::DagView) -> String { #[cfg(test)] mod tests { - use hive_sh4re::jobs::{DagView, NodeView, Source, State}; + use hive_host_sock::jobs::{DagView, NodeView, Source, State}; use hive_sh4re::wire_time::from_secs; use super::render_dag_line;