fix(cli): show auth prompt in REPL when credential is missing

The AuthRequired SSE event was emitted but only reached the web gateway.
The REPL never saw it because it receives events through
forward_event_to_channel which converts ThreadEvents to StatusUpdates.

Fix: when forward_event_to_channel sees an ActionFailed with
"authentication_required" in the error, emit StatusUpdate::AuthRequired
to the channel. Also add AuthRequired/AuthCompleted rendering to the
REPL (was missing — fell through to unmatched arm).

CLI now shows:
  ⚿ Authentication required: github_token
    Store the credential with: ironclaw secret set <name> <value>

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
This commit is contained in:
2026-03-27 23:11:46 -07:00
co-authored by Claude Opus 4.6
parent 84b182b7e2
commit 7018ddafab
2 changed files with 62 additions and 0 deletions
+26
View File
@@ -1223,6 +1223,32 @@ async fn forward_event_to_channel(
metadata,
)
.await;
// When the HTTP tool fails with authentication_required, show the
// auth prompt in the CLI/REPL so the user can authenticate.
if error.contains("authentication_required") {
let cred_name = error
.split("credential '")
.nth(1)
.and_then(|s| s.split('\'').next())
.unwrap_or("unknown")
.to_string();
let _ = channels
.send_status(
channel_name,
StatusUpdate::AuthRequired {
extension_name: cred_name,
instructions: Some(
"Store the credential with: ironclaw secret set <name> <value>"
.into(),
),
auth_url: None,
setup_url: None,
},
metadata,
)
.await;
}
}
EventKind::StepCompleted { tokens, .. } => {
let tok_msg = format!(
+36
View File
@@ -887,6 +887,42 @@ impl Channel for ReplChannel {
);
}
}
StatusUpdate::AuthRequired {
extension_name,
instructions,
auth_url,
..
} => {
eprintln!();
eprintln!(
" \x1b[33m\u{26BF} Authentication required: {}\x1b[0m",
extension_name
);
if let Some(url) = auth_url {
eprintln!(" \x1b[36mAuth URL: {}\x1b[0m", url);
}
if let Some(instr) = instructions {
eprintln!(" \x1b[90m{}\x1b[0m", instr);
}
eprintln!();
}
StatusUpdate::AuthCompleted {
extension_name,
success,
message,
} => {
if success {
eprintln!(
" \x1b[32m\u{2713} {} authenticated: {}\x1b[0m",
extension_name, message
);
} else {
eprintln!(
" \x1b[31m\u{2717} {} auth failed: {}\x1b[0m",
extension_name, message
);
}
}
}
Ok(())
}