Add PKCE support for Tetrate Agent Router Service (#4165)

Signed-off-by: John Landa <jonathanlanda@gmail.com>
Co-authored-by: Michael Neale <michael.neale@gmail.com>
This commit is contained in:
John Landa
2025-08-21 08:04:09 +02:00
committed by GitHub
parent 69ce34a828
commit e86c7ddb19
16 changed files with 1342 additions and 97 deletions
+123
View File
@@ -72,6 +72,11 @@ pub async fn handle_configure() -> Result<(), Box<dyn Error>> {
"OpenRouter Login (Recommended)",
"Sign in with OpenRouter to automatically configure models",
)
.item(
"tetrate",
"Tetrate Agent Router Service Login",
"Sign in with Tetrate Agent Router Service to automatically configure models",
)
.item(
"manual",
"Manual Configuration",
@@ -95,6 +100,21 @@ pub async fn handle_configure() -> Result<(), Box<dyn Error>> {
}
}
}
"tetrate" => {
match handle_tetrate_auth().await {
Ok(_) => {
// Tetrate auth already handles everything including enabling developer extension
}
Err(e) => {
let _ = config.clear();
println!(
"\n {} Tetrate Agent Router Service authentication failed: {} \n Please try again or use manual configuration",
style("Error").red().italic(),
e,
);
}
}
}
"manual" => {
match configure_provider_dialog().await {
Ok(true) => {
@@ -1770,6 +1790,109 @@ pub async fn handle_openrouter_auth() -> Result<(), Box<dyn Error>> {
Ok(())
}
/// Handle Tetrate Agent Router Service authentication
pub async fn handle_tetrate_auth() -> Result<(), Box<dyn Error>> {
use goose::config::{configure_tetrate, signup_tetrate::TetrateAuth};
use goose::conversation::message::Message;
use goose::providers::create;
// Use the Tetrate Agent Router Service authentication flow
let mut auth_flow = TetrateAuth::new()?;
match auth_flow.complete_flow().await {
Ok(api_key) => {
println!("\nAuthentication complete!");
let config = Config::global();
// Use the existing configure_tetrate function to set everything up
println!("\nConfiguring Tetrate Agent Router Service...");
if let Err(e) = configure_tetrate(config, api_key) {
eprintln!("Failed to configure Tetrate Agent Router Service: {}", e);
return Err(e.into());
}
println!("✓ Tetrate Agent Router Service configuration complete");
println!("✓ Models configured successfully");
// Test configuration - get the model that was configured
println!("\nTesting configuration...");
let configured_model: String = config.get_param("GOOSE_MODEL")?;
let model_config = match goose::model::ModelConfig::new(&configured_model) {
Ok(config) => config,
Err(e) => {
eprintln!("⚠️ Invalid model configuration: {}", e);
eprintln!(
"Your settings have been saved. Please check your model configuration."
);
return Ok(());
}
};
match create("tetrate", model_config) {
Ok(provider) => {
// Simple test request
let test_result = provider
.complete(
"You are Goose, an AI assistant.",
&[Message::user().with_text("Say 'Configuration test successful!'")],
&[],
)
.await;
match test_result {
Ok(_) => {
println!("✓ Configuration test passed!");
// Enable the developer extension by default if not already enabled
let entries = ExtensionConfigManager::get_all()?;
let has_developer = entries
.iter()
.any(|e| e.config.name() == "developer" && e.enabled);
if !has_developer {
match ExtensionConfigManager::set(ExtensionEntry {
enabled: true,
config: ExtensionConfig::Builtin {
name: "developer".to_string(),
display_name: Some(
goose::config::DEFAULT_DISPLAY_NAME.to_string(),
),
timeout: Some(goose::config::DEFAULT_EXTENSION_TIMEOUT),
bundled: Some(true),
description: None,
available_tools: Vec::new(),
},
}) {
Ok(_) => println!("✓ Developer extension enabled"),
Err(e) => {
eprintln!("⚠️ Failed to enable developer extension: {}", e)
}
}
}
cliclack::outro("Tetrate Agent Router Service setup complete! You can now use Goose.")?;
}
Err(e) => {
eprintln!("⚠️ Configuration test failed: {}", e);
eprintln!("Your settings have been saved, but there may be an issue with the connection.");
}
}
}
Err(e) => {
eprintln!("⚠️ Failed to create provider for testing: {}", e);
eprintln!("Your settings have been saved. Please check your configuration.");
}
}
}
Err(e) => {
eprintln!("Authentication failed: {}", e);
return Err(e.into());
}
}
Ok(())
}
fn add_provider() -> Result<(), Box<dyn Error>> {
let provider_type = cliclack::select("What type of API is this?")
.item(