Dont exit silently when storing api key fails (#5260)

Co-authored-by: Douwe Osinga <douwe@squareup.com>
This commit is contained in:
Douwe Osinga
2025-10-28 15:28:31 -04:00
committed by GitHub
parent 4034f3fcef
commit 41e5c053db
3 changed files with 185 additions and 210 deletions
+17 -27
View File
@@ -78,19 +78,19 @@ async fn get_or_create_session_id(
} }
let Some(id) = identifier else { let Some(id) = identifier else {
if resume { return if resume {
let sessions = SessionManager::list_sessions().await?; let sessions = SessionManager::list_sessions().await?;
let session_id = sessions let session_id = sessions
.first() .first()
.map(|s| s.id.clone()) .map(|s| s.id.clone())
.ok_or_else(|| anyhow::anyhow!("No session found to resume"))?; .ok_or_else(|| anyhow::anyhow!("No session found to resume"))?;
return Ok(Some(session_id)); Ok(Some(session_id))
} else { } else {
let session = let session =
SessionManager::create_session(std::env::current_dir()?, "CLI Session".to_string()) SessionManager::create_session(std::env::current_dir()?, "CLI Session".to_string())
.await?; .await?;
return Ok(Some(session.id)); Ok(Some(session.id))
} };
}; };
if let Some(session_id) = id.session_id { if let Some(session_id) = id.session_id {
@@ -818,7 +818,7 @@ pub struct RecipeInfo {
pub retry_config: Option<goose::agents::types::RetryConfig>, pub retry_config: Option<goose::agents::types::RetryConfig>,
} }
pub async fn cli() -> Result<()> { pub async fn cli() -> anyhow::Result<()> {
let cli = Cli::parse(); let cli = Cli::parse();
if let Err(e) = crate::project_tracker::update_project_tracker(None, None) { if let Err(e) = crate::project_tracker::update_project_tracker(None, None) {
@@ -850,20 +850,17 @@ pub async fn cli() -> Result<()> {
match cli.command { match cli.command {
Some(Command::Configure {}) => { Some(Command::Configure {}) => {
let _ = handle_configure().await; handle_configure().await?;
return Ok(());
} }
Some(Command::Info { verbose }) => { Some(Command::Info { verbose }) => {
handle_info(verbose)?; handle_info(verbose)?;
return Ok(());
} }
Some(Command::Mcp { name }) => { Some(Command::Mcp { name }) => {
crate::logging::setup_logging(Some(&format!("mcp-{name}")), None)?; crate::logging::setup_logging(Some(&format!("mcp-{name}")), None)?;
let _ = goose_mcp::mcp_server_runner::run_mcp_server(&name).await; goose_mcp::mcp_server_runner::run_mcp_server(&name).await?;
} }
Some(Command::Acp {}) => { Some(Command::Acp {}) => {
let _ = run_acp_agent().await; run_acp_agent().await?;
return Ok(());
} }
Some(Command::Session { Some(Command::Session {
command, command,
@@ -884,13 +881,9 @@ pub async fn cli() -> Result<()> {
ascending, ascending,
working_dir, working_dir,
limit, limit,
}) => { }) => Ok(handle_session_list(format, ascending, working_dir, limit).await?),
handle_session_list(format, ascending, working_dir, limit).await?;
Ok(())
}
Some(SessionCommand::Remove { id, regex }) => { Some(SessionCommand::Remove { id, regex }) => {
handle_session_remove(id, regex).await?; Ok(handle_session_remove(id, regex).await?)
return Ok(());
} }
Some(SessionCommand::Export { Some(SessionCommand::Export {
identifier, identifier,
@@ -1183,7 +1176,7 @@ pub async fn cli() -> Result<()> {
.await; .await;
if interactive { if interactive {
let _ = session.interactive(input_config.contents).await; session.interactive(input_config.contents).await?;
} else if let Some(contents) = input_config.contents { } else if let Some(contents) = input_config.contents {
let session_start = std::time::Instant::now(); let session_start = std::time::Instant::now();
let session_type = if recipe_info.is_some() { let session_type = if recipe_info.is_some() {
@@ -1237,8 +1230,9 @@ pub async fn cli() -> Result<()> {
result?; result?;
} else { } else {
eprintln!("Error: no text provided for prompt in headless mode"); return Err(anyhow::anyhow!(
std::process::exit(1); "no text provided for prompt in headless mode"
));
} }
return Ok(()); return Ok(());
@@ -1290,8 +1284,7 @@ pub async fn cli() -> Result<()> {
BenchCommand::Selectors { config } => BenchRunner::list_selectors(config)?, BenchCommand::Selectors { config } => BenchRunner::list_selectors(config)?,
BenchCommand::InitConfig { name } => { BenchCommand::InitConfig { name } => {
let mut config = BenchRunConfig::default(); let mut config = BenchRunConfig::default();
let cwd = let cwd = std::env::current_dir()?;
std::env::current_dir().expect("Failed to get current working directory");
config.output_dir = Some(cwd); config.output_dir = Some(cwd);
config.save(name); config.save(name);
} }
@@ -1334,7 +1327,7 @@ pub async fn cli() -> Result<()> {
} }
None => { None => {
return if !Config::global().exists() { return if !Config::global().exists() {
let _ = handle_configure().await; handle_configure().await?;
Ok(()) Ok(())
} else { } else {
// Run session command by default // Run session command by default
@@ -1364,10 +1357,7 @@ pub async fn cli() -> Result<()> {
retry_config: None, retry_config: None,
}) })
.await; .await;
if let Err(e) = session.interactive(None).await { session.interactive(None).await?;
eprintln!("Session ended with error: {}", e);
std::process::exit(1);
}
Ok(()) Ok(())
}; };
} }
+168 -182
View File
@@ -11,7 +11,10 @@ use goose::config::extensions::{
name_to_key, remove_extension, set_extension, set_extension_enabled, name_to_key, remove_extension, set_extension, set_extension_enabled,
}; };
use goose::config::permission::PermissionLevel; use goose::config::permission::PermissionLevel;
use goose::config::{Config, ConfigError, ExperimentManager, ExtensionEntry, PermissionManager}; use goose::config::signup_tetrate::TetrateAuth;
use goose::config::{
configure_tetrate, Config, ConfigError, ExperimentManager, ExtensionEntry, PermissionManager,
};
use goose::conversation::message::Message; use goose::conversation::message::Message;
use goose::model::ModelConfig; use goose::model::ModelConfig;
use goose::providers::{create, providers}; use goose::providers::{create, providers};
@@ -19,13 +22,12 @@ use rmcp::model::{Tool, ToolAnnotations};
use rmcp::object; use rmcp::object;
use serde_json::Value; use serde_json::Value;
use std::collections::HashMap; use std::collections::HashMap;
use std::error::Error;
// useful for light themes where there is no dicernible colour contrast between // useful for light themes where there is no dicernible colour contrast between
// cursor-selected and cursor-unselected items. // cursor-selected and cursor-unselected items.
const MULTISELECT_VISIBILITY_HINT: &str = "<"; const MULTISELECT_VISIBILITY_HINT: &str = "<";
pub async fn handle_configure() -> Result<(), Box<dyn Error>> { pub async fn handle_configure() -> anyhow::Result<()> {
let config = Config::global(); let config = Config::global();
if !config.exists() { if !config.exists() {
@@ -235,8 +237,8 @@ pub async fn handle_configure() -> Result<(), Box<dyn Error>> {
"toggle" => toggle_extensions_dialog(), "toggle" => toggle_extensions_dialog(),
"add" => configure_extensions_dialog(), "add" => configure_extensions_dialog(),
"remove" => remove_extension_dialog(), "remove" => remove_extension_dialog(),
"settings" => configure_settings_dialog().await.and(Ok(())), "settings" => configure_settings_dialog().await,
"providers" => configure_provider_dialog().await.and(Ok(())), "providers" => configure_provider_dialog().await.map(|_| ()),
"custom_providers" => configure_custom_provider_dialog(), "custom_providers" => configure_custom_provider_dialog(),
_ => unreachable!(), _ => unreachable!(),
} }
@@ -244,10 +246,7 @@ pub async fn handle_configure() -> Result<(), Box<dyn Error>> {
} }
/// Helper function to handle OAuth configuration for a provider /// Helper function to handle OAuth configuration for a provider
async fn handle_oauth_configuration( async fn handle_oauth_configuration(provider_name: &str, key_name: &str) -> anyhow::Result<()> {
provider_name: &str,
key_name: &str,
) -> Result<(), Box<dyn Error>> {
let _ = cliclack::log::info(format!( let _ = cliclack::log::info(format!(
"Configuring {} using OAuth device code flow...", "Configuring {} using OAuth device code flow...",
key_name key_name
@@ -263,17 +262,24 @@ async fn handle_oauth_configuration(
} }
Err(e) => { Err(e) => {
let _ = cliclack::log::error(format!("Failed to authenticate: {}", e)); let _ = cliclack::log::error(format!("Failed to authenticate: {}", e));
Err(format!("OAuth authentication failed for {}: {}", key_name, e).into()) Err(anyhow::anyhow!(
"OAuth authentication failed for {}: {}",
key_name,
e
))
} }
}, },
Err(e) => { Err(e) => {
let _ = cliclack::log::error(format!("Failed to create provider for OAuth: {}", e)); let _ = cliclack::log::error(format!("Failed to create provider for OAuth: {}", e));
Err(format!("Failed to create provider for OAuth: {}", e).into()) Err(anyhow::anyhow!(
"Failed to create provider for OAuth: {}",
e
))
} }
} }
} }
fn interactive_model_search(models: &[String]) -> Result<String, Box<dyn Error>> { fn interactive_model_search(models: &[String]) -> anyhow::Result<String> {
const MAX_VISIBLE: usize = 30; const MAX_VISIBLE: usize = 30;
let mut query = String::new(); let mut query = String::new();
@@ -355,7 +361,7 @@ fn interactive_model_search(models: &[String]) -> Result<String, Box<dyn Error>>
fn select_model_from_list( fn select_model_from_list(
models: &[String], models: &[String],
provider_meta: &goose::providers::base::ProviderMetadata, provider_meta: &goose::providers::base::ProviderMetadata,
) -> Result<String, Box<dyn std::error::Error>> { ) -> anyhow::Result<String> {
const MAX_MODELS: usize = 10; const MAX_MODELS: usize = 10;
// Smart model selection: // Smart model selection:
// If we have more than MAX_MODELS models, show the recommended models with additional search option. // If we have more than MAX_MODELS models, show the recommended models with additional search option.
@@ -411,8 +417,20 @@ fn select_model_from_list(
} }
} }
/// Dialog for configuring the A provider and model fn try_store_secret(config: &Config, key_name: &str, value: String) -> anyhow::Result<bool> {
pub async fn configure_provider_dialog() -> Result<bool, Box<dyn Error>> { match config.set_secret(key_name, Value::String(value)) {
Ok(_) => Ok(true),
Err(e) => {
cliclack::outro(style(format!(
"Failed to store {} securely: {}. Please ensure your system's secure storage is accessible. Alternatively you can run with GOOSE_DISABLE_KEYRING=true or set the key in your environment variables",
key_name, e
)).on_red().white())?;
Ok(false)
}
}
}
pub async fn configure_provider_dialog() -> anyhow::Result<bool> {
// Get global config instance // Get global config instance
let config = Config::global(); let config = Config::global();
@@ -462,7 +480,9 @@ pub async fn configure_provider_dialog() -> Result<bool, Box<dyn Error>> {
.interact()? .interact()?
{ {
if key.secret { if key.secret {
config.set_secret(&key.name, Value::String(env_value))?; if !try_store_secret(config, &key.name, env_value)? {
return Ok(false);
}
} else { } else {
config.set_param(&key.name, Value::String(env_value))?; config.set_param(&key.name, Value::String(env_value))?;
} }
@@ -502,7 +522,9 @@ pub async fn configure_provider_dialog() -> Result<bool, Box<dyn Error>> {
}; };
if key.secret { if key.secret {
config.set_secret(&key.name, Value::String(value))?; if !try_store_secret(config, &key.name, value)? {
return Ok(false);
}
} else { } else {
config.set_param(&key.name, Value::String(value))?; config.set_param(&key.name, Value::String(value))?;
} }
@@ -510,7 +532,6 @@ pub async fn configure_provider_dialog() -> Result<bool, Box<dyn Error>> {
} }
} }
Err(_) => { Err(_) => {
// Check if this key uses OAuth flow
if key.oauth_flow { if key.oauth_flow {
handle_oauth_configuration(provider_name, &key.name).await?; handle_oauth_configuration(provider_name, &key.name).await?;
} else { } else {
@@ -640,7 +661,7 @@ pub async fn configure_provider_dialog() -> Result<bool, Box<dyn Error>> {
/// Configure extensions that can be used with goose /// Configure extensions that can be used with goose
/// Dialog for toggling which extensions are enabled/disabled /// Dialog for toggling which extensions are enabled/disabled
pub fn toggle_extensions_dialog() -> Result<(), Box<dyn Error>> { pub fn toggle_extensions_dialog() -> anyhow::Result<()> {
let extensions = get_all_extensions(); let extensions = get_all_extensions();
if extensions.is_empty() { if extensions.is_empty() {
@@ -692,7 +713,7 @@ pub fn toggle_extensions_dialog() -> Result<(), Box<dyn Error>> {
Ok(()) Ok(())
} }
pub fn configure_extensions_dialog() -> Result<(), Box<dyn Error>> { pub fn configure_extensions_dialog() -> anyhow::Result<()> {
let extension_type = cliclack::select("What type of extension would you like to add?") let extension_type = cliclack::select("What type of extension would you like to add?")
.item( .item(
"built-in", "built-in",
@@ -1105,7 +1126,7 @@ pub fn configure_extensions_dialog() -> Result<(), Box<dyn Error>> {
Ok(()) Ok(())
} }
pub fn remove_extension_dialog() -> Result<(), Box<dyn Error>> { pub fn remove_extension_dialog() -> anyhow::Result<()> {
let extensions = get_all_extensions(); let extensions = get_all_extensions();
// Create a list of extension names and their enabled status // Create a list of extension names and their enabled status
@@ -1160,7 +1181,7 @@ pub fn remove_extension_dialog() -> Result<(), Box<dyn Error>> {
Ok(()) Ok(())
} }
pub async fn configure_settings_dialog() -> Result<(), Box<dyn Error>> { pub async fn configure_settings_dialog() -> anyhow::Result<()> {
let setting_type = cliclack::select("What setting would you like to configure?") let setting_type = cliclack::select("What setting would you like to configure?")
.item("goose_mode", "goose mode", "Configure goose mode") .item("goose_mode", "goose mode", "Configure goose mode")
.item( .item(
@@ -1231,7 +1252,7 @@ pub async fn configure_settings_dialog() -> Result<(), Box<dyn Error>> {
Ok(()) Ok(())
} }
pub fn configure_goose_mode_dialog() -> Result<(), Box<dyn Error>> { pub fn configure_goose_mode_dialog() -> anyhow::Result<()> {
let config = Config::global(); let config = Config::global();
// Check if GOOSE_MODE is set as an environment variable // Check if GOOSE_MODE is set as an environment variable
@@ -1284,7 +1305,7 @@ pub fn configure_goose_mode_dialog() -> Result<(), Box<dyn Error>> {
Ok(()) Ok(())
} }
pub fn configure_goose_router_strategy_dialog() -> Result<(), Box<dyn Error>> { pub fn configure_goose_router_strategy_dialog() -> anyhow::Result<()> {
let config = Config::global(); let config = Config::global();
let enable_router = cliclack::select("Would you like to enable smart tool routing?") let enable_router = cliclack::select("Would you like to enable smart tool routing?")
@@ -1314,7 +1335,7 @@ pub fn configure_goose_router_strategy_dialog() -> Result<(), Box<dyn Error>> {
Ok(()) Ok(())
} }
pub fn configure_tool_output_dialog() -> Result<(), Box<dyn Error>> { pub fn configure_tool_output_dialog() -> anyhow::Result<()> {
let config = Config::global(); let config = Config::global();
// Check if GOOSE_CLI_MIN_PRIORITY is set as an environment variable // Check if GOOSE_CLI_MIN_PRIORITY is set as an environment variable
if std::env::var("GOOSE_CLI_MIN_PRIORITY").is_ok() { if std::env::var("GOOSE_CLI_MIN_PRIORITY").is_ok() {
@@ -1347,7 +1368,7 @@ pub fn configure_tool_output_dialog() -> Result<(), Box<dyn Error>> {
/// Configure experiment features that can be used with goose /// Configure experiment features that can be used with goose
/// Dialog for toggling which experiments are enabled/disabled /// Dialog for toggling which experiments are enabled/disabled
pub fn toggle_experiments_dialog() -> Result<(), Box<dyn Error>> { pub fn toggle_experiments_dialog() -> anyhow::Result<()> {
let experiments = ExperimentManager::get_all()?; let experiments = ExperimentManager::get_all()?;
if experiments.is_empty() { if experiments.is_empty() {
@@ -1385,7 +1406,7 @@ pub fn toggle_experiments_dialog() -> Result<(), Box<dyn Error>> {
Ok(()) Ok(())
} }
pub async fn configure_tool_permissions_dialog() -> Result<(), Box<dyn Error>> { pub async fn configure_tool_permissions_dialog() -> anyhow::Result<()> {
let mut extensions: Vec<String> = get_enabled_extensions() let mut extensions: Vec<String> = get_enabled_extensions()
.into_iter() .into_iter()
.map(|ext| ext.name().clone()) .map(|ext| ext.name().clone())
@@ -1537,7 +1558,7 @@ pub async fn configure_tool_permissions_dialog() -> Result<(), Box<dyn Error>> {
Ok(()) Ok(())
} }
fn configure_recipe_dialog() -> Result<(), Box<dyn Error>> { fn configure_recipe_dialog() -> anyhow::Result<()> {
let key_name = GOOSE_RECIPE_GITHUB_REPO_CONFIG_KEY; let key_name = GOOSE_RECIPE_GITHUB_REPO_CONFIG_KEY;
let config = Config::global(); let config = Config::global();
let default_recipe_repo = std::env::var(key_name) let default_recipe_repo = std::env::var(key_name)
@@ -1559,7 +1580,7 @@ fn configure_recipe_dialog() -> Result<(), Box<dyn Error>> {
Ok(()) Ok(())
} }
fn configure_scheduler_dialog() -> Result<(), Box<dyn Error>> { fn configure_scheduler_dialog() -> anyhow::Result<()> {
let config = Config::global(); let config = Config::global();
// Check if GOOSE_SCHEDULER_TYPE is set as an environment variable // Check if GOOSE_SCHEDULER_TYPE is set as an environment variable
@@ -1613,7 +1634,7 @@ fn configure_scheduler_dialog() -> Result<(), Box<dyn Error>> {
Ok(()) Ok(())
} }
pub fn configure_max_turns_dialog() -> Result<(), Box<dyn Error>> { pub fn configure_max_turns_dialog() -> anyhow::Result<()> {
let config = Config::global(); let config = Config::global();
let current_max_turns: u32 = config.get_param("GOOSE_MAX_TURNS").unwrap_or(1000); let current_max_turns: u32 = config.get_param("GOOSE_MAX_TURNS").unwrap_or(1000);
@@ -1646,205 +1667,170 @@ pub fn configure_max_turns_dialog() -> Result<(), Box<dyn Error>> {
} }
/// Handle OpenRouter authentication /// Handle OpenRouter authentication
pub async fn handle_openrouter_auth() -> Result<(), Box<dyn Error>> { pub async fn handle_openrouter_auth() -> anyhow::Result<()> {
use goose::config::{configure_openrouter, signup_openrouter::OpenRouterAuth}; use goose::config::{configure_openrouter, signup_openrouter::OpenRouterAuth};
use goose::conversation::message::Message; use goose::conversation::message::Message;
use goose::providers::create; use goose::providers::create;
// Use the OpenRouter authentication flow // Use the OpenRouter authentication flow
let mut auth_flow = OpenRouterAuth::new()?; let mut auth_flow = OpenRouterAuth::new()?;
match auth_flow.complete_flow().await { let api_key = auth_flow.complete_flow().await?;
Ok(api_key) => { println!("\nAuthentication complete!");
println!("\nAuthentication complete!");
// Get config instance // Get config instance
let config = Config::global(); let config = Config::global();
// Use the existing configure_openrouter function to set everything up // Use the existing configure_openrouter function to set everything up
println!("\nConfiguring OpenRouter..."); println!("\nConfiguring OpenRouter...");
if let Err(e) = configure_openrouter(config, api_key) { configure_openrouter(config, api_key)?;
eprintln!("Failed to configure OpenRouter: {}", e);
return Err(e.into());
}
println!("✓ OpenRouter configuration complete"); println!("✓ OpenRouter configuration complete");
println!("✓ Models configured successfully"); println!("✓ Models configured successfully");
// Test configuration - get the model that was configured // Test configuration - get the model that was configured
println!("\nTesting configuration..."); println!("\nTesting configuration...");
let configured_model: String = config.get_param("GOOSE_MODEL")?; let configured_model: String = config.get_param("GOOSE_MODEL")?;
let model_config = match goose::model::ModelConfig::new(&configured_model) { let model_config = match goose::model::ModelConfig::new(&configured_model) {
Ok(config) => config, Ok(config) => config,
Err(e) => { Err(e) => {
eprintln!("⚠️ Invalid model configuration: {}", e); eprintln!("⚠️ Invalid model configuration: {}", e);
eprintln!( eprintln!("Your settings have been saved. Please check your model configuration.");
"Your settings have been saved. Please check your model configuration." return Ok(());
); }
return Ok(()); };
}
};
match create("openrouter", model_config).await { match create("openrouter", model_config).await {
Ok(provider) => { Ok(provider) => {
// Simple test request // Simple test request
let test_result = provider let test_result = provider
.complete( .complete(
"You are goose, an AI assistant.", "You are goose, an AI assistant.",
&[Message::user().with_text("Say 'Configuration test successful!'")], &[Message::user().with_text("Say 'Configuration test successful!'")],
&[], &[],
) )
.await; .await;
match test_result { match test_result {
Ok(_) => { Ok(_) => {
println!("✓ Configuration test passed!"); println!("✓ Configuration test passed!");
// Enable the developer extension by default if not already enabled // Enable the developer extension by default if not already enabled
let entries = get_all_extensions(); let entries = get_all_extensions();
let has_developer = entries let has_developer = entries
.iter() .iter()
.any(|e| e.config.name() == "developer" && e.enabled); .any(|e| e.config.name() == "developer" && e.enabled);
if !has_developer { if !has_developer {
set_extension(ExtensionEntry { set_extension(ExtensionEntry {
enabled: true, enabled: true,
config: ExtensionConfig::Builtin { config: ExtensionConfig::Builtin {
name: "developer".to_string(), name: "developer".to_string(),
display_name: Some( display_name: Some(goose::config::DEFAULT_DISPLAY_NAME.to_string()),
goose::config::DEFAULT_DISPLAY_NAME.to_string(), timeout: Some(goose::config::DEFAULT_EXTENSION_TIMEOUT),
), bundled: Some(true),
timeout: Some(goose::config::DEFAULT_EXTENSION_TIMEOUT), description: "Developer extension".to_string(),
bundled: Some(true), available_tools: Vec::new(),
description: "Developer extension".to_string(), },
available_tools: Vec::new(), });
}, println!("✓ Developer extension enabled");
});
println!("✓ Developer extension enabled");
}
cliclack::outro("OpenRouter 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.");
}
} }
cliclack::outro("OpenRouter setup complete! You can now use goose.")?;
} }
Err(e) => { Err(e) => {
eprintln!("⚠️ Failed to create provider for testing: {}", e); eprintln!("⚠️ Configuration test failed: {}", e);
eprintln!("Your settings have been saved. Please check your configuration."); eprintln!("Your settings have been saved, but there may be an issue with the connection.");
} }
} }
} }
Err(e) => { Err(e) => {
eprintln!("Authentication failed: {}", e); eprintln!("⚠️ Failed to create provider for testing: {}", e);
return Err(e.into()); eprintln!("Your settings have been saved. Please check your configuration.");
} }
} }
Ok(()) Ok(())
} }
/// Handle Tetrate Agent Router Service authentication pub async fn handle_tetrate_auth() -> anyhow::Result<()> {
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()?; let mut auth_flow = TetrateAuth::new()?;
match auth_flow.complete_flow().await { let api_key = auth_flow.complete_flow().await?;
Ok(api_key) => {
println!("\nAuthentication complete!");
let config = Config::global(); println!("\nAuthentication complete!");
// Use the existing configure_tetrate function to set everything up let config = Config::global();
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!("\nConfiguring Tetrate Agent Router Service...");
println!("✓ Models configured successfully"); configure_tetrate(config, api_key)?;
// Test configuration - get the model that was configured println!("✓ Tetrate Agent Router Service configuration complete");
println!("\nTesting configuration..."); println!("✓ Models configured successfully");
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).await { // Test configuration
Ok(provider) => { println!("\nTesting configuration...");
// Simple test request let configured_model: String = config.get_param("GOOSE_MODEL")?;
let test_result = provider let model_config = match goose::model::ModelConfig::new(&configured_model) {
.complete( Ok(config) => config,
"You are goose, an AI assistant.", Err(e) => {
&[Message::user().with_text("Say 'Configuration test successful!'")], eprintln!("⚠️ Invalid model configuration: {}", e);
&[], eprintln!("Your settings have been saved. Please check your model configuration.");
) return Ok(());
.await; }
};
match test_result { match create("tetrate", model_config).await {
Ok(_) => { Ok(provider) => {
println!("✓ Configuration test passed!"); let test_result = provider
.complete(
"You are goose, an AI assistant.",
&[Message::user().with_text("Say 'Configuration test successful!'")],
&[],
)
.await;
// Enable the developer extension by default if not already enabled match test_result {
let entries = get_all_extensions(); Ok(_) => {
let has_developer = entries println!("✓ Configuration test passed!");
.iter()
.any(|e| e.config.name() == "developer" && e.enabled);
if !has_developer { let entries = get_all_extensions();
set_extension(ExtensionEntry { let has_developer = entries
enabled: true, .iter()
config: ExtensionConfig::Builtin { .any(|e| e.config.name() == "developer" && e.enabled);
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: "Developer extension".to_string(),
available_tools: Vec::new(),
},
});
println!("✓ Developer extension enabled");
}
cliclack::outro("Tetrate Agent Router Service setup complete! You can now use goose.")?; if !has_developer {
} set_extension(ExtensionEntry {
Err(e) => { enabled: true,
eprintln!("⚠️ Configuration test failed: {}", e); config: ExtensionConfig::Builtin {
eprintln!("Your settings have been saved, but there may be an issue with the connection."); 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: "Developer extension".to_string(),
available_tools: Vec::new(),
},
});
println!("✓ Developer extension enabled");
} }
cliclack::outro(
"Tetrate Agent Router Service setup complete! You can now use goose.",
)?;
} }
Err(e) => { Err(e) => {
eprintln!("⚠️ Failed to create provider for testing: {}", e); eprintln!("⚠️ Configuration test failed: {}", e);
eprintln!("Your settings have been saved. Please check your configuration."); eprintln!("Your settings have been saved, but there may be an issue with the connection.");
} }
} }
} }
Err(e) => { Err(e) => {
eprintln!("Authentication failed: {}", e); eprintln!("⚠️ Failed to create provider for testing: {}", e);
return Err(e.into()); eprintln!("Your settings have been saved. Please check your configuration.");
} }
} }
Ok(()) Ok(())
} }
fn add_provider() -> Result<(), Box<dyn Error>> { fn add_provider() -> anyhow::Result<()> {
let provider_type = cliclack::select("What type of API is this?") let provider_type = cliclack::select("What type of API is this?")
.item( .item(
"openai_compatible", "openai_compatible",
@@ -1924,7 +1910,7 @@ fn add_provider() -> Result<(), Box<dyn Error>> {
Ok(()) Ok(())
} }
fn remove_provider() -> Result<(), Box<dyn Error>> { fn remove_provider() -> anyhow::Result<()> {
let custom_providers_dir = goose::config::declarative_providers::custom_providers_dir(); let custom_providers_dir = goose::config::declarative_providers::custom_providers_dir();
let custom_providers = if custom_providers_dir.exists() { let custom_providers = if custom_providers_dir.exists() {
goose::config::declarative_providers::load_custom_providers(&custom_providers_dir)? goose::config::declarative_providers::load_custom_providers(&custom_providers_dir)?
@@ -1951,7 +1937,7 @@ fn remove_provider() -> Result<(), Box<dyn Error>> {
Ok(()) Ok(())
} }
pub fn configure_custom_provider_dialog() -> Result<(), Box<dyn Error>> { pub fn configure_custom_provider_dialog() -> anyhow::Result<()> {
let action = cliclack::select("What would you like to do?") let action = cliclack::select("What would you like to do?")
.item( .item(
"add", "add",
-1
View File
@@ -30,7 +30,6 @@ async fn main() -> Result<()> {
} }
} }
// Then shutdown the providers
goose::tracing::shutdown_otlp(); goose::tracing::shutdown_otlp();
} }