fix: add rollback, dedup and config preservation to Codex MCP toggle handlers

Apply same review fixes from Claude handlers to Codex handlers:
- Add previousConfig snapshot for rollback on API failure
- Guard against duplicate tool entries with includes() check
- Read config before optimistic update to avoid stale data
- Use spread to preserve full config shape
This commit is contained in:
catlog22
2026-02-13 11:52:00 +08:00
parent 659fb48c08
commit ac32b28c7b

View File

@@ -413,9 +413,10 @@ export function McpManagerPage() {
const handleToggleCcwToolCodex = async (tool: string, enabled: boolean) => { const handleToggleCcwToolCodex = async (tool: string, enabled: boolean) => {
const currentConfig = queryClient.getQueryData<CcwMcpConfig>(['ccwMcpConfigCodex']) ?? ccwCodexConfig; const currentConfig = queryClient.getQueryData<CcwMcpConfig>(['ccwMcpConfigCodex']) ?? ccwCodexConfig;
const currentTools = currentConfig.enabledTools; const currentTools = currentConfig.enabledTools;
const previousConfig = queryClient.getQueryData<CcwMcpConfig>(['ccwMcpConfigCodex']);
const updatedTools = enabled const updatedTools = enabled
? [...currentTools, tool] ? (currentTools.includes(tool) ? currentTools : [...currentTools, tool])
: currentTools.filter((t) => t !== tool); : currentTools.filter((t) => t !== tool);
queryClient.setQueryData(['ccwMcpConfigCodex'], (old: CcwMcpConfig | undefined) => { queryClient.setQueryData(['ccwMcpConfigCodex'], (old: CcwMcpConfig | undefined) => {
@@ -424,34 +425,28 @@ export function McpManagerPage() {
}); });
try { try {
await updateCcwConfigForCodex({ await updateCcwConfigForCodex({ ...currentConfig, enabledTools: updatedTools });
enabledTools: updatedTools,
projectRoot: currentConfig.projectRoot,
allowedDirs: currentConfig.allowedDirs,
disableSandbox: currentConfig.disableSandbox,
});
} catch (error) { } catch (error) {
console.error('Failed to toggle CCW tool (Codex):', error); console.error('Failed to toggle CCW tool (Codex):', error);
queryClient.setQueryData(['ccwMcpConfigCodex'], previousConfig);
} }
ccwMcpCodexQuery.refetch(); ccwMcpCodexQuery.refetch();
}; };
const handleUpdateCcwConfigCodex = async (config: Partial<CcwMcpConfig>) => { const handleUpdateCcwConfigCodex = async (config: Partial<CcwMcpConfig>) => {
const currentConfig = queryClient.getQueryData<CcwMcpConfig>(['ccwMcpConfigCodex']) ?? ccwCodexConfig;
const previousConfig = queryClient.getQueryData<CcwMcpConfig>(['ccwMcpConfigCodex']);
queryClient.setQueryData(['ccwMcpConfigCodex'], (old: CcwMcpConfig | undefined) => { queryClient.setQueryData(['ccwMcpConfigCodex'], (old: CcwMcpConfig | undefined) => {
if (!old) return old; if (!old) return old;
return { ...old, ...config }; return { ...old, ...config };
}); });
try { try {
const currentConfig = queryClient.getQueryData<CcwMcpConfig>(['ccwMcpConfigCodex']) ?? ccwCodexConfig; await updateCcwConfigForCodex({ ...currentConfig, ...config });
await updateCcwConfigForCodex({
enabledTools: config.enabledTools ?? currentConfig.enabledTools,
projectRoot: config.projectRoot ?? currentConfig.projectRoot,
allowedDirs: config.allowedDirs ?? currentConfig.allowedDirs,
disableSandbox: config.disableSandbox ?? currentConfig.disableSandbox,
});
} catch (error) { } catch (error) {
console.error('Failed to update CCW config (Codex):', error); console.error('Failed to update CCW config (Codex):', error);
queryClient.setQueryData(['ccwMcpConfigCodex'], previousConfig);
} }
ccwMcpCodexQuery.refetch(); ccwMcpCodexQuery.refetch();
}; };