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(()) }