From 3d5f4fb18e763447b2c394e25395e2703a2efb15 Mon Sep 17 00:00:00 2001 From: xlx1212 Date: Thu, 6 Aug 2026 20:44:51 +0800 Subject: [PATCH] fix(acp-agents): allow deleting remote server entries from ACP agents page Add a Delete button to each remote server row in the ACP Agents config page. The handler calls sshApi.deleteConnection() to remove the saved SSH connection, cleans up probe state, and shows success/error notifications. A confirmation dialog prevents accidental deletion. Fixes #2035 --- pr-body.md | 25 +++++++++++ .../config/components/AcpAgentsConfig.tsx | 43 +++++++++++++++++++ .../locales/en-US/settings/acp-agents.json | 6 ++- .../locales/zh-CN/settings/acp-agents.json | 6 ++- .../locales/zh-TW/settings/acp-agents.json | 6 ++- 5 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 pr-body.md diff --git a/pr-body.md b/pr-body.md new file mode 100644 index 0000000000..179ce7682c --- /dev/null +++ b/pr-body.md @@ -0,0 +1,25 @@ +## Summary + +Fixes #2035 + +Adds the ability to delete remote server entries from the ACP Agents configuration page. Previously, when a remote SSH server was used, an ACP Agent entry was auto-created but could not be removed. If the server was no longer in use, the entry cluttered the UI indefinitely. + +## Changes + +- **AcpAgentsConfig.tsx**: Added a `deleteRemoteConnection` handler that calls `sshApi.deleteConnection()`, removes the connection from `savedConnections` state, cleans up probe data, and shows success/error notifications. Added a Delete button (red/danger variant) next to the existing Refresh button for each remote server row. +- **Locale files** (`en-US`, `zh-CN`, `zh-TW`): Added `remote.deleteConnection`, `remote.deleteConfirm`, `notifications.deleteConnectionSuccess`, and `notifications.deleteConnectionFailed` keys. + +## Behavior + +1. User clicks the Delete button on a remote server row +2. A confirmation dialog asks: "Remove the remote server \"{{name}}\" from saved SSH connections? Its ACP agent entries will also be removed." +3. On confirm, `sshApi.deleteConnection(connectionId)` is called +4. The connection is removed from the saved connections list and probe data is cleaned up +5. A success notification is shown (or error notification on failure) + +## Validation + +- TypeScript: `tsc --noEmit` passes (no errors in modified files) +- Tests: All 7 existing `AcpAgentsConfig.test.tsx` tests pass +- i18n audit: `pnpm run i18n:audit` passes with 0 warnings +- Locale key parity verified across en-US, zh-CN, zh-TW diff --git a/src/web-ui/src/infrastructure/config/components/AcpAgentsConfig.tsx b/src/web-ui/src/infrastructure/config/components/AcpAgentsConfig.tsx index be35d42ca4..ad21f4c3c3 100644 --- a/src/web-ui/src/infrastructure/config/components/AcpAgentsConfig.tsx +++ b/src/web-ui/src/infrastructure/config/components/AcpAgentsConfig.tsx @@ -13,6 +13,7 @@ import { Search, Server, Terminal, + Trash2, } from 'lucide-react'; import { Button, Input, Select, Textarea } from '@/component-library'; import { @@ -379,6 +380,7 @@ const AcpAgentsConfig: React.FC = () => { const [registryFilter, setRegistryFilter] = useState('all'); const [installingClientIds, setInstallingClientIds] = useState>(() => new Set()); const [installingRemoteClientIds, setInstallingRemoteClientIds] = useState>(() => new Set()); + const [deletingRemoteIds, setDeletingRemoteIds] = useState>(() => new Set()); const requirementProbeRequestIdRef = useRef(0); const savingConfigRef = useRef(false); const loadedRemoteProbeIdsRef = useRef>(new Set()); @@ -533,6 +535,35 @@ const AcpAgentsConfig: React.FC = () => { } }, [notifyError, t]); + const deleteRemoteConnection = useCallback(async (connectionId: string, connectionName: string) => { + const confirmed = await window.confirm(t('remote.deleteConfirm', { name: connectionName })); + if (!confirmed) return; + + setDeletingRemoteIds(prev => new Set(prev).add(connectionId)); + try { + await sshApi.deleteConnection(connectionId); + setSavedConnections(prev => prev.filter(conn => conn.id !== connectionId)); + loadedRemoteProbeIdsRef.current.delete(connectionId); + setRemoteRequirementProbes(prev => { + const next = { ...prev }; + delete next[connectionId]; + return next; + }); + notifySuccess(t('notifications.deleteConnectionSuccess')); + } catch (error) { + log.error('Failed to delete remote SSH connection', error); + notifyError(error instanceof Error ? error.message : String(error), { + title: t('notifications.deleteConnectionFailed'), + }); + } finally { + setDeletingRemoteIds(prev => { + const next = new Set(prev); + next.delete(connectionId); + return next; + }); + } + }, [notifyError, notifySuccess, t]); + const loadConfig = useCallback(async ( options: { showLoading?: boolean; refreshRequirements?: boolean } = {} ) => { @@ -1397,6 +1428,7 @@ const AcpAgentsConfig: React.FC = () => { connection.id ); const probingRemote = probingRemoteRequirements.has(connection.id); + const deletingRemote = deletingRemoteIds.has(connection.id); const remoteRows = remoteAgentIds.map(clientId => { const preset = PRESET_BY_ID.get(clientId); const clientConfig = config.acpClients[clientId]; @@ -1502,6 +1534,17 @@ const AcpAgentsConfig: React.FC = () => { {t('remote.refreshDetection')} +