feat: 添加更好的 SQLite3 模块加载和错误处理,更新相关组件以支持项目路径

This commit is contained in:
catlog22
2026-02-06 23:07:56 +08:00
parent 5cdbb43b3b
commit 3d862e6ed8
15 changed files with 541 additions and 38 deletions

View File

@@ -17,8 +17,9 @@ import {
import { Checkbox } from '@/components/ui/Checkbox';
import { Badge } from '@/components/ui/Badge';
import { useMcpServers } from '@/hooks';
import { crossCliCopy } from '@/lib/api';
import { crossCliCopy, fetchCodexMcpServers } from '@/lib/api';
import { cn } from '@/lib/utils';
import { useWorkflowStore, selectProjectPath } from '@/stores/workflowStore';
// ========== Types ==========
@@ -69,13 +70,11 @@ export function CrossCliCopyButton({
const [serverItems, setServerItems] = useState<ServerCheckboxItem[]>([]);
const { servers } = useMcpServers();
const projectPath = useWorkflowStore(selectProjectPath);
const [isCopying, setIsCopying] = useState(false);
// Initialize server items when dialog opens
const handleOpenChange = (open: boolean) => {
setIsOpen(open);
if (open) {
setDirection(currentMode === 'claude' ? 'claude-to-codex' : 'codex-to-claude');
const loadServerItems = async (nextDirection: CopyDirection) => {
if (nextDirection === 'claude-to-codex') {
setServerItems(
servers.map((s) => ({
name: s.name,
@@ -84,6 +83,34 @@ export function CrossCliCopyButton({
selected: false,
}))
);
return;
}
try {
const codex = await fetchCodexMcpServers();
setServerItems(
(codex.servers ?? []).map((s) => ({
name: s.name,
command: s.command,
enabled: s.enabled,
selected: false,
}))
);
} catch (error) {
console.error('Failed to load Codex MCP servers:', error);
setServerItems([]);
}
};
// Initialize server items when dialog opens
const handleOpenChange = (open: boolean) => {
setIsOpen(open);
if (open) {
const nextDirection = currentMode === 'claude' ? 'claude-to-codex' : 'codex-to-claude';
setDirection(nextDirection);
void loadServerItems(nextDirection);
} else {
setServerItems([]);
}
};
@@ -93,10 +120,9 @@ export function CrossCliCopyButton({
// Toggle direction
const handleToggleDirection = () => {
setDirection((prev) =>
prev === 'claude-to-codex' ? 'codex-to-claude' : 'claude-to-codex'
);
setServerItems((prev) => prev.map((item) => ({ ...item, selected: false })));
const next = direction === 'claude-to-codex' ? 'codex-to-claude' : 'claude-to-codex';
setDirection(next);
void loadServerItems(next);
};
// Toggle server selection
@@ -124,10 +150,15 @@ export function CrossCliCopyButton({
setIsCopying(true);
try {
if (targetCli === 'claude' && !projectPath) {
throw new Error('Project path is required to copy servers into Claude project');
}
const result = await crossCliCopy({
source: sourceCli,
target: targetCli,
serverNames: selectedServers,
projectPath: projectPath ?? undefined,
});
if (result.success) {

View File

@@ -28,10 +28,12 @@ import {
updateMcpServer,
fetchMcpServers,
type McpServer,
type McpProjectConfigType,
} from '@/lib/api';
import { mcpServersKeys, useMcpTemplates } from '@/hooks';
import { cn } from '@/lib/utils';
import { ConfigTypeToggle, type McpConfigType } from './ConfigTypeToggle';
import { useWorkflowStore, selectProjectPath } from '@/stores/workflowStore';
// ========== Types ==========
@@ -73,6 +75,7 @@ export function McpServerDialog({
}: McpServerDialogProps) {
const { formatMessage } = useIntl();
const queryClient = useQueryClient();
const projectPath = useWorkflowStore(selectProjectPath);
// Fetch templates from backend
const { templates, isLoading: templatesLoading } = useMcpTemplates();
@@ -92,6 +95,7 @@ export function McpServerDialog({
const [argsInput, setArgsInput] = useState('');
const [envInput, setEnvInput] = useState('');
const [configType, setConfigType] = useState<McpConfigType>('mcp-json');
const projectConfigType: McpProjectConfigType = configType === 'claude-json' ? 'claude' : 'mcp';
// Initialize form from server prop (edit mode)
useEffect(() => {
@@ -129,7 +133,8 @@ export function McpServerDialog({
// Mutations
const createMutation = useMutation({
mutationFn: (data: Omit<McpServer, 'name'>) => createMcpServer(data),
mutationFn: ({ server, configType }: { server: McpServer; configType?: McpProjectConfigType }) =>
createMcpServer(server, { projectPath: projectPath ?? undefined, configType }),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: mcpServersKeys.all });
handleClose();
@@ -138,8 +143,8 @@ export function McpServerDialog({
});
const updateMutation = useMutation({
mutationFn: ({ serverName, config }: { serverName: string; config: Partial<McpServer> }) =>
updateMcpServer(serverName, config),
mutationFn: ({ serverName, config, configType }: { serverName: string; config: Partial<McpServer>; configType?: McpProjectConfigType }) =>
updateMcpServer(serverName, config, { projectPath: projectPath ?? undefined, configType }),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: mcpServersKeys.all });
handleClose();
@@ -234,7 +239,7 @@ export function McpServerDialog({
const checkNameExists = async (name: string): Promise<boolean> => {
try {
const data = await fetchMcpServers();
const data = await fetchMcpServers(projectPath ?? undefined);
const allServers = [...data.project, ...data.global];
// In edit mode, exclude current server
return allServers.some(
@@ -258,11 +263,15 @@ export function McpServerDialog({
if (mode === 'add') {
createMutation.mutate({
command: formData.command,
args: formData.args,
env: formData.env,
scope: formData.scope,
enabled: formData.enabled,
server: {
name: formData.name,
command: formData.command,
args: formData.args,
env: formData.env,
scope: formData.scope,
enabled: formData.enabled,
},
configType: formData.scope === 'project' ? projectConfigType : undefined,
});
} else {
updateMutation.mutate({
@@ -274,6 +283,7 @@ export function McpServerDialog({
scope: formData.scope,
enabled: formData.enabled,
},
configType: formData.scope === 'project' ? projectConfigType : undefined,
});
}
};
@@ -441,6 +451,7 @@ export function McpServerDialog({
checked={formData.scope === 'project'}
onChange={(e) => handleFieldChange('scope', e.target.value as 'project' | 'global')}
className="w-4 h-4"
disabled={mode === 'edit'}
/>
<span className="text-sm">
{formatMessage({ id: 'mcp.scope.project' })}
@@ -454,6 +465,7 @@ export function McpServerDialog({
checked={formData.scope === 'global'}
onChange={(e) => handleFieldChange('scope', e.target.value as 'project' | 'global')}
className="w-4 h-4"
disabled={mode === 'edit'}
/>
<span className="text-sm">
{formatMessage({ id: 'mcp.scope.global' })}

View File

@@ -63,9 +63,10 @@ export function OtherProjectsSection({
for (const [path, serverList] of Object.entries(response.servers)) {
const projectName = path.split(/[/\\]/).filter(Boolean).pop() || path;
for (const server of (serverList as McpServer[])) {
for (const server of (serverList as Omit<McpServer, 'scope'>[])) {
servers.push({
...server,
scope: 'project',
projectPath: path,
projectName,
});
@@ -88,6 +89,7 @@ export function OtherProjectsSection({
const uniqueName = `${server.projectName}-${server.name}`.toLowerCase().replace(/\s+/g, '-');
await createServer({
name: uniqueName,
command: server.command,
args: server.args,
env: server.env,

View File

@@ -25,6 +25,7 @@ import {
import { mcpServersKeys } from '@/hooks';
import { useNotifications } from '@/hooks/useNotifications';
import { cn } from '@/lib/utils';
import { useWorkflowStore, selectProjectPath } from '@/stores/workflowStore';
// Icon map for MCP definitions
const ICON_MAP: Record<string, React.ComponentType<{ className?: string }>> = {
@@ -96,6 +97,7 @@ export function RecommendedMcpWizard({
const { formatMessage } = useIntl();
const queryClient = useQueryClient();
const { success: showSuccess, error: showError } = useNotifications();
const projectPath = useWorkflowStore(selectProjectPath);
// State for field values
const [fieldValues, setFieldValues] = useState<Record<string, any>>({});
@@ -138,7 +140,10 @@ export function RecommendedMcpWizard({
if (selectedScope === 'global') {
return addGlobalMcpServer(mcpDefinition.id, serverConfig);
} else {
return copyMcpServerToProject(mcpDefinition.id, serverConfig);
if (!projectPath) {
throw new Error('Project path is required to install to project scope');
}
return copyMcpServerToProject(mcpDefinition.id, serverConfig, projectPath);
}
},
onSuccess: (result) => {