diff --git a/crates/goose-server/src/routes/config_management.rs b/crates/goose-server/src/routes/config_management.rs index a0caa8b3..1f45333f 100644 --- a/crates/goose-server/src/routes/config_management.rs +++ b/crates/goose-server/src/routes/config_management.rs @@ -123,7 +123,14 @@ pub async fn remove_config( let config = Config::global(); - match config.delete(&query.key) { + // Check if the secret flag is true and call the appropriate method + let result = if query.is_secret { + config.delete_secret(&query.key) + } else { + config.delete(&query.key) + }; + + match result { Ok(_) => Ok(Json(format!("Removed key {}", query.key))), Err(_) => Err(StatusCode::NOT_FOUND), } diff --git a/ui/desktop/src/components/settings_v2/models/modelInterface.ts b/ui/desktop/src/components/settings_v2/models/modelInterface.ts index d82017f0..4a397647 100644 --- a/ui/desktop/src/components/settings_v2/models/modelInterface.ts +++ b/ui/desktop/src/components/settings_v2/models/modelInterface.ts @@ -37,5 +37,5 @@ export async function getProviderMetadata( if (!matches) { throw Error(`No match for provider: ${providerName}`); } - return matches[0].metadata; + return matches.metadata; } diff --git a/ui/desktop/src/components/settings_v2/providers/ProviderGrid.tsx b/ui/desktop/src/components/settings_v2/providers/ProviderGrid.tsx index b2b93548..9537ef0d 100644 --- a/ui/desktop/src/components/settings_v2/providers/ProviderGrid.tsx +++ b/ui/desktop/src/components/settings_v2/providers/ProviderGrid.tsx @@ -42,6 +42,21 @@ const ProviderCards = memo(function ProviderCards({ [openModal, refreshProviders] ); + const deleteProviderConfigViaModal = useCallback( + (provider: ProviderDetails) => { + openModal(provider, { + onDelete: () => { + // Only refresh if the function is provided + if (refreshProviders) { + refreshProviders(); + } + }, + formProps: {}, + }); + }, + [openModal, refreshProviders] + ); + // We don't need an intermediate function here // Just pass the onProviderLaunch directly @@ -52,6 +67,7 @@ const ProviderCards = memo(function ProviderCards({ key={provider.name} provider={provider} onConfigure={() => configureProviderViaModal(provider)} + onDelete={() => deleteProviderConfigViaModal(provider)} onLaunch={() => onProviderLaunch(provider)} isOnboarding={isOnboarding} /> @@ -87,6 +103,5 @@ export default memo(function ProviderGrid({ ), [providers, isOnboarding, refreshProviders, onProviderLaunch] ); - return {modalProviderContent}; }); diff --git a/ui/desktop/src/components/settings_v2/providers/ProviderSettingsPage.tsx b/ui/desktop/src/components/settings_v2/providers/ProviderSettingsPage.tsx index 71a3db8f..9b8cd4e3 100644 --- a/ui/desktop/src/components/settings_v2/providers/ProviderSettingsPage.tsx +++ b/ui/desktop/src/components/settings_v2/providers/ProviderSettingsPage.tsx @@ -79,15 +79,14 @@ export default function ProviderSettings({ onClose, isOnboarding }: ProviderSett ); return ( -
+
- {isOnboarding && ( -
- -
- )} - - + + {isOnboarding && ( +
+ +
+ )}
{/* Only show back button if not in onboarding mode */} {!isOnboarding && } @@ -96,7 +95,7 @@ export default function ProviderSettings({ onClose, isOnboarding }: ProviderSett {isOnboarding && (

- Select an AI model provider to get started with goose. You’ll need to use API keys + Select an AI model provider to get started with goose. You'll need to use API keys generated by each provider which will be encrypted and stored locally. You can change your provider at any time in settings.

diff --git a/ui/desktop/src/components/settings_v2/providers/modal/ProviderConfiguationModal.tsx b/ui/desktop/src/components/settings_v2/providers/modal/ProviderConfiguationModal.tsx index 85fff361..f75127ec 100644 --- a/ui/desktop/src/components/settings_v2/providers/modal/ProviderConfiguationModal.tsx +++ b/ui/desktop/src/components/settings_v2/providers/modal/ProviderConfiguationModal.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import Modal from '../../../../components/Modal'; import ProviderSetupHeader from './subcomponents/ProviderSetupHeader'; import DefaultProviderSetupForm from './subcomponents/forms/DefaultProviderSetupForm'; @@ -10,6 +10,7 @@ import { DefaultSubmitHandler } from './subcomponents/handlers/DefaultSubmitHand import OllamaSubmitHandler from './subcomponents/handlers/OllamaSubmitHandler'; import OllamaForm from './subcomponents/forms/OllamaForm'; import { useConfig } from '../../../ConfigContext'; +import { AlertTriangle } from 'lucide-react'; const customSubmitHandlerMap = { provider_name: OllamaSubmitHandler, // example @@ -20,15 +21,31 @@ const customFormsMap = { }; export default function ProviderConfigurationModal() { - const { upsert } = useConfig(); const [validationErrors, setValidationErrors] = useState({}); + const { upsert, remove } = useConfig(); const { isOpen, currentProvider, modalProps, closeModal } = useProviderModal(); const [configValues, setConfigValues] = useState({}); + const [showDeleteConfirmation, setShowDeleteConfirmation] = useState(false); + + useEffect(() => { + if (isOpen && currentProvider) { + // Reset form state when the modal opens with a new provider + setConfigValues({}); + setValidationErrors({}); + setShowDeleteConfirmation(false); + } + }, [isOpen, currentProvider]); if (!isOpen || !currentProvider) return null; - const headerText = `Configure ${currentProvider.metadata.display_name}`; - const descriptionText = `Add your API key(s) for this provider to integrate into Goose`; + const isConfigured = currentProvider.is_configured; + const headerText = showDeleteConfirmation + ? `Delete configuration for ${currentProvider.metadata.display_name}` + : `Configure ${currentProvider.metadata.display_name}`; + + const descriptionText = showDeleteConfirmation + ? 'This will permanently delete the current provider configuration.' + : `Add your API key(s) for this provider to integrate into Goose`; const SubmitHandler = customSubmitHandlerMap[currentProvider.name] || DefaultSubmitHandler; const FormComponent = customFormsMap[currentProvider.name] || DefaultProviderSetupForm; @@ -80,6 +97,9 @@ export default function ProviderConfigurationModal() { }; const handleCancel = () => { + // Reset delete confirmation state + setShowDeleteConfirmation(false); + // Use custom cancel handler if provided if (modalProps.onCancel) { modalProps.onCancel(); @@ -88,30 +108,87 @@ export default function ProviderConfigurationModal() { closeModal(); }; + const handleDelete = () => { + setShowDeleteConfirmation(true); + }; + + const handleConfirmDelete = async () => { + try { + // Remove the provider configuration + // get the keys + const params = currentProvider.metadata.config_keys; + + // go through the keys are remove them + for (const param of params) { + console.log('param', param.name, 'secret', param.secret); + await remove(param.name, param.secret); + } + + // Call onDelete callback if provided + // This should trigger the refreshProviders function + if (modalProps.onDelete) { + modalProps.onDelete(currentProvider.name); + } + + // Reset the delete confirmation state before closing + setShowDeleteConfirmation(false); + + // Close the modal + // Close the modal after deletion and callback + closeModal(); + } catch (error) { + console.error('Failed to delete provider:', error); + // Keep modal open if there's an error + } + }; + // Function to determine which icon to display + const getModalIcon = () => { + if (showDeleteConfirmation) { + return ; + } + return ; + }; + return ( } + footer={ + setShowDeleteConfirmation(false)} + canDelete={isConfigured} + providerName={currentProvider.metadata.display_name} + /> + } >
- {/* Logo area - centered above title */} - + {/* Logo area or warning icon */} +
{getModalIcon()}
{/* Title and some information - centered */}
{/* Contains information used to set up each provider */} - + {/* Only show the form when NOT in delete confirmation mode */} + {!showDeleteConfirmation ? ( + <> + {/* Contains information used to set up each provider */} + - {currentProvider.metadata.config_keys && currentProvider.metadata.config_keys.length > 0 && ( - - )} + {currentProvider.metadata.config_keys && + currentProvider.metadata.config_keys.length > 0 && } + + ) : null}
); } diff --git a/ui/desktop/src/components/settings_v2/providers/modal/ProviderModalProvider.tsx b/ui/desktop/src/components/settings_v2/providers/modal/ProviderModalProvider.tsx index 072a5587..79c9aee5 100644 --- a/ui/desktop/src/components/settings_v2/providers/modal/ProviderModalProvider.tsx +++ b/ui/desktop/src/components/settings_v2/providers/modal/ProviderModalProvider.tsx @@ -4,6 +4,7 @@ import { ProviderDetails } from '../../../../api/types.gen'; interface ModalProps { onSubmit?: (values: any) => void; onCancel?: () => void; + onDelete?: (values: any) => void; formProps?: any; } diff --git a/ui/desktop/src/components/settings_v2/providers/modal/interfaces/ProviderConfigurationModalProps.tsx b/ui/desktop/src/components/settings_v2/providers/modal/interfaces/ProviderConfigurationModalProps.tsx deleted file mode 100644 index 8a486988..00000000 --- a/ui/desktop/src/components/settings_v2/providers/modal/interfaces/ProviderConfigurationModalProps.tsx +++ /dev/null @@ -1,9 +0,0 @@ -// used both for initial config and editing config -import ProviderDetails from '../../interfaces/ProviderDetails'; - -export default interface ProviderConfiguationModalProps { - provider: ProviderDetails; - title?: string; - onSubmit: (configValues: { [key: string]: string }) => void; - onCancel: () => void; -} diff --git a/ui/desktop/src/components/settings_v2/providers/modal/subcomponents/ProviderSetupActions.tsx b/ui/desktop/src/components/settings_v2/providers/modal/subcomponents/ProviderSetupActions.tsx index 42d16b37..aad15b08 100644 --- a/ui/desktop/src/components/settings_v2/providers/modal/subcomponents/ProviderSetupActions.tsx +++ b/ui/desktop/src/components/settings_v2/providers/modal/subcomponents/ProviderSetupActions.tsx @@ -1,19 +1,71 @@ import React from 'react'; import { Button } from '../../../../ui/button'; +import { Trash2 } from 'lucide-react'; interface ProviderSetupActionsProps { onCancel: () => void; onSubmit: (e: any) => void; + onDelete?: () => void; + showDeleteConfirmation?: boolean; + onConfirmDelete?: () => void; + onCancelDelete?: () => void; + canDelete?: boolean; + providerName?: string; } /** - * Renders the "Submit" and "Cancel" buttons at the bottom. - * Updated to match the design from screenshots. + * Renders the action buttons at the bottom of the provider modal. + * Includes submit, cancel, and delete functionality with confirmation. */ -export default function ProviderSetupActions({ onCancel, onSubmit }: ProviderSetupActionsProps) { +export default function ProviderSetupActions({ + onCancel, + onSubmit, + onDelete, + showDeleteConfirmation, + onConfirmDelete, + onCancelDelete, + canDelete, + providerName, +}: ProviderSetupActionsProps) { + // If we're showing delete confirmation, render the delete confirmation buttons + if (showDeleteConfirmation) { + return ( + <> +
+

+ Are you sure you want to delete the configuration parameters for {providerName}? This + action cannot be undone. +

+
+ + + + ); + } + + // Regular buttons (with delete if applicable) return (
- {/* We rely on the
"onSubmit" for the actual Submit logic */} + {canDelete && onDelete && ( + + )}