@yancyyu/openhermit 1.6.38 → 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.
Files changed (188) hide show
  1. package/dist-renderer/assets/ProjectEditorOverlay-krO5vQxX.js +58 -0
  2. package/dist-renderer/assets/{TeamGraphOverlay-ZEDfZyHb.js → TeamGraphOverlay-DqhQzcTr.js} +1 -1
  3. package/dist-renderer/assets/{_basePickBy-CIhniz70.js → _basePickBy-B7kSYPxr.js} +1 -1
  4. package/dist-renderer/assets/{_baseUniq-cKAW4Q8I.js → _baseUniq-CnjxqwAk.js} +1 -1
  5. package/dist-renderer/assets/{arc-YmNsoDXW.js → arc-CLeZuINP.js} +1 -1
  6. package/dist-renderer/assets/{architectureDiagram-VXUJARFQ-DHEls2sX.js → architectureDiagram-VXUJARFQ-QKtqaqdY.js} +1 -1
  7. package/dist-renderer/assets/{blockDiagram-VD42YOAC-Bpwf1Sbg.js → blockDiagram-VD42YOAC-BqdrzO_f.js} +1 -1
  8. package/dist-renderer/assets/{c4Diagram-YG6GDRKO-B0IaQ4w5.js → c4Diagram-YG6GDRKO-gwPlCxDC.js} +1 -1
  9. package/dist-renderer/assets/channel-DpMHF50r.js +1 -0
  10. package/dist-renderer/assets/{chunk-4BX2VUAB-DLk-hcFc.js → chunk-4BX2VUAB-C6XLurL4.js} +1 -1
  11. package/dist-renderer/assets/{chunk-55IACEB6-1XRmX_Zm.js → chunk-55IACEB6-Ds6quhEP.js} +1 -1
  12. package/dist-renderer/assets/{chunk-B4BG7PRW-1waH1DAD.js → chunk-B4BG7PRW-5UlA1_e9.js} +1 -1
  13. package/dist-renderer/assets/{chunk-DI55MBZ5-BqpZBtrN.js → chunk-DI55MBZ5-ywFrqIsY.js} +1 -1
  14. package/dist-renderer/assets/{chunk-FMBD7UC4-Bly7vVym.js → chunk-FMBD7UC4-C7ifUA17.js} +1 -1
  15. package/dist-renderer/assets/{chunk-QN33PNHL-Ci2QWBAs.js → chunk-QN33PNHL-BxGCo80U.js} +1 -1
  16. package/dist-renderer/assets/{chunk-QZHKN3VN-YCqFW7d-.js → chunk-QZHKN3VN-B2CuaZs6.js} +1 -1
  17. package/dist-renderer/assets/{chunk-TZMSLE5B-B0xGXInl.js → chunk-TZMSLE5B-Ds1hInvp.js} +1 -1
  18. package/dist-renderer/assets/classDiagram-2ON5EDUG-CBYCBVRl.js +1 -0
  19. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-CBYCBVRl.js +1 -0
  20. package/dist-renderer/assets/clone-DcMF6Psb.js +1 -0
  21. package/dist-renderer/assets/{cose-bilkent-S5V4N54A-DxcFNQKT.js → cose-bilkent-S5V4N54A-Cz1GVtLp.js} +1 -1
  22. package/dist-renderer/assets/{dagre-6UL2VRFP-DPo_RfZY.js → dagre-6UL2VRFP-BrmR-P4h.js} +1 -1
  23. package/dist-renderer/assets/{diagram-PSM6KHXK-U3hQsFe4.js → diagram-PSM6KHXK-DbNjC5Rg.js} +1 -1
  24. package/dist-renderer/assets/{diagram-QEK2KX5R-OrwrAy0V.js → diagram-QEK2KX5R-qkRX5_Mq.js} +1 -1
  25. package/dist-renderer/assets/{diagram-S2PKOQOG-CXATPWVw.js → diagram-S2PKOQOG-CyL5rCv2.js} +1 -1
  26. package/dist-renderer/assets/{erDiagram-Q2GNP2WA-B0e8AfMF.js → erDiagram-Q2GNP2WA-Dox3-bA5.js} +1 -1
  27. package/dist-renderer/assets/{flowDiagram-NV44I4VS-CXfzA4jJ.js → flowDiagram-NV44I4VS-BtkaxlDL.js} +1 -1
  28. package/dist-renderer/assets/{ganttDiagram-JELNMOA3-CMr08qVl.js → ganttDiagram-JELNMOA3-Dhy_d9GK.js} +1 -1
  29. package/dist-renderer/assets/{gitGraphDiagram-V2S2FVAM-vYFHpPmy.js → gitGraphDiagram-V2S2FVAM-B5XRhIQA.js} +1 -1
  30. package/dist-renderer/assets/{graph-DOe5j8dH.js → graph-CsoEwUhS.js} +1 -1
  31. package/dist-renderer/assets/{index-BySQS7AB.js → index-BWPWmJNo.js} +1 -1
  32. package/dist-renderer/assets/{index-V7dAKPqd.js → index-Bu2R-Se7.js} +587 -705
  33. package/dist-renderer/assets/index-CnWV3BhG.css +32 -0
  34. package/dist-renderer/assets/{index-CzWxVCRL.js → index-D-3KgskL.js} +1 -1
  35. package/dist-renderer/assets/{index-VJ-MM9xa.js → index-DGEBzLNT.js} +1 -1
  36. package/dist-renderer/assets/{index-B2Dy7M2G.js → index-NhHNs2Oo.js} +1 -1
  37. package/dist-renderer/assets/{index-C_okzZXP.js → index-h17WuEyf.js} +1 -1
  38. package/dist-renderer/assets/{infoDiagram-HS3SLOUP-D_WubR0B.js → infoDiagram-HS3SLOUP-hMGmNojH.js} +1 -1
  39. package/dist-renderer/assets/{journeyDiagram-XKPGCS4Q-w9ca-1TI.js → journeyDiagram-XKPGCS4Q-DXV2rBDl.js} +1 -1
  40. package/dist-renderer/assets/{kanban-definition-3W4ZIXB7-Jg9p6_pN.js → kanban-definition-3W4ZIXB7-Bf99WLRy.js} +1 -1
  41. package/dist-renderer/assets/{layout-B-z3y17c.js → layout-C3XWrpwo.js} +1 -1
  42. package/dist-renderer/assets/{linear-D-RTX5UW.js → linear-OEEcn8KN.js} +1 -1
  43. package/dist-renderer/assets/{mindmap-definition-VGOIOE7T-CDQmHOYP.js → mindmap-definition-VGOIOE7T-Dpi3S2x4.js} +1 -1
  44. package/dist-renderer/assets/{pieDiagram-ADFJNKIX-D_odsQL7.js → pieDiagram-ADFJNKIX-xTPPhtNx.js} +1 -1
  45. package/dist-renderer/assets/{quadrantDiagram-AYHSOK5B-BRsmYWSA.js → quadrantDiagram-AYHSOK5B-euniyDlz.js} +1 -1
  46. package/dist-renderer/assets/{requirementDiagram-UZGBJVZJ-ChNE_BOV.js → requirementDiagram-UZGBJVZJ-D9Uiw4kF.js} +1 -1
  47. package/dist-renderer/assets/{sankeyDiagram-TZEHDZUN-C8FtpwKc.js → sankeyDiagram-TZEHDZUN-CySU4nED.js} +1 -1
  48. package/dist-renderer/assets/{sequenceDiagram-WL72ISMW-DmLCzNcc.js → sequenceDiagram-WL72ISMW-JVGpET6V.js} +1 -1
  49. package/dist-renderer/assets/splashScene-D0YB9uxm.js +17 -0
  50. package/dist-renderer/assets/{stateDiagram-FKZM4ZOC-WJBm4bhu.js → stateDiagram-FKZM4ZOC-B2FY5qqi.js} +1 -1
  51. package/dist-renderer/assets/stateDiagram-v2-4FDKWEC3-DcoMiR8H.js +1 -0
  52. package/dist-renderer/assets/{timeline-definition-IT6M3QCI-BXs_hOJs.js → timeline-definition-IT6M3QCI-DmycNUUe.js} +1 -1
  53. package/dist-renderer/assets/{treemap-GDKQZRPO-o04MA0G9.js → treemap-GDKQZRPO-DPq4gZuB.js} +1 -1
  54. package/dist-renderer/assets/{xychartDiagram-PRI3JC2R-Czj69XRd.js → xychartDiagram-PRI3JC2R-J6VVJzRq.js} +1 -1
  55. package/dist-renderer/index.html +20 -53
  56. package/package.json +25 -18
  57. package/src/main/ipc/extensions.ts +2 -1
  58. package/src/main/server.ts +873 -221
  59. package/src/main/services/extensions/ExtensionFacadeService.ts +2 -5
  60. package/src/main/services/extensions/catalog/PluginCatalogService.ts +4 -2
  61. package/src/main/services/session-intelligence/ConversationTelemetryService.ts +1101 -0
  62. package/src/main/services/session-intelligence/LocalSessionScanner.ts +512 -0
  63. package/src/main/services/session-intelligence/SessionUsageParser.ts +4 -4
  64. package/src/main/services/system-manager/SystemManagerConfigService.ts +122 -0
  65. package/src/main/services/system-manager/SystemManagerPtyService.ts +233 -0
  66. package/src/main/services/system-manager/WorkflowPromptService.ts +75 -0
  67. package/src/main/services/teams-mvp/TaskDispatchService.ts +5 -6
  68. package/src/main/services/teams-mvp/TeamProvisioningService.ts +39 -2
  69. package/src/main/services/teams-mvp/TeamWorkspaceService.ts +22 -4
  70. package/src/main/utils/teamProjectResolution.ts +15 -0
  71. package/src/renderer/App.tsx +8 -4
  72. package/src/renderer/api/httpClient.ts +68 -18
  73. package/src/renderer/api/providers.ts +23 -2
  74. package/src/renderer/assets/participant-avatars/01.svg +3 -0
  75. package/src/renderer/assets/participant-avatars/02.svg +3 -0
  76. package/src/renderer/assets/participant-avatars/03.svg +3 -0
  77. package/src/renderer/assets/participant-avatars/04.svg +3 -0
  78. package/src/renderer/assets/participant-avatars/05.svg +3 -0
  79. package/src/renderer/assets/participant-avatars/06.svg +3 -0
  80. package/src/renderer/assets/participant-avatars/07.svg +3 -0
  81. package/src/renderer/assets/participant-avatars/08.svg +3 -0
  82. package/src/renderer/assets/participant-avatars/09.svg +3 -0
  83. package/src/renderer/assets/participant-avatars/10.svg +3 -0
  84. package/src/renderer/assets/participant-avatars/11.svg +3 -0
  85. package/src/renderer/assets/participant-avatars/12.svg +3 -0
  86. package/src/renderer/assets/participant-avatars/13.svg +3 -0
  87. package/src/renderer/components/common/TerminalPane.tsx +213 -0
  88. package/src/renderer/components/dashboard/DashboardView.tsx +9 -36
  89. package/src/renderer/components/extensions/ExtensionStoreView.tsx +6 -125
  90. package/src/renderer/components/extensions/ExtensionsSubTabTrigger.tsx +1 -1
  91. package/src/renderer/components/extensions/mcp/McpLibraryEnableDialog.tsx +305 -0
  92. package/src/renderer/components/extensions/mcp/McpLibraryEntryDialog.tsx +418 -0
  93. package/src/renderer/components/extensions/mcp/McpLibraryPanel.tsx +404 -0
  94. package/src/renderer/components/extensions/plugins/PluginCard.tsx +6 -6
  95. package/src/renderer/components/extensions/plugins/PluginsPanel.tsx +34 -21
  96. package/src/renderer/components/extensions/skills/SkillsLibraryPanel.tsx +335 -0
  97. package/src/renderer/components/layout/PaneContent.tsx +8 -1
  98. package/src/renderer/components/layout/Sidebar.tsx +11 -54
  99. package/src/renderer/components/layout/SortableTab.tsx +20 -31
  100. package/src/renderer/components/layout/TabBar.tsx +1 -1
  101. package/src/renderer/components/layout/TabContextMenu.tsx +1 -1
  102. package/src/renderer/components/runtime/ProviderRuntimeSettingsDialog.tsx +768 -157
  103. package/src/renderer/components/schedules/SchedulesView.tsx +51 -462
  104. package/src/renderer/components/schedules/calendar/CalendarDayView.tsx +173 -0
  105. package/src/renderer/components/schedules/calendar/CalendarEventBlock.tsx +113 -0
  106. package/src/renderer/components/schedules/calendar/CalendarHeader.tsx +148 -0
  107. package/src/renderer/components/schedules/calendar/CalendarMonthView.tsx +142 -0
  108. package/src/renderer/components/schedules/calendar/CalendarWeekView.tsx +219 -0
  109. package/src/renderer/components/schedules/calendar/ScheduleCalendarBoard.tsx +41 -0
  110. package/src/renderer/components/schedules/calendar/TeamGanttView.tsx +405 -0
  111. package/src/renderer/components/schedules/calendar/computeOccurrences.ts +234 -0
  112. package/src/renderer/components/schedules/calendar/index.ts +2 -0
  113. package/src/renderer/components/schedules/calendar/types.ts +44 -0
  114. package/src/renderer/components/settings/SettingsTabs.tsx +50 -55
  115. package/src/renderer/components/settings/SettingsView.tsx +30 -35
  116. package/src/renderer/components/settings/components/SettingsSectionHeader.tsx +5 -1
  117. package/src/renderer/components/settings/components/SettingsSelect.tsx +5 -3
  118. package/src/renderer/components/settings/components/SettingsToggle.tsx +2 -2
  119. package/src/renderer/components/settings/sections/AdvancedSection.tsx +11 -42
  120. package/src/renderer/components/settings/sections/CliStatusSection.tsx +71 -112
  121. package/src/renderer/components/settings/sections/ConfigEditorDialog.tsx +1 -1
  122. package/src/renderer/components/settings/sections/GeneralSection.tsx +11 -3
  123. package/src/renderer/components/settings/sections/HarnessSection.tsx +18 -14
  124. package/src/renderer/components/settings/sections/PlatformsSection.tsx +3 -3
  125. package/src/renderer/components/settings/sections/TaskBusSection.tsx +33 -40
  126. package/src/renderer/components/settings/sections/index.ts +0 -1
  127. package/src/renderer/components/sidebar/SidebarSessions.tsx +182 -4
  128. package/src/renderer/components/sidebar/SidebarTaskItem.tsx +4 -4
  129. package/src/renderer/components/sidebar/WorkspaceBrowser.tsx +39 -4
  130. package/src/renderer/components/splash/splashScene.ts +121 -929
  131. package/src/renderer/components/system-manager/FolderBrowser.tsx +163 -0
  132. package/src/renderer/components/system-manager/SystemManagerView.tsx +351 -0
  133. package/src/renderer/components/tasks/TasksView.tsx +112 -134
  134. package/src/renderer/components/team/CcSessionsSection.tsx +431 -89
  135. package/src/renderer/components/team/CollapsibleTeamSection.tsx +17 -32
  136. package/src/renderer/components/team/TeamDetailView.tsx +319 -123
  137. package/src/renderer/components/team/TeamListView.tsx +108 -123
  138. package/src/renderer/components/team/dialogs/CreateTaskDialog.tsx +2 -2
  139. package/src/renderer/components/team/dialogs/CreateTeamDialog.tsx +84 -306
  140. package/src/renderer/components/team/dialogs/EditTeamDialog.tsx +259 -342
  141. package/src/renderer/components/team/dialogs/GlobalTaskDetailDialog.tsx +1 -1
  142. package/src/renderer/components/team/dialogs/LaunchTeamDialog.tsx +17 -15
  143. package/src/renderer/components/team/dialogs/PlatformBindingDialog.tsx +221 -0
  144. package/src/renderer/components/team/dialogs/PlatformManualForm.tsx +7 -0
  145. package/src/renderer/components/team/dialogs/PlatformSetupQR.tsx +1 -1
  146. package/src/renderer/components/team/dialogs/RuntimeConfigDialog.tsx +361 -0
  147. package/src/renderer/components/team/dialogs/platformMeta.ts +122 -11
  148. package/src/renderer/components/team/dialogs/useTeamEditForm.ts +17 -5
  149. package/src/renderer/components/team/kanban/KanbanBoard.tsx +9 -9
  150. package/src/renderer/components/team/members/MemberCard.tsx +14 -47
  151. package/src/renderer/components/team/members/MemberDetailDialog.tsx +3 -95
  152. package/src/renderer/components/team/members/MemberDetailStats.tsx +50 -65
  153. package/src/renderer/components/team/messages/MessageComposer.tsx +8 -110
  154. package/src/renderer/components/team/messages/MessagesPanel.tsx +131 -114
  155. package/src/renderer/components/team/schedule/ScheduleStatusBadge.tsx +2 -2
  156. package/src/renderer/components/team/tools/AddMcpInline.tsx +27 -17
  157. package/src/renderer/components/team/tools/McpChip.tsx +6 -3
  158. package/src/renderer/components/team/tools/SkillChip.tsx +2 -2
  159. package/src/renderer/components/team/tools/ToolsSection.tsx +418 -70
  160. package/src/renderer/hooks/useExtensionsTabState.ts +3 -114
  161. package/src/renderer/index.css +39 -22
  162. package/src/renderer/index.html +17 -50
  163. package/src/renderer/store/index.ts +2 -1
  164. package/src/renderer/store/slices/scheduleSlice.ts +1 -1
  165. package/src/renderer/store/slices/teamSlice.ts +45 -168
  166. package/src/renderer/utils/claudeCodeOnlyProviders.ts +3 -10
  167. package/src/renderer/utils/memberHelpers.ts +5 -17
  168. package/src/renderer/utils/openCodeRuntimeDeliveryDiagnostics.ts +4 -2
  169. package/src/renderer/utils/providerSlashCommands.ts +0 -5
  170. package/src/renderer/utils/scheduleFormatters.ts +3 -1
  171. package/src/renderer/utils/teamMessageFiltering.ts +14 -1
  172. package/src/renderer/utils/teamModelAvailability.ts +18 -2
  173. package/src/shared/types/api.ts +121 -2
  174. package/src/shared/types/ccConnect.ts +2 -0
  175. package/src/shared/types/index.ts +3 -0
  176. package/src/shared/types/systemManager.ts +49 -0
  177. package/src/shared/types/team.ts +29 -0
  178. package/src/shared/types/terminal.ts +4 -2
  179. package/src/shared/utils/extensionNormalizers.ts +15 -8
  180. package/src/shared/utils/providerExtensionCapabilities.ts +2 -2
  181. package/dist-renderer/assets/ProjectEditorOverlay-lJZi-9Hp.js +0 -52
  182. package/dist-renderer/assets/channel-yIlSKy0e.js +0 -1
  183. package/dist-renderer/assets/classDiagram-2ON5EDUG-24fHez0s.js +0 -1
  184. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-24fHez0s.js +0 -1
  185. package/dist-renderer/assets/clone-BTNuUva-.js +0 -1
  186. package/dist-renderer/assets/index-Bi6nrZ4z.css +0 -1
  187. package/dist-renderer/assets/splashScene-C8lWNnm4.js +0 -1
  188. package/dist-renderer/assets/stateDiagram-v2-4FDKWEC3-_m6iPPUR.js +0 -1
@@ -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
+ };