Feat: Added custom headers and toggle keyring CLI options (#5017)

Signed-off-by: Blair Allan <Blairallan@icloud.com>
Co-authored-by: dianed-square <73617011+dianed-square@users.noreply.github.com>
This commit is contained in:
Blair Allan
2025-12-01 21:01:03 +00:00
committed by GitHub
parent c3c71a2ff4
commit ccaf7ca56d
5 changed files with 129 additions and 3 deletions
+115 -2
View File
@@ -1174,6 +1174,11 @@ pub async fn configure_settings_dialog() -> anyhow::Result<()> {
"Max Turns",
"Set maximum number of turns without user input",
)
.item(
"keyring",
"Secret Storage",
"Configure how secrets are stored (keyring vs file)",
)
.item(
"experiment",
"Toggle Experiment",
@@ -1206,6 +1211,9 @@ pub async fn configure_settings_dialog() -> anyhow::Result<()> {
"max_turns" => {
configure_max_turns_dialog()?;
}
"keyring" => {
configure_keyring_dialog()?;
}
"experiment" => {
toggle_experiments_dialog()?;
}
@@ -1225,7 +1233,6 @@ pub async fn configure_settings_dialog() -> anyhow::Result<()> {
pub fn configure_goose_mode_dialog() -> anyhow::Result<()> {
let config = Config::global();
// Check if GOOSE_MODE is set as an environment variable
if std::env::var("GOOSE_MODE").is_ok() {
let _ = cliclack::log::info("Notice: GOOSE_MODE environment variable is set and will override the configuration here.");
}
@@ -1293,7 +1300,7 @@ pub fn configure_goose_router_strategy_dialog() -> anyhow::Result<()> {
pub fn configure_tool_output_dialog() -> anyhow::Result<()> {
let config = Config::global();
// Check if GOOSE_CLI_MIN_PRIORITY is set as an environment variable
if std::env::var("GOOSE_CLI_MIN_PRIORITY").is_ok() {
let _ = cliclack::log::info("Notice: GOOSE_CLI_MIN_PRIORITY environment variable is set and will override the configuration here.");
}
@@ -1322,6 +1329,60 @@ pub fn configure_tool_output_dialog() -> anyhow::Result<()> {
Ok(())
}
pub fn configure_keyring_dialog() -> anyhow::Result<()> {
let config = Config::global();
if std::env::var("GOOSE_DISABLE_KEYRING").is_ok() {
let _ = cliclack::log::info("Notice: GOOSE_DISABLE_KEYRING environment variable is set and will override the configuration here.");
}
let currently_disabled = config.get_param::<String>("GOOSE_DISABLE_KEYRING").is_ok();
let current_status = if currently_disabled {
"Disabled (using file-based storage)"
} else {
"Enabled (using system keyring)"
};
let _ = cliclack::log::info(format!("Current secret storage: {}", current_status));
let _ = cliclack::log::warning("Note: Disabling the keyring stores secrets in a plain text file (~/.config/goose/secrets.yaml)");
let storage_option = cliclack::select("How would you like to store secrets?")
.item(
"keyring",
"System Keyring (recommended)",
"Use secure system keyring for storing API keys and secrets",
)
.item(
"file",
"File-based Storage",
"Store secrets in a local file (useful when keyring access is restricted)",
)
.interact()?;
match storage_option {
"keyring" => {
// Set to empty string to enable keyring (absence or empty = enabled)
config.set_param("GOOSE_DISABLE_KEYRING", Value::String("".to_string()))?;
cliclack::outro("Secret storage set to system keyring (secure)")?;
let _ =
cliclack::log::info("You may need to restart goose for this change to take effect");
}
"file" => {
// Set the disable flag to use file storage
config.set_param("GOOSE_DISABLE_KEYRING", Value::String("true".to_string()))?;
cliclack::outro(
"Secret storage set to file (~/.config/goose/secrets.yaml). Keep this file secure!",
)?;
let _ =
cliclack::log::info("You may need to restart goose for this change to take effect");
}
_ => unreachable!(),
};
Ok(())
}
/// Configure experiment features that can be used with goose
/// Dialog for toggling which experiments are enabled/disabled
pub fn toggle_experiments_dialog() -> anyhow::Result<()> {
@@ -1740,6 +1801,50 @@ pub async fn handle_tetrate_auth() -> anyhow::Result<()> {
Ok(())
}
/// Prompts the user to collect custom HTTP headers for a provider.
fn collect_custom_headers() -> anyhow::Result<Option<std::collections::HashMap<String, String>>> {
let use_custom_headers = cliclack::confirm("Does this provider require custom headers?")
.initial_value(false)
.interact()?;
if !use_custom_headers {
return Ok(None);
}
let mut custom_headers = std::collections::HashMap::new();
loop {
let header_name: String = cliclack::input("Header name:")
.placeholder("e.g., x-origin-client-id")
.required(false)
.interact()?;
if header_name.is_empty() {
break;
}
let header_value: String = cliclack::password(format!("Value for '{}':", header_name))
.mask('▪')
.interact()?;
custom_headers.insert(header_name, header_value);
let add_more = cliclack::confirm("Add another header?")
.initial_value(false)
.interact()?;
if !add_more {
break;
}
}
if custom_headers.is_empty() {
Ok(None)
} else {
Ok(Some(custom_headers))
}
}
fn add_provider() -> anyhow::Result<()> {
let provider_type = cliclack::select("What type of API is this?")
.item(
@@ -1807,6 +1912,13 @@ fn add_provider() -> anyhow::Result<()> {
.initial_value(true)
.interact()?;
// Ask about custom headers for OpenAI compatible providers
let headers = if provider_type == "openai_compatible" {
collect_custom_headers()?
} else {
None
};
create_custom_provider(
provider_type,
display_name.clone(),
@@ -1814,6 +1926,7 @@ fn add_provider() -> anyhow::Result<()> {
api_key,
models,
Some(supports_streaming),
headers,
)?;
cliclack::outro(format!("Custom provider added: {}", display_name))?;
@@ -87,6 +87,7 @@ pub struct UpdateCustomProviderRequest {
pub api_key: String,
pub models: Vec<String>,
pub supports_streaming: Option<bool>,
pub headers: Option<std::collections::HashMap<String, String>>,
}
#[derive(Deserialize, ToSchema)]
@@ -706,6 +707,7 @@ pub async fn create_custom_provider(
request.api_key,
request.models,
request.supports_streaming,
request.headers,
)
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
@@ -92,6 +92,7 @@ pub fn create_custom_provider(
api_key: String,
models: Vec<String>,
supports_streaming: Option<bool>,
headers: Option<HashMap<String, String>>,
) -> Result<DeclarativeProviderConfig> {
let id = generate_id(&display_name);
let api_key_name = generate_api_key_name(&id);
@@ -117,7 +118,7 @@ pub fn create_custom_provider(
api_key_env: api_key_name,
base_url: api_url,
models: model_infos,
headers: None,
headers,
timeout_seconds: None,
supports_streaming,
};
+7
View File
@@ -5275,6 +5275,13 @@
"engine": {
"type": "string"
},
"headers": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"nullable": true
},
"models": {
"type": "array",
"items": {
+3
View File
@@ -935,6 +935,9 @@ export type UpdateCustomProviderRequest = {
api_url: string;
display_name: string;
engine: string;
headers?: {
[key: string]: string;
} | null;
models: Array<string>;
supports_streaming?: boolean | null;
};