chore: refactor handle_configure to reduce line count (#6276)
This commit is contained in:
@@ -33,219 +33,225 @@ pub async fn handle_configure() -> anyhow::Result<()> {
|
|||||||
let config = Config::global();
|
let config = Config::global();
|
||||||
|
|
||||||
if !config.exists() {
|
if !config.exists() {
|
||||||
// First time setup flow
|
handle_first_time_setup(config).await
|
||||||
println!();
|
|
||||||
println!(
|
|
||||||
"{}",
|
|
||||||
style("Welcome to goose! Let's get you set up with a provider.").dim()
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
"{}",
|
|
||||||
style(" you can rerun this command later to update your configuration").dim()
|
|
||||||
);
|
|
||||||
println!();
|
|
||||||
cliclack::intro(style(" goose-configure ").on_cyan().black())?;
|
|
||||||
|
|
||||||
// Check if user wants to use OpenRouter login or manual configuration
|
|
||||||
let setup_method = cliclack::select("How would you like to set up your provider?")
|
|
||||||
.item(
|
|
||||||
"openrouter",
|
|
||||||
"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",
|
|
||||||
"Choose a provider and enter credentials manually",
|
|
||||||
)
|
|
||||||
.interact()?;
|
|
||||||
|
|
||||||
match setup_method {
|
|
||||||
"openrouter" => {
|
|
||||||
match handle_openrouter_auth().await {
|
|
||||||
Ok(_) => {
|
|
||||||
// OpenRouter auth already handles everything including enabling developer extension
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
let _ = config.clear();
|
|
||||||
println!(
|
|
||||||
"\n {} OpenRouter authentication failed: {} \n Please try again or use manual configuration",
|
|
||||||
style("Error").red().italic(),
|
|
||||||
e,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"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) => {
|
|
||||||
println!(
|
|
||||||
"\n {}: Run '{}' again to adjust your config or add extensions",
|
|
||||||
style("Tip").green().italic(),
|
|
||||||
style("goose configure").cyan()
|
|
||||||
);
|
|
||||||
// Since we are setting up for the first time, we'll also enable the developer system
|
|
||||||
// This operation is best-effort and errors are ignored
|
|
||||||
set_extension(ExtensionEntry {
|
|
||||||
enabled: true,
|
|
||||||
config: ExtensionConfig::default(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok(false) => {
|
|
||||||
let _ = config.clear();
|
|
||||||
println!(
|
|
||||||
"\n {}: We did not save your config, inspect your credentials\n and run '{}' again to ensure goose can connect",
|
|
||||||
style("Warning").yellow().italic(),
|
|
||||||
style("goose configure").cyan()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
let _ = config.clear();
|
|
||||||
|
|
||||||
match e.downcast_ref::<ConfigError>() {
|
|
||||||
Some(ConfigError::NotFound(key)) => {
|
|
||||||
println!(
|
|
||||||
"\n {} Required configuration key '{}' not found \n Please provide this value and run '{}' again",
|
|
||||||
style("Error").red().italic(),
|
|
||||||
key,
|
|
||||||
style("goose configure").cyan()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Some(ConfigError::KeyringError(msg)) => {
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
println!(
|
|
||||||
"\n {} Failed to access secure storage (keyring): {} \n Please check your system keychain and run '{}' again. \n If your system is unable to use the keyring, please try setting secret key(s) via environment variables.",
|
|
||||||
style("Error").red().italic(),
|
|
||||||
msg,
|
|
||||||
style("goose configure").cyan()
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
println!(
|
|
||||||
"\n {} Failed to access Windows Credential Manager: {} \n Please check Windows Credential Manager and run '{}' again. \n If your system is unable to use the Credential Manager, please try setting secret key(s) via environment variables.",
|
|
||||||
style("Error").red().italic(),
|
|
||||||
msg,
|
|
||||||
style("goose configure").cyan()
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
|
|
||||||
println!(
|
|
||||||
"\n {} Failed to access secure storage: {} \n Please check your system's secure storage and run '{}' again. \n If your system is unable to use secure storage, please try setting secret key(s) via environment variables.",
|
|
||||||
style("Error").red().italic(),
|
|
||||||
msg,
|
|
||||||
style("goose configure").cyan()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Some(ConfigError::DeserializeError(msg)) => {
|
|
||||||
println!(
|
|
||||||
"\n {} Invalid configuration value: {} \n Please check your input and run '{}' again",
|
|
||||||
style("Error").red().italic(),
|
|
||||||
msg,
|
|
||||||
style("goose configure").cyan()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Some(ConfigError::FileError(e)) => {
|
|
||||||
println!(
|
|
||||||
"\n {} Failed to access config file: {} \n Please check file permissions and run '{}' again",
|
|
||||||
style("Error").red().italic(),
|
|
||||||
e,
|
|
||||||
style("goose configure").cyan()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Some(ConfigError::DirectoryError(msg)) => {
|
|
||||||
println!(
|
|
||||||
"\n {} Failed to access config directory: {} \n Please check directory permissions and run '{}' again",
|
|
||||||
style("Error").red().italic(),
|
|
||||||
msg,
|
|
||||||
style("goose configure").cyan()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// handle all other nonspecific errors
|
|
||||||
_ => {
|
|
||||||
println!(
|
|
||||||
"\n {} {} \n We did not save your config, inspect your credentials\n and run '{}' again to ensure goose can connect",
|
|
||||||
style("Error").red().italic(),
|
|
||||||
e,
|
|
||||||
style("goose configure").cyan()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
} else {
|
} else {
|
||||||
let config_dir = Paths::config_dir().display().to_string();
|
handle_existing_config().await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
println!();
|
async fn handle_first_time_setup(config: &Config) -> anyhow::Result<()> {
|
||||||
println!(
|
println!();
|
||||||
"{}",
|
println!(
|
||||||
style("This will update your existing config files").dim()
|
"{}",
|
||||||
);
|
style("Welcome to goose! Let's get you set up with a provider.").dim()
|
||||||
println!(
|
);
|
||||||
"{} {}",
|
println!(
|
||||||
style(" if you prefer, you can edit them directly at").dim(),
|
"{}",
|
||||||
config_dir
|
style(" you can rerun this command later to update your configuration").dim()
|
||||||
);
|
);
|
||||||
println!();
|
println!();
|
||||||
|
cliclack::intro(style(" goose-configure ").on_cyan().black())?;
|
||||||
|
|
||||||
cliclack::intro(style(" goose-configure ").on_cyan().black())?;
|
let setup_method = cliclack::select("How would you like to set up your provider?")
|
||||||
let action = cliclack::select("What would you like to configure?")
|
.item(
|
||||||
.item(
|
"openrouter",
|
||||||
"providers",
|
"OpenRouter Login (Recommended)",
|
||||||
"Configure Providers",
|
"Sign in with OpenRouter to automatically configure models",
|
||||||
"Change provider or update credentials",
|
)
|
||||||
)
|
.item(
|
||||||
.item(
|
"tetrate",
|
||||||
"custom_providers",
|
"Tetrate Agent Router Service Login",
|
||||||
"Custom Providers",
|
"Sign in with Tetrate Agent Router Service to automatically configure models",
|
||||||
"Add custom provider with compatible API",
|
)
|
||||||
)
|
.item(
|
||||||
.item("add", "Add Extension", "Connect to a new extension")
|
"manual",
|
||||||
.item(
|
"Manual Configuration",
|
||||||
"toggle",
|
"Choose a provider and enter credentials manually",
|
||||||
"Toggle Extensions",
|
)
|
||||||
"Enable or disable connected extensions",
|
.interact()?;
|
||||||
)
|
|
||||||
.item("remove", "Remove Extension", "Remove an extension")
|
|
||||||
.item(
|
|
||||||
"settings",
|
|
||||||
"goose settings",
|
|
||||||
"Set the goose mode, Tool Output, Tool Permissions, Experiment, goose recipe github repo and more",
|
|
||||||
)
|
|
||||||
.interact()?;
|
|
||||||
|
|
||||||
match action {
|
match setup_method {
|
||||||
"toggle" => toggle_extensions_dialog(),
|
"openrouter" => {
|
||||||
"add" => configure_extensions_dialog(),
|
if let Err(e) = handle_openrouter_auth().await {
|
||||||
"remove" => remove_extension_dialog(),
|
let _ = config.clear();
|
||||||
"settings" => configure_settings_dialog().await,
|
println!(
|
||||||
"providers" => configure_provider_dialog().await.map(|_| ()),
|
"\n {} OpenRouter authentication failed: {} \n Please try again or use manual configuration",
|
||||||
"custom_providers" => configure_custom_provider_dialog(),
|
style("Error").red().italic(),
|
||||||
_ => unreachable!(),
|
e,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
"tetrate" => {
|
||||||
|
if let Err(e) = handle_tetrate_auth().await {
|
||||||
|
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" => handle_manual_provider_setup(config).await,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_manual_provider_setup(config: &Config) {
|
||||||
|
match configure_provider_dialog().await {
|
||||||
|
Ok(true) => {
|
||||||
|
println!(
|
||||||
|
"\n {}: Run '{}' again to adjust your config or add extensions",
|
||||||
|
style("Tip").green().italic(),
|
||||||
|
style("goose configure").cyan()
|
||||||
|
);
|
||||||
|
set_extension(ExtensionEntry {
|
||||||
|
enabled: true,
|
||||||
|
config: ExtensionConfig::default(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(false) => {
|
||||||
|
let _ = config.clear();
|
||||||
|
println!(
|
||||||
|
"\n {}: We did not save your config, inspect your credentials\n and run '{}' again to ensure goose can connect",
|
||||||
|
style("Warning").yellow().italic(),
|
||||||
|
style("goose configure").cyan()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
let _ = config.clear();
|
||||||
|
print_manual_config_error(&e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_manual_config_error(e: &anyhow::Error) {
|
||||||
|
match e.downcast_ref::<ConfigError>() {
|
||||||
|
Some(ConfigError::NotFound(key)) => {
|
||||||
|
println!(
|
||||||
|
"\n {} Required configuration key '{}' not found \n Please provide this value and run '{}' again",
|
||||||
|
style("Error").red().italic(),
|
||||||
|
key,
|
||||||
|
style("goose configure").cyan()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Some(ConfigError::KeyringError(msg)) => {
|
||||||
|
print_keyring_error(msg);
|
||||||
|
}
|
||||||
|
Some(ConfigError::DeserializeError(msg)) => {
|
||||||
|
println!(
|
||||||
|
"\n {} Invalid configuration value: {} \n Please check your input and run '{}' again",
|
||||||
|
style("Error").red().italic(),
|
||||||
|
msg,
|
||||||
|
style("goose configure").cyan()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Some(ConfigError::FileError(err)) => {
|
||||||
|
println!(
|
||||||
|
"\n {} Failed to access config file: {} \n Please check file permissions and run '{}' again",
|
||||||
|
style("Error").red().italic(),
|
||||||
|
err,
|
||||||
|
style("goose configure").cyan()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Some(ConfigError::DirectoryError(msg)) => {
|
||||||
|
println!(
|
||||||
|
"\n {} Failed to access config directory: {} \n Please check directory permissions and run '{}' again",
|
||||||
|
style("Error").red().italic(),
|
||||||
|
msg,
|
||||||
|
style("goose configure").cyan()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
println!(
|
||||||
|
"\n {} {} \n We did not save your config, inspect your credentials\n and run '{}' again to ensure goose can connect",
|
||||||
|
style("Error").red().italic(),
|
||||||
|
e,
|
||||||
|
style("goose configure").cyan()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
fn print_keyring_error(msg: &str) {
|
||||||
|
println!(
|
||||||
|
"\n {} Failed to access secure storage (keyring): {} \n Please check your system keychain and run '{}' again. \n If your system is unable to use the keyring, please try setting secret key(s) via environment variables.",
|
||||||
|
style("Error").red().italic(),
|
||||||
|
msg,
|
||||||
|
style("goose configure").cyan()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
fn print_keyring_error(msg: &str) {
|
||||||
|
println!(
|
||||||
|
"\n {} Failed to access Windows Credential Manager: {} \n Please check Windows Credential Manager and run '{}' again. \n If your system is unable to use the Credential Manager, please try setting secret key(s) via environment variables.",
|
||||||
|
style("Error").red().italic(),
|
||||||
|
msg,
|
||||||
|
style("goose configure").cyan()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
|
||||||
|
fn print_keyring_error(msg: &str) {
|
||||||
|
println!(
|
||||||
|
"\n {} Failed to access secure storage: {} \n Please check your system's secure storage and run '{}' again. \n If your system is unable to use secure storage, please try setting secret key(s) via environment variables.",
|
||||||
|
style("Error").red().italic(),
|
||||||
|
msg,
|
||||||
|
style("goose configure").cyan()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_existing_config() -> anyhow::Result<()> {
|
||||||
|
let config_dir = Paths::config_dir().display().to_string();
|
||||||
|
|
||||||
|
println!();
|
||||||
|
println!(
|
||||||
|
"{}",
|
||||||
|
style("This will update your existing config files").dim()
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
"{} {}",
|
||||||
|
style(" if you prefer, you can edit them directly at").dim(),
|
||||||
|
config_dir
|
||||||
|
);
|
||||||
|
println!();
|
||||||
|
|
||||||
|
cliclack::intro(style(" goose-configure ").on_cyan().black())?;
|
||||||
|
let action = cliclack::select("What would you like to configure?")
|
||||||
|
.item(
|
||||||
|
"providers",
|
||||||
|
"Configure Providers",
|
||||||
|
"Change provider or update credentials",
|
||||||
|
)
|
||||||
|
.item(
|
||||||
|
"custom_providers",
|
||||||
|
"Custom Providers",
|
||||||
|
"Add custom provider with compatible API",
|
||||||
|
)
|
||||||
|
.item("add", "Add Extension", "Connect to a new extension")
|
||||||
|
.item(
|
||||||
|
"toggle",
|
||||||
|
"Toggle Extensions",
|
||||||
|
"Enable or disable connected extensions",
|
||||||
|
)
|
||||||
|
.item("remove", "Remove Extension", "Remove an extension")
|
||||||
|
.item(
|
||||||
|
"settings",
|
||||||
|
"goose settings",
|
||||||
|
"Set the goose mode, Tool Output, Tool Permissions, Experiment, goose recipe github repo and more",
|
||||||
|
)
|
||||||
|
.interact()?;
|
||||||
|
|
||||||
|
match action {
|
||||||
|
"toggle" => toggle_extensions_dialog(),
|
||||||
|
"add" => configure_extensions_dialog(),
|
||||||
|
"remove" => remove_extension_dialog(),
|
||||||
|
"settings" => configure_settings_dialog().await,
|
||||||
|
"providers" => configure_provider_dialog().await.map(|_| ()),
|
||||||
|
"custom_providers" => configure_custom_provider_dialog(),
|
||||||
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user