@yancyyu/openhermit 1.6.37 → 1.6.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-renderer/assets/ProjectEditorOverlay-krO5vQxX.js +58 -0
- package/dist-renderer/assets/{TeamGraphOverlay-DYT3bwFR.js → TeamGraphOverlay-DqhQzcTr.js} +1 -1
- package/dist-renderer/assets/{_basePickBy-Dbt_EU-e.js → _basePickBy-B7kSYPxr.js} +1 -1
- package/dist-renderer/assets/{_baseUniq-DWo68sXI.js → _baseUniq-CnjxqwAk.js} +1 -1
- package/dist-renderer/assets/{arc-DXH1iZQK.js → arc-CLeZuINP.js} +1 -1
- package/dist-renderer/assets/{architectureDiagram-VXUJARFQ-cjffS2Qr.js → architectureDiagram-VXUJARFQ-QKtqaqdY.js} +1 -1
- package/dist-renderer/assets/{blockDiagram-VD42YOAC-BKdZF02Y.js → blockDiagram-VD42YOAC-BqdrzO_f.js} +1 -1
- package/dist-renderer/assets/{c4Diagram-YG6GDRKO-CN27pqaI.js → c4Diagram-YG6GDRKO-gwPlCxDC.js} +1 -1
- package/dist-renderer/assets/channel-DpMHF50r.js +1 -0
- package/dist-renderer/assets/{chunk-4BX2VUAB-CXPCI7g_.js → chunk-4BX2VUAB-C6XLurL4.js} +1 -1
- package/dist-renderer/assets/{chunk-55IACEB6-BGAXQZRC.js → chunk-55IACEB6-Ds6quhEP.js} +1 -1
- package/dist-renderer/assets/{chunk-B4BG7PRW-TPDaA_KQ.js → chunk-B4BG7PRW-5UlA1_e9.js} +1 -1
- package/dist-renderer/assets/{chunk-DI55MBZ5-D1ADe_tq.js → chunk-DI55MBZ5-ywFrqIsY.js} +1 -1
- package/dist-renderer/assets/{chunk-FMBD7UC4-Beimtg3a.js → chunk-FMBD7UC4-C7ifUA17.js} +1 -1
- package/dist-renderer/assets/{chunk-QN33PNHL-OjNBu854.js → chunk-QN33PNHL-BxGCo80U.js} +1 -1
- package/dist-renderer/assets/{chunk-QZHKN3VN-DinqvbH8.js → chunk-QZHKN3VN-B2CuaZs6.js} +1 -1
- package/dist-renderer/assets/{chunk-TZMSLE5B-BfFtlPSZ.js → chunk-TZMSLE5B-Ds1hInvp.js} +1 -1
- package/dist-renderer/assets/classDiagram-2ON5EDUG-CBYCBVRl.js +1 -0
- package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-CBYCBVRl.js +1 -0
- package/dist-renderer/assets/clone-DcMF6Psb.js +1 -0
- package/dist-renderer/assets/{cose-bilkent-S5V4N54A-D9z9Dgt7.js → cose-bilkent-S5V4N54A-Cz1GVtLp.js} +1 -1
- package/dist-renderer/assets/{dagre-6UL2VRFP-n1g-DhEE.js → dagre-6UL2VRFP-BrmR-P4h.js} +1 -1
- package/dist-renderer/assets/{diagram-PSM6KHXK-BvxFq-BE.js → diagram-PSM6KHXK-DbNjC5Rg.js} +1 -1
- package/dist-renderer/assets/{diagram-QEK2KX5R-wVnJuwza.js → diagram-QEK2KX5R-qkRX5_Mq.js} +1 -1
- package/dist-renderer/assets/{diagram-S2PKOQOG-B707WJQw.js → diagram-S2PKOQOG-CyL5rCv2.js} +1 -1
- package/dist-renderer/assets/{erDiagram-Q2GNP2WA-C-_1dGHs.js → erDiagram-Q2GNP2WA-Dox3-bA5.js} +1 -1
- package/dist-renderer/assets/{flowDiagram-NV44I4VS-CMTSi3H6.js → flowDiagram-NV44I4VS-BtkaxlDL.js} +1 -1
- package/dist-renderer/assets/{ganttDiagram-JELNMOA3-DZ0bNrAA.js → ganttDiagram-JELNMOA3-Dhy_d9GK.js} +1 -1
- package/dist-renderer/assets/{gitGraphDiagram-V2S2FVAM-DNVfGooQ.js → gitGraphDiagram-V2S2FVAM-B5XRhIQA.js} +1 -1
- package/dist-renderer/assets/{graph-865j_tM_.js → graph-CsoEwUhS.js} +1 -1
- package/dist-renderer/assets/{index-C_F9N5x-.js → index-BWPWmJNo.js} +1 -1
- package/dist-renderer/assets/{index-LwDIsXJN.js → index-Bu2R-Se7.js} +586 -740
- package/dist-renderer/assets/index-CnWV3BhG.css +32 -0
- package/dist-renderer/assets/{index-DuUaf8at.js → index-D-3KgskL.js} +1 -1
- package/dist-renderer/assets/{index-BTx1nc4T.js → index-DGEBzLNT.js} +1 -1
- package/dist-renderer/assets/{index-2EW-eu3q.js → index-NhHNs2Oo.js} +1 -1
- package/dist-renderer/assets/{index-4dEMStJj.js → index-h17WuEyf.js} +1 -1
- package/dist-renderer/assets/{infoDiagram-HS3SLOUP-CyqtElLq.js → infoDiagram-HS3SLOUP-hMGmNojH.js} +1 -1
- package/dist-renderer/assets/{journeyDiagram-XKPGCS4Q-BvjQ0Hm0.js → journeyDiagram-XKPGCS4Q-DXV2rBDl.js} +1 -1
- package/dist-renderer/assets/{kanban-definition-3W4ZIXB7-CJJ-k0zT.js → kanban-definition-3W4ZIXB7-Bf99WLRy.js} +1 -1
- package/dist-renderer/assets/{layout-CnV6rQAG.js → layout-C3XWrpwo.js} +1 -1
- package/dist-renderer/assets/{linear-Cw3UQgyX.js → linear-OEEcn8KN.js} +1 -1
- package/dist-renderer/assets/{mindmap-definition-VGOIOE7T-C5tDaGSK.js → mindmap-definition-VGOIOE7T-Dpi3S2x4.js} +1 -1
- package/dist-renderer/assets/{pieDiagram-ADFJNKIX-CiIpPsau.js → pieDiagram-ADFJNKIX-xTPPhtNx.js} +1 -1
- package/dist-renderer/assets/{quadrantDiagram-AYHSOK5B-C3gtowNj.js → quadrantDiagram-AYHSOK5B-euniyDlz.js} +1 -1
- package/dist-renderer/assets/{requirementDiagram-UZGBJVZJ-CXBTrAnU.js → requirementDiagram-UZGBJVZJ-D9Uiw4kF.js} +1 -1
- package/dist-renderer/assets/{sankeyDiagram-TZEHDZUN-wziX77xG.js → sankeyDiagram-TZEHDZUN-CySU4nED.js} +1 -1
- package/dist-renderer/assets/{sequenceDiagram-WL72ISMW-sYqopcrj.js → sequenceDiagram-WL72ISMW-JVGpET6V.js} +1 -1
- package/dist-renderer/assets/splashScene-D0YB9uxm.js +17 -0
- package/dist-renderer/assets/{stateDiagram-FKZM4ZOC-Bl1-0_Cp.js → stateDiagram-FKZM4ZOC-B2FY5qqi.js} +1 -1
- package/dist-renderer/assets/stateDiagram-v2-4FDKWEC3-DcoMiR8H.js +1 -0
- package/dist-renderer/assets/{timeline-definition-IT6M3QCI-CIRjJUBo.js → timeline-definition-IT6M3QCI-DmycNUUe.js} +1 -1
- package/dist-renderer/assets/{treemap-GDKQZRPO-CVPuNe1n.js → treemap-GDKQZRPO-DPq4gZuB.js} +1 -1
- package/dist-renderer/assets/{xychartDiagram-PRI3JC2R-3nT9yHwp.js → xychartDiagram-PRI3JC2R-J6VVJzRq.js} +1 -1
- package/dist-renderer/index.html +20 -53
- package/package.json +25 -18
- package/src/main/ipc/extensions.ts +30 -50
- package/src/main/server.ts +890 -247
- package/src/main/services/extensions/ExtensionFacadeService.ts +4 -56
- package/src/main/services/extensions/catalog/PluginCatalogService.ts +4 -2
- package/src/main/services/extensions/library/McpLibraryService.ts +243 -0
- package/src/main/services/session-intelligence/ConversationTelemetryService.ts +1101 -0
- package/src/main/services/session-intelligence/LocalSessionScanner.ts +512 -0
- package/src/main/services/session-intelligence/SessionUsageParser.ts +4 -4
- package/src/main/services/session-intelligence/UsageTelemetryService.ts +14 -1
- package/src/main/services/system-manager/SystemManagerConfigService.ts +122 -0
- package/src/main/services/system-manager/SystemManagerPtyService.ts +233 -0
- package/src/main/services/system-manager/WorkflowPromptService.ts +75 -0
- package/src/main/services/teams-mvp/TaskDispatchService.ts +32 -8
- package/src/main/services/teams-mvp/TeamProvisioningService.ts +39 -2
- package/src/main/services/teams-mvp/TeamWorkspaceService.ts +22 -4
- package/src/main/utils/teamProjectResolution.ts +15 -0
- package/src/renderer/App.tsx +8 -4
- package/src/renderer/api/httpClient.ts +174 -38
- package/src/renderer/api/providers.ts +23 -2
- package/src/renderer/assets/participant-avatars/01.svg +3 -0
- package/src/renderer/assets/participant-avatars/02.svg +3 -0
- package/src/renderer/assets/participant-avatars/03.svg +3 -0
- package/src/renderer/assets/participant-avatars/04.svg +3 -0
- package/src/renderer/assets/participant-avatars/05.svg +3 -0
- package/src/renderer/assets/participant-avatars/06.svg +3 -0
- package/src/renderer/assets/participant-avatars/07.svg +3 -0
- package/src/renderer/assets/participant-avatars/08.svg +3 -0
- package/src/renderer/assets/participant-avatars/09.svg +3 -0
- package/src/renderer/assets/participant-avatars/10.svg +3 -0
- package/src/renderer/assets/participant-avatars/11.svg +3 -0
- package/src/renderer/assets/participant-avatars/12.svg +3 -0
- package/src/renderer/assets/participant-avatars/13.svg +3 -0
- package/src/renderer/components/common/TerminalPane.tsx +213 -0
- package/src/renderer/components/dashboard/DashboardView.tsx +9 -36
- package/src/renderer/components/extensions/ExtensionStoreView.tsx +12 -221
- package/src/renderer/components/extensions/ExtensionsSubTabTrigger.tsx +1 -1
- package/src/renderer/components/extensions/mcp/McpLibraryEnableDialog.tsx +305 -0
- package/src/renderer/components/extensions/mcp/McpLibraryEntryDialog.tsx +418 -0
- package/src/renderer/components/extensions/mcp/McpLibraryPanel.tsx +404 -0
- package/src/renderer/components/extensions/plugins/PluginCard.tsx +10 -2
- package/src/renderer/components/extensions/plugins/PluginsPanel.tsx +40 -22
- package/src/renderer/components/extensions/skills/SkillsLibraryPanel.tsx +335 -0
- package/src/renderer/components/layout/PaneContent.tsx +8 -1
- package/src/renderer/components/layout/Sidebar.tsx +11 -54
- package/src/renderer/components/layout/SortableTab.tsx +20 -31
- package/src/renderer/components/layout/TabBar.tsx +1 -1
- package/src/renderer/components/layout/TabContextMenu.tsx +1 -1
- package/src/renderer/components/runtime/ProviderRuntimeSettingsDialog.tsx +768 -157
- package/src/renderer/components/schedules/SchedulesView.tsx +51 -462
- package/src/renderer/components/schedules/calendar/CalendarDayView.tsx +173 -0
- package/src/renderer/components/schedules/calendar/CalendarEventBlock.tsx +113 -0
- package/src/renderer/components/schedules/calendar/CalendarHeader.tsx +148 -0
- package/src/renderer/components/schedules/calendar/CalendarMonthView.tsx +142 -0
- package/src/renderer/components/schedules/calendar/CalendarWeekView.tsx +219 -0
- package/src/renderer/components/schedules/calendar/ScheduleCalendarBoard.tsx +41 -0
- package/src/renderer/components/schedules/calendar/TeamGanttView.tsx +405 -0
- package/src/renderer/components/schedules/calendar/computeOccurrences.ts +234 -0
- package/src/renderer/components/schedules/calendar/index.ts +2 -0
- package/src/renderer/components/schedules/calendar/types.ts +44 -0
- package/src/renderer/components/settings/SettingsTabs.tsx +50 -55
- package/src/renderer/components/settings/SettingsView.tsx +30 -35
- package/src/renderer/components/settings/components/SettingsSectionHeader.tsx +5 -1
- package/src/renderer/components/settings/components/SettingsSelect.tsx +5 -3
- package/src/renderer/components/settings/components/SettingsToggle.tsx +2 -2
- package/src/renderer/components/settings/sections/AdvancedSection.tsx +11 -42
- package/src/renderer/components/settings/sections/CliStatusSection.tsx +71 -112
- package/src/renderer/components/settings/sections/ConfigEditorDialog.tsx +1 -1
- package/src/renderer/components/settings/sections/GeneralSection.tsx +11 -3
- package/src/renderer/components/settings/sections/HarnessSection.tsx +18 -14
- package/src/renderer/components/settings/sections/PlatformsSection.tsx +3 -3
- package/src/renderer/components/settings/sections/TaskBusSection.tsx +33 -40
- package/src/renderer/components/settings/sections/index.ts +0 -1
- package/src/renderer/components/sidebar/SidebarSessions.tsx +182 -4
- package/src/renderer/components/sidebar/SidebarTaskItem.tsx +4 -4
- package/src/renderer/components/sidebar/WorkspaceBrowser.tsx +39 -4
- package/src/renderer/components/splash/splashScene.ts +121 -929
- package/src/renderer/components/system-manager/FolderBrowser.tsx +163 -0
- package/src/renderer/components/system-manager/SystemManagerView.tsx +351 -0
- package/src/renderer/components/tasks/TasksView.tsx +112 -134
- package/src/renderer/components/team/CcSessionsSection.tsx +431 -89
- package/src/renderer/components/team/CollapsibleTeamSection.tsx +17 -32
- package/src/renderer/components/team/TeamDetailView.tsx +325 -114
- package/src/renderer/components/team/TeamListView.tsx +108 -123
- package/src/renderer/components/team/dialogs/CreateTaskDialog.tsx +2 -2
- package/src/renderer/components/team/dialogs/CreateTeamDialog.tsx +84 -306
- package/src/renderer/components/team/dialogs/EditTeamDialog.tsx +259 -342
- package/src/renderer/components/team/dialogs/GlobalTaskDetailDialog.tsx +1 -1
- package/src/renderer/components/team/dialogs/LaunchTeamDialog.tsx +17 -15
- package/src/renderer/components/team/dialogs/PlatformBindingDialog.tsx +221 -0
- package/src/renderer/components/team/dialogs/PlatformManualForm.tsx +7 -0
- package/src/renderer/components/team/dialogs/PlatformSetupQR.tsx +1 -1
- package/src/renderer/components/team/dialogs/RuntimeConfigDialog.tsx +361 -0
- package/src/renderer/components/team/dialogs/platformMeta.ts +122 -11
- package/src/renderer/components/team/dialogs/useTeamEditForm.ts +17 -5
- package/src/renderer/components/team/kanban/KanbanBoard.tsx +9 -9
- package/src/renderer/components/team/members/MemberCard.tsx +14 -47
- package/src/renderer/components/team/members/MemberDetailDialog.tsx +3 -95
- package/src/renderer/components/team/members/MemberDetailStats.tsx +50 -65
- package/src/renderer/components/team/messages/MessageComposer.tsx +8 -110
- package/src/renderer/components/team/messages/MessagesPanel.tsx +131 -114
- package/src/renderer/components/team/schedule/ScheduleStatusBadge.tsx +2 -2
- package/src/renderer/components/team/tools/AddMcpInline.tsx +57 -0
- package/src/renderer/components/team/tools/AddSkillInline.tsx +61 -0
- package/src/renderer/components/team/tools/McpChip.tsx +45 -0
- package/src/renderer/components/team/tools/SkillChip.tsx +35 -0
- package/src/renderer/components/team/tools/ToolsSection.tsx +556 -0
- package/src/renderer/hooks/useExtensionsTabState.ts +3 -114
- package/src/renderer/index.css +39 -22
- package/src/renderer/index.html +17 -50
- package/src/renderer/store/index.ts +2 -1
- package/src/renderer/store/slices/scheduleSlice.ts +1 -1
- package/src/renderer/store/slices/teamSlice.ts +45 -168
- package/src/renderer/utils/claudeCodeOnlyProviders.ts +3 -10
- package/src/renderer/utils/memberHelpers.ts +5 -17
- package/src/renderer/utils/openCodeRuntimeDeliveryDiagnostics.ts +4 -2
- package/src/renderer/utils/providerSlashCommands.ts +0 -5
- package/src/renderer/utils/scheduleFormatters.ts +3 -1
- package/src/renderer/utils/teamMessageFiltering.ts +14 -1
- package/src/renderer/utils/teamModelAvailability.ts +18 -2
- package/src/shared/types/api.ts +121 -2
- package/src/shared/types/ccConnect.ts +2 -0
- package/src/shared/types/extensions/api.ts +9 -0
- package/src/shared/types/extensions/index.ts +4 -0
- package/src/shared/types/extensions/mcp.ts +41 -0
- package/src/shared/types/index.ts +3 -0
- package/src/shared/types/systemManager.ts +49 -0
- package/src/shared/types/team.ts +29 -0
- package/src/shared/types/terminal.ts +4 -2
- package/src/shared/utils/extensionNormalizers.ts +29 -0
- package/src/shared/utils/providerExtensionCapabilities.ts +2 -2
- package/dist-renderer/assets/ProjectEditorOverlay-Va_Vz-zz.js +0 -52
- package/dist-renderer/assets/channel-5dJIx68e.js +0 -1
- package/dist-renderer/assets/classDiagram-2ON5EDUG-BMGXWJ2d.js +0 -1
- package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-BMGXWJ2d.js +0 -1
- package/dist-renderer/assets/clone-D7FWfGY9.js +0 -1
- package/dist-renderer/assets/index-B2z_IyRH.css +0 -1
- package/dist-renderer/assets/splashScene-C8lWNnm4.js +0 -1
- package/dist-renderer/assets/stateDiagram-v2-4FDKWEC3-DOYYvDbi.js +0 -1
- package/src/main/services/extensions/catalog/GlamaMcpEnrichmentService.ts +0 -190
- package/src/main/services/extensions/catalog/McpCatalogAggregator.ts +0 -150
- package/src/main/services/extensions/catalog/OfficialMcpRegistryService.ts +0 -381
- package/src/main/services/extensions/install/McpInstallService.ts +0 -407
- package/src/main/services/extensions/state/McpInstallationStateService.ts +0 -42
- package/src/renderer/components/extensions/mcp/McpServerCard.tsx +0 -314
- package/src/renderer/components/extensions/mcp/McpServerDetailDialog.tsx +0 -765
- package/src/renderer/components/extensions/mcp/McpServersPanel.tsx +0 -593
- package/src/renderer/components/extensions/skills/SkillDetailDialog.tsx +0 -372
- package/src/renderer/components/extensions/skills/SkillImportDialog.tsx +0 -343
- package/src/renderer/components/extensions/skills/SkillsPanel.tsx +0 -659
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Global catalog data comes from Zustand store.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { useCallback, useEffect, useMemo
|
|
7
|
+
import { useCallback, useEffect, useMemo } from 'react';
|
|
8
8
|
|
|
9
9
|
// Stubs for removed codex-account feature
|
|
10
10
|
function useCodexAccountSnapshot(_opts: { enabled: boolean; includeRateLimits?: boolean }) {
|
|
@@ -31,32 +31,16 @@ import { useStore } from '@renderer/store';
|
|
|
31
31
|
import { createLoadingMultimodelCliStatus } from '@renderer/store/slices/cliInstallerSlice';
|
|
32
32
|
import {
|
|
33
33
|
filterExtensionStoreProviders,
|
|
34
|
-
formatCliExtensionCapabilityStatus,
|
|
35
34
|
getVisibleMultimodelProviders,
|
|
36
35
|
isMultimodelRuntimeStatus,
|
|
37
36
|
} from '@renderer/utils/multimodelProviderVisibility';
|
|
38
37
|
import { resolveProjectPathById } from '@renderer/utils/projectLookup';
|
|
39
38
|
import { refreshCliStatusForCurrentMode } from '@renderer/utils/refreshCliStatus';
|
|
40
39
|
import { getRuntimeDisplayName } from '@renderer/utils/runtimeDisplayName';
|
|
41
|
-
import {
|
|
42
|
-
import {
|
|
43
|
-
AlertTriangle,
|
|
44
|
-
BookOpen,
|
|
45
|
-
Info,
|
|
46
|
-
Loader2,
|
|
47
|
-
Plus,
|
|
48
|
-
Puzzle,
|
|
49
|
-
RefreshCw,
|
|
50
|
-
Server,
|
|
51
|
-
Sliders,
|
|
52
|
-
} from 'lucide-react';
|
|
40
|
+
import { AlertTriangle, Info, Loader2, Puzzle, RefreshCw } from 'lucide-react';
|
|
53
41
|
import { useShallow } from 'zustand/react/shallow';
|
|
54
42
|
|
|
55
|
-
import { CustomMcpServerDialog } from './mcp/CustomMcpServerDialog';
|
|
56
|
-
import { EnvVarPanel } from './env/EnvVarPanel';
|
|
57
|
-
import { McpServersPanel } from './mcp/McpServersPanel';
|
|
58
43
|
import { PluginsPanel } from './plugins/PluginsPanel';
|
|
59
|
-
import { SkillsPanel } from './skills/SkillsPanel';
|
|
60
44
|
import { StoreExtensionToast } from './common/ExtensionToast';
|
|
61
45
|
import { ExtensionsSubTabTrigger } from './ExtensionsSubTabTrigger';
|
|
62
46
|
|
|
@@ -87,7 +71,7 @@ const ProviderCapabilityCardSkeleton = ({
|
|
|
87
71
|
</Badge>
|
|
88
72
|
</div>
|
|
89
73
|
<div className="mt-2 flex flex-wrap gap-1.5">
|
|
90
|
-
{Array.from({ length:
|
|
74
|
+
{Array.from({ length: 4 }, (_, index) => (
|
|
91
75
|
<span
|
|
92
76
|
key={index}
|
|
93
77
|
className="h-7 w-28 animate-pulse rounded-md border border-border bg-surface"
|
|
@@ -124,37 +108,13 @@ const EXTENSION_SUB_TABS = [
|
|
|
124
108
|
icon: Puzzle,
|
|
125
109
|
description: 'Claude Code 私有扩展,增强运行时的能力与集成。',
|
|
126
110
|
},
|
|
127
|
-
{
|
|
128
|
-
value: 'mcp-servers' as const,
|
|
129
|
-
label: 'MCP 服务器',
|
|
130
|
-
icon: Server,
|
|
131
|
-
description: '连接外部工具和应用,让运行时可以读取数据或执行本应用之外的操作。',
|
|
132
|
-
},
|
|
133
|
-
{
|
|
134
|
-
value: 'skills' as const,
|
|
135
|
-
label: '技能',
|
|
136
|
-
icon: BookOpen,
|
|
137
|
-
description: '面向常见任务的可复用指令,帮助运行时更稳定地处理重复工作。',
|
|
138
|
-
},
|
|
139
|
-
{
|
|
140
|
-
value: 'env-vars' as const,
|
|
141
|
-
label: '环境变量',
|
|
142
|
-
icon: Sliders,
|
|
143
|
-
description: '管理运行时环境变量,启动 agent 时自动注入。',
|
|
144
|
-
},
|
|
145
111
|
] as const;
|
|
146
112
|
|
|
147
113
|
export const ExtensionStoreView = (): React.JSX.Element => {
|
|
148
|
-
const tabId = useTabIdOptional();
|
|
149
114
|
const {
|
|
150
115
|
bootstrapCliStatus,
|
|
151
116
|
fetchCliStatus,
|
|
152
117
|
fetchPluginCatalog,
|
|
153
|
-
fetchSkillsCatalog,
|
|
154
|
-
mcpBrowse,
|
|
155
|
-
mcpFetchInstalled,
|
|
156
|
-
mcpBrowseLoading,
|
|
157
|
-
skillsLoading,
|
|
158
118
|
cliStatus,
|
|
159
119
|
cliStatusLoading,
|
|
160
120
|
cliProviderStatusLoading,
|
|
@@ -168,12 +128,7 @@ export const ExtensionStoreView = (): React.JSX.Element => {
|
|
|
168
128
|
bootstrapCliStatus: s.bootstrapCliStatus,
|
|
169
129
|
fetchCliStatus: s.fetchCliStatus,
|
|
170
130
|
fetchPluginCatalog: s.fetchPluginCatalog,
|
|
171
|
-
fetchSkillsCatalog: s.fetchSkillsCatalog,
|
|
172
131
|
pluginCatalog: s.pluginCatalog,
|
|
173
|
-
mcpBrowse: s.mcpBrowse,
|
|
174
|
-
mcpFetchInstalled: s.mcpFetchInstalled,
|
|
175
|
-
mcpBrowseLoading: s.mcpBrowseLoading,
|
|
176
|
-
skillsLoading: s.skillsLoading,
|
|
177
132
|
cliStatus: s.cliStatus,
|
|
178
133
|
cliStatusLoading: s.cliStatusLoading,
|
|
179
134
|
cliProviderStatusLoading: s.cliProviderStatusLoading,
|
|
@@ -229,6 +184,9 @@ export const ExtensionStoreView = (): React.JSX.Element => {
|
|
|
229
184
|
const runtimeDisplayName = getRuntimeDisplayName(effectiveCliStatus, multimodelEnabled);
|
|
230
185
|
const cliInstalled = effectiveCliStatus?.installed ?? true;
|
|
231
186
|
const hasOngoingSessions = sessions.some((sess) => sess.isOngoing);
|
|
187
|
+
|
|
188
|
+
const tabState = useExtensionsTabState();
|
|
189
|
+
const tabId = useTabIdOptional();
|
|
232
190
|
const extensionsTabProjectId = useStore((s) =>
|
|
233
191
|
tabId
|
|
234
192
|
? (s.paneLayout.panes.flatMap((pane) => pane.tabs).find((tab) => tab.id === tabId)
|
|
@@ -236,14 +194,11 @@ export const ExtensionStoreView = (): React.JSX.Element => {
|
|
|
236
194
|
: null
|
|
237
195
|
);
|
|
238
196
|
|
|
239
|
-
const tabState = useExtensionsTabState();
|
|
240
|
-
const [customMcpDialogOpen, setCustomMcpDialogOpen] = useState(false);
|
|
241
197
|
const resolvedProject = useMemo(
|
|
242
198
|
() => resolveProjectPathById(extensionsTabProjectId, projects, repositoryGroups),
|
|
243
199
|
[extensionsTabProjectId, projects, repositoryGroups]
|
|
244
200
|
);
|
|
245
201
|
const projectPath = resolvedProject?.path ?? null;
|
|
246
|
-
const projectLabel = resolvedProject?.name ?? null;
|
|
247
202
|
const subTabs = EXTENSION_SUB_TABS;
|
|
248
203
|
|
|
249
204
|
useEffect(() => {
|
|
@@ -254,42 +209,21 @@ export const ExtensionStoreView = (): React.JSX.Element => {
|
|
|
254
209
|
});
|
|
255
210
|
}, [bootstrapCliStatus, fetchCliStatus, multimodelEnabled]);
|
|
256
211
|
|
|
257
|
-
// Fetch MCP installed state on mount
|
|
258
|
-
useEffect(() => {
|
|
259
|
-
void mcpFetchInstalled(projectPath ?? undefined);
|
|
260
|
-
}, [mcpFetchInstalled, projectPath]);
|
|
261
|
-
|
|
262
212
|
// Fetch Plugin catalog on mount / project change
|
|
263
213
|
useEffect(() => {
|
|
264
214
|
void fetchPluginCatalog(projectPath ?? undefined);
|
|
265
215
|
}, [fetchPluginCatalog, projectPath]);
|
|
266
216
|
|
|
267
|
-
//
|
|
268
|
-
useEffect(() => {
|
|
269
|
-
void fetchSkillsCatalog(projectPath ?? undefined);
|
|
270
|
-
}, [fetchSkillsCatalog, projectPath]);
|
|
271
|
-
|
|
272
|
-
// Refresh all data (MCP + skills + runtime status)
|
|
217
|
+
// Refresh all data
|
|
273
218
|
const handleRefresh = useCallback(() => {
|
|
274
219
|
void refreshCliStatusForCurrentMode({
|
|
275
220
|
multimodelEnabled,
|
|
276
221
|
bootstrapCliStatus,
|
|
277
222
|
fetchCliStatus,
|
|
278
223
|
});
|
|
279
|
-
|
|
280
|
-
void mcpFetchInstalled(projectPath ?? undefined);
|
|
281
|
-
void fetchSkillsCatalog(projectPath ?? undefined);
|
|
282
|
-
}, [
|
|
283
|
-
bootstrapCliStatus,
|
|
284
|
-
fetchCliStatus,
|
|
285
|
-
fetchSkillsCatalog,
|
|
286
|
-
multimodelEnabled,
|
|
287
|
-
mcpBrowse,
|
|
288
|
-
mcpFetchInstalled,
|
|
289
|
-
projectPath,
|
|
290
|
-
]);
|
|
224
|
+
}, [bootstrapCliStatus, fetchCliStatus, multimodelEnabled]);
|
|
291
225
|
|
|
292
|
-
const isRefreshing = effectiveCliStatusLoading
|
|
226
|
+
const isRefreshing = effectiveCliStatusLoading;
|
|
293
227
|
const cliStatusBanner = useMemo(() => {
|
|
294
228
|
const providers = effectiveCliStatus?.providers ?? [];
|
|
295
229
|
const visibleProviders = filterExtensionStoreProviders(
|
|
@@ -309,7 +243,7 @@ export const ExtensionStoreView = (): React.JSX.Element => {
|
|
|
309
243
|
<div>
|
|
310
244
|
<p className="text-sm font-medium text-text">正在检查扩展运行时可用性</p>
|
|
311
245
|
<p className="mt-0.5 text-xs text-text-muted">
|
|
312
|
-
|
|
246
|
+
扩展需要配置好的运行时来管理插件和提供商连接。
|
|
313
247
|
</p>
|
|
314
248
|
</div>
|
|
315
249
|
</div>
|
|
@@ -366,101 +300,7 @@ export const ExtensionStoreView = (): React.JSX.Element => {
|
|
|
366
300
|
);
|
|
367
301
|
}
|
|
368
302
|
|
|
369
|
-
|
|
370
|
-
return (
|
|
371
|
-
<div className="bg-surface/70 mx-4 mt-3 rounded-md border border-border px-4 py-3">
|
|
372
|
-
<div className="flex items-start gap-3">
|
|
373
|
-
<Info className="mt-0.5 size-4 shrink-0 text-text-secondary" />
|
|
374
|
-
<div className="min-w-0 flex-1">
|
|
375
|
-
<p className="text-sm font-medium text-text">多模型运行时能力</p>
|
|
376
|
-
<p className="mt-0.5 text-xs text-text-muted">
|
|
377
|
-
不同区域支持的提供商可能不同。只有运行时明确声明支持时,MCP 与技能能力才会显示。
|
|
378
|
-
</p>
|
|
379
|
-
</div>
|
|
380
|
-
</div>
|
|
381
|
-
{visibleProviders.length > 0 && (
|
|
382
|
-
<div className="mt-3 grid gap-2 md:grid-cols-2">
|
|
383
|
-
{visibleProviders.map((provider) => {
|
|
384
|
-
const providerLoading = cliProviderStatusLoading[provider.providerId] === true;
|
|
385
|
-
if (
|
|
386
|
-
isProviderCapabilityCardLoading(provider, providerLoading) ||
|
|
387
|
-
isCodexSnapshotPending(provider, codexSnapshotPending)
|
|
388
|
-
) {
|
|
389
|
-
return (
|
|
390
|
-
<ProviderCapabilityCardSkeleton
|
|
391
|
-
key={provider.providerId}
|
|
392
|
-
providerId={provider.providerId}
|
|
393
|
-
displayName={provider.displayName}
|
|
394
|
-
/>
|
|
395
|
-
);
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
const statusTone = provider.authenticated
|
|
399
|
-
? 'border-emerald-500/30 bg-emerald-500/5 text-emerald-300'
|
|
400
|
-
: provider.supported
|
|
401
|
-
? 'border-amber-500/30 bg-amber-500/5 text-amber-300'
|
|
402
|
-
: 'border-border bg-surface-raised text-text-muted';
|
|
403
|
-
const statusLabel = provider.authenticated
|
|
404
|
-
? '已连接'
|
|
405
|
-
: provider.supported
|
|
406
|
-
? '需要设置'
|
|
407
|
-
: '不支持';
|
|
408
|
-
const extensionCapabilities = getCliProviderExtensionCapabilities(provider);
|
|
409
|
-
|
|
410
|
-
return (
|
|
411
|
-
<div
|
|
412
|
-
key={provider.providerId}
|
|
413
|
-
className={`rounded-md border px-3 py-2 ${statusTone}`}
|
|
414
|
-
>
|
|
415
|
-
<div className="flex items-center justify-between gap-2">
|
|
416
|
-
<div className="min-w-0">
|
|
417
|
-
<p className="inline-flex items-center gap-2 text-sm font-medium">
|
|
418
|
-
<ProviderBrandLogo
|
|
419
|
-
providerId={provider.providerId}
|
|
420
|
-
className="size-4 shrink-0"
|
|
421
|
-
/>
|
|
422
|
-
<span>{provider.displayName}</span>
|
|
423
|
-
</p>
|
|
424
|
-
<p className="truncate text-[11px] text-text-muted">
|
|
425
|
-
{provider.statusMessage ?? provider.backend?.label ?? '可配置'}
|
|
426
|
-
</p>
|
|
427
|
-
</div>
|
|
428
|
-
<Badge variant="outline" className="shrink-0">
|
|
429
|
-
{statusLabel}
|
|
430
|
-
</Badge>
|
|
431
|
-
</div>
|
|
432
|
-
<div className="mt-2 flex flex-wrap gap-1.5 text-[11px]">
|
|
433
|
-
<Badge variant="secondary">
|
|
434
|
-
MCP: {formatCliExtensionCapabilityStatus(extensionCapabilities.mcp.status)}
|
|
435
|
-
</Badge>
|
|
436
|
-
<Badge variant="secondary">
|
|
437
|
-
技能:{extensionCapabilities.skills.ownership}
|
|
438
|
-
</Badge>
|
|
439
|
-
</div>
|
|
440
|
-
</div>
|
|
441
|
-
);
|
|
442
|
-
})}
|
|
443
|
-
</div>
|
|
444
|
-
)}
|
|
445
|
-
</div>
|
|
446
|
-
);
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
return (
|
|
450
|
-
<div className="mx-4 mt-3 flex items-start gap-3 rounded-md border border-emerald-500/30 bg-emerald-500/5 px-4 py-3">
|
|
451
|
-
<Info className="mt-0.5 size-4 shrink-0 text-emerald-300" />
|
|
452
|
-
<div>
|
|
453
|
-
<p className="text-sm font-medium text-emerald-300">{runtimeDisplayName} 已就绪</p>
|
|
454
|
-
<p className="mt-0.5 text-xs text-text-muted">
|
|
455
|
-
可以从此页面管理 MCP 服务器与技能
|
|
456
|
-
{effectiveCliStatus.installedVersion
|
|
457
|
-
? `,使用 ${runtimeDisplayName} ${effectiveCliStatus.installedVersion}`
|
|
458
|
-
: ''}
|
|
459
|
-
.
|
|
460
|
-
</p>
|
|
461
|
-
</div>
|
|
462
|
-
</div>
|
|
463
|
-
);
|
|
303
|
+
return null;
|
|
464
304
|
}, [
|
|
465
305
|
cliProviderStatusLoading,
|
|
466
306
|
codexSnapshotPending,
|
|
@@ -470,7 +310,7 @@ export const ExtensionStoreView = (): React.JSX.Element => {
|
|
|
470
310
|
]);
|
|
471
311
|
|
|
472
312
|
// Browser mode guard
|
|
473
|
-
if (!api.plugins
|
|
313
|
+
if (!api.plugins) {
|
|
474
314
|
return (
|
|
475
315
|
<div className="flex flex-1 items-center justify-center">
|
|
476
316
|
<div className="text-center">
|
|
@@ -535,17 +375,6 @@ export const ExtensionStoreView = (): React.JSX.Element => {
|
|
|
535
375
|
/>
|
|
536
376
|
))}
|
|
537
377
|
</TabsList>
|
|
538
|
-
{tabState.activeSubTab === 'mcp-servers' && (
|
|
539
|
-
<Button
|
|
540
|
-
variant="outline"
|
|
541
|
-
size="sm"
|
|
542
|
-
onClick={() => setCustomMcpDialogOpen(true)}
|
|
543
|
-
className="mb-1 whitespace-nowrap"
|
|
544
|
-
>
|
|
545
|
-
<Plus className="mr-1 size-3.5" />
|
|
546
|
-
添加自定义
|
|
547
|
-
</Button>
|
|
548
|
-
)}
|
|
549
378
|
</div>
|
|
550
379
|
|
|
551
380
|
<TabsContent value="plugins" className="mt-0 pt-4">
|
|
@@ -566,45 +395,7 @@ export const ExtensionStoreView = (): React.JSX.Element => {
|
|
|
566
395
|
cliStatusLoading={effectiveCliStatusLoading}
|
|
567
396
|
/>
|
|
568
397
|
</TabsContent>
|
|
569
|
-
|
|
570
|
-
<TabsContent value="mcp-servers" className="mt-0 pt-4">
|
|
571
|
-
<McpServersPanel
|
|
572
|
-
projectPath={projectPath}
|
|
573
|
-
mcpSearchQuery={tabState.mcpSearchQuery}
|
|
574
|
-
mcpSearch={tabState.mcpSearch}
|
|
575
|
-
mcpSearchResults={tabState.mcpSearchResults}
|
|
576
|
-
mcpSearchLoading={tabState.mcpSearchLoading}
|
|
577
|
-
mcpSearchWarnings={tabState.mcpSearchWarnings}
|
|
578
|
-
selectedMcpServerId={tabState.selectedMcpServerId}
|
|
579
|
-
setSelectedMcpServerId={tabState.setSelectedMcpServerId}
|
|
580
|
-
cliStatus={effectiveCliStatus}
|
|
581
|
-
cliStatusLoading={effectiveCliStatusLoading}
|
|
582
|
-
/>
|
|
583
|
-
</TabsContent>
|
|
584
|
-
|
|
585
|
-
<TabsContent value="skills" className="mt-0 pt-4">
|
|
586
|
-
<SkillsPanel
|
|
587
|
-
projectPath={projectPath}
|
|
588
|
-
projectLabel={projectLabel}
|
|
589
|
-
skillsSearchQuery={tabState.skillsSearchQuery}
|
|
590
|
-
setSkillsSearchQuery={tabState.setSkillsSearchQuery}
|
|
591
|
-
skillsSort={tabState.skillsSort}
|
|
592
|
-
setSkillsSort={tabState.setSkillsSort}
|
|
593
|
-
selectedSkillId={tabState.selectedSkillId}
|
|
594
|
-
setSelectedSkillId={tabState.setSelectedSkillId}
|
|
595
|
-
/>
|
|
596
|
-
</TabsContent>
|
|
597
|
-
|
|
598
|
-
<TabsContent value="env-vars" className="mt-0 pt-4">
|
|
599
|
-
<EnvVarPanel projectPath={projectPath} />
|
|
600
|
-
</TabsContent>
|
|
601
398
|
</Tabs>
|
|
602
|
-
|
|
603
|
-
{/* Custom MCP server dialog (lifted to store view level) */}
|
|
604
|
-
<CustomMcpServerDialog
|
|
605
|
-
open={customMcpDialogOpen}
|
|
606
|
-
onClose={() => setCustomMcpDialogOpen(false)}
|
|
607
|
-
/>
|
|
608
399
|
</div>
|
|
609
400
|
</div>
|
|
610
401
|
</div>
|
|
@@ -5,7 +5,7 @@ import { Info } from 'lucide-react';
|
|
|
5
5
|
import type { LucideIcon } from 'lucide-react';
|
|
6
6
|
|
|
7
7
|
interface ExtensionsSubTabTriggerProps {
|
|
8
|
-
value: 'plugins'
|
|
8
|
+
value: 'plugins';
|
|
9
9
|
label: string;
|
|
10
10
|
description: string;
|
|
11
11
|
icon: LucideIcon;
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* McpLibraryEnableDialog — enables a global MCP template as a project instance.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
6
|
+
|
|
7
|
+
import { Button } from '@renderer/components/ui/button';
|
|
8
|
+
import {
|
|
9
|
+
Dialog,
|
|
10
|
+
DialogContent,
|
|
11
|
+
DialogDescription,
|
|
12
|
+
DialogHeader,
|
|
13
|
+
DialogTitle,
|
|
14
|
+
} from '@renderer/components/ui/dialog';
|
|
15
|
+
import { Input } from '@renderer/components/ui/input';
|
|
16
|
+
import { Label } from '@renderer/components/ui/label';
|
|
17
|
+
import { useStore } from '@renderer/store';
|
|
18
|
+
import { Plus, Server, Trash2 } from 'lucide-react';
|
|
19
|
+
|
|
20
|
+
import type { InstalledMcpEntry, McpHeaderDef, McpLibraryEntry } from '@shared/types/extensions';
|
|
21
|
+
|
|
22
|
+
interface McpLibraryEnableDialogProps {
|
|
23
|
+
open: boolean;
|
|
24
|
+
entry: McpLibraryEntry | null;
|
|
25
|
+
projectPath: string | null;
|
|
26
|
+
installedServers?: InstalledMcpEntry[];
|
|
27
|
+
harnessType?: string;
|
|
28
|
+
onClose: () => void;
|
|
29
|
+
onEnabled: () => void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface EnvRow {
|
|
33
|
+
key: string;
|
|
34
|
+
value: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const SERVER_NAME_RE = /^[\w.-]{1,100}$/;
|
|
38
|
+
|
|
39
|
+
function summarizeMcp(entry: McpLibraryEntry): string {
|
|
40
|
+
if (entry.installSpec.type === 'stdio') {
|
|
41
|
+
return `stdio · ${entry.installSpec.npmPackage}${entry.installSpec.npmVersion ? `@${entry.installSpec.npmVersion}` : ''}`;
|
|
42
|
+
}
|
|
43
|
+
return `${entry.installSpec.transportType} · ${entry.installSpec.url}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function envRowsFromRecord(record?: Record<string, string>): EnvRow[] {
|
|
47
|
+
return Object.entries(record ?? {}).map(([key, value]) => ({ key, value }));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function envRecordFromRows(rows: EnvRow[]): Record<string, string> {
|
|
51
|
+
return Object.fromEntries(
|
|
52
|
+
rows.map((row) => [row.key.trim(), row.value]).filter(([key]) => key.length > 0)
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function cleanHeaders(headers: McpHeaderDef[]): McpHeaderDef[] {
|
|
57
|
+
return headers
|
|
58
|
+
.map((header) => ({ ...header, key: header.key.trim() }))
|
|
59
|
+
.filter((header) => header.key && header.value);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const McpLibraryEnableDialog = ({
|
|
63
|
+
open,
|
|
64
|
+
entry,
|
|
65
|
+
projectPath,
|
|
66
|
+
installedServers = [],
|
|
67
|
+
harnessType,
|
|
68
|
+
onClose,
|
|
69
|
+
onEnabled,
|
|
70
|
+
}: McpLibraryEnableDialogProps): React.JSX.Element => {
|
|
71
|
+
const installCustomMcpServer = useStore((s) => s.installCustomMcpServer);
|
|
72
|
+
const mcpFetchInstalled = useStore((s) => s.mcpFetchInstalled);
|
|
73
|
+
const runMcpDiagnostics = useStore((s) => s.runMcpDiagnostics);
|
|
74
|
+
|
|
75
|
+
const [serverName, setServerName] = useState('');
|
|
76
|
+
const [envRows, setEnvRows] = useState<EnvRow[]>([]);
|
|
77
|
+
const [headers, setHeaders] = useState<McpHeaderDef[]>([]);
|
|
78
|
+
const [installing, setInstalling] = useState(false);
|
|
79
|
+
const [error, setError] = useState<string | null>(null);
|
|
80
|
+
|
|
81
|
+
const installedNames = useMemo(
|
|
82
|
+
() => new Set(installedServers.map((server) => server.name.toLowerCase())),
|
|
83
|
+
[installedServers]
|
|
84
|
+
);
|
|
85
|
+
const trimmedName = serverName.trim();
|
|
86
|
+
const nameExists = Boolean(trimmedName) && installedNames.has(trimmedName.toLowerCase());
|
|
87
|
+
const canSubmit = Boolean(entry && projectPath && trimmedName && !nameExists && !installing);
|
|
88
|
+
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
if (!open || !entry) return;
|
|
91
|
+
setServerName(entry.name);
|
|
92
|
+
setEnvRows(envRowsFromRecord(entry.envValues));
|
|
93
|
+
setHeaders(entry.headers ?? []);
|
|
94
|
+
setError(null);
|
|
95
|
+
setInstalling(false);
|
|
96
|
+
}, [entry, open]);
|
|
97
|
+
|
|
98
|
+
const addEnvRow = (): void => setEnvRows((prev) => [...prev, { key: '', value: '' }]);
|
|
99
|
+
const removeEnvRow = (index: number): void =>
|
|
100
|
+
setEnvRows((prev) => prev.filter((_, i) => i !== index));
|
|
101
|
+
const updateEnvRow = (index: number, field: keyof EnvRow, value: string): void =>
|
|
102
|
+
setEnvRows((prev) => prev.map((row, i) => (i === index ? { ...row, [field]: value } : row)));
|
|
103
|
+
|
|
104
|
+
const addHeader = (): void => setHeaders((prev) => [...prev, { key: '', value: '' }]);
|
|
105
|
+
const removeHeader = (index: number): void =>
|
|
106
|
+
setHeaders((prev) => prev.filter((_, i) => i !== index));
|
|
107
|
+
const updateHeader = (index: number, field: 'key' | 'value', value: string): void =>
|
|
108
|
+
setHeaders((prev) =>
|
|
109
|
+
prev.map((header, i) => (i === index ? { ...header, [field]: value } : header))
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const handleEnable = async (): Promise<void> => {
|
|
113
|
+
if (!entry || !projectPath) return;
|
|
114
|
+
|
|
115
|
+
if (!trimmedName) {
|
|
116
|
+
setError('实例名称不能为空');
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if (!SERVER_NAME_RE.test(trimmedName)) {
|
|
120
|
+
setError('实例名称只能包含字母、数字、下划线、短横线和点号,最多 100 个字符');
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (nameExists) {
|
|
124
|
+
setError('当前项目已存在同名实例,请改用其他实例名');
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
setInstalling(true);
|
|
129
|
+
setError(null);
|
|
130
|
+
try {
|
|
131
|
+
await installCustomMcpServer({
|
|
132
|
+
serverName: trimmedName,
|
|
133
|
+
scope: 'project',
|
|
134
|
+
projectPath,
|
|
135
|
+
installSpec: entry.installSpec,
|
|
136
|
+
envValues: envRecordFromRows(envRows),
|
|
137
|
+
headers: cleanHeaders(headers),
|
|
138
|
+
harnessType: harnessType ?? 'claudecode',
|
|
139
|
+
});
|
|
140
|
+
await Promise.all([mcpFetchInstalled(projectPath), runMcpDiagnostics(projectPath)]);
|
|
141
|
+
onEnabled();
|
|
142
|
+
} catch (err) {
|
|
143
|
+
setError(err instanceof Error ? err.message : '添加项目实例失败');
|
|
144
|
+
} finally {
|
|
145
|
+
setInstalling(false);
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
return (
|
|
150
|
+
<Dialog open={open} onOpenChange={(nextOpen) => !nextOpen && !installing && onClose()}>
|
|
151
|
+
<DialogContent className="max-h-[85vh] w-full max-w-2xl overflow-y-auto">
|
|
152
|
+
<DialogHeader>
|
|
153
|
+
<div className="flex items-center gap-2">
|
|
154
|
+
<div className="flex size-8 items-center justify-center rounded-lg border border-border bg-surface-raised">
|
|
155
|
+
<Server className="size-4 text-text-muted" />
|
|
156
|
+
</div>
|
|
157
|
+
<div>
|
|
158
|
+
<DialogTitle>添加 MCP 项目实例</DialogTitle>
|
|
159
|
+
<DialogDescription>
|
|
160
|
+
从全局模板创建当前项目专属实例,可覆盖实例名、环境变量和请求头。
|
|
161
|
+
</DialogDescription>
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
</DialogHeader>
|
|
165
|
+
|
|
166
|
+
{entry ? (
|
|
167
|
+
<div className="space-y-4">
|
|
168
|
+
<div className="bg-surface-raised/40 rounded-md border border-border px-3 py-2">
|
|
169
|
+
<div className="text-xs font-medium text-text">模板:{entry.name}</div>
|
|
170
|
+
<div className="mt-1 truncate font-mono text-[11px] text-text-muted">
|
|
171
|
+
{summarizeMcp(entry)}
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
|
|
175
|
+
<div className="space-y-1.5">
|
|
176
|
+
<Label htmlFor="mcp-enable-instance-name" className="text-xs">
|
|
177
|
+
项目实例名称
|
|
178
|
+
</Label>
|
|
179
|
+
<Input
|
|
180
|
+
id="mcp-enable-instance-name"
|
|
181
|
+
value={serverName}
|
|
182
|
+
onChange={(event) => setServerName(event.target.value)}
|
|
183
|
+
placeholder={entry.name}
|
|
184
|
+
className="h-8 font-mono text-sm"
|
|
185
|
+
autoFocus
|
|
186
|
+
/>
|
|
187
|
+
<p
|
|
188
|
+
className={
|
|
189
|
+
nameExists ? 'text-[11px] text-amber-300' : 'text-[11px] text-text-muted'
|
|
190
|
+
}
|
|
191
|
+
>
|
|
192
|
+
{nameExists
|
|
193
|
+
? '当前项目已存在同名实例,请改用其他实例名。'
|
|
194
|
+
: '实例名只写入当前项目,可与模板名不同。'}
|
|
195
|
+
</p>
|
|
196
|
+
</div>
|
|
197
|
+
|
|
198
|
+
<div className="space-y-2 rounded-lg border border-border p-3">
|
|
199
|
+
<div className="flex items-center justify-between gap-2">
|
|
200
|
+
<div>
|
|
201
|
+
<Label className="text-xs">项目环境变量</Label>
|
|
202
|
+
<p className="text-[11px] text-text-muted">
|
|
203
|
+
已预填模板默认值,可按当前项目覆盖。
|
|
204
|
+
</p>
|
|
205
|
+
</div>
|
|
206
|
+
<Button variant="ghost" size="sm" onClick={addEnvRow} className="h-7 px-2 text-xs">
|
|
207
|
+
<Plus className="mr-1 size-3" />
|
|
208
|
+
添加
|
|
209
|
+
</Button>
|
|
210
|
+
</div>
|
|
211
|
+
{envRows.length > 0 ? (
|
|
212
|
+
<div className="space-y-2">
|
|
213
|
+
{envRows.map((row, index) => (
|
|
214
|
+
<div key={index} className="flex items-center gap-2">
|
|
215
|
+
<Input
|
|
216
|
+
value={row.key}
|
|
217
|
+
onChange={(event) => updateEnvRow(index, 'key', event.target.value)}
|
|
218
|
+
className="h-7 w-40 font-mono text-xs"
|
|
219
|
+
placeholder="ENV_NAME"
|
|
220
|
+
/>
|
|
221
|
+
<Input
|
|
222
|
+
value={row.value}
|
|
223
|
+
onChange={(event) => updateEnvRow(index, 'value', event.target.value)}
|
|
224
|
+
className="h-7 flex-1 text-xs"
|
|
225
|
+
placeholder="value"
|
|
226
|
+
/>
|
|
227
|
+
<Button
|
|
228
|
+
variant="ghost"
|
|
229
|
+
size="icon"
|
|
230
|
+
className="size-7 text-red-400 hover:bg-red-500/10"
|
|
231
|
+
onClick={() => removeEnvRow(index)}
|
|
232
|
+
>
|
|
233
|
+
<Trash2 className="size-3" />
|
|
234
|
+
</Button>
|
|
235
|
+
</div>
|
|
236
|
+
))}
|
|
237
|
+
</div>
|
|
238
|
+
) : (
|
|
239
|
+
<p className="text-[11px] text-text-muted">此模板没有环境变量默认值。</p>
|
|
240
|
+
)}
|
|
241
|
+
</div>
|
|
242
|
+
|
|
243
|
+
<div className="space-y-2 rounded-lg border border-border p-3">
|
|
244
|
+
<div className="flex items-center justify-between gap-2">
|
|
245
|
+
<div>
|
|
246
|
+
<Label className="text-xs">项目请求头</Label>
|
|
247
|
+
<p className="text-[11px] text-text-muted">HTTP/SSE 实例可用,stdio 通常忽略。</p>
|
|
248
|
+
</div>
|
|
249
|
+
<Button variant="ghost" size="sm" onClick={addHeader} className="h-7 px-2 text-xs">
|
|
250
|
+
<Plus className="mr-1 size-3" />
|
|
251
|
+
添加
|
|
252
|
+
</Button>
|
|
253
|
+
</div>
|
|
254
|
+
{headers.length > 0 ? (
|
|
255
|
+
<div className="space-y-2">
|
|
256
|
+
{headers.map((header, index) => (
|
|
257
|
+
<div key={index} className="flex items-center gap-2">
|
|
258
|
+
<Input
|
|
259
|
+
value={header.key}
|
|
260
|
+
onChange={(event) => updateHeader(index, 'key', event.target.value)}
|
|
261
|
+
className="h-7 w-40 text-xs"
|
|
262
|
+
placeholder="Header-Name"
|
|
263
|
+
/>
|
|
264
|
+
<Input
|
|
265
|
+
value={header.value}
|
|
266
|
+
onChange={(event) => updateHeader(index, 'value', event.target.value)}
|
|
267
|
+
className="h-7 flex-1 text-xs"
|
|
268
|
+
placeholder="value"
|
|
269
|
+
/>
|
|
270
|
+
<Button
|
|
271
|
+
variant="ghost"
|
|
272
|
+
size="icon"
|
|
273
|
+
className="size-7 text-red-400 hover:bg-red-500/10"
|
|
274
|
+
onClick={() => removeHeader(index)}
|
|
275
|
+
>
|
|
276
|
+
<Trash2 className="size-3" />
|
|
277
|
+
</Button>
|
|
278
|
+
</div>
|
|
279
|
+
))}
|
|
280
|
+
</div>
|
|
281
|
+
) : (
|
|
282
|
+
<p className="text-[11px] text-text-muted">此模板没有请求头默认值。</p>
|
|
283
|
+
)}
|
|
284
|
+
</div>
|
|
285
|
+
|
|
286
|
+
{error && (
|
|
287
|
+
<div className="rounded-md border border-red-500/30 bg-red-500/5 px-3 py-2 text-xs text-red-400">
|
|
288
|
+
{error}
|
|
289
|
+
</div>
|
|
290
|
+
)}
|
|
291
|
+
|
|
292
|
+
<div className="flex justify-end gap-2 pt-2">
|
|
293
|
+
<Button variant="ghost" size="sm" onClick={onClose} disabled={installing}>
|
|
294
|
+
取消
|
|
295
|
+
</Button>
|
|
296
|
+
<Button size="sm" disabled={!canSubmit} onClick={() => void handleEnable()}>
|
|
297
|
+
{installing ? '添加中...' : '添加到当前项目'}
|
|
298
|
+
</Button>
|
|
299
|
+
</div>
|
|
300
|
+
</div>
|
|
301
|
+
) : null}
|
|
302
|
+
</DialogContent>
|
|
303
|
+
</Dialog>
|
|
304
|
+
);
|
|
305
|
+
};
|