fix(agent): key session detection off credential filenames; own clear_session in login.rs

This commit is contained in:
müde 2026-07-05 21:48:56 +02:00
commit d83d03e4c0
2 changed files with 107 additions and 65 deletions

View file

@ -74,17 +74,9 @@ pub(super) async fn post_login_cancel(State(state): State<AppState>) -> Response
(axum::http::StatusCode::OK, "ok").into_response()
}
/// OAuth credential filenames inside `paths::claude_dir()`. Wiping
/// only these (and not the rest of `~/.claude/`) preserves session
/// history so `claude --continue` keeps working after a fresh login.
/// Rationale + the previous wholesale-wipe shape we replaced live in
/// [`docs/web-ui/agent.md::Per-agent endpoints`](../../../docs/web-ui/agent.md)
/// (the `/api/logout` bullet).
const CRED_FILE_NAMES: &[&str] = &[".credentials.json", "mcp-needs-auth-cache.json"];
/// Operator-driven `/logout`: SIGINT claude, delete the credential
/// files in `CRED_FILE_NAMES`, flip `LoginState::NeedsLogin`. The
/// turn loop's next iteration parks into `wait_for_login` which
/// files (via [`crate::login::clear_session`]), flip `LoginState::NeedsLogin`.
/// The turn loop's next iteration parks into `wait_for_login` which
/// resumes when a fresh credentials file appears via `/login/code`.
/// Always returns 200 with a body describing what happened. See
/// [`docs/web-ui/agent.md::Per-agent endpoints`](../../../docs/web-ui/agent.md)
@ -96,32 +88,19 @@ pub(super) async fn post_logout(State(state): State<AppState>) -> Response {
.args(["-INT", "claude"])
.output()
.await;
// Step 2: delete OAuth credential files only — preserve session
// history files alongside them.
// Step 2: delete OAuth credential files only — login::clear_session owns
// the file set and preserves session-history files alongside them.
let dir = crate::paths::claude_dir();
let mut warnings: Vec<String> = Vec::new();
let mut wiped: Vec<&str> = Vec::new();
for name in CRED_FILE_NAMES {
let path = dir.join(name);
match tokio::fs::remove_file(&path).await {
Ok(()) => wiped.push(name),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
// Already gone — operator clicked /logout while
// already logged out, or the file simply didn't exist
// for this agent. Idempotent.
}
Err(e) => warnings.push(format!("{name}: {e}")),
}
}
let wipe_summary = if wiped.is_empty() {
let cleared = crate::login::clear_session(&dir).await;
let wipe_summary = if cleared.wiped.is_empty() {
"no credential files present (already logged out)".to_owned()
} else {
format!("wiped {}", wiped.join(", "))
format!("wiped {}", cleared.wiped.join(", "))
};
let warn_suffix = if warnings.is_empty() {
let warn_suffix = if cleared.warnings.is_empty() {
String::new()
} else {
format!(" (warnings: {})", warnings.join("; "))
format!(" (warnings: {})", cleared.warnings.join("; "))
};
// Step 3: flip LoginState + emit Note. Turn loop sees the flip on
// its next iteration and parks into wait_for_login.