@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.
Files changed (205) hide show
  1. package/dist-renderer/assets/ProjectEditorOverlay-krO5vQxX.js +58 -0
  2. package/dist-renderer/assets/{TeamGraphOverlay-DYT3bwFR.js → TeamGraphOverlay-DqhQzcTr.js} +1 -1
  3. package/dist-renderer/assets/{_basePickBy-Dbt_EU-e.js → _basePickBy-B7kSYPxr.js} +1 -1
  4. package/dist-renderer/assets/{_baseUniq-DWo68sXI.js → _baseUniq-CnjxqwAk.js} +1 -1
  5. package/dist-renderer/assets/{arc-DXH1iZQK.js → arc-CLeZuINP.js} +1 -1
  6. package/dist-renderer/assets/{architectureDiagram-VXUJARFQ-cjffS2Qr.js → architectureDiagram-VXUJARFQ-QKtqaqdY.js} +1 -1
  7. package/dist-renderer/assets/{blockDiagram-VD42YOAC-BKdZF02Y.js → blockDiagram-VD42YOAC-BqdrzO_f.js} +1 -1
  8. package/dist-renderer/assets/{c4Diagram-YG6GDRKO-CN27pqaI.js → c4Diagram-YG6GDRKO-gwPlCxDC.js} +1 -1
  9. package/dist-renderer/assets/channel-DpMHF50r.js +1 -0
  10. package/dist-renderer/assets/{chunk-4BX2VUAB-CXPCI7g_.js → chunk-4BX2VUAB-C6XLurL4.js} +1 -1
  11. package/dist-renderer/assets/{chunk-55IACEB6-BGAXQZRC.js → chunk-55IACEB6-Ds6quhEP.js} +1 -1
  12. package/dist-renderer/assets/{chunk-B4BG7PRW-TPDaA_KQ.js → chunk-B4BG7PRW-5UlA1_e9.js} +1 -1
  13. package/dist-renderer/assets/{chunk-DI55MBZ5-D1ADe_tq.js → chunk-DI55MBZ5-ywFrqIsY.js} +1 -1
  14. package/dist-renderer/assets/{chunk-FMBD7UC4-Beimtg3a.js → chunk-FMBD7UC4-C7ifUA17.js} +1 -1
  15. package/dist-renderer/assets/{chunk-QN33PNHL-OjNBu854.js → chunk-QN33PNHL-BxGCo80U.js} +1 -1
  16. package/dist-renderer/assets/{chunk-QZHKN3VN-DinqvbH8.js → chunk-QZHKN3VN-B2CuaZs6.js} +1 -1
  17. package/dist-renderer/assets/{chunk-TZMSLE5B-BfFtlPSZ.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-D9z9Dgt7.js → cose-bilkent-S5V4N54A-Cz1GVtLp.js} +1 -1
  22. package/dist-renderer/assets/{dagre-6UL2VRFP-n1g-DhEE.js → dagre-6UL2VRFP-BrmR-P4h.js} +1 -1
  23. package/dist-renderer/assets/{diagram-PSM6KHXK-BvxFq-BE.js → diagram-PSM6KHXK-DbNjC5Rg.js} +1 -1
  24. package/dist-renderer/assets/{diagram-QEK2KX5R-wVnJuwza.js → diagram-QEK2KX5R-qkRX5_Mq.js} +1 -1
  25. package/dist-renderer/assets/{diagram-S2PKOQOG-B707WJQw.js → diagram-S2PKOQOG-CyL5rCv2.js} +1 -1
  26. package/dist-renderer/assets/{erDiagram-Q2GNP2WA-C-_1dGHs.js → erDiagram-Q2GNP2WA-Dox3-bA5.js} +1 -1
  27. package/dist-renderer/assets/{flowDiagram-NV44I4VS-CMTSi3H6.js → flowDiagram-NV44I4VS-BtkaxlDL.js} +1 -1
  28. package/dist-renderer/assets/{ganttDiagram-JELNMOA3-DZ0bNrAA.js → ganttDiagram-JELNMOA3-Dhy_d9GK.js} +1 -1
  29. package/dist-renderer/assets/{gitGraphDiagram-V2S2FVAM-DNVfGooQ.js → gitGraphDiagram-V2S2FVAM-B5XRhIQA.js} +1 -1
  30. package/dist-renderer/assets/{graph-865j_tM_.js → graph-CsoEwUhS.js} +1 -1
  31. package/dist-renderer/assets/{index-C_F9N5x-.js → index-BWPWmJNo.js} +1 -1
  32. package/dist-renderer/assets/{index-LwDIsXJN.js → index-Bu2R-Se7.js} +586 -740
  33. package/dist-renderer/assets/index-CnWV3BhG.css +32 -0
  34. package/dist-renderer/assets/{index-DuUaf8at.js → index-D-3KgskL.js} +1 -1
  35. package/dist-renderer/assets/{index-BTx1nc4T.js → index-DGEBzLNT.js} +1 -1
  36. package/dist-renderer/assets/{index-2EW-eu3q.js → index-NhHNs2Oo.js} +1 -1
  37. package/dist-renderer/assets/{index-4dEMStJj.js → index-h17WuEyf.js} +1 -1
  38. package/dist-renderer/assets/{infoDiagram-HS3SLOUP-CyqtElLq.js → infoDiagram-HS3SLOUP-hMGmNojH.js} +1 -1
  39. package/dist-renderer/assets/{journeyDiagram-XKPGCS4Q-BvjQ0Hm0.js → journeyDiagram-XKPGCS4Q-DXV2rBDl.js} +1 -1
  40. package/dist-renderer/assets/{kanban-definition-3W4ZIXB7-CJJ-k0zT.js → kanban-definition-3W4ZIXB7-Bf99WLRy.js} +1 -1
  41. package/dist-renderer/assets/{layout-CnV6rQAG.js → layout-C3XWrpwo.js} +1 -1
  42. package/dist-renderer/assets/{linear-Cw3UQgyX.js → linear-OEEcn8KN.js} +1 -1
  43. package/dist-renderer/assets/{mindmap-definition-VGOIOE7T-C5tDaGSK.js → mindmap-definition-VGOIOE7T-Dpi3S2x4.js} +1 -1
  44. package/dist-renderer/assets/{pieDiagram-ADFJNKIX-CiIpPsau.js → pieDiagram-ADFJNKIX-xTPPhtNx.js} +1 -1
  45. package/dist-renderer/assets/{quadrantDiagram-AYHSOK5B-C3gtowNj.js → quadrantDiagram-AYHSOK5B-euniyDlz.js} +1 -1
  46. package/dist-renderer/assets/{requirementDiagram-UZGBJVZJ-CXBTrAnU.js → requirementDiagram-UZGBJVZJ-D9Uiw4kF.js} +1 -1
  47. package/dist-renderer/assets/{sankeyDiagram-TZEHDZUN-wziX77xG.js → sankeyDiagram-TZEHDZUN-CySU4nED.js} +1 -1
  48. package/dist-renderer/assets/{sequenceDiagram-WL72ISMW-sYqopcrj.js → sequenceDiagram-WL72ISMW-JVGpET6V.js} +1 -1
  49. package/dist-renderer/assets/splashScene-D0YB9uxm.js +17 -0
  50. package/dist-renderer/assets/{stateDiagram-FKZM4ZOC-Bl1-0_Cp.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-CIRjJUBo.js → timeline-definition-IT6M3QCI-DmycNUUe.js} +1 -1
  53. package/dist-renderer/assets/{treemap-GDKQZRPO-CVPuNe1n.js → treemap-GDKQZRPO-DPq4gZuB.js} +1 -1
  54. package/dist-renderer/assets/{xychartDiagram-PRI3JC2R-3nT9yHwp.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 +30 -50
  58. package/src/main/server.ts +890 -247
  59. package/src/main/services/extensions/ExtensionFacadeService.ts +4 -56
  60. package/src/main/services/extensions/catalog/PluginCatalogService.ts +4 -2
  61. package/src/main/services/extensions/library/McpLibraryService.ts +243 -0
  62. package/src/main/services/session-intelligence/ConversationTelemetryService.ts +1101 -0
  63. package/src/main/services/session-intelligence/LocalSessionScanner.ts +512 -0
  64. package/src/main/services/session-intelligence/SessionUsageParser.ts +4 -4
  65. package/src/main/services/session-intelligence/UsageTelemetryService.ts +14 -1
  66. package/src/main/services/system-manager/SystemManagerConfigService.ts +122 -0
  67. package/src/main/services/system-manager/SystemManagerPtyService.ts +233 -0
  68. package/src/main/services/system-manager/WorkflowPromptService.ts +75 -0
  69. package/src/main/services/teams-mvp/TaskDispatchService.ts +32 -8
  70. package/src/main/services/teams-mvp/TeamProvisioningService.ts +39 -2
  71. package/src/main/services/teams-mvp/TeamWorkspaceService.ts +22 -4
  72. package/src/main/utils/teamProjectResolution.ts +15 -0
  73. package/src/renderer/App.tsx +8 -4
  74. package/src/renderer/api/httpClient.ts +174 -38
  75. package/src/renderer/api/providers.ts +23 -2
  76. package/src/renderer/assets/participant-avatars/01.svg +3 -0
  77. package/src/renderer/assets/participant-avatars/02.svg +3 -0
  78. package/src/renderer/assets/participant-avatars/03.svg +3 -0
  79. package/src/renderer/assets/participant-avatars/04.svg +3 -0
  80. package/src/renderer/assets/participant-avatars/05.svg +3 -0
  81. package/src/renderer/assets/participant-avatars/06.svg +3 -0
  82. package/src/renderer/assets/participant-avatars/07.svg +3 -0
  83. package/src/renderer/assets/participant-avatars/08.svg +3 -0
  84. package/src/renderer/assets/participant-avatars/09.svg +3 -0
  85. package/src/renderer/assets/participant-avatars/10.svg +3 -0
  86. package/src/renderer/assets/participant-avatars/11.svg +3 -0
  87. package/src/renderer/assets/participant-avatars/12.svg +3 -0
  88. package/src/renderer/assets/participant-avatars/13.svg +3 -0
  89. package/src/renderer/components/common/TerminalPane.tsx +213 -0
  90. package/src/renderer/components/dashboard/DashboardView.tsx +9 -36
  91. package/src/renderer/components/extensions/ExtensionStoreView.tsx +12 -221
  92. package/src/renderer/components/extensions/ExtensionsSubTabTrigger.tsx +1 -1
  93. package/src/renderer/components/extensions/mcp/McpLibraryEnableDialog.tsx +305 -0
  94. package/src/renderer/components/extensions/mcp/McpLibraryEntryDialog.tsx +418 -0
  95. package/src/renderer/components/extensions/mcp/McpLibraryPanel.tsx +404 -0
  96. package/src/renderer/components/extensions/plugins/PluginCard.tsx +10 -2
  97. package/src/renderer/components/extensions/plugins/PluginsPanel.tsx +40 -22
  98. package/src/renderer/components/extensions/skills/SkillsLibraryPanel.tsx +335 -0
  99. package/src/renderer/components/layout/PaneContent.tsx +8 -1
  100. package/src/renderer/components/layout/Sidebar.tsx +11 -54
  101. package/src/renderer/components/layout/SortableTab.tsx +20 -31
  102. package/src/renderer/components/layout/TabBar.tsx +1 -1
  103. package/src/renderer/components/layout/TabContextMenu.tsx +1 -1
  104. package/src/renderer/components/runtime/ProviderRuntimeSettingsDialog.tsx +768 -157
  105. package/src/renderer/components/schedules/SchedulesView.tsx +51 -462
  106. package/src/renderer/components/schedules/calendar/CalendarDayView.tsx +173 -0
  107. package/src/renderer/components/schedules/calendar/CalendarEventBlock.tsx +113 -0
  108. package/src/renderer/components/schedules/calendar/CalendarHeader.tsx +148 -0
  109. package/src/renderer/components/schedules/calendar/CalendarMonthView.tsx +142 -0
  110. package/src/renderer/components/schedules/calendar/CalendarWeekView.tsx +219 -0
  111. package/src/renderer/components/schedules/calendar/ScheduleCalendarBoard.tsx +41 -0
  112. package/src/renderer/components/schedules/calendar/TeamGanttView.tsx +405 -0
  113. package/src/renderer/components/schedules/calendar/computeOccurrences.ts +234 -0
  114. package/src/renderer/components/schedules/calendar/index.ts +2 -0
  115. package/src/renderer/components/schedules/calendar/types.ts +44 -0
  116. package/src/renderer/components/settings/SettingsTabs.tsx +50 -55
  117. package/src/renderer/components/settings/SettingsView.tsx +30 -35
  118. package/src/renderer/components/settings/components/SettingsSectionHeader.tsx +5 -1
  119. package/src/renderer/components/settings/components/SettingsSelect.tsx +5 -3
  120. package/src/renderer/components/settings/components/SettingsToggle.tsx +2 -2
  121. package/src/renderer/components/settings/sections/AdvancedSection.tsx +11 -42
  122. package/src/renderer/components/settings/sections/CliStatusSection.tsx +71 -112
  123. package/src/renderer/components/settings/sections/ConfigEditorDialog.tsx +1 -1
  124. package/src/renderer/components/settings/sections/GeneralSection.tsx +11 -3
  125. package/src/renderer/components/settings/sections/HarnessSection.tsx +18 -14
  126. package/src/renderer/components/settings/sections/PlatformsSection.tsx +3 -3
  127. package/src/renderer/components/settings/sections/TaskBusSection.tsx +33 -40
  128. package/src/renderer/components/settings/sections/index.ts +0 -1
  129. package/src/renderer/components/sidebar/SidebarSessions.tsx +182 -4
  130. package/src/renderer/components/sidebar/SidebarTaskItem.tsx +4 -4
  131. package/src/renderer/components/sidebar/WorkspaceBrowser.tsx +39 -4
  132. package/src/renderer/components/splash/splashScene.ts +121 -929
  133. package/src/renderer/components/system-manager/FolderBrowser.tsx +163 -0
  134. package/src/renderer/components/system-manager/SystemManagerView.tsx +351 -0
  135. package/src/renderer/components/tasks/TasksView.tsx +112 -134
  136. package/src/renderer/components/team/CcSessionsSection.tsx +431 -89
  137. package/src/renderer/components/team/CollapsibleTeamSection.tsx +17 -32
  138. package/src/renderer/components/team/TeamDetailView.tsx +325 -114
  139. package/src/renderer/components/team/TeamListView.tsx +108 -123
  140. package/src/renderer/components/team/dialogs/CreateTaskDialog.tsx +2 -2
  141. package/src/renderer/components/team/dialogs/CreateTeamDialog.tsx +84 -306
  142. package/src/renderer/components/team/dialogs/EditTeamDialog.tsx +259 -342
  143. package/src/renderer/components/team/dialogs/GlobalTaskDetailDialog.tsx +1 -1
  144. package/src/renderer/components/team/dialogs/LaunchTeamDialog.tsx +17 -15
  145. package/src/renderer/components/team/dialogs/PlatformBindingDialog.tsx +221 -0
  146. package/src/renderer/components/team/dialogs/PlatformManualForm.tsx +7 -0
  147. package/src/renderer/components/team/dialogs/PlatformSetupQR.tsx +1 -1
  148. package/src/renderer/components/team/dialogs/RuntimeConfigDialog.tsx +361 -0
  149. package/src/renderer/components/team/dialogs/platformMeta.ts +122 -11
  150. package/src/renderer/components/team/dialogs/useTeamEditForm.ts +17 -5
  151. package/src/renderer/components/team/kanban/KanbanBoard.tsx +9 -9
  152. package/src/renderer/components/team/members/MemberCard.tsx +14 -47
  153. package/src/renderer/components/team/members/MemberDetailDialog.tsx +3 -95
  154. package/src/renderer/components/team/members/MemberDetailStats.tsx +50 -65
  155. package/src/renderer/components/team/messages/MessageComposer.tsx +8 -110
  156. package/src/renderer/components/team/messages/MessagesPanel.tsx +131 -114
  157. package/src/renderer/components/team/schedule/ScheduleStatusBadge.tsx +2 -2
  158. package/src/renderer/components/team/tools/AddMcpInline.tsx +57 -0
  159. package/src/renderer/components/team/tools/AddSkillInline.tsx +61 -0
  160. package/src/renderer/components/team/tools/McpChip.tsx +45 -0
  161. package/src/renderer/components/team/tools/SkillChip.tsx +35 -0
  162. package/src/renderer/components/team/tools/ToolsSection.tsx +556 -0
  163. package/src/renderer/hooks/useExtensionsTabState.ts +3 -114
  164. package/src/renderer/index.css +39 -22
  165. package/src/renderer/index.html +17 -50
  166. package/src/renderer/store/index.ts +2 -1
  167. package/src/renderer/store/slices/scheduleSlice.ts +1 -1
  168. package/src/renderer/store/slices/teamSlice.ts +45 -168
  169. package/src/renderer/utils/claudeCodeOnlyProviders.ts +3 -10
  170. package/src/renderer/utils/memberHelpers.ts +5 -17
  171. package/src/renderer/utils/openCodeRuntimeDeliveryDiagnostics.ts +4 -2
  172. package/src/renderer/utils/providerSlashCommands.ts +0 -5
  173. package/src/renderer/utils/scheduleFormatters.ts +3 -1
  174. package/src/renderer/utils/teamMessageFiltering.ts +14 -1
  175. package/src/renderer/utils/teamModelAvailability.ts +18 -2
  176. package/src/shared/types/api.ts +121 -2
  177. package/src/shared/types/ccConnect.ts +2 -0
  178. package/src/shared/types/extensions/api.ts +9 -0
  179. package/src/shared/types/extensions/index.ts +4 -0
  180. package/src/shared/types/extensions/mcp.ts +41 -0
  181. package/src/shared/types/index.ts +3 -0
  182. package/src/shared/types/systemManager.ts +49 -0
  183. package/src/shared/types/team.ts +29 -0
  184. package/src/shared/types/terminal.ts +4 -2
  185. package/src/shared/utils/extensionNormalizers.ts +29 -0
  186. package/src/shared/utils/providerExtensionCapabilities.ts +2 -2
  187. package/dist-renderer/assets/ProjectEditorOverlay-Va_Vz-zz.js +0 -52
  188. package/dist-renderer/assets/channel-5dJIx68e.js +0 -1
  189. package/dist-renderer/assets/classDiagram-2ON5EDUG-BMGXWJ2d.js +0 -1
  190. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-BMGXWJ2d.js +0 -1
  191. package/dist-renderer/assets/clone-D7FWfGY9.js +0 -1
  192. package/dist-renderer/assets/index-B2z_IyRH.css +0 -1
  193. package/dist-renderer/assets/splashScene-C8lWNnm4.js +0 -1
  194. package/dist-renderer/assets/stateDiagram-v2-4FDKWEC3-DOYYvDbi.js +0 -1
  195. package/src/main/services/extensions/catalog/GlamaMcpEnrichmentService.ts +0 -190
  196. package/src/main/services/extensions/catalog/McpCatalogAggregator.ts +0 -150
  197. package/src/main/services/extensions/catalog/OfficialMcpRegistryService.ts +0 -381
  198. package/src/main/services/extensions/install/McpInstallService.ts +0 -407
  199. package/src/main/services/extensions/state/McpInstallationStateService.ts +0 -42
  200. package/src/renderer/components/extensions/mcp/McpServerCard.tsx +0 -314
  201. package/src/renderer/components/extensions/mcp/McpServerDetailDialog.tsx +0 -765
  202. package/src/renderer/components/extensions/mcp/McpServersPanel.tsx +0 -593
  203. package/src/renderer/components/extensions/skills/SkillDetailDialog.tsx +0 -372
  204. package/src/renderer/components/extensions/skills/SkillImportDialog.tsx +0 -343
  205. package/src/renderer/components/extensions/skills/SkillsPanel.tsx +0 -659
@@ -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
+ };
@@ -10,6 +10,7 @@ import {
10
10
  getPluginOperationKey,
11
11
  hasInstallationInScope,
12
12
  inferCapabilities,
13
+ isEssentialPlugin,
13
14
  normalizeCategory,
14
15
  } from '@shared/utils/extensionNormalizers';
