hivectl: rename hivectl agents to hivectl agent <name> <verb>

This commit is contained in:
damocles 2026-07-27 19:00:44 +02:00 committed by mara
commit 03afbd1316
19 changed files with 367 additions and 525 deletions

View file

@ -17,8 +17,9 @@ recovery / debugging verbs.\
)]
pub struct Cli {
/// Path to the hive-c0re host admin socket, used by the daemon-assisted
/// verbs (`agents`, `stop`, `start`). Global: accepted before or after
/// the subcommand. Verbs that don't talk to the daemon ignore it.
/// verbs (`agent`, `list-agents`, `stop`, `start`). Global: accepted
/// before or after the subcommand. Verbs that don't talk to the daemon
/// ignore it.
#[arg(long, global = true, default_value = DEFAULT_HOST_SOCKET)]
pub(crate) socket: PathBuf,
#[command(subcommand)]
@ -61,14 +62,36 @@ pub enum Cmd {
#[command(subcommand)]
cmd: GatewayCmd,
},
/// Agent container management.
/// Lifecycle actions on ONE managed agent container. Needs the
/// hive-c0re daemon running.
///
/// Lifecycle actions on managed agent containers. Needs the hive-c0re
/// daemon running.
Agents {
/// Everything here targets a single named agent (`hivectl agent foo
/// restart`, `hivectl agent foo choom`, …) — anything that acts
/// hive-wide lives at the top level instead (`list-agents`,
/// `restart`/`stop`/`start` with a scope, `quota-enable`).
Agent {
/// Agent name (e.g. `damocles`, `iris`).
name: String,
#[command(subcommand)]
cmd: AgentsCmd,
cmd: AgentCmd,
},
/// Show all managed agents with their status and technical state.
///
/// Global — not scoped to one agent, so it lives at the top level
/// rather than under `hivectl agent <name>`. Needs the hive-c0re
/// daemon running.
ListAgents {
/// Emit the raw JSON rows instead of the padded table (for
/// scripting). The table is the default human-readable shape.
#[arg(long)]
json: bool,
},
/// Enable btrfs qgroup accounting on the agent-state filesystem.
///
/// Global one-shot toggle (not per-agent), hence top-level rather than
/// under `hivectl agent <name>`. Idempotent — safe to re-run. Once
/// enabled, `hivectl agent <name> quota show`/`quota set` work.
QuotaEnable,
/// Operator approval queue: list, approve, or deny pending requests.
///
/// Needs the hive-c0re daemon running.
@ -98,22 +121,6 @@ pub enum Cmd {
#[arg(long)]
wg_endpoint: Option<String>,
},
/// Open an interactive Claude session inside an agent container.
///
/// A fresh session by default, or resume a prior one. Requires root
/// and a running container.
Choom {
/// Agent name (e.g. `damocles`, `iris`).
name: String,
/// Resume a prior claude session by its session id, passed
/// through as `claude --resume <value>` (claude's `--continue`
/// takes no value — it resumes the cwd's latest session, which
/// is the harness's, so choom never uses it; this flag matches
/// the claude flag it maps to). Omit for a fresh blank session.
/// A value is required when the flag is given.
#[arg(long = "resume", value_name = "SESSION")]
resume_session: Option<String>,
},
/// Stop containers hive-wide in one operator action.
///
/// Bare `hivectl stop` stops everything; scope flags narrow it to
@ -435,128 +442,68 @@ pub enum WgCmd {
Status,
}
#[derive(Subcommand)]
pub enum QuotaCmd {
/// Enable btrfs qgroup accounting on the agent-state filesystem.
///
/// Run once before `show` / `limit`. No-op on non-btrfs hosts.
Enable,
/// Report per-agent disk usage from btrfs qgroups (all agents, or one
/// by name).
Show {
/// Agent to show (omit for all agents with a state subvolume).
name: Option<String>,
},
/// Set or clear an agent's disk-usage quota.
///
/// Named `set` rather than `set-quota` because the enclosing `quota`
/// group already carries the noun — `agents quota set iris 5G`. The
/// `set-<noun>` spelling stays for the flat verbs (`set-parent`,
/// `set-limits`), which have no group to inherit it from.
Set {
/// Agent whose state subvolume to limit.
name: String,
/// Size cap (`5G`, `500M`, `1073741824`) or `none` to clear.
size: String,
},
}
// Default host admin socket path. Shared with `hive-c0re`'s `main.rs`
// default via `hive_host_sock::HOST_SOCKET` — the daemon binds there
// and `hivectl agents` connects to it.
// and `hivectl` connects to it.
pub(crate) use hive_host_sock::HOST_SOCKET as DEFAULT_HOST_SOCKET;
/// Verbs under `hivectl agent <name> <verb>` — every one of these targets
/// the single agent named on the parent command, so none of them carry
/// their own `name` field.
#[derive(Subcommand)]
pub enum AgentsCmd {
/// Show all managed agents with their status and technical state.
List {
/// Emit the raw JSON rows instead of the padded table (for
/// scripting). The table is the default human-readable shape.
#[arg(long)]
json: bool,
},
/// Stop and start a single agent container without rebuilding config.
pub enum AgentCmd {
/// Stop and start this agent container without rebuilding config.
Restart {
/// Agent name (e.g. `damocles`, `ruth`).
name: String,
/// Return immediately after the restart DAG is queued.
#[arg(long)]
no_wait: bool,
},
/// Restart all managed agent containers.
RestartAll {
/// Return immediately after the restart DAGs are queued.
#[arg(long)]
no_wait: bool,
},
/// Park an agent's turn loop, leaving the container running.
/// Park this agent's turn loop, leaving the container running.
///
/// The harness stops driving turns but keeps serving its web UI and
/// MCP daemons, so the container, its mounts and its warm caches stay
/// up while it burns no tokens. Inbox messages queue unacked and the
/// backlog drains on `resume`. Sticky: it survives a restart, and
/// pausing a stopped agent makes it come up paused.
Pause {
/// Agent name.
name: String,
},
/// Resume a paused agent — it drains whatever queued up while parked.
Resume {
/// Agent name.
name: String,
},
/// Spawn a new agent container directly, bypassing the approval queue.
Pause,
/// Resume this paused agent — it drains whatever queued up while parked.
Resume,
/// Spawn this agent container directly, bypassing the approval queue.
///
/// Operator-on-the-host only; use `request-spawn` for an approval-gated
/// spawn.
Spawn {
/// Agent name (e.g. `iris`).
name: String,
},
Spawn,
/// Queue a spawn request for operator approval.
RequestSpawn {
/// Agent name.
name: String,
},
/// Stop a managed container (graceful).
Kill {
/// Agent name.
name: String,
},
/// Tear down a sub-agent container, keeping its state by default. No
/// undo.
RequestSpawn,
/// Stop this managed container (graceful).
Kill,
/// Tear down this sub-agent container, keeping its state by default.
/// No undo.
Destroy {
/// Agent name.
name: String,
/// Also wipe the agent's state dirs (config + creds + notes).
#[arg(long)]
purge: bool,
},
/// Apply pending config to a managed container.
Rebuild {
/// Agent name.
name: String,
},
/// Move an agent in the topology tree — under a new parent, or to root.
/// Apply pending config to this managed container.
Rebuild,
/// Move this agent in the topology tree — under a new parent, or to
/// root.
SetParent {
/// Agent to move.
child: String,
/// New parent agent name. Mutually exclusive with `--root`.
#[arg(long, conflicts_with = "root", required_unless_present = "root")]
parent: Option<String>,
/// Promote `child` to root (no parent).
/// Promote this agent to root (no parent).
#[arg(long)]
root: bool,
},
/// Declare an agent's CPU/memory limits, overriding the hive-wide defaults.
/// Declare this agent's CPU/memory limits, overriding the hive-wide
/// defaults.
///
/// Replaces the agent's whole override entry rather than merging into
/// it: any limit you don't pass returns to the hive-wide default. To
/// change one and keep the other, pass both. Disk is a separate
/// resource with its own group — see `agents quota`.
/// resource with its own group — see `quota`.
SetLimits {
/// Agent name.
name: String,
/// systemd `CPUQuota=` value, e.g. `400%` (100% = one full core).
#[arg(long, conflicts_with = "reset")]
cpu_quota: Option<String>,
@ -573,15 +520,29 @@ pub enum AgentsCmd {
)]
reset: bool,
},
/// Per-agent disk accounting + optional quotas via btrfs qgroups.
/// Open an interactive Claude session inside this agent's container.
///
/// Opt-in: enable qgroup accounting, then report per-agent usage or
/// cap an agent. No-op on non-btrfs hosts.
/// A fresh session by default, or resume a prior one. Requires root
/// and a running container.
Choom {
/// Resume a prior claude session by its session id, passed
/// through as `claude --resume <value>` (claude's `--continue`
/// takes no value — it resumes the cwd's latest session, which
/// is the harness's, so choom never uses it; this flag matches
/// the claude flag it maps to). Omit for a fresh blank session.
/// A value is required when the flag is given.
#[arg(long = "resume", value_name = "SESSION")]
resume_session: Option<String>,
},
/// This agent's disk accounting + optional quota via btrfs qgroups.
///
/// Needs `hivectl quota-enable` run once hive-wide first. No-op on
/// non-btrfs hosts.
Quota {
#[command(subcommand)]
cmd: QuotaCmd,
cmd: AgentQuotaCmd,
},
/// btrfs subvolume management for agent state dirs.
/// btrfs subvolume management for this agent's state dir.
///
/// Upgrade an existing plain-dir agent's state into a btrfs subvolume
/// so it gains snapshots and per-subvol usage/quota.
@ -591,6 +552,17 @@ pub enum AgentsCmd {
},
}
#[derive(Subcommand)]
pub enum AgentQuotaCmd {
/// Report this agent's disk usage from btrfs qgroups.
Show,
/// Set or clear this agent's disk-usage quota.
Set {
/// Size cap (`5G`, `500M`, `1073741824`) or `none` to clear.
size: String,
},
}
/// Operator approval queue: list, approve, or deny pending requests.
#[derive(Subcommand)]
pub enum ApprovalsCmd {
@ -615,14 +587,12 @@ pub enum SubvolCmd {
///
/// Bounces the agent to migrate its state, so it requires `--yes`.
Upgrade {
/// Agent name (e.g. `damocles`, `iris`).
name: String,
/// Confirm: this stops the agent, migrates its state dir, and
/// restarts it. Required — the command refuses without it.
#[arg(long)]
yes: bool,
},
/// Read-only snapshots of an agent's state subvolume.
/// Read-only snapshots of this agent's state subvolume.
Snapshot {
#[command(subcommand)]
cmd: SnapshotCmd,
@ -633,8 +603,6 @@ pub enum SubvolCmd {
pub enum SnapshotCmd {
/// Create a read-only snapshot (agent must already be a subvolume).
Create {
/// Agent name (e.g. `damocles`, `iris`).
name: String,
/// Snapshot label. Mandatory, and must start with `hive-` — the
/// prefix doubles as an allow-list hive-priv checks so only
/// hivectl-issued snapshot names can reach the `btrfs subvolume
@ -644,8 +612,6 @@ pub enum SnapshotCmd {
},
/// Delete a snapshot created by `subvol snapshot create`.
Delete {
/// Agent name the snapshot belongs to.
name: String,
/// Snapshot label passed to `subvol snapshot create --label`.
label: String,
},
@ -655,8 +621,6 @@ pub enum SnapshotCmd {
/// point-in-time backup: a full send with no `--parent` produces a
/// self-contained archive of the snapshot.
Send {
/// Agent name the snapshot belongs to.
name: String,
/// Snapshot label passed to `subvol snapshot create --label`.
label: String,
/// Optional parent snapshot label for an incremental send