@@ -1174,6 +1174,11 @@ pub async fn configure_settings_dialog() -> anyhow::Result<()> {
11741174 "Max Turns" ,
11751175 "Set maximum number of turns without user input" ,
11761176 )
1177+ . item (
1178+ "keyring" ,
1179+ "Secret Storage" ,
1180+ "Configure how secrets are stored (keyring vs file)" ,
1181+ )
11771182 . item (
11781183 "experiment" ,
11791184 "Toggle Experiment" ,
@@ -1206,6 +1211,9 @@ pub async fn configure_settings_dialog() -> anyhow::Result<()> {
12061211 "max_turns" => {
12071212 configure_max_turns_dialog ( ) ?;
12081213 }
1214+ "keyring" => {
1215+ configure_keyring_dialog ( ) ?;
1216+ }
12091217 "experiment" => {
12101218 toggle_experiments_dialog ( ) ?;
12111219 }
@@ -1225,7 +1233,6 @@ pub async fn configure_settings_dialog() -> anyhow::Result<()> {
12251233pub fn configure_goose_mode_dialog ( ) -> anyhow:: Result < ( ) > {
12261234 let config = Config :: global ( ) ;
12271235
1228- // Check if GOOSE_MODE is set as an environment variable
12291236 if std:: env:: var ( "GOOSE_MODE" ) . is_ok ( ) {
12301237 let _ = cliclack:: log:: info ( "Notice: GOOSE_MODE environment variable is set and will override the configuration here." ) ;
12311238 }
@@ -1293,7 +1300,7 @@ pub fn configure_goose_router_strategy_dialog() -> anyhow::Result<()> {
12931300
12941301pub fn configure_tool_output_dialog ( ) -> anyhow:: Result < ( ) > {
12951302 let config = Config :: global ( ) ;
1296- // Check if GOOSE_CLI_MIN_PRIORITY is set as an environment variable
1303+
12971304 if std:: env:: var ( "GOOSE_CLI_MIN_PRIORITY" ) . is_ok ( ) {
12981305 let _ = cliclack:: log:: info ( "Notice: GOOSE_CLI_MIN_PRIORITY environment variable is set and will override the configuration here." ) ;
12991306 }
@@ -1322,6 +1329,60 @@ pub fn configure_tool_output_dialog() -> anyhow::Result<()> {
13221329 Ok ( ( ) )
13231330}
13241331
1332+ pub fn configure_keyring_dialog ( ) -> anyhow:: Result < ( ) > {
1333+ let config = Config :: global ( ) ;
1334+
1335+ if std:: env:: var ( "GOOSE_DISABLE_KEYRING" ) . is_ok ( ) {
1336+ let _ = cliclack:: log:: info ( "Notice: GOOSE_DISABLE_KEYRING environment variable is set and will override the configuration here." ) ;
1337+ }
1338+
1339+ let currently_disabled = config. get_param :: < String > ( "GOOSE_DISABLE_KEYRING" ) . is_ok ( ) ;
1340+
1341+ let current_status = if currently_disabled {
1342+ "Disabled (using file-based storage)"
1343+ } else {
1344+ "Enabled (using system keyring)"
1345+ } ;
1346+
1347+ let _ = cliclack:: log:: info ( format ! ( "Current secret storage: {}" , current_status) ) ;
1348+ let _ = cliclack:: log:: warning ( "Note: Disabling the keyring stores secrets in a plain text file (~/.config/goose/secrets.yaml)" ) ;
1349+
1350+ let storage_option = cliclack:: select ( "How would you like to store secrets?" )
1351+ . item (
1352+ "keyring" ,
1353+ "System Keyring (recommended)" ,
1354+ "Use secure system keyring for storing API keys and secrets" ,
1355+ )
1356+ . item (
1357+ "file" ,
1358+ "File-based Storage" ,
1359+ "Store secrets in a local file (useful when keyring access is restricted)" ,
1360+ )
1361+ . interact ( ) ?;
1362+
1363+ match storage_option {
1364+ "keyring" => {
1365+ // Set to empty string to enable keyring (absence or empty = enabled)
1366+ config. set_param ( "GOOSE_DISABLE_KEYRING" , Value :: String ( "" . to_string ( ) ) ) ?;
1367+ cliclack:: outro ( "Secret storage set to system keyring (secure)" ) ?;
1368+ let _ =
1369+ cliclack:: log:: info ( "You may need to restart goose for this change to take effect" ) ;
1370+ }
1371+ "file" => {
1372+ // Set the disable flag to use file storage
1373+ config. set_param ( "GOOSE_DISABLE_KEYRING" , Value :: String ( "true" . to_string ( ) ) ) ?;
1374+ cliclack:: outro (
1375+ "Secret storage set to file (~/.config/goose/secrets.yaml). Keep this file secure!" ,
1376+ ) ?;
1377+ let _ =
1378+ cliclack:: log:: info ( "You may need to restart goose for this change to take effect" ) ;
1379+ }
1380+ _ => unreachable ! ( ) ,
1381+ } ;
1382+
1383+ Ok ( ( ) )
1384+ }
1385+
13251386/// Configure experiment features that can be used with goose
13261387/// Dialog for toggling which experiments are enabled/disabled
13271388pub fn toggle_experiments_dialog ( ) -> anyhow:: Result < ( ) > {
@@ -1740,6 +1801,50 @@ pub async fn handle_tetrate_auth() -> anyhow::Result<()> {
17401801 Ok ( ( ) )
17411802}
17421803
1804+ /// Prompts the user to collect custom HTTP headers for a provider.
1805+ fn collect_custom_headers ( ) -> anyhow:: Result < Option < std:: collections:: HashMap < String , String > > > {
1806+ let use_custom_headers = cliclack:: confirm ( "Does this provider require custom headers?" )
1807+ . initial_value ( false )
1808+ . interact ( ) ?;
1809+
1810+ if !use_custom_headers {
1811+ return Ok ( None ) ;
1812+ }
1813+
1814+ let mut custom_headers = std:: collections:: HashMap :: new ( ) ;
1815+
1816+ loop {
1817+ let header_name: String = cliclack:: input ( "Header name:" )
1818+ . placeholder ( "e.g., x-origin-client-id" )
1819+ . required ( false )
1820+ . interact ( ) ?;
1821+
1822+ if header_name. is_empty ( ) {
1823+ break ;
1824+ }
1825+
1826+ let header_value: String = cliclack:: password ( format ! ( "Value for '{}':" , header_name) )
1827+ . mask ( '▪' )
1828+ . interact ( ) ?;
1829+
1830+ custom_headers. insert ( header_name, header_value) ;
1831+
1832+ let add_more = cliclack:: confirm ( "Add another header?" )
1833+ . initial_value ( false )
1834+ . interact ( ) ?;
1835+
1836+ if !add_more {
1837+ break ;
1838+ }
1839+ }
1840+
1841+ if custom_headers. is_empty ( ) {
1842+ Ok ( None )
1843+ } else {
1844+ Ok ( Some ( custom_headers) )
1845+ }
1846+ }
1847+
17431848fn add_provider ( ) -> anyhow:: Result < ( ) > {
17441849 let provider_type = cliclack:: select ( "What type of API is this?" )
17451850 . item (
@@ -1807,13 +1912,21 @@ fn add_provider() -> anyhow::Result<()> {
18071912 . initial_value ( true )
18081913 . interact ( ) ?;
18091914
1915+ // Ask about custom headers for OpenAI compatible providers
1916+ let headers = if provider_type == "openai_compatible" {
1917+ collect_custom_headers ( ) ?
1918+ } else {
1919+ None
1920+ } ;
1921+
18101922 create_custom_provider (
18111923 provider_type,
18121924 display_name. clone ( ) ,
18131925 api_url,
18141926 api_key,
18151927 models,
18161928 Some ( supports_streaming) ,
1929+ headers,
18171930 ) ?;
18181931
18191932 cliclack:: outro ( format ! ( "Custom provider added: {}" , display_name) ) ?;
0 commit comments