mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-10 02:24:35 +08:00
feat: 添加更好的 SQLite3 模块加载和错误处理,更新相关组件以支持项目路径
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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' })}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
Reference in New Issue
Block a user