diff --git a/CLAUDE.md b/CLAUDE.md index e958146..ef10b1f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -271,7 +271,7 @@ See PLAN.md → "Phase 8" for the full design. Summary: login` badge in the container list. "Valid session" today is a heuristic (any regular file inside `/root/.claude/`); we may refine once the filename layout claude writes is locked in. -- **Login from the per-agent web UI.** Spawn `claude /login` with plain +- **Login from the per-agent web UI.** Spawn `claude auth login` with plain stdio pipes (no PTY initially), surface the OAuth URL from stdout on the page, accept the resulting code via a paste field, write it to the process stdin. Once `~/.claude/` populates, the existing needs-login polling loop diff --git a/PLAN.md b/PLAN.md index 8aecbbb..e0f8002 100644 --- a/PLAN.md +++ b/PLAN.md @@ -263,7 +263,7 @@ knows where to click. **Login over the per-agent web UI.** No more `nixos-container root-login` for the common case. The agent's web UI exposes a "log in" action that: -1. Spawns `claude /login` (or equivalent) inside the container with plain +1. Spawns `claude auth login` (or equivalent) inside the container with plain stdio pipes — no PTY unless we discover we need one. 2. Reads the OAuth URL from the process stdout and shows it on the page. 3. Provides a paste field for the resulting code; writes it to the process diff --git a/hive-ag3nt/src/bin/hive-ag3nt.rs b/hive-ag3nt/src/bin/hive-ag3nt.rs index 17d76fa..0dd9d5c 100644 --- a/hive-ag3nt/src/bin/hive-ag3nt.rs +++ b/hive-ag3nt/src/bin/hive-ag3nt.rs @@ -71,7 +71,7 @@ async fn main() -> Result<()> { // stays bound) but don't drive the turn loop. Poll the // claude dir periodically so a successful login (whether // from the dashboard PTY path in step 4 or via - // `root-login` + `claude /login` in the meantime) + // `root-login` + `claude auth login` in the meantime) // transitions us into the turn loop without a restart. needs_login_loop(&cli.socket, &claude_dir, login_state, poll_ms).await } diff --git a/hive-ag3nt/src/login.rs b/hive-ag3nt/src/login.rs index 6f8ae69..208045b 100644 --- a/hive-ag3nt/src/login.rs +++ b/hive-ag3nt/src/login.rs @@ -3,7 +3,7 @@ //! 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 /login` +//! 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. diff --git a/hive-ag3nt/src/login_session.rs b/hive-ag3nt/src/login_session.rs index 100b5b1..68aca36 100644 --- a/hive-ag3nt/src/login_session.rs +++ b/hive-ag3nt/src/login_session.rs @@ -1,4 +1,4 @@ -//! `claude /login` driver. Spawns the login command under plain stdio pipes, +//! `claude auth login` driver. Spawns the login command under plain stdio pipes, //! accumulates stdout+stderr in a shared buffer (so the web UI can show //! whatever URL/prompt claude emits), and writes paste-back codes from the //! UI into the child's stdin. @@ -16,7 +16,7 @@ use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; use tokio::process::{Child, ChildStdin, Command}; const DEFAULT_CMD: &str = "claude"; -const DEFAULT_ARGS: &[&str] = &["/login"]; +const DEFAULT_ARGS: &[&str] = &["auth", "login"]; #[derive(Default)] struct State { @@ -33,7 +33,7 @@ struct State { exit_note: Option, } -/// A running `claude /login` subprocess. +/// A running `claude auth login` subprocess. pub struct LoginSession { child: Mutex, /// Tokio mutex because we hold the guard across the `write_all().await` @@ -46,7 +46,7 @@ pub struct LoginSession { impl LoginSession { /// Spawn the login command. The exact binary/args are configurable via /// `HYPERHIVE_LOGIN_CMD` (single string, shell-split into argv); by - /// default we run `claude /login`. Failing to spawn returns an error + /// default we run `claude auth login`. Failing to spawn returns an error /// before any state is registered. pub fn start() -> Result { let (cmd, args) = resolve_command(); @@ -146,7 +146,7 @@ impl LoginSession { fn resolve_command() -> (String, Vec) { if let Ok(raw) = std::env::var("HYPERHIVE_LOGIN_CMD") { - // Whitespace-only split — no quote handling. Fine for "claude /login" + // Whitespace-only split — no quote handling. Fine for "claude auth login" // style overrides; if we need anything with embedded spaces we'll // switch to shell-words. let mut parts = raw.split_whitespace().map(str::to_owned); diff --git a/hive-ag3nt/src/web_ui.rs b/hive-ag3nt/src/web_ui.rs index e09adb2..689e8ea 100644 --- a/hive-ag3nt/src/web_ui.rs +++ b/hive-ag3nt/src/web_ui.rs @@ -71,7 +71,7 @@ fn render_online() -> String { } fn render_needs_login_idle() -> String { - "

▓█▓▒░ NEEDS L0G1N ▓█▓▒░

\n

No Claude session in ~/.claude/. The harness is up but the turn loop is paused until you log in.

\n
\n \n
\n

Spawns claude /login over plain stdio pipes. The OAuth URL will appear here when claude emits it; paste the resulting code back into the form below.

".into() + "

▓█▓▒░ NEEDS L0G1N ▓█▓▒░

\n

No Claude session in ~/.claude/. The harness is up but the turn loop is paused until you log in.

\n
\n \n
\n

Spawns claude auth login over plain stdio pipes. The OAuth URL will appear here when claude emits it; paste the resulting code back into the form below.

".into() } fn render_login_in_progress(session: &Arc) -> String {