From 7018ddafabe91dec42b5807b8f6020154bacd8ac Mon Sep 17 00:00:00 2001 From: "ilblackdragon@gmail.com" Date: Fri, 27 Mar 2026 23:11:46 -0700 Subject: [PATCH] fix(cli): show auth prompt in REPL when credential is missing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Co-Authored-By: Claude Opus 4.6 (1M context) --- src/bridge/router.rs | 26 ++++++++++++++++++++++++++ src/channels/repl.rs | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/src/bridge/router.rs b/src/bridge/router.rs index 2ef67022..783e0f80 100644 --- a/src/bridge/router.rs +++ b/src/bridge/router.rs @@ -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 " + .into(), + ), + auth_url: None, + setup_url: None, + }, + metadata, + ) + .await; + } } EventKind::StepCompleted { tokens, .. } => { let tok_msg = format!( diff --git a/src/channels/repl.rs b/src/channels/repl.rs index 25c8f33f..66fe1c23 100644 --- a/src/channels/repl.rs +++ b/src/channels/repl.rs @@ -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(()) }