Compare commits
1 changed files with 6 additions and 25 deletions
|
|
@ -933,27 +933,8 @@ fn human_bytes(n: u64) -> String {
|
||||||
/// state dir (not the live container list) so kept-state tombstones
|
/// state dir (not the live container list) so kept-state tombstones
|
||||||
/// still resolve as agents — re-provisioning a destroyed-but-kept agent
|
/// still resolve as agents — re-provisioning a destroyed-but-kept agent
|
||||||
/// should still drop its token in the existing state tree.
|
/// should still drop its token in the existing state tree.
|
||||||
///
|
fn is_agent(name: &str) -> bool {
|
||||||
/// Uses `try_exists()` rather than `Path::exists()` so a permission
|
Coordinator::agent_state_root(name).exists()
|
||||||
/// error reaching the agents root is surfaced, not collapsed into
|
|
||||||
/// `false`. The agents root is `0700 hive-core`, so running hivectl
|
|
||||||
/// without root yields EACCES on traversal — `Path::exists()` would
|
|
||||||
/// silently report `false`, which callers turn into a misleading "no
|
|
||||||
/// such agent" (or, for the create-user paths, a silent misclassify of
|
|
||||||
/// a real agent as a non-agent account). Mapping EACCES to an explicit
|
|
||||||
/// "needs root" error fixes that first-run footgun, where running a
|
|
||||||
/// privileged verb without sudo reported as a missing agent.
|
|
||||||
fn agent_exists(name: &str) -> Result<bool> {
|
|
||||||
let root = Coordinator::agent_state_root(name);
|
|
||||||
match root.try_exists() {
|
|
||||||
Ok(found) => Ok(found),
|
|
||||||
Err(e) if e.kind() == std::io::ErrorKind::PermissionDenied => bail!(
|
|
||||||
"cannot read the agents root {} (permission denied) - this command needs root; \
|
|
||||||
re-run with sudo",
|
|
||||||
root.parent().unwrap_or(&root).display()
|
|
||||||
),
|
|
||||||
Err(e) => Err(e).with_context(|| format!("check agent state dir {}", root.display())),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Drop into an interactive Claude session in the agent container.
|
/// Drop into an interactive Claude session in the agent container.
|
||||||
|
|
@ -992,7 +973,7 @@ fn agent_exists(name: &str) -> Result<bool> {
|
||||||
/// `machinectl shell` inherits the caller's PTY, so the session is
|
/// `machinectl shell` inherits the caller's PTY, so the session is
|
||||||
/// fully interactive. Requires root and a running container.
|
/// fully interactive. Requires root and a running container.
|
||||||
fn choom(name: &str, fresh: bool) -> Result<()> {
|
fn choom(name: &str, fresh: bool) -> Result<()> {
|
||||||
if !agent_exists(name)? {
|
if !is_agent(name) {
|
||||||
bail!("no such agent: '{name}' (no state dir under /var/lib/hyperhive/agents/)");
|
bail!("no such agent: '{name}' (no state dir under /var/lib/hyperhive/agents/)");
|
||||||
}
|
}
|
||||||
let container = hive_c0re::lifecycle::container_name(name);
|
let container = hive_c0re::lifecycle::container_name(name);
|
||||||
|
|
@ -1042,7 +1023,7 @@ async fn forge_create_user(name: &str, password: Option<&str>, password_stdin: b
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let user_password = resolve_password(password, password_stdin)?;
|
let user_password = resolve_password(password, password_stdin)?;
|
||||||
if agent_exists(name)? {
|
if is_agent(name) {
|
||||||
if user_password.is_some() {
|
if user_password.is_some() {
|
||||||
bail!(
|
bail!(
|
||||||
"forge create-user: --password / --password-stdin is for non-agent (operator) accounts only; '{name}' is an agent which authenticates via API token"
|
"forge create-user: --password / --password-stdin is for non-agent (operator) accounts only; '{name}' is an agent which authenticates via API token"
|
||||||
|
|
@ -1114,7 +1095,7 @@ async fn matrix_create_user(
|
||||||
.build()
|
.build()
|
||||||
.context("build reqwest client")?;
|
.context("build reqwest client")?;
|
||||||
let user_password = resolve_password(password, password_stdin)?;
|
let user_password = resolve_password(password, password_stdin)?;
|
||||||
if agent_exists(name)? {
|
if is_agent(name) {
|
||||||
if user_password.is_some() {
|
if user_password.is_some() {
|
||||||
// The boot-sweep / approval-time agent provisioning path
|
// The boot-sweep / approval-time agent provisioning path
|
||||||
// doesn't accept a password — agents auth by access_token,
|
// doesn't accept a password — agents auth by access_token,
|
||||||
|
|
@ -1440,7 +1421,7 @@ fn single_agent_scope(name: &str) -> hive_sh4re::LifecycleScope {
|
||||||
/// regardless of the migration outcome so a failed migration never leaves
|
/// regardless of the migration outcome so a failed migration never leaves
|
||||||
/// the agent down; the migration error (if any) is surfaced afterwards.
|
/// the agent down; the migration error (if any) is surfaced afterwards.
|
||||||
async fn subvol_upgrade(socket: &Path, name: &str, yes: bool) -> Result<()> {
|
async fn subvol_upgrade(socket: &Path, name: &str, yes: bool) -> Result<()> {
|
||||||
if !agent_exists(name)? {
|
if !is_agent(name) {
|
||||||
bail!("no agent named {name:?} (no state dir under the agents root)");
|
bail!("no agent named {name:?} (no state dir under the agents root)");
|
||||||
}
|
}
|
||||||
if !yes {
|
if !yes {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue