@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,404 @@
1
+ /**
2
+ * McpLibraryPanel — global reusable MCP server definition library.
3
+ */
4
+
5
+ import { useCallback, useEffect, useMemo, useState } from 'react';
6
+
7
+ import { api } from '@renderer/api';
8
+ import { confirm } from '@renderer/components/common/ConfirmDialog';
9
+ import { Badge } from '@renderer/components/ui/badge';
10
+ import { Button } from '@renderer/components/ui/button';
11
+ import { useStore } from '@renderer/store';
12
+ import { getMcpProjectStateKey } from '@shared/utils/extensionNormalizers';
13
+ import { Plus, RefreshCw, Search, Server, Trash2, Upload } from 'lucide-react';
14
+
15
+ import { SearchInput } from '../common/SearchInput';
16
+
17
+ import { McpLibraryEnableDialog } from './McpLibraryEnableDialog';
18
+ import { McpLibraryEntryDialog } from './McpLibraryEntryDialog';
19
+
20
+ import type { InstalledMcpEntry, McpInstallSpec, McpLibraryEntry } from '@shared/types/extensions';
21
+
22
+ interface McpLibraryPanelProps {
23
+ projectPath: string | null;
24
+ }
25
+
26
+ function summarizeTransport(spec: McpInstallSpec): string {
27
+ if (spec.type === 'stdio') {
28
+ return `stdio · ${spec.npmPackage}${spec.npmVersion ? `@${spec.npmVersion}` : ''}`;
29
+ }
30
+ return `${spec.transportType} · ${spec.url}`;
31
+ }
32
+
33
+ function formatUpdatedAt(timestamp: number): string {
34
+ if (!timestamp) return '未知时间';
35
+ return new Intl.DateTimeFormat('zh-CN', {
36
+ month: '2-digit',
37
+ day: '2-digit',
38
+ hour: '2-digit',
39
+ minute: '2-digit',
40
+ }).format(new Date(timestamp));
41
+ }
42
+
43
+ function countBestEffortProjectInstances(
44
+ entry: McpLibraryEntry,
45
+ installedServers: InstalledMcpEntry[]
46
+ ): number {
47
+ return installedServers.filter(
48
+ (server) => server.scope === 'project' && server.name === entry.name
49
+ ).length;
50
+ }
51
+
52
+ export const McpLibraryPanel = ({ projectPath }: McpLibraryPanelProps): React.JSX.Element => {
53
+ const mcpInstalledServersByProjectPath = useStore((s) => s.mcpInstalledServersByProjectPath);
54
+ const mcpFetchInstalled = useStore((s) => s.mcpFetchInstalled);
55
+ const runMcpDiagnostics = useStore((s) => s.runMcpDiagnostics);
56
+
57
+ const [entries, setEntries] = useState<McpLibraryEntry[]>([]);
58
+ const [search, setSearch] = useState('');
59
+ const [loading, setLoading] = useState(false);
60
+ const [error, setError] = useState<string | null>(null);
61
+ const [dialogOpen, setDialogOpen] = useState(false);
62
+ const [editingEntry, setEditingEntry] = useState<McpLibraryEntry | null>(null);
63
+ const [importing, setImporting] = useState(false);
64
+ const [importMessage, setImportMessage] = useState<string | null>(null);
65
+ const [deletingId, setDeletingId] = useState<string | null>(null);
66
+ const [enablingEntry, setEnablingEntry] = useState<McpLibraryEntry | null>(null);
67
+
68
+ const projectStateKey = getMcpProjectStateKey(projectPath);
69
+ const projectInstalledServers = projectPath
70
+ ? (mcpInstalledServersByProjectPath[projectStateKey] ?? [])
71
+ : [];
72
+
73
+ const refresh = useCallback(async (): Promise<void> => {
74
+ if (!api.mcpRegistry?.libraryList) {
75
+ setEntries([]);
76
+ setError('MCP 能力库 API 不可用');
77
+ return;
78
+ }
79
+
80
+ setLoading(true);
81
+ setError(null);
82
+ try {
83
+ const libraryEntries = await api.mcpRegistry.libraryList();
84
+ setEntries(libraryEntries);
85
+ } catch (err) {
86
+ setError(err instanceof Error ? err.message : '加载 MCP 能力库失败');
87
+ } finally {
88
+ setLoading(false);
89
+ }
90
+ }, []);
91
+
92
+ useEffect(() => {
93
+ void refresh();
94
+ }, [refresh]);
95
+
96
+ useEffect(() => {
97
+ if (!projectPath) return;
98
+ void mcpFetchInstalled(projectPath);
99
+ }, [mcpFetchInstalled, projectPath]);
100
+
101
+ const filteredEntries = useMemo(() => {
102
+ const query = search.trim().toLowerCase();
103
+ const sorted = [...entries].sort((a, b) => b.updatedAt - a.updatedAt);
104
+ if (!query) return sorted;
105
+ return sorted.filter((entry) => {
106
+ const transportSummary = summarizeTransport(entry.installSpec).toLowerCase();
107
+ return (
108
+ entry.name.toLowerCase().includes(query) ||
109
+ (entry.description ?? '').toLowerCase().includes(query) ||
110
+ transportSummary.includes(query)
111
+ );
112
+ });
113
+ }, [entries, search]);
114
+
115
+ const openCreateDialog = (): void => {
116
+ setEditingEntry(null);
117
+ setDialogOpen(true);
118
+ };
119
+
120
+ const openEditDialog = (entry: McpLibraryEntry): void => {
121
+ setEditingEntry(entry);
122
+ setDialogOpen(true);
123
+ };
124
+
125
+ const handleSaved = (entry: McpLibraryEntry): void => {
126
+ setEntries((prev) => {
127
+ const exists = prev.some((item) => item.id === entry.id);
128
+ return exists ? prev.map((item) => (item.id === entry.id ? entry : item)) : [entry, ...prev];
129
+ });
130
+ setDialogOpen(false);
131
+ setEditingEntry(null);
132
+ };
133
+
134
+ const handleDelete = (entry: McpLibraryEntry): void => {
135
+ void (async () => {
136
+ if (!api.mcpRegistry?.libraryDelete) {
137
+ setError('MCP 能力库 API 不可用');
138
+ return;
139
+ }
140
+
141
+ const confirmed = await confirm({
142
+ title: '删除 MCP 定义',
143
+ message: `确认从全局能力库删除「${entry.name}」?已安装到项目中的服务器不会被自动移除。`,
144
+ confirmLabel: '删除',
145
+ cancelLabel: '取消',
146
+ variant: 'danger',
147
+ });
148
+ if (!confirmed) return;
149
+
150
+ setDeletingId(entry.id);
151
+ setError(null);
152
+ try {
153
+ await api.mcpRegistry.libraryDelete(entry.id);
154
+ setEntries((prev) => prev.filter((item) => item.id !== entry.id));
155
+ } catch (err) {
156
+ setError(err instanceof Error ? err.message : '删除 MCP 定义失败');
157
+ } finally {
158
+ setDeletingId(null);
159
+ }
160
+ })();
161
+ };
162
+
163
+ const handleInstanceInstalled = (): void => {
164
+ setEnablingEntry(null);
165
+ if (projectPath) {
166
+ void mcpFetchInstalled(projectPath);
167
+ void runMcpDiagnostics(projectPath);
168
+ }
169
+ };
170
+
171
+ const handleImport = (): void => {
172
+ void (async () => {
173
+ if (!api.mcpRegistry?.libraryImport) {
174
+ setError('MCP 能力库 API 不可用');
175
+ return;
176
+ }
177
+
178
+ setImporting(true);
179
+ setImportMessage(null);
180
+ setError(null);
181
+ try {
182
+ const result = await api.mcpRegistry.libraryImport({
183
+ projectPath: projectPath ?? undefined,
184
+ });
185
+ await refresh();
186
+ const imported = result.imported.length;
187
+ const skipped = result.skipped.length;
188
+ setImportMessage(`导入完成:新增 ${imported} 个,跳过 ${skipped} 个。`);
189
+ } catch (err) {
190
+ setError(err instanceof Error ? err.message : '导入现有 MCP 定义失败');
191
+ } finally {
192
+ setImporting(false);
193
+ }
194
+ })();
195
+ };
196
+
197
+ const hasActiveSearch = search.trim().length > 0;
198
+
199
+ return (
200
+ <div className="flex flex-col gap-4">
201
+ <div className="flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between">
202
+ <div>
203
+ <h2 className="text-sm font-semibold text-text">MCP 全局能力库</h2>
204
+ <p className="mt-1 text-xs text-text-muted">
205
+ 保存可复用的 MCP 服务器定义,再按需安装到团队项目。
206
+ </p>
207
+ </div>
208
+ <div className="flex flex-wrap gap-2">
209
+ <Button variant="outline" size="sm" onClick={() => void refresh()} disabled={loading}>
210
+ <RefreshCw className={`mr-1.5 size-3.5 ${loading ? 'animate-spin' : ''}`} />
211
+ 刷新
212
+ </Button>
213
+ <Button variant="outline" size="sm" onClick={handleImport} disabled={importing}>
214
+ <Upload className="mr-1.5 size-3.5" />
215
+ {importing ? '导入中...' : '导入现有定义'}
216
+ </Button>
217
+ <Button size="sm" onClick={openCreateDialog}>
218
+ <Plus className="mr-1.5 size-3.5" />
219
+ 添加定义
220
+ </Button>
221
+ </div>
222
+ </div>
223
+
224
+ <div className="flex flex-col gap-3 sm:flex-row sm:items-center">
225
+ <div className="flex-1">
226
+ <SearchInput value={search} onChange={setSearch} placeholder="搜索 MCP 定义..." />
227
+ </div>
228
+ <Badge variant="secondary" className="w-fit font-normal">
229
+ {filteredEntries.length} / {entries.length} 个定义
230
+ </Badge>
231
+ </div>
232
+
233
+ {importMessage && (
234
+ <div className="rounded-md border border-emerald-500/30 bg-emerald-500/5 px-3 py-2 text-xs text-emerald-300">
235
+ {importMessage}
236
+ </div>
237
+ )}
238
+
239
+ {error && (
240
+ <div className="rounded-md border border-red-500/30 bg-red-500/5 p-4 text-sm text-red-400">
241
+ {error}
242
+ </div>
243
+ )}
244
+
245
+ {loading && (
246
+ <div className="grid grid-cols-1 gap-3 sm:grid-cols-2 xl:grid-cols-3">
247
+ {Array.from({ length: 6 }, (_, index) => (
248
+ <div
249
+ key={index}
250
+ className="skeleton-card flex flex-col gap-3 rounded-lg border border-border p-4"
251
+ style={{ animationDelay: `${index * 80}ms` }}
252
+ >
253
+ <div className="h-4 w-32 rounded bg-surface-raised" />
254
+ <div className="h-3 w-full rounded bg-surface-raised" />
255
+ <div className="h-3 w-3/4 rounded bg-surface-raised" />
256
+ <div className="flex gap-2">
257
+ <div className="h-5 w-16 rounded-full bg-surface-raised" />
258
+ <div className="h-5 w-20 rounded-full bg-surface-raised" />
259
+ </div>
260
+ </div>
261
+ ))}
262
+ </div>
263
+ )}
264
+
265
+ {!loading && !error && filteredEntries.length === 0 && (
266
+ <div className="flex flex-col items-center gap-3 rounded-sm border border-dashed border-border px-8 py-16">
267
+ <div className="flex size-10 items-center justify-center rounded-lg border border-border bg-surface-raised">
268
+ {hasActiveSearch ? (
269
+ <Search className="size-5 text-text-muted" />
270
+ ) : (
271
+ <Server className="size-5 text-text-muted" />
272
+ )}
273
+ </div>
274
+ <p className="text-sm text-text-secondary">
275
+ {hasActiveSearch ? '没有匹配搜索条件的 MCP 定义' : '暂无 MCP 定义'}
276
+ </p>
277
+ <p className="text-xs text-text-muted">
278
+ {hasActiveSearch ? '试着调整搜索关键词' : '添加新定义或从现有 MCP 配置导入'}
279
+ </p>
280
+ {!hasActiveSearch && (
281
+ <div className="flex flex-wrap justify-center gap-2">
282
+ <Button variant="outline" size="sm" onClick={handleImport} disabled={importing}>
283
+ 导入现有定义
284
+ </Button>
285
+ <Button size="sm" onClick={openCreateDialog}>
286
+ 添加定义
287
+ </Button>
288
+ </div>
289
+ )}
290
+ </div>
291
+ )}
292
+
293
+ {!loading && filteredEntries.length > 0 && (
294
+ <div className="grid grid-cols-1 gap-3 sm:grid-cols-2 xl:grid-cols-3">
295
+ {filteredEntries.map((entry) => {
296
+ const envCount = Object.keys(entry.envValues ?? {}).length;
297
+ const headerCount = entry.headers?.length ?? 0;
298
+ const isDeleting = deletingId === entry.id;
299
+ const isEnabling = enablingEntry?.id === entry.id;
300
+ const projectInstanceCount = countBestEffortProjectInstances(
301
+ entry,
302
+ projectInstalledServers
303
+ );
304
+
305
+ return (
306
+ <div
307
+ key={entry.id}
308
+ className="hover:bg-surface-raised/40 group flex min-h-44 flex-col rounded-lg border border-border bg-surface p-4 text-left transition-all hover:-translate-y-0.5 hover:border-border-emphasis hover:shadow-lg"
309
+ >
310
+ <div className="flex items-start justify-between gap-3">
311
+ <div className="min-w-0">
312
+ <h3 className="truncate text-sm font-semibold text-text">{entry.name}</h3>
313
+ <p className="mt-1 line-clamp-2 text-xs text-text-muted">
314
+ {entry.description || '无描述'}
315
+ </p>
316
+ </div>
317
+ <Badge variant="outline" className="shrink-0 font-mono text-[10px] uppercase">
318
+ {entry.installSpec.type === 'stdio' ? 'stdio' : entry.installSpec.transportType}
319
+ </Badge>
320
+ </div>
321
+
322
+ <div className="bg-surface-raised/40 mt-3 rounded-md border border-border px-2 py-1.5">
323
+ <p className="truncate font-mono text-[11px] text-text-secondary">
324
+ {summarizeTransport(entry.installSpec)}
325
+ </p>
326
+ </div>
327
+
328
+ <div className="mt-3 flex flex-wrap gap-1.5">
329
+ <Badge variant="secondary" className="font-normal">
330
+ 模板 · {entry.installSpec.type === 'stdio' ? 'Node / npm' : 'HTTP'}
331
+ </Badge>
332
+ {projectPath && (
333
+ <Badge variant="secondary" className="font-normal">
334
+ 当前项目同名实例 {projectInstanceCount}
335
+ </Badge>
336
+ )}
337
+ <Badge variant="secondary" className="font-normal">
338
+ Env {envCount}
339
+ </Badge>
340
+ <Badge variant="secondary" className="font-normal">
341
+ Headers {headerCount}
342
+ </Badge>
343
+ <Badge variant="secondary" className="font-normal">
344
+ 更新 {formatUpdatedAt(entry.updatedAt)}
345
+ </Badge>
346
+ </div>
347
+
348
+ <div className="mt-auto flex items-center justify-end gap-2 pt-4">
349
+ {projectPath && (
350
+ <Button
351
+ variant={isEnabling ? 'secondary' : 'outline'}
352
+ size="sm"
353
+ className="h-7 px-2 text-xs"
354
+ onClick={() => setEnablingEntry(isEnabling ? null : entry)}
355
+ >
356
+ 添加实例到当前项目
357
+ </Button>
358
+ )}
359
+ <Button
360
+ variant="ghost"
361
+ size="sm"
362
+ className="h-7 px-2 text-xs"
363
+ onClick={() => openEditDialog(entry)}
364
+ >
365
+ 编辑
366
+ </Button>
367
+ <Button
368
+ variant="ghost"
369
+ size="sm"
370
+ className="h-7 px-2 text-xs text-red-400 hover:bg-red-500/10 hover:text-red-300"
371
+ disabled={isDeleting}
372
+ onClick={() => handleDelete(entry)}
373
+ >
374
+ <Trash2 className="mr-1 size-3" />
375
+ {isDeleting ? '删除中...' : '删除'}
376
+ </Button>
377
+ </div>
378
+ </div>
379
+ );
380
+ })}
381
+ </div>
382
+ )}
383
+
384
+ <McpLibraryEnableDialog
385
+ open={Boolean(enablingEntry)}
386
+ entry={enablingEntry}
387
+ projectPath={projectPath}
388
+ installedServers={projectInstalledServers}
389
+ onClose={() => setEnablingEntry(null)}
390
+ onEnabled={handleInstanceInstalled}
391
+ />
392
+
393
+ <McpLibraryEntryDialog
394
+ open={dialogOpen}
395
+ entry={editingEntry}
396
+ onClose={() => {
397
+ setDialogOpen(false);
398
+ setEditingEntry(null);
399
+ }}
400
+ onSaved={handleSaved}
401
+ />
402
+ </div>
403
+ );
404
+ };
@@ -71,17 +71,17 @@ export const PluginCard = ({
71
71
  plugin.isInstalled ? 'border-l-2 border-border border-l-emerald-500/35' : 'border-border'
72
72
  }`}
73
73
  >
74
- {plugin.source === 'official' && (
74
+ {isEssentialPlugin(plugin) && (
75
75
  <div className="pointer-events-none absolute -left-px -top-px size-16 overflow-hidden">
76
- <div className="absolute left-[-24px] top-[4px] w-[80px] -rotate-45 bg-blue-500/90 text-center text-[9px] font-semibold leading-[18px] text-white shadow-sm">
77
- Official
76
+ <div className="absolute left-[-24px] top-[4px] w-[80px] -rotate-45 bg-amber-500/90 text-center text-[9px] font-semibold leading-[18px] text-white shadow-sm">
77
+ ⭐ 必装
78
78
  </div>
79
79
  </div>
80
80
  )}
81
- {isEssentialPlugin(plugin) && (
81
+ {plugin.source === 'official' && (
82
82
  <div className="pointer-events-none absolute -right-px -top-px size-16 overflow-hidden">
83
- <div className="absolute right-[-24px] top-[4px] w-[80px] rotate-45 bg-amber-500/90 text-center text-[9px] font-semibold leading-[18px] text-white shadow-sm">
84
- ⭐ 必装
83
+ <div className="absolute right-[-24px] top-[4px] w-[80px] rotate-45 bg-blue-500/90 text-center text-[9px] font-semibold leading-[18px] text-white shadow-sm">
84
+ Official
85
85
  </div>
86
86
  </div>
87
87
  )}
@@ -19,6 +19,7 @@ import { useStore } from '@renderer/store';
19
19
  import {
20
20
  inferCapabilities,
21
21
  isEssentialPlugin,
22
+ isHiddenPluginFromStore,
22
23
  normalizeCategory,
23
24
  } from '@shared/utils/extensionNormalizers';
24
25
  import { getCliProviderExtensionCapability } from '@shared/utils/providerExtensionCapabilities';
@@ -73,7 +74,7 @@ function selectFilteredPlugins(
73
74
  filters: PluginFilters,
74
75
  sort: { field: PluginSortField; order: 'asc' | 'desc' }
75
76
  ): EnrichedPlugin[] {
76
- let result = catalog;
77
+ let result = catalog.filter((plugin) => !isHiddenPluginFromStore(plugin));
77
78
 
78
79
  // Search
79
80
  if (filters.search) {
@@ -144,7 +145,12 @@ export const PluginsPanel = ({
144
145
  cliStatus: cliStatusOverride,
145
146
  cliStatusLoading,
146
147
  }: PluginsPanelProps): React.JSX.Element => {
147
- const { catalog, loading, error, cliStatus: storedCliStatus } = useStore(
148
+ const {
149
+ catalog,
150
+ loading,
151
+ error,
152
+ cliStatus: storedCliStatus,
153
+ } = useStore(
148
154
  useShallow((s) => ({
149
155
  catalog: s.pluginCatalog,
150
156
  loading: s.pluginCatalogLoading,
@@ -196,27 +202,34 @@ export const PluginsPanel = ({
196
202
  }
197
203
  return counts.size;
198
204
  }, [catalog]);
205
+ const unsupportedPluginProviders = useMemo(() => {
206
+ if (cliStatus?.flavor !== 'agent_teams_orchestrator') {
207
+ return [];
208
+ }
209
+
210
+ return cliStatus.providers
211
+ .map((provider) => ({
212
+ provider,
213
+ capability: getCliProviderExtensionCapability(provider, 'plugins'),
214
+ }))
215
+ .filter(({ capability }) => capability.status !== 'supported');
216
+ }, [cliStatus]);
217
+
199
218
  return (
200
219
  <div className="flex flex-col gap-4">
201
- {cliStatus?.flavor === 'agent_teams_orchestrator' &&
202
- (() => {
203
- const codexProvider = cliStatus.providers.find(
204
- (provider) => provider.providerId === 'codex'
205
- );
206
- if (!codexProvider) return null;
207
- const capability = getCliProviderExtensionCapability(codexProvider, 'plugins');
208
- if (capability.status === 'supported') {
209
- return null;
210
- }
211
-
212
- return (
213
- <div className="rounded-md border border-amber-500/30 bg-amber-500/5 px-4 py-3 text-sm text-amber-300">
214
- 在多模型运行时中,目前插件仅保证用于 Anthropic
215
- 会话。我们正在为所有智能体构建更广泛的插件支持,包括通用插件和智能体专用插件。
216
- {capability.reason ? ` ${capability.reason}` : ''}
217
- </div>
218
- );
219
- })()}
220
+ {unsupportedPluginProviders.length > 0 && (
221
+ <div className="rounded-md border border-amber-500/30 bg-amber-500/5 px-4 py-3 text-sm text-amber-300">
222
+ 多模型运行时中,部分提供商暂不支持插件管理:
223
+ {unsupportedPluginProviders
224
+ .map(({ provider, capability }) =>
225
+ capability.reason
226
+ ? `${provider.displayName}(${capability.reason})`
227
+ : provider.displayName
228
+ )
229
+ .join('、')}
230
+
231
+ </div>
232
+ )}
220
233
  {/* Search + Sort + Installed only row */}
221
234
  <div className="flex flex-col gap-3 lg:flex-row lg:items-center">
222
235
  <div className="flex-1">