hyperhive/hive-ag3nt/src/login.rs

52 lines
1.7 KiB
Rust

//! Login-state probe for the bind-mounted `~/.claude/` dir. The dir is
//! provided by hive-c0re (Phase 8 step 1) and persists across container
//! destroy/recreate so OAuth tokens survive.
//!
//! "Has session" today means "the dir contains at least one regular file."
//! That's a heuristic: a fresh bind-mount starts empty, and `claude auth login`
//! writes credentials into the dir. We may refine later (probe for the
//! specific credentials filename, or run a no-op `claude` call) once the
//! exact layout is locked in.
use std::path::Path;
/// Mount point of the per-agent Claude credentials dir inside the container.
/// Matches `hive_c0re::lifecycle::CONTAINER_CLAUDE_MOUNT`.
pub const DEFAULT_CLAUDE_DIR: &str = "/root/.claude";
/// Returns `true` if `dir` exists and contains any regular file. Used at
/// startup to decide whether to enter the turn loop (logged in) or stay in
/// the partial-run "needs login" state.
#[must_use]
pub fn has_session(dir: &Path) -> bool {
let Ok(entries) = std::fs::read_dir(dir) else {
return false;
};
for entry in entries.flatten() {
if entry.file_type().is_ok_and(|t| t.is_file()) {
return true;
}
}
false
}
/// Login state the harness reports to its web UI.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LoginState {
/// `~/.claude/` has credentials; turn loop is running.
Online,
/// `~/.claude/` is empty; harness is up, web UI is bound, turn loop is NOT
/// running. Operator needs to complete login from the web UI.
NeedsLogin,
}
impl LoginState {
#[must_use]
pub fn from_dir(dir: &Path) -> Self {
if has_session(dir) {
Self::Online
} else {
Self::NeedsLogin
}
}
}