Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions pr-body.md
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
Search,
Server,
Terminal,
Trash2,
} from 'lucide-react';
import { Button, Input, Select, Textarea } from '@/component-library';
import {
Expand Down Expand Up @@ -379,6 +380,7 @@ const AcpAgentsConfig: React.FC = () => {
const [registryFilter, setRegistryFilter] = useState<RegistryFilter>('all');
const [installingClientIds, setInstallingClientIds] = useState<Set<string>>(() => new Set());
const [installingRemoteClientIds, setInstallingRemoteClientIds] = useState<Set<string>>(() => new Set());
const [deletingRemoteIds, setDeletingRemoteIds] = useState<Set<string>>(() => new Set());
const requirementProbeRequestIdRef = useRef(0);
const savingConfigRef = useRef(false);
const loadedRemoteProbeIdsRef = useRef<Set<string>>(new Set());
Expand Down Expand Up @@ -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 } = {}
) => {
Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -1502,6 +1534,17 @@ const AcpAgentsConfig: React.FC = () => {
<RefreshCw size={14} />
{t('remote.refreshDetection')}
</Button>
<Button
variant="danger"
size="small"
onClick={() => {
void deleteRemoteConnection(connection.id, connection.name || connection.id);
}}
isLoading={deletingRemote}
>
<Trash2 size={14} />
{t('remote.deleteConnection')}
</Button>
</div>
</div>
<div
Expand Down
6 changes: 5 additions & 1 deletion src/web-ui/src/locales/en-US/settings/acp-agents.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@
"empty": "No saved SSH servers.",
"noAgents": "Add an ACP agent before checking remote servers.",
"refreshDetection": "Refresh detection",
"deleteConnection": "Delete",
"deleteConfirm": "Remove the remote server \"{{name}}\" from saved SSH connections? Its ACP agent entries will also be removed.",
"summary": "{{available}} / {{total}} available",
"issueSummary": "{{count}} issue(s)"
},
Expand Down Expand Up @@ -112,6 +114,8 @@
"downloadSuccess": "ACP agent CLI downloaded",
"downloadFailed": "Failed to download ACP agent CLI",
"predownloadSuccess": "ACP adapter downloaded",
"predownloadFailed": "Failed to download ACP adapter"
"predownloadFailed": "Failed to download ACP adapter",
"deleteConnectionSuccess": "Remote server connection deleted",
"deleteConnectionFailed": "Failed to delete remote server connection"
}
}
6 changes: 5 additions & 1 deletion src/web-ui/src/locales/zh-CN/settings/acp-agents.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@
"empty": "没有已保存的 SSH 服务器。",
"noAgents": "请先添加 ACP Agent,再检测远程服务器。",
"refreshDetection": "刷新检测",
"deleteConnection": "删除",
"deleteConfirm": "确定从已保存的 SSH 连接中移除远程服务器「{{name}}」吗?其 ACP Agent 条目也将一并移除。",
"summary": "{{available}} / {{total}} 可用",
"issueSummary": "{{count}} 个异常"
},
Expand Down Expand Up @@ -112,6 +114,8 @@
"downloadSuccess": "ACP Agent CLI 已下载",
"downloadFailed": "下载 ACP Agent CLI 失败",
"predownloadSuccess": "ACP 适配器已下载",
"predownloadFailed": "下载 ACP 适配器失败"
"predownloadFailed": "下载 ACP 适配器失败",
"deleteConnectionSuccess": "远程服务器连接已删除",
"deleteConnectionFailed": "删除远程服务器连接失败"
}
}
6 changes: 5 additions & 1 deletion src/web-ui/src/locales/zh-TW/settings/acp-agents.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@
"empty": "沒有已儲存的 SSH 伺服器。",
"noAgents": "請先新增 ACP Agent,再檢測遠端伺服器。",
"refreshDetection": "重新整理檢測",
"deleteConnection": "刪除",
"deleteConfirm": "確定從已儲存的 SSH 連線中移除遠端伺服器「{{name}}」嗎?其 ACP Agent 條目也將一併移除。",
"summary": "{{available}} / {{total}} 可用",
"issueSummary": "{{count}} 個異常"
},
Expand Down Expand Up @@ -112,6 +114,8 @@
"downloadSuccess": "ACP Agent CLI 已下載",
"downloadFailed": "下載 ACP Agent CLI 失敗",
"predownloadSuccess": "ACP 適配器已下載",
"predownloadFailed": "下載 ACP 適配器失敗"
"predownloadFailed": "下載 ACP 適配器失敗",
"deleteConnectionSuccess": "遠端伺服器連線已刪除",
"deleteConnectionFailed": "刪除遠端伺服器連線失敗"
}
}