//! 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 } } }