hivectl: add start/stop agent verbs, rename spawn to create
This commit is contained in:
parent
e6ecd8db60
commit
3421925442
7 changed files with 127 additions and 35 deletions
|
|
@ -39,9 +39,10 @@ hand-maintained per-file tree drifts out of sync with the code.
|
|||
- **`hivectl/`** — standalone operator CLI (`hivectl` binary). Talks to
|
||||
the `hive-c0re` daemon over the host admin socket (`hive-host-sock`
|
||||
wire types) — does NOT link `hive-c0re`. Verbs: `list-agents`,
|
||||
`agent <name> <spawn|kill|destroy|rebuild|restart|set-parent|choom|
|
||||
watch|…>`, `approvals <pending|approve|deny>`, `forge`/`matrix`/
|
||||
`github`/`gateway` provisioning, `stop`/`start`, `wg`/`peer-config`.
|
||||
`agent <name> <start|create|request-create|stop|kill|destroy|rebuild|
|
||||
restart|set-parent|choom|watch|…>`, `approvals <pending|approve|deny>`,
|
||||
`forge`/`matrix`/`github`/`gateway` provisioning, `stop`/`start`,
|
||||
`wg`/`peer-config`.
|
||||
- **`hive-agent/`**, **`hive-agent-mcp/`** —
|
||||
in-container harness, two sibling crates for every agent (not a
|
||||
single `hive-ag3nt/` dir — that's the runtime/binary-family nickname,
|
||||
|
|
|
|||
|
|
@ -119,10 +119,10 @@ kind-specific payload carrier.
|
|||
- `Spawn` — direct container creation from the agent's config repo.
|
||||
`commit_ref` is empty. Submitted via `HostRequest::RequestSpawn`
|
||||
(operator-gated, the `◆ R3QU3ST SP4WN` dashboard button +
|
||||
`hivectl agent <name> request-spawn` CLI). The host-level `HostRequest::Spawn`
|
||||
`hivectl agent <name> request-create` CLI). The host-level `HostRequest::Spawn`
|
||||
variant bypasses the approval queue entirely — privileged-context use
|
||||
only (operator on the host shell, test scripts, one-off recoveries).
|
||||
This is the **canonical first-spawn**: a new agent's `InitConfig`
|
||||
only (operator on the host shell, test scripts, one-off recoveries;
|
||||
`hivectl agent <name> create`). This is the **canonical first-spawn**: a new agent's `InitConfig`
|
||||
seeds its config repo, the submitting agent customises it, then the
|
||||
operator spawns to create the container. Subsequent config changes go
|
||||
through a `MergeConfigPr` PR.
|
||||
|
|
|
|||
|
|
@ -24,8 +24,10 @@ This document contains the help content for the `hivectl` command-line program.
|
|||
* [`hivectl agent restart`↴](#hivectl-agent-restart)
|
||||
* [`hivectl agent pause`↴](#hivectl-agent-pause)
|
||||
* [`hivectl agent resume`↴](#hivectl-agent-resume)
|
||||
* [`hivectl agent spawn`↴](#hivectl-agent-spawn)
|
||||
* [`hivectl agent request-spawn`↴](#hivectl-agent-request-spawn)
|
||||
* [`hivectl agent start`↴](#hivectl-agent-start)
|
||||
* [`hivectl agent create`↴](#hivectl-agent-create)
|
||||
* [`hivectl agent request-create`↴](#hivectl-agent-request-create)
|
||||
* [`hivectl agent stop`↴](#hivectl-agent-stop)
|
||||
* [`hivectl agent kill`↴](#hivectl-agent-kill)
|
||||
* [`hivectl agent destroy`↴](#hivectl-agent-destroy)
|
||||
* [`hivectl agent rebuild`↴](#hivectl-agent-rebuild)
|
||||
|
|
@ -342,9 +344,11 @@ Everything here targets a single named agent (`hivectl agent foo restart`, `hive
|
|||
* `restart` — Stop and start this agent container without rebuilding config
|
||||
* `pause` — Park this agent's turn loop, leaving the container running
|
||||
* `resume` — Resume this paused agent — it drains whatever queued up while parked
|
||||
* `spawn` — Spawn this agent container directly, bypassing the approval queue
|
||||
* `request-spawn` — Queue a spawn request for operator approval
|
||||
* `kill` — Stop this managed container (graceful)
|
||||
* `start` — Start this EXISTING agent container. Fails immediately if `name` has no config/topology entry at all — it never attempts first-time creation. Use `create` for that
|
||||
* `create` — Create this agent container from scratch (full first-time provisioning), bypassing the approval queue
|
||||
* `request-create` — Queue a first-creation request for operator approval
|
||||
* `stop` — Gracefully stop this agent container: signal → drain → reconcile. Never escalates to a hard kill — use `kill` for that
|
||||
* `kill` — Hard-stop this managed container
|
||||
* `destroy` — Tear down this sub-agent container, keeping its state by default. No undo
|
||||
* `rebuild` — Apply pending config to this managed container
|
||||
* `set-parent` — Move this agent in the topology tree — under a new parent, or to root
|
||||
|
|
@ -390,27 +394,43 @@ Resume this paused agent — it drains whatever queued up while parked
|
|||
|
||||
|
||||
|
||||
## `hivectl agent spawn`
|
||||
## `hivectl agent start`
|
||||
|
||||
Spawn this agent container directly, bypassing the approval queue.
|
||||
Start this EXISTING agent container. Fails immediately if `name` has no config/topology entry at all — it never attempts first-time creation. Use `create` for that
|
||||
|
||||
Operator-on-the-host only; use `request-spawn` for an approval-gated spawn.
|
||||
|
||||
**Usage:** `hivectl agent spawn`
|
||||
**Usage:** `hivectl agent start`
|
||||
|
||||
|
||||
|
||||
## `hivectl agent request-spawn`
|
||||
## `hivectl agent create`
|
||||
|
||||
Queue a spawn request for operator approval
|
||||
Create this agent container from scratch (full first-time provisioning), bypassing the approval queue.
|
||||
|
||||
**Usage:** `hivectl agent request-spawn`
|
||||
Operator-on-the-host only; use `request-create` for an approval-gated creation.
|
||||
|
||||
**Usage:** `hivectl agent create`
|
||||
|
||||
|
||||
|
||||
## `hivectl agent request-create`
|
||||
|
||||
Queue a first-creation request for operator approval
|
||||
|
||||
**Usage:** `hivectl agent request-create`
|
||||
|
||||
|
||||
|
||||
## `hivectl agent stop`
|
||||
|
||||
Gracefully stop this agent container: signal → drain → reconcile. Never escalates to a hard kill — use `kill` for that
|
||||
|
||||
**Usage:** `hivectl agent stop`
|
||||
|
||||
|
||||
|
||||
## `hivectl agent kill`
|
||||
|
||||
Stop this managed container (graceful)
|
||||
Hard-stop this managed container
|
||||
|
||||
**Usage:** `hivectl agent kill`
|
||||
|
||||
|
|
|
|||
|
|
@ -99,14 +99,26 @@ pub enum ReconcileDirection {
|
|||
#[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)`.
|
||||
/// Create and start a brand-new sub-agent container directly (full
|
||||
/// first-time provisioning: proposed/applied repos, state subvolume,
|
||||
/// meta-flake sync, `nixos-container create`), bypassing the approval
|
||||
/// queue. Privileged-context only. Exposed on the CLI as `hivectl
|
||||
/// agent <name> create`. See `docs/approvals.md::Approval kinds
|
||||
/// (wire shapes)`. Wire name kept as `Spawn` (unrenamed underneath
|
||||
/// the CLI-verb rename — `hivectl agent <name> start` reuses the
|
||||
/// existing scope-based [`HostRequest::Start`] below instead of a
|
||||
/// new per-agent variant, see its doc comment).
|
||||
Spawn { name: Ident },
|
||||
/// Submit a spawn request for the operator to approve. See
|
||||
/// Submit a first-creation request for the operator to approve. See
|
||||
/// `docs/approvals.md::Approval kinds (wire shapes)` (`Spawn`).
|
||||
/// Exposed on the CLI as `hivectl agent <name> request-create`.
|
||||
RequestSpawn { name: Ident },
|
||||
/// Stop a managed container (graceful).
|
||||
/// Hard stop a managed container. Exposed on the CLI as `hivectl
|
||||
/// agent <name> kill` — kept distinct from the new graceful-only
|
||||
/// `hivectl agent <name> stop` (which reuses the scope-based
|
||||
/// [`HostRequest::Stop`] below with `graceful: true`); `Kill`
|
||||
/// escalates to a hard stop, `Stop` never does. Wire name kept as
|
||||
/// `Kill`.
|
||||
Kill { name: Ident },
|
||||
/// Tear down a sub-agent container, optionally purging state.
|
||||
/// See `docs/approvals.md::Destroy semantics`.
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ talk to the daemon over their own MCP tool surface
|
|||
One module per subcommand family; `main.rs` is just the clap parse +
|
||||
dispatch:
|
||||
|
||||
- **`agents.rs`** — container lifecycle (spawn/kill/rebuild/restart/…).
|
||||
- **`agents.rs`** — container lifecycle (start/stop/create/kill/rebuild/restart/…).
|
||||
- **`approvals.rs`** — the config/init-config/meta-input approval queue.
|
||||
- **`dag_progress.rs`** — rebuild-queue progress rendering.
|
||||
- **`power.rs`** — restart/start/stop at the container level.
|
||||
|
|
|
|||
|
|
@ -36,6 +36,55 @@ async fn agents_restart(socket: &Path, name: &str, no_wait: bool) -> Result<()>
|
|||
}
|
||||
}
|
||||
|
||||
/// `hivectl agent <name> start` — start an EXISTING agent container.
|
||||
/// Fails immediately (no request even sent) if `name` has no state dir
|
||||
/// at all, rather than silently resolving to an empty scope. Reuses the
|
||||
/// exact hive-wide `hivectl start` DAG (`HostRequest::Start`'s
|
||||
/// scope-based path), just scoped to this one name — a separate
|
||||
/// per-agent wire request would have duplicated logic this scope-based
|
||||
/// one already covers.
|
||||
async fn agents_start(socket: &Path, name: &str) -> Result<()> {
|
||||
if !crate::util::agent_exists(socket, name).await? {
|
||||
bail!(
|
||||
"no such agent: '{name}' (no state dir under {}/) — use 'hivectl agent {name} create' to provision a brand-new agent",
|
||||
hive_host_sock::AGENTS_ROOT
|
||||
);
|
||||
}
|
||||
render(
|
||||
crate::client::request(
|
||||
socket,
|
||||
HostRequest::Start {
|
||||
scope: hive_host_sock::LifecycleScope {
|
||||
agent_names: vec![name.to_owned()],
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
|
||||
/// `hivectl agent <name> stop` — graceful-only stop (signal → drain →
|
||||
/// reconcile), never escalating to a hard kill. Reuses the hive-wide
|
||||
/// `hivectl stop --graceful` DAG (`HostRequest::Stop`'s scope-based
|
||||
/// path), scoped to this one name. Distinct from `AgentCmd::Kill`, which
|
||||
/// hard-stops via a separate DAG that does escalate.
|
||||
async fn agents_stop(socket: &Path, name: &str) -> Result<()> {
|
||||
render(
|
||||
crate::client::request(
|
||||
socket,
|
||||
HostRequest::Stop {
|
||||
scope: hive_host_sock::LifecycleScope {
|
||||
agent_names: vec![name.to_owned()],
|
||||
..Default::default()
|
||||
},
|
||||
graceful: true,
|
||||
},
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
|
||||
/// `hivectl list-agents` — fetch the per-agent status roster from the
|
||||
/// daemon (`HostRequest::AgentStatus`) and render it as a padded table,
|
||||
/// or the raw JSON rows with `--json`. Reuses the dashboard's
|
||||
|
|
@ -149,14 +198,16 @@ pub(crate) async fn run_agent(socket: &Path, name: &str, cmd: AgentCmd) -> Resul
|
|||
AgentCmd::Restart { no_wait } => agents_restart(socket, name, no_wait).await,
|
||||
AgentCmd::Pause => set_paused(socket, name, true).await,
|
||||
AgentCmd::Resume => set_paused(socket, name, false).await,
|
||||
AgentCmd::Spawn => {
|
||||
AgentCmd::Start => agents_start(socket, name).await,
|
||||
AgentCmd::Create => {
|
||||
let name = crate::util::parse_ident(name)?;
|
||||
render(crate::client::request(socket, HostRequest::Spawn { name }).await?)
|
||||
}
|
||||
AgentCmd::RequestSpawn => {
|
||||
AgentCmd::RequestCreate => {
|
||||
let name = crate::util::parse_ident(name)?;
|
||||
render(crate::client::request(socket, HostRequest::RequestSpawn { name }).await?)
|
||||
}
|
||||
AgentCmd::Stop => agents_stop(socket, name).await,
|
||||
AgentCmd::Kill => {
|
||||
let name = crate::util::parse_ident(name)?;
|
||||
render(crate::client::request(socket, HostRequest::Kill { name }).await?)
|
||||
|
|
|
|||
|
|
@ -468,14 +468,22 @@ pub enum AgentCmd {
|
|||
Pause,
|
||||
/// Resume this paused agent — it drains whatever queued up while parked.
|
||||
Resume,
|
||||
/// Spawn this agent container directly, bypassing the approval queue.
|
||||
/// Start this EXISTING agent container. Fails immediately if `name`
|
||||
/// has no config/topology entry at all — it never attempts
|
||||
/// first-time creation. Use `create` for that.
|
||||
Start,
|
||||
/// Create this agent container from scratch (full first-time
|
||||
/// provisioning), bypassing the approval queue.
|
||||
///
|
||||
/// Operator-on-the-host only; use `request-spawn` for an approval-gated
|
||||
/// spawn.
|
||||
Spawn,
|
||||
/// Queue a spawn request for operator approval.
|
||||
RequestSpawn,
|
||||
/// Stop this managed container (graceful).
|
||||
/// Operator-on-the-host only; use `request-create` for an
|
||||
/// approval-gated creation.
|
||||
Create,
|
||||
/// Queue a first-creation request for operator approval.
|
||||
RequestCreate,
|
||||
/// Gracefully stop this agent container: signal → drain → reconcile.
|
||||
/// Never escalates to a hard kill — use `kill` for that.
|
||||
Stop,
|
||||
/// Hard-stop this managed container.
|
||||
Kill,
|
||||
/// Tear down this sub-agent container, keeping its state by default.
|
||||
/// No undo.
|
||||
|
|
|
|||
Loading…
Reference in a new issue