phase 8 step 3: needs-login partial-run mode + dashboard badge

This commit is contained in:
müde 2026-05-15 12:57:06 +02:00
parent c59fa8541c
commit 78fae44ee5
7 changed files with 191 additions and 11 deletions

View file

@ -223,9 +223,15 @@ fn render_containers(
);
} else if let Some(name) = container.strip_prefix(AGENT_PREFIX) {
let port = lifecycle::agent_web_port(name);
let claude_dir = Coordinator::agent_claude_dir(name);
let login_badge = if claude_has_session(&claude_dir) {
""
} else {
" <span class=\"role role-pending\">needs login</span>"
};
let _ = writeln!(
out,
"<li><span class=\"glyph\">▒░▒░░</span> <a href=\"http://{hostname}:{port}/\">{name}</a> <span class=\"role role-ag3nt\">ag3nt</span> <span class=\"meta\">{container} :{port}</span>\n <form method=\"POST\" action=\"/destroy/{name}\" class=\"inline\" onsubmit=\"return confirm('destroy {name}? container is removed; state + creds kept.');\"><button class=\"btn btn-destroy\" type=\"submit\">DESTR0Y</button></form>\n</li>",
"<li><span class=\"glyph\">▒░▒░░</span> <a href=\"http://{hostname}:{port}/\">{name}</a> <span class=\"role role-ag3nt\">ag3nt</span>{login_badge} <span class=\"meta\">{container} :{port}</span>\n <form method=\"POST\" action=\"/destroy/{name}\" class=\"inline\" onsubmit=\"return confirm('destroy {name}? container is removed; state + creds kept.');\"><button class=\"btn btn-destroy\" type=\"submit\">DESTR0Y</button></form>\n</li>",
);
}
}
@ -311,6 +317,19 @@ fn gc_orphans(coord: &Coordinator, approvals: Vec<Approval>) -> Vec<Approval> {
.collect()
}
/// Host-side mirror of `hive_ag3nt::login::has_session`. Returns true if the
/// agent's bound `~/.claude/` dir on disk contains any regular file. The
/// dashboard reads this each render so logins driven from the agent web UI
/// (Phase 8 step 4) reflect within one auto-refresh cycle.
fn claude_has_session(dir: &Path) -> bool {
let Ok(entries) = std::fs::read_dir(dir) else {
return false;
};
entries
.flatten()
.any(|e| e.file_type().is_ok_and(|t| t.is_file()))
}
async fn approval_diff(agent: &str, commit_ref: &str) -> String {
let proposed = Coordinator::agent_proposed_dir(agent);
if !proposed.exists() {