15
16
  import { Tag } from 'lucide-react';
@@ -70,9 +71,16 @@ export const PluginCard = ({
70
71
  plugin.isInstalled ? 'border-l-2 border-border border-l-emerald-500/35' : 'border-border'
71
72
  }`}
72
73
  >
73
- {plugin.source === 'official' && (
74
+ {isEssentialPlugin(plugin) && (
74
75
  <div className="pointer-events-none absolute -left-px -top-px size-16 overflow-hidden">
75
- <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">
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
+ </div>
79
+ </div>
80
+ )}
81
+ {plugin.source === 'official' && (
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-blue-500/90 text-center text-[9px] font-semibold leading-[18px] text-white shadow-sm">
76
84
  Official
77
85
  </div>
78
86
  </div>
@@ -16,7 +16,12 @@ import {
16
16
  SelectValue,
17
17
  } from '@renderer/components/ui/select';
18
18
  import { useStore } from '@renderer/store';
19
- import { inferCapabilities, normalizeCategory } from '@shared/utils/extensionNormalizers';
19
+ import {
20
+ inferCapabilities,
21
+ isEssentialPlugin,
22
+ isHiddenPluginFromStore,
23
+ normalizeCategory,
24
+ } from '@shared/utils/extensionNormalizers';
20
25
  import { getCliProviderExtensionCapability } from '@shared/utils/providerExtensionCapabilities';
21
26
  import { ArrowUpDown, Filter, Puzzle, Search } from 'lucide-react';
22
27
  import { useShallow } from 'zustand/react/shallow';
@@ -69,7 +74,7 @@ function selectFilteredPlugins(
69
74
  filters: PluginFilters,
70
75
  sort: { field: PluginSortField; order: 'asc' | 'desc' }
71
76
  ): EnrichedPlugin[] {
72
- let result = catalog;
77
+ let result = catalog.filter((plugin) => !isHiddenPluginFromStore(plugin));
73
78
 
74
79
  // Search
75
80
  if (filters.search) {
@@ -100,9 +105,15 @@ function selectFilteredPlugins(
100
105
  result = result.filter((p) => p.isInstalled);
101
106
  }
102
107
 
103
- // Sort
108
+ // Sort. Essential plugins (oh-my-claudecode, codex) are pinned to the top of
109
+ // the grid — installed or not — so the recommendation lives inside the store
110
+ // list itself (rather than a separate banner). Within each group the chosen
111
+ // sort field still applies.
104
112
  const direction = sort.order === 'asc' ? 1 : -1;
113
+ const recommendRank = (p: EnrichedPlugin): number => (isEssentialPlugin(p) ? 0 : 1);
105
114
  result = [...result].sort((a, b) => {
115
+ const rankDelta = recommendRank(a) - recommendRank(b);
116
+ if (rankDelta !== 0) return rankDelta;
106
117
  switch (sort.field) {
107
118
  case 'popularity':
108
119
  return (a.installCount - b.installCount) * direction;
@@ -191,27 +202,34 @@ export const PluginsPanel = ({
191
202
  }
192
203
  return counts.size;
193
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
+
194
218
  return (
195
219
  <div className="flex flex-col gap-4">
196
- {cliStatus?.flavor === 'agent_teams_orchestrator' &&
197
- (() => {
198
- const codexProvider = cliStatus.providers.find(
199
- (provider) => provider.providerId === 'codex'
200
- );
201
- if (!codexProvider) return null;
202
- const capability = getCliProviderExtensionCapability(codexProvider, 'plugins');
203
- if (capability.status === 'supported') {
204
- return null;
205
- }
206
-
207
- return (
208
- <div className="rounded-md border border-amber-500/30 bg-amber-500/5 px-4 py-3 text-sm text-amber-300">
209
- 在多模型运行时中,目前插件仅保证用于 Anthropic
210
- 会话。我们正在为所有智能体构建更广泛的插件支持,包括通用插件和智能体专用插件。
211
- {capability.reason ? ` ${capability.reason}` : ''}
212
- </div>
213
- );
214
- })()}
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
+ )}
215
233
  {/* Search + Sort + Installed only row */}
216
234
  <div className="flex flex-col gap-3 lg:flex-row lg:items-center">
217
235
  <div className="flex-1">