refactor(#2352): re-home host-control wire types into hive-host-sock crate
This commit is contained in:
parent
e13a2cb33a
commit
96c748475e
11 changed files with 334 additions and 297 deletions
9
Cargo.lock
generated
9
Cargo.lock
generated
|
|
@ -1519,6 +1519,7 @@ dependencies = [
|
||||||
"clap-markdown",
|
"clap-markdown",
|
||||||
"clap_complete",
|
"clap_complete",
|
||||||
"forgejo-api",
|
"forgejo-api",
|
||||||
|
"hive-host-sock",
|
||||||
"hive-sh4re",
|
"hive-sh4re",
|
||||||
"hmac",
|
"hmac",
|
||||||
"indicatif",
|
"indicatif",
|
||||||
|
|
@ -1564,6 +1565,14 @@ dependencies = [
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hive-host-sock"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"hive-sh4re",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hive-matrix-mcp"
|
name = "hive-matrix-mcp"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ members = [
|
||||||
"hive-c0re",
|
"hive-c0re",
|
||||||
"hive-claude",
|
"hive-claude",
|
||||||
"hive-forge",
|
"hive-forge",
|
||||||
|
"hive-host-sock",
|
||||||
"hive-matrix-mcp",
|
"hive-matrix-mcp",
|
||||||
"hive-metric",
|
"hive-metric",
|
||||||
"hive-priv",
|
"hive-priv",
|
||||||
|
|
@ -39,6 +40,7 @@ clap_complete = "4"
|
||||||
indicatif = "0.17"
|
indicatif = "0.17"
|
||||||
hive-sh4re = { path = "hive-sh4re" }
|
hive-sh4re = { path = "hive-sh4re" }
|
||||||
hive-claude = { path = "hive-claude" }
|
hive-claude = { path = "hive-claude" }
|
||||||
|
hive-host-sock = { path = "hive-host-sock" }
|
||||||
thiserror = "2"
|
thiserror = "2"
|
||||||
tower-http = { version = "0.6", features = ["fs"] }
|
tower-http = { version = "0.6", features = ["fs"] }
|
||||||
rmcp = { version = "1.7", default-features = false, features = [
|
rmcp = { version = "1.7", default-features = false, features = [
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ clap_complete.workspace = true
|
||||||
clap-markdown = "0.1"
|
clap-markdown = "0.1"
|
||||||
indicatif.workspace = true
|
indicatif.workspace = true
|
||||||
hive-sh4re.workspace = true
|
hive-sh4re.workspace = true
|
||||||
|
hive-host-sock.workspace = true
|
||||||
libc.workspace = true
|
libc.workspace = true
|
||||||
listenfd = "1"
|
listenfd = "1"
|
||||||
petgraph.workspace = true
|
petgraph.workspace = true
|
||||||
|
|
|
||||||
|
|
@ -277,7 +277,7 @@ enum OpenTarget {
|
||||||
/// additively.
|
/// additively.
|
||||||
// One bool per selectable container class, each mapping 1:1 to a clap flag;
|
// One bool per selectable container class, each mapping 1:1 to a clap flag;
|
||||||
// orthogonal toggles, not a state machine — hence the bools allow (mirrors
|
// orthogonal toggles, not a state machine — hence the bools allow (mirrors
|
||||||
// `hive_sh4re::LifecycleScope`).
|
// `hive_host_sock::LifecycleScope`).
|
||||||
#[allow(clippy::struct_excessive_bools)]
|
#[allow(clippy::struct_excessive_bools)]
|
||||||
#[derive(Args)]
|
#[derive(Args)]
|
||||||
struct ScopeArgs {
|
struct ScopeArgs {
|
||||||
|
|
@ -302,8 +302,8 @@ struct ScopeArgs {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScopeArgs {
|
impl ScopeArgs {
|
||||||
fn to_scope(&self) -> hive_sh4re::LifecycleScope {
|
fn to_scope(&self) -> hive_host_sock::LifecycleScope {
|
||||||
hive_sh4re::LifecycleScope {
|
hive_host_sock::LifecycleScope {
|
||||||
agents: self.agents,
|
agents: self.agents,
|
||||||
agent_names: self.agent.clone(),
|
agent_names: self.agent.clone(),
|
||||||
ci: self.ci,
|
ci: self.ci,
|
||||||
|
|
@ -794,8 +794,8 @@ async fn query_hive_domain(socket: &Path) -> Option<String> {
|
||||||
|
|
||||||
/// Best-effort query for this hive's domain + browser-facing web URLs
|
/// Best-effort query for this hive's domain + browser-facing web URLs
|
||||||
/// (`HostRequest::Urls`). `None` when the daemon is unreachable.
|
/// (`HostRequest::Urls`). `None` when the daemon is unreachable.
|
||||||
async fn query_hive_urls(socket: &Path) -> Option<hive_sh4re::HiveUrls> {
|
async fn query_hive_urls(socket: &Path) -> Option<hive_host_sock::HiveUrls> {
|
||||||
hive_c0re::client::request(socket, hive_sh4re::HostRequest::Urls)
|
hive_c0re::client::request(socket, hive_host_sock::HostRequest::Urls)
|
||||||
.await
|
.await
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|r| r.urls)
|
.and_then(|r| r.urls)
|
||||||
|
|
@ -1514,7 +1514,7 @@ fn gateway_list_users(file: &Path) -> Result<()> {
|
||||||
async fn agents_restart(socket: &Path, name: &str, no_wait: bool) -> Result<()> {
|
async fn agents_restart(socket: &Path, name: &str, no_wait: bool) -> Result<()> {
|
||||||
let resp = hive_c0re::client::request(
|
let resp = hive_c0re::client::request(
|
||||||
socket,
|
socket,
|
||||||
hive_sh4re::HostRequest::Restart {
|
hive_host_sock::HostRequest::Restart {
|
||||||
name: name.to_owned(),
|
name: name.to_owned(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -1536,7 +1536,7 @@ async fn agents_restart(socket: &Path, name: &str, no_wait: bool) -> Result<()>
|
||||||
/// or the raw JSON rows with `--json`. Reuses the dashboard's
|
/// or the raw JSON rows with `--json`. Reuses the dashboard's
|
||||||
/// `ContainerView` aggregation, so the CLI and the web UI never drift.
|
/// `ContainerView` aggregation, so the CLI and the web UI never drift.
|
||||||
async fn agents_list(socket: &Path, json: bool) -> Result<()> {
|
async fn agents_list(socket: &Path, json: bool) -> Result<()> {
|
||||||
let resp = hive_c0re::client::request(socket, hive_sh4re::HostRequest::AgentStatus)
|
let resp = hive_c0re::client::request(socket, hive_host_sock::HostRequest::AgentStatus)
|
||||||
.await
|
.await
|
||||||
.with_context(|| format!("connect to daemon socket {}", socket.display()))?;
|
.with_context(|| format!("connect to daemon socket {}", socket.display()))?;
|
||||||
if !resp.ok {
|
if !resp.ok {
|
||||||
|
|
@ -1608,7 +1608,7 @@ async fn agents_list(socket: &Path, json: bool) -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn agents_restart_all(socket: &Path, no_wait: bool) -> Result<()> {
|
async fn agents_restart_all(socket: &Path, no_wait: bool) -> Result<()> {
|
||||||
let resp = hive_c0re::client::request(socket, hive_sh4re::HostRequest::RestartAll)
|
let resp = hive_c0re::client::request(socket, hive_host_sock::HostRequest::RestartAll)
|
||||||
.await
|
.await
|
||||||
.with_context(|| format!("connect to daemon socket {}", socket.display()))?;
|
.with_context(|| format!("connect to daemon socket {}", socket.display()))?;
|
||||||
let agents = resp.agents.as_deref().unwrap_or(&[]);
|
let agents = resp.agents.as_deref().unwrap_or(&[]);
|
||||||
|
|
@ -1630,14 +1630,16 @@ async fn agents_restart_all(socket: &Path, no_wait: bool) -> Result<()> {
|
||||||
|
|
||||||
async fn stop(
|
async fn stop(
|
||||||
socket: &Path,
|
socket: &Path,
|
||||||
scope: hive_sh4re::LifecycleScope,
|
scope: hive_host_sock::LifecycleScope,
|
||||||
graceful: bool,
|
graceful: bool,
|
||||||
no_wait: bool,
|
no_wait: bool,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let resp =
|
let resp = hive_c0re::client::request(
|
||||||
hive_c0re::client::request(socket, hive_sh4re::HostRequest::Stop { scope, graceful })
|
socket,
|
||||||
.await
|
hive_host_sock::HostRequest::Stop { scope, graceful },
|
||||||
.with_context(|| format!("connect to daemon socket {}", socket.display()))?;
|
)
|
||||||
|
.await
|
||||||
|
.with_context(|| format!("connect to daemon socket {}", socket.display()))?;
|
||||||
// Render first, but even when an infra failure makes it bail,
|
// Render first, but even when an infra failure makes it bail,
|
||||||
// watch the already-queued agent DAGs before surfacing the error —
|
// watch the already-queued agent DAGs before surfacing the error —
|
||||||
// they run regardless.
|
// they run regardless.
|
||||||
|
|
@ -1646,8 +1648,8 @@ async fn stop(
|
||||||
rendered
|
rendered
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn start(socket: &Path, scope: hive_sh4re::LifecycleScope, no_wait: bool) -> Result<()> {
|
async fn start(socket: &Path, scope: hive_host_sock::LifecycleScope, no_wait: bool) -> Result<()> {
|
||||||
let resp = hive_c0re::client::request(socket, hive_sh4re::HostRequest::Start { scope })
|
let resp = hive_c0re::client::request(socket, hive_host_sock::HostRequest::Start { scope })
|
||||||
.await
|
.await
|
||||||
.with_context(|| format!("connect to daemon socket {}", socket.display()))?;
|
.with_context(|| format!("connect to daemon socket {}", socket.display()))?;
|
||||||
let rendered = render_lifecycle(&resp, "start queued");
|
let rendered = render_lifecycle(&resp, "start queued");
|
||||||
|
|
@ -1664,15 +1666,19 @@ async fn start(socket: &Path, scope: hive_sh4re::LifecycleScope, no_wait: bool)
|
||||||
/// No `--no-wait` here on purpose: the stop DAGs must complete before
|
/// No `--no-wait` here on purpose: the stop DAGs must complete before
|
||||||
/// the start submits, otherwise the start's `wanted = Up` write would
|
/// the start submits, otherwise the start's `wanted = Up` write would
|
||||||
/// land before the queued stops execute and turn them into noops.
|
/// land before the queued stops execute and turn them into noops.
|
||||||
async fn restart(socket: &Path, scope: hive_sh4re::LifecycleScope, graceful: bool) -> Result<()> {
|
async fn restart(
|
||||||
|
socket: &Path,
|
||||||
|
scope: hive_host_sock::LifecycleScope,
|
||||||
|
graceful: bool,
|
||||||
|
) -> Result<()> {
|
||||||
stop(socket, scope.clone(), graceful, false).await?;
|
stop(socket, scope.clone(), graceful, false).await?;
|
||||||
start(socket, scope, false).await
|
start(socket, scope, false).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A [`LifecycleScope`](hive_sh4re::LifecycleScope) targeting exactly one
|
/// A [`LifecycleScope`](hive_host_sock::LifecycleScope) targeting exactly one
|
||||||
/// agent by name (no infra containers, no all-agents flag).
|
/// agent by name (no infra containers, no all-agents flag).
|
||||||
fn single_agent_scope(name: &str) -> hive_sh4re::LifecycleScope {
|
fn single_agent_scope(name: &str) -> hive_host_sock::LifecycleScope {
|
||||||
hive_sh4re::LifecycleScope {
|
hive_host_sock::LifecycleScope {
|
||||||
agents: false,
|
agents: false,
|
||||||
agent_names: vec![name.to_owned()],
|
agent_names: vec![name.to_owned()],
|
||||||
ci: false,
|
ci: false,
|
||||||
|
|
@ -1702,7 +1708,7 @@ async fn subvol_upgrade(socket: &Path, name: &str, yes: bool) -> Result<()> {
|
||||||
println!("stopping {name} (releasing its state bind-mount)…");
|
println!("stopping {name} (releasing its state bind-mount)…");
|
||||||
let stop_resp = hive_c0re::client::request(
|
let stop_resp = hive_c0re::client::request(
|
||||||
socket,
|
socket,
|
||||||
hive_sh4re::HostRequest::Stop {
|
hive_host_sock::HostRequest::Stop {
|
||||||
scope: single_agent_scope(name),
|
scope: single_agent_scope(name),
|
||||||
graceful: false,
|
graceful: false,
|
||||||
},
|
},
|
||||||
|
|
@ -1732,7 +1738,7 @@ async fn subvol_upgrade(socket: &Path, name: &str, yes: bool) -> Result<()> {
|
||||||
println!("starting {name}…");
|
println!("starting {name}…");
|
||||||
let start_result = hive_c0re::client::request(
|
let start_result = hive_c0re::client::request(
|
||||||
socket,
|
socket,
|
||||||
hive_sh4re::HostRequest::Start {
|
hive_host_sock::HostRequest::Start {
|
||||||
scope: single_agent_scope(name),
|
scope: single_agent_scope(name),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -1776,7 +1782,7 @@ async fn subvol_upgrade(socket: &Path, name: &str, yes: bool) -> Result<()> {
|
||||||
/// touched container, then surface any aggregated per-target failure as a
|
/// touched container, then surface any aggregated per-target failure as a
|
||||||
/// non-zero exit. `verb` is the past-tense word printed per item
|
/// non-zero exit. `verb` is the past-tense word printed per item
|
||||||
/// (`stopped` / `started`).
|
/// (`stopped` / `started`).
|
||||||
fn render_lifecycle(resp: &hive_sh4re::HostResponse, verb: &str) -> Result<()> {
|
fn render_lifecycle(resp: &hive_host_sock::HostResponse, verb: &str) -> Result<()> {
|
||||||
let items = resp.agents.as_deref().unwrap_or(&[]);
|
let items = resp.agents.as_deref().unwrap_or(&[]);
|
||||||
if items.is_empty() {
|
if items.is_empty() {
|
||||||
println!("{verb}: nothing matched the requested scope");
|
println!("{verb}: nothing matched the requested scope");
|
||||||
|
|
|
||||||
|
|
@ -42,9 +42,10 @@ async fn wait_for_dags_plain(socket: &Path, ids: Vec<u64>) -> Result<()> {
|
||||||
let mut failed: Vec<String> = Vec::new();
|
let mut failed: Vec<String> = Vec::new();
|
||||||
while !pending.is_empty() {
|
while !pending.is_empty() {
|
||||||
for id in pending.clone() {
|
for id in pending.clone() {
|
||||||
let resp = hive_c0re::client::request(socket, hive_sh4re::HostRequest::QueueDag { id })
|
let resp =
|
||||||
.await
|
hive_c0re::client::request(socket, hive_host_sock::HostRequest::QueueDag { id })
|
||||||
.with_context(|| format!("connect to daemon socket {}", socket.display()))?;
|
.await
|
||||||
|
.with_context(|| format!("connect to daemon socket {}", socket.display()))?;
|
||||||
let dags = resp.dags.unwrap_or_default();
|
let dags = resp.dags.unwrap_or_default();
|
||||||
if dags.is_empty() {
|
if dags.is_empty() {
|
||||||
// Evicted from the queue's history tail — it finished a
|
// Evicted from the queue's history tail — it finished a
|
||||||
|
|
@ -115,9 +116,10 @@ async fn wait_for_dags_animated(socket: &Path, ids: Vec<u64>) -> Result<()> {
|
||||||
while !pending.is_empty() {
|
while !pending.is_empty() {
|
||||||
let now = now_unix();
|
let now = now_unix();
|
||||||
for id in pending.clone() {
|
for id in pending.clone() {
|
||||||
let resp = hive_c0re::client::request(socket, hive_sh4re::HostRequest::QueueDag { id })
|
let resp =
|
||||||
.await
|
hive_c0re::client::request(socket, hive_host_sock::HostRequest::QueueDag { id })
|
||||||
.with_context(|| format!("connect to daemon socket {}", socket.display()))?;
|
.await
|
||||||
|
.with_context(|| format!("connect to daemon socket {}", socket.display()))?;
|
||||||
let dags = resp.dags.unwrap_or_default();
|
let dags = resp.dags.unwrap_or_default();
|
||||||
if dags.is_empty() {
|
if dags.is_empty() {
|
||||||
mp.println(format!("job #{id}: gone from queue history"))
|
mp.println(format!("job #{id}: gone from queue history"))
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use anyhow::{Context, Result, bail};
|
use anyhow::{Context, Result, bail};
|
||||||
use hive_sh4re::{HostRequest, HostResponse};
|
use hive_host_sock::{HostRequest, HostResponse};
|
||||||
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
|
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
|
||||||
use tokio::net::UnixStream;
|
use tokio::net::UnixStream;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use anyhow::{Context as _, Result, bail};
|
use anyhow::{Context as _, Result, bail};
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use hive_sh4re::{HostRequest, HostResponse};
|
use hive_host_sock::{HostRequest, HostResponse};
|
||||||
|
|
||||||
// Every module hangs off the `hive_c0re` library (see `src/lib.rs`).
|
// Every module hangs off the `hive_c0re` library (see `src/lib.rs`).
|
||||||
// The daemon and the `hivectl` sibling binary share the same module
|
// The daemon and the `hivectl` sibling binary share the same module
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ use std::path::Path;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
use hive_host_sock::{HostRequest, HostResponse, LifecycleScope};
|
||||||
use hive_sh4re::priv_proto::{InfraAction, InfraContainer};
|
use hive_sh4re::priv_proto::{InfraAction, InfraContainer};
|
||||||
use hive_sh4re::{HostRequest, HostResponse, LifecycleScope};
|
|
||||||
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
|
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
|
||||||
use tokio::net::{UnixListener, UnixStream};
|
use tokio::net::{UnixListener, UnixStream};
|
||||||
|
|
||||||
|
|
@ -480,12 +480,12 @@ fn is_broad_scope(scope: &LifecycleScope) -> bool {
|
||||||
/// gateway, matrix GUI off), so the CLI can hint precisely instead of
|
/// gateway, matrix GUI off), so the CLI can hint precisely instead of
|
||||||
/// opening a dead link. Scheme matches the existing `HIVE_FORGE_PUBLIC_URL`
|
/// opening a dead link. Scheme matches the existing `HIVE_FORGE_PUBLIC_URL`
|
||||||
/// convention (gateway terminates TLS, so https).
|
/// convention (gateway terminates TLS, so https).
|
||||||
fn hive_urls() -> hive_sh4re::HiveUrls {
|
fn hive_urls() -> hive_host_sock::HiveUrls {
|
||||||
// Treat an empty env value as unset everywhere — an empty domain would
|
// Treat an empty env value as unset everywhere — an empty domain would
|
||||||
// otherwise render `swarm.peers."" = …` (invalid nix) and `https:///`.
|
// otherwise render `swarm.peers."" = …` (invalid nix) and `https:///`.
|
||||||
let env = |k: &str| std::env::var(k).ok().filter(|v| !v.is_empty());
|
let env = |k: &str| std::env::var(k).ok().filter(|v| !v.is_empty());
|
||||||
let domain = env("HYPERHIVE_HIVE_DOMAIN");
|
let domain = env("HYPERHIVE_HIVE_DOMAIN");
|
||||||
hive_sh4re::HiveUrls {
|
hive_host_sock::HiveUrls {
|
||||||
home: domain.as_ref().map(|d| format!("https://{d}/")),
|
home: domain.as_ref().map(|d| format!("https://{d}/")),
|
||||||
forge: env("HIVE_FORGE_PUBLIC_URL"),
|
forge: env("HIVE_FORGE_PUBLIC_URL"),
|
||||||
matrix: env("HIVE_MATRIX_PUBLIC_URL"),
|
matrix: env("HIVE_MATRIX_PUBLIC_URL"),
|
||||||
|
|
|
||||||
11
hive-host-sock/Cargo.toml
Normal file
11
hive-host-sock/Cargo.toml
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
[package]
|
||||||
|
name = "hive-host-sock"
|
||||||
|
edition.workspace = true
|
||||||
|
version.workspace = true
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
hive-sh4re.workspace = true
|
||||||
|
serde.workspace = true
|
||||||
270
hive-host-sock/src/lib.rs
Normal file
270
hive-host-sock/src/lib.rs
Normal file
|
|
@ -0,0 +1,270 @@
|
||||||
|
//! Host admin socket wire types (`/run/hyperhive/host.sock`).
|
||||||
|
//!
|
||||||
|
//! 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`.
|
||||||
|
|
||||||
|
use hive_sh4re::{AgentStatusRow, Approval, jobs};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// Requests on the host admin socket.
|
||||||
|
///
|
||||||
|
/// Wire format: one JSON object per line.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
#[serde(tag = "cmd", rename_all = "snake_case")]
|
||||||
|
pub enum HostRequest {
|
||||||
|
/// Create and start a sub-agent container directly, bypassing the
|
||||||
|
/// approval queue. Privileged-context only. See
|
||||||
|
/// `docs/approvals.md::Approval kinds (wire shapes)`.
|
||||||
|
Spawn { name: String },
|
||||||
|
/// Submit a spawn request for the operator to approve. See
|
||||||
|
/// `docs/approvals.md::Approval kinds (wire shapes)` (`Spawn`).
|
||||||
|
RequestSpawn { name: String },
|
||||||
|
/// Stop a managed container (graceful).
|
||||||
|
Kill { name: String },
|
||||||
|
/// Tear down a sub-agent container, optionally purging state.
|
||||||
|
/// See `docs/approvals.md::Destroy semantics`.
|
||||||
|
Destroy {
|
||||||
|
name: String,
|
||||||
|
#[serde(default)]
|
||||||
|
purge: bool,
|
||||||
|
},
|
||||||
|
/// Stop and start a managed container without rebuilding config.
|
||||||
|
/// For "kick the container" operations that don't touch the flake or
|
||||||
|
/// nspawn flags. Mirrors `lifecycle::restart` (kill + start).
|
||||||
|
Restart { name: String },
|
||||||
|
/// Stop and restart all managed containers in sequence. Convenience
|
||||||
|
/// wrapper for `hivectl agents restart-all`; iterates the live
|
||||||
|
/// container list and restarts each one.
|
||||||
|
RestartAll,
|
||||||
|
/// Apply pending config to a managed container.
|
||||||
|
Rebuild { name: String },
|
||||||
|
/// List managed containers.
|
||||||
|
List,
|
||||||
|
/// List managed agents with their full status + technical state
|
||||||
|
/// (running / needs-login / needs-update / deployed sha / parent /
|
||||||
|
/// pending reminders) — the `hivectl agents list` roster view.
|
||||||
|
/// Reuses the dashboard's per-agent `ContainerView` aggregation.
|
||||||
|
AgentStatus,
|
||||||
|
/// Report this hive's canonical DNS domain
|
||||||
|
/// (`services.hyperhive.domain`) plus the browser-facing home /
|
||||||
|
/// forge / matrix URLs, daemon-sourced so custom forge/matrix
|
||||||
|
/// domains resolve correctly. Each URL is `None` when its subsystem
|
||||||
|
/// is unreachable from a browser (e.g. forge not behind the gateway,
|
||||||
|
/// matrix GUI disabled). Backs `hivectl open` + the federation
|
||||||
|
/// peer-config block (which reads the bare `domain`).
|
||||||
|
Urls,
|
||||||
|
/// Fetch one job-queue DAG (plus its live fan-out children, linked
|
||||||
|
/// via `parent_id`) by id — the polling surface behind `hivectl`'s
|
||||||
|
/// wait/progress loop. Result: [`HostResponse::dags`].
|
||||||
|
QueueDag { id: u64 },
|
||||||
|
/// List pending approval requests.
|
||||||
|
Pending,
|
||||||
|
/// Approve a pending request by id; the action runs immediately.
|
||||||
|
Approve { id: i64 },
|
||||||
|
/// Deny a pending request by id.
|
||||||
|
Deny { id: i64 },
|
||||||
|
/// Move an agent in the topology tree. `new_parent = None`
|
||||||
|
/// promotes the agent to root, `Some(name)` sets a new parent.
|
||||||
|
/// Validation rules + bind-mount caveat documented in
|
||||||
|
/// `docs/agent-hierarchy.md::Current state`.
|
||||||
|
SetParent {
|
||||||
|
child: String,
|
||||||
|
new_parent: Option<String>,
|
||||||
|
},
|
||||||
|
/// Stop managed containers hive-wide in one operator action
|
||||||
|
/// (`hivectl stop`): agents plus the selected infra containers. `scope`
|
||||||
|
/// selects which classes; an all-false scope means **everything** (the
|
||||||
|
/// bare `hivectl stop`). `graceful` runs the per-agent quiesce (graceful
|
||||||
|
/// agent stop, issue tracker `graceful agent stop`) instead of a hard
|
||||||
|
/// stop. Agents stop via the lifecycle path; infra containers via the
|
||||||
|
/// host `container@<name>` units.
|
||||||
|
Stop {
|
||||||
|
#[serde(default)]
|
||||||
|
scope: LifecycleScope,
|
||||||
|
#[serde(default)]
|
||||||
|
graceful: bool,
|
||||||
|
},
|
||||||
|
/// Start managed containers hive-wide — the inverse of `Stop`
|
||||||
|
/// (`hivectl start`). Same `scope` semantics (all-false = everything);
|
||||||
|
/// no graceful flag (start is unconditional).
|
||||||
|
Start {
|
||||||
|
#[serde(default)]
|
||||||
|
scope: LifecycleScope,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Selects which container classes a hive-wide [`HostRequest::Stop`] /
|
||||||
|
/// [`HostRequest::Start`] touches. An all-false scope means **everything**
|
||||||
|
/// (the bare `hivectl stop` / `start`); set individual fields to restrict
|
||||||
|
/// (e.g. only `agents` → just the sub-agent containers). `agents` covers
|
||||||
|
/// every managed sub-agent container; the rest are the named infra
|
||||||
|
/// containers (`hive-ci`, `hive-forge`, `hive-gateway`, `hive-matrix`).
|
||||||
|
//
|
||||||
|
// A flat bag of independent flag toggles — one bool per selectable
|
||||||
|
// container class — is exactly the right shape here: each maps 1:1 to a
|
||||||
|
// `hivectl` `--ci` / `--forge` / `--gateway` / `--matrix` flag, and they're
|
||||||
|
// orthogonal (any subset is valid), so a state machine or two-variant enums
|
||||||
|
// would only obscure the mapping. Hence the `struct_excessive_bools` allow.
|
||||||
|
#[allow(clippy::struct_excessive_bools)]
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||||
|
pub struct LifecycleScope {
|
||||||
|
/// All sub-agent containers (`--agents`).
|
||||||
|
#[serde(default)]
|
||||||
|
pub agents: bool,
|
||||||
|
/// Specific sub-agents by logical name (`--agent <name>`, repeatable).
|
||||||
|
/// Additive with the rest of the scope; redundant when `agents` is set
|
||||||
|
/// (which already covers every sub-agent).
|
||||||
|
#[serde(default)]
|
||||||
|
pub agent_names: Vec<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub ci: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub forge: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub gateway: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub matrix: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LifecycleScope {
|
||||||
|
/// True when nothing is explicitly selected — interpreted as "all
|
||||||
|
/// classes" (the bare `hivectl stop` / `start` with no scope flags).
|
||||||
|
pub fn is_everything(&self) -> bool {
|
||||||
|
!(self.agents || self.ci || self.forge || self.gateway || self.matrix)
|
||||||
|
&& self.agent_names.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This hive's canonical domain plus the browser-facing URLs for its
|
||||||
|
/// web surfaces — the `Urls` request result. Every field is `None` when
|
||||||
|
/// the corresponding surface can't be reached from a browser (domain
|
||||||
|
/// unset, forge not behind the gateway, matrix GUI disabled), so the CLI
|
||||||
|
/// can give a precise hint instead of opening a dead link.
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||||
|
pub struct HiveUrls {
|
||||||
|
/// Canonical hive domain (`services.hyperhive.domain`).
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub domain: Option<String>,
|
||||||
|
/// Operator dashboard root (`https://<domain>/`).
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub home: Option<String>,
|
||||||
|
/// Forge browser URL (`HIVE_FORGE_PUBLIC_URL`) — only the
|
||||||
|
/// behind-gateway public URL; `None` on direct-port forge deploys.
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub forge: Option<String>,
|
||||||
|
/// Matrix GUI (fluffychat) browser URL — `None` when the matrix GUI
|
||||||
|
/// is disabled.
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub matrix: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||||
|
pub struct HostResponse {
|
||||||
|
pub ok: bool,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub error: Option<String>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub agents: Option<Vec<String>>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub approvals: Option<Vec<Approval>>,
|
||||||
|
/// `Urls` result — this hive's domain plus the browser-facing
|
||||||
|
/// home / forge / matrix URLs. `None` for every other request kind.
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub urls: Option<HiveUrls>,
|
||||||
|
/// `AgentStatus` result — one row per managed agent with its
|
||||||
|
/// running/health flags + technical state. `None` for every other
|
||||||
|
/// request kind.
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub agent_statuses: Option<Vec<AgentStatusRow>>,
|
||||||
|
/// Ids of the job-queue DAGs this request submitted (rebuild /
|
||||||
|
/// restart / power ops). Clients poll them via
|
||||||
|
/// [`HostRequest::QueueDag`]; `None` for non-submitting requests.
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub queued_dags: Option<Vec<u64>>,
|
||||||
|
/// `QueueDag` result — the requested DAG followed by its live
|
||||||
|
/// fan-out children ([`jobs::DagView`]). Empty when the DAG has
|
||||||
|
/// been evicted from the queue's history tail.
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub dags: Option<Vec<jobs::DagView>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HostResponse {
|
||||||
|
#[must_use]
|
||||||
|
pub fn success() -> Self {
|
||||||
|
Self {
|
||||||
|
ok: true,
|
||||||
|
..Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn error(message: impl Into<String>) -> Self {
|
||||||
|
Self {
|
||||||
|
ok: false,
|
||||||
|
error: Some(message.into()),
|
||||||
|
..Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn list(agents: Vec<String>) -> Self {
|
||||||
|
Self {
|
||||||
|
ok: true,
|
||||||
|
agents: Some(agents),
|
||||||
|
..Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn pending(approvals: Vec<Approval>) -> Self {
|
||||||
|
Self {
|
||||||
|
ok: true,
|
||||||
|
approvals: Some(approvals),
|
||||||
|
..Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Urls` result — this hive's domain + browser-facing web URLs.
|
||||||
|
#[must_use]
|
||||||
|
pub fn urls(urls: HiveUrls) -> Self {
|
||||||
|
Self {
|
||||||
|
ok: true,
|
||||||
|
urls: Some(urls),
|
||||||
|
..Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `AgentStatus` result — one row per managed agent.
|
||||||
|
#[must_use]
|
||||||
|
pub fn agent_statuses(rows: Vec<AgentStatusRow>) -> Self {
|
||||||
|
Self {
|
||||||
|
ok: true,
|
||||||
|
agent_statuses: Some(rows),
|
||||||
|
..Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A request that submitted job-queue DAGs — carries their ids for
|
||||||
|
/// the client's wait/progress loop.
|
||||||
|
#[must_use]
|
||||||
|
pub fn queued(ids: Vec<u64>) -> Self {
|
||||||
|
Self {
|
||||||
|
ok: true,
|
||||||
|
queued_dags: Some(ids),
|
||||||
|
..Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `QueueDag` result — the polled DAG + its live children.
|
||||||
|
#[must_use]
|
||||||
|
pub fn dags(dags: Vec<jobs::DagView>) -> Self {
|
||||||
|
Self {
|
||||||
|
ok: true,
|
||||||
|
dags: Some(dags),
|
||||||
|
..Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -19,192 +19,6 @@ pub mod wire_time;
|
||||||
/// constant instead of a scattered magic value.
|
/// constant instead of a scattered magic value.
|
||||||
pub const RECV_BATCH_MAX: u32 = 5;
|
pub const RECV_BATCH_MAX: u32 = 5;
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// Host admin socket — /run/hyperhive/host.sock
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/// Requests on the host admin socket.
|
|
||||||
///
|
|
||||||
/// Wire format: one JSON object per line.
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
#[serde(tag = "cmd", rename_all = "snake_case")]
|
|
||||||
pub enum HostRequest {
|
|
||||||
/// Create and start a sub-agent container directly, bypassing the
|
|
||||||
/// approval queue. Privileged-context only. See
|
|
||||||
/// `docs/approvals.md::Approval kinds (wire shapes)`.
|
|
||||||
Spawn { name: String },
|
|
||||||
/// Submit a spawn request for the operator to approve. See
|
|
||||||
/// `docs/approvals.md::Approval kinds (wire shapes)` (`Spawn`).
|
|
||||||
RequestSpawn { name: String },
|
|
||||||
/// Stop a managed container (graceful).
|
|
||||||
Kill { name: String },
|
|
||||||
/// Tear down a sub-agent container, optionally purging state.
|
|
||||||
/// See `docs/approvals.md::Destroy semantics`.
|
|
||||||
Destroy {
|
|
||||||
name: String,
|
|
||||||
#[serde(default)]
|
|
||||||
purge: bool,
|
|
||||||
},
|
|
||||||
/// Stop and start a managed container without rebuilding config.
|
|
||||||
/// For "kick the container" operations that don't touch the flake or
|
|
||||||
/// nspawn flags. Mirrors `lifecycle::restart` (kill + start).
|
|
||||||
Restart { name: String },
|
|
||||||
/// Stop and restart all managed containers in sequence. Convenience
|
|
||||||
/// wrapper for `hivectl agents restart-all`; iterates the live
|
|
||||||
/// container list and restarts each one.
|
|
||||||
RestartAll,
|
|
||||||
/// Apply pending config to a managed container.
|
|
||||||
Rebuild { name: String },
|
|
||||||
/// List managed containers.
|
|
||||||
List,
|
|
||||||
/// List managed agents with their full status + technical state
|
|
||||||
/// (running / needs-login / needs-update / deployed sha / parent /
|
|
||||||
/// pending reminders) — the `hivectl agents list` roster view.
|
|
||||||
/// Reuses the dashboard's per-agent `ContainerView` aggregation.
|
|
||||||
AgentStatus,
|
|
||||||
/// Report this hive's canonical DNS domain
|
|
||||||
/// (`services.hyperhive.domain`) plus the browser-facing home /
|
|
||||||
/// forge / matrix URLs, daemon-sourced so custom forge/matrix
|
|
||||||
/// domains resolve correctly. Each URL is `None` when its subsystem
|
|
||||||
/// is unreachable from a browser (e.g. forge not behind the gateway,
|
|
||||||
/// matrix GUI disabled). Backs `hivectl open` + the federation
|
|
||||||
/// peer-config block (which reads the bare `domain`).
|
|
||||||
Urls,
|
|
||||||
/// Fetch one job-queue DAG (plus its live fan-out children, linked
|
|
||||||
/// via `parent_id`) by id — the polling surface behind `hivectl`'s
|
|
||||||
/// wait/progress loop. Result: [`HostResponse::dags`].
|
|
||||||
QueueDag { id: u64 },
|
|
||||||
/// List pending approval requests.
|
|
||||||
Pending,
|
|
||||||
/// Approve a pending request by id; the action runs immediately.
|
|
||||||
Approve { id: i64 },
|
|
||||||
/// Deny a pending request by id.
|
|
||||||
Deny { id: i64 },
|
|
||||||
/// Move an agent in the topology tree. `new_parent = None`
|
|
||||||
/// promotes the agent to root, `Some(name)` sets a new parent.
|
|
||||||
/// Validation rules + bind-mount caveat documented in
|
|
||||||
/// `docs/agent-hierarchy.md::Current state`.
|
|
||||||
SetParent {
|
|
||||||
child: String,
|
|
||||||
new_parent: Option<String>,
|
|
||||||
},
|
|
||||||
/// Stop managed containers hive-wide in one operator action
|
|
||||||
/// (`hivectl stop`): agents plus the selected infra containers. `scope`
|
|
||||||
/// selects which classes; an all-false scope means **everything** (the
|
|
||||||
/// bare `hivectl stop`). `graceful` runs the per-agent quiesce (graceful
|
|
||||||
/// agent stop, issue tracker `graceful agent stop`) instead of a hard
|
|
||||||
/// stop. Agents stop via the lifecycle path; infra containers via the
|
|
||||||
/// host `container@<name>` units.
|
|
||||||
Stop {
|
|
||||||
#[serde(default)]
|
|
||||||
scope: LifecycleScope,
|
|
||||||
#[serde(default)]
|
|
||||||
graceful: bool,
|
|
||||||
},
|
|
||||||
/// Start managed containers hive-wide — the inverse of `Stop`
|
|
||||||
/// (`hivectl start`). Same `scope` semantics (all-false = everything);
|
|
||||||
/// no graceful flag (start is unconditional).
|
|
||||||
Start {
|
|
||||||
#[serde(default)]
|
|
||||||
scope: LifecycleScope,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Selects which container classes a hive-wide [`HostRequest::Stop`] /
|
|
||||||
/// [`HostRequest::Start`] touches. An all-false scope means **everything**
|
|
||||||
/// (the bare `hivectl stop` / `start`); set individual fields to restrict
|
|
||||||
/// (e.g. only `agents` → just the sub-agent containers). `agents` covers
|
|
||||||
/// every managed sub-agent container; the rest are the named infra
|
|
||||||
/// containers (`hive-ci`, `hive-forge`, `hive-gateway`, `hive-matrix`).
|
|
||||||
//
|
|
||||||
// A flat bag of independent flag toggles — one bool per selectable
|
|
||||||
// container class — is exactly the right shape here: each maps 1:1 to a
|
|
||||||
// `hivectl` `--ci` / `--forge` / `--gateway` / `--matrix` flag, and they're
|
|
||||||
// orthogonal (any subset is valid), so a state machine or two-variant enums
|
|
||||||
// would only obscure the mapping. Hence the `struct_excessive_bools` allow.
|
|
||||||
#[allow(clippy::struct_excessive_bools)]
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
|
||||||
pub struct LifecycleScope {
|
|
||||||
/// All sub-agent containers (`--agents`).
|
|
||||||
#[serde(default)]
|
|
||||||
pub agents: bool,
|
|
||||||
/// Specific sub-agents by logical name (`--agent <name>`, repeatable).
|
|
||||||
/// Additive with the rest of the scope; redundant when `agents` is set
|
|
||||||
/// (which already covers every sub-agent).
|
|
||||||
#[serde(default)]
|
|
||||||
pub agent_names: Vec<String>,
|
|
||||||
#[serde(default)]
|
|
||||||
pub ci: bool,
|
|
||||||
#[serde(default)]
|
|
||||||
pub forge: bool,
|
|
||||||
#[serde(default)]
|
|
||||||
pub gateway: bool,
|
|
||||||
#[serde(default)]
|
|
||||||
pub matrix: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LifecycleScope {
|
|
||||||
/// True when nothing is explicitly selected — interpreted as "all
|
|
||||||
/// classes" (the bare `hivectl stop` / `start` with no scope flags).
|
|
||||||
pub fn is_everything(&self) -> bool {
|
|
||||||
!(self.agents || self.ci || self.forge || self.gateway || self.matrix)
|
|
||||||
&& self.agent_names.is_empty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This hive's canonical domain plus the browser-facing URLs for its
|
|
||||||
/// web surfaces — the `Urls` request result. Every field is `None` when
|
|
||||||
/// the corresponding surface can't be reached from a browser (domain
|
|
||||||
/// unset, forge not behind the gateway, matrix GUI disabled), so the CLI
|
|
||||||
/// can give a precise hint instead of opening a dead link.
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
|
||||||
pub struct HiveUrls {
|
|
||||||
/// Canonical hive domain (`services.hyperhive.domain`).
|
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
||||||
pub domain: Option<String>,
|
|
||||||
/// Operator dashboard root (`https://<domain>/`).
|
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
||||||
pub home: Option<String>,
|
|
||||||
/// Forge browser URL (`HIVE_FORGE_PUBLIC_URL`) — only the
|
|
||||||
/// behind-gateway public URL; `None` on direct-port forge deploys.
|
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
||||||
pub forge: Option<String>,
|
|
||||||
/// Matrix GUI (fluffychat) browser URL — `None` when the matrix GUI
|
|
||||||
/// is disabled.
|
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
||||||
pub matrix: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
|
||||||
pub struct HostResponse {
|
|
||||||
pub ok: bool,
|
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
||||||
pub error: Option<String>,
|
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
||||||
pub agents: Option<Vec<String>>,
|
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
||||||
pub approvals: Option<Vec<Approval>>,
|
|
||||||
/// `Urls` result — this hive's domain plus the browser-facing
|
|
||||||
/// home / forge / matrix URLs. `None` for every other request kind.
|
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
||||||
pub urls: Option<HiveUrls>,
|
|
||||||
/// `AgentStatus` result — one row per managed agent with its
|
|
||||||
/// running/health flags + technical state. `None` for every other
|
|
||||||
/// request kind.
|
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
||||||
pub agent_statuses: Option<Vec<AgentStatusRow>>,
|
|
||||||
/// Ids of the job-queue DAGs this request submitted (rebuild /
|
|
||||||
/// restart / power ops). Clients poll them via
|
|
||||||
/// [`HostRequest::QueueDag`]; `None` for non-submitting requests.
|
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
||||||
pub queued_dags: Option<Vec<u64>>,
|
|
||||||
/// `QueueDag` result — the requested DAG followed by its live
|
|
||||||
/// fan-out children ([`jobs::DagView`]). Empty when the DAG has
|
|
||||||
/// been evicted from the queue's history tail.
|
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
||||||
pub dags: Option<Vec<jobs::DagView>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// One row in the approval queue. `commit_ref` is overloaded per
|
/// One row in the approval queue. `commit_ref` is overloaded per
|
||||||
/// `kind` — see `docs/approvals.md::Approval kinds (wire shapes)`
|
/// `kind` — see `docs/approvals.md::Approval kinds (wire shapes)`
|
||||||
/// for the encoding table and lifecycle.
|
/// for the encoding table and lifecycle.
|
||||||
|
|
@ -308,84 +122,6 @@ pub struct ReminderStats {
|
||||||
pub pending: u64,
|
pub pending: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HostResponse {
|
|
||||||
#[must_use]
|
|
||||||
pub fn success() -> Self {
|
|
||||||
Self {
|
|
||||||
ok: true,
|
|
||||||
..Self::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn error(message: impl Into<String>) -> Self {
|
|
||||||
Self {
|
|
||||||
ok: false,
|
|
||||||
error: Some(message.into()),
|
|
||||||
..Self::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn list(agents: Vec<String>) -> Self {
|
|
||||||
Self {
|
|
||||||
ok: true,
|
|
||||||
agents: Some(agents),
|
|
||||||
..Self::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn pending(approvals: Vec<Approval>) -> Self {
|
|
||||||
Self {
|
|
||||||
ok: true,
|
|
||||||
approvals: Some(approvals),
|
|
||||||
..Self::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `Urls` result — this hive's domain + browser-facing web URLs.
|
|
||||||
#[must_use]
|
|
||||||
pub fn urls(urls: HiveUrls) -> Self {
|
|
||||||
Self {
|
|
||||||
ok: true,
|
|
||||||
urls: Some(urls),
|
|
||||||
..Self::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `AgentStatus` result — one row per managed agent.
|
|
||||||
#[must_use]
|
|
||||||
pub fn agent_statuses(rows: Vec<AgentStatusRow>) -> Self {
|
|
||||||
Self {
|
|
||||||
ok: true,
|
|
||||||
agent_statuses: Some(rows),
|
|
||||||
..Self::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A request that submitted job-queue DAGs — carries their ids for
|
|
||||||
/// the client's wait/progress loop.
|
|
||||||
#[must_use]
|
|
||||||
pub fn queued(ids: Vec<u64>) -> Self {
|
|
||||||
Self {
|
|
||||||
ok: true,
|
|
||||||
queued_dags: Some(ids),
|
|
||||||
..Self::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `QueueDag` result — the polled DAG + its live children.
|
|
||||||
#[must_use]
|
|
||||||
pub fn dags(dags: Vec<jobs::DagView>) -> Self {
|
|
||||||
Self {
|
|
||||||
ok: true,
|
|
||||||
dags: Some(dags),
|
|
||||||
..Self::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Per-agent socket — /run/hyperhive/agents/<name>/mcp.sock on the host,
|
// Per-agent socket — /run/hyperhive/agents/<name>/mcp.sock on the host,
|
||||||
// bind-mounted into the container at /run/hive/mcp.sock.
|
// bind-mounted into the container at /run/hive/mcp.sock.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue