@yancyyu/openhermit 1.6.37 → 1.6.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-renderer/assets/ProjectEditorOverlay-krO5vQxX.js +58 -0
- package/dist-renderer/assets/{TeamGraphOverlay-DYT3bwFR.js → TeamGraphOverlay-DqhQzcTr.js} +1 -1
- package/dist-renderer/assets/{_basePickBy-Dbt_EU-e.js → _basePickBy-B7kSYPxr.js} +1 -1
- package/dist-renderer/assets/{_baseUniq-DWo68sXI.js → _baseUniq-CnjxqwAk.js} +1 -1
- package/dist-renderer/assets/{arc-DXH1iZQK.js → arc-CLeZuINP.js} +1 -1
- package/dist-renderer/assets/{architectureDiagram-VXUJARFQ-cjffS2Qr.js → architectureDiagram-VXUJARFQ-QKtqaqdY.js} +1 -1
- package/dist-renderer/assets/{blockDiagram-VD42YOAC-BKdZF02Y.js → blockDiagram-VD42YOAC-BqdrzO_f.js} +1 -1
- package/dist-renderer/assets/{c4Diagram-YG6GDRKO-CN27pqaI.js → c4Diagram-YG6GDRKO-gwPlCxDC.js} +1 -1
- package/dist-renderer/assets/channel-DpMHF50r.js +1 -0
- package/dist-renderer/assets/{chunk-4BX2VUAB-CXPCI7g_.js → chunk-4BX2VUAB-C6XLurL4.js} +1 -1
- package/dist-renderer/assets/{chunk-55IACEB6-BGAXQZRC.js → chunk-55IACEB6-Ds6quhEP.js} +1 -1
- package/dist-renderer/assets/{chunk-B4BG7PRW-TPDaA_KQ.js → chunk-B4BG7PRW-5UlA1_e9.js} +1 -1
- package/dist-renderer/assets/{chunk-DI55MBZ5-D1ADe_tq.js → chunk-DI55MBZ5-ywFrqIsY.js} +1 -1
- package/dist-renderer/assets/{chunk-FMBD7UC4-Beimtg3a.js → chunk-FMBD7UC4-C7ifUA17.js} +1 -1
- package/dist-renderer/assets/{chunk-QN33PNHL-OjNBu854.js → chunk-QN33PNHL-BxGCo80U.js} +1 -1
- package/dist-renderer/assets/{chunk-QZHKN3VN-DinqvbH8.js → chunk-QZHKN3VN-B2CuaZs6.js} +1 -1
- package/dist-renderer/assets/{chunk-TZMSLE5B-BfFtlPSZ.js → chunk-TZMSLE5B-Ds1hInvp.js} +1 -1
- package/dist-renderer/assets/classDiagram-2ON5EDUG-CBYCBVRl.js +1 -0
- package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-CBYCBVRl.js +1 -0
- package/dist-renderer/assets/clone-DcMF6Psb.js +1 -0
- package/dist-renderer/assets/{cose-bilkent-S5V4N54A-D9z9Dgt7.js → cose-bilkent-S5V4N54A-Cz1GVtLp.js} +1 -1
- package/dist-renderer/assets/{dagre-6UL2VRFP-n1g-DhEE.js → dagre-6UL2VRFP-BrmR-P4h.js} +1 -1
- package/dist-renderer/assets/{diagram-PSM6KHXK-BvxFq-BE.js → diagram-PSM6KHXK-DbNjC5Rg.js} +1 -1
- package/dist-renderer/assets/{diagram-QEK2KX5R-wVnJuwza.js → diagram-QEK2KX5R-qkRX5_Mq.js} +1 -1
- package/dist-renderer/assets/{diagram-S2PKOQOG-B707WJQw.js → diagram-S2PKOQOG-CyL5rCv2.js} +1 -1
- package/dist-renderer/assets/{erDiagram-Q2GNP2WA-C-_1dGHs.js → erDiagram-Q2GNP2WA-Dox3-bA5.js} +1 -1
- package/dist-renderer/assets/{flowDiagram-NV44I4VS-CMTSi3H6.js → flowDiagram-NV44I4VS-BtkaxlDL.js} +1 -1
- package/dist-renderer/assets/{ganttDiagram-JELNMOA3-DZ0bNrAA.js → ganttDiagram-JELNMOA3-Dhy_d9GK.js} +1 -1
- package/dist-renderer/assets/{gitGraphDiagram-V2S2FVAM-DNVfGooQ.js → gitGraphDiagram-V2S2FVAM-B5XRhIQA.js} +1 -1
- package/dist-renderer/assets/{graph-865j_tM_.js → graph-CsoEwUhS.js} +1 -1
- package/dist-renderer/assets/{index-C_F9N5x-.js → index-BWPWmJNo.js} +1 -1
- package/dist-renderer/assets/{index-LwDIsXJN.js → index-Bu2R-Se7.js} +586 -740
- package/dist-renderer/assets/index-CnWV3BhG.css +32 -0
- package/dist-renderer/assets/{index-DuUaf8at.js → index-D-3KgskL.js} +1 -1
- package/dist-renderer/assets/{index-BTx1nc4T.js → index-DGEBzLNT.js} +1 -1
- package/dist-renderer/assets/{index-2EW-eu3q.js → index-NhHNs2Oo.js} +1 -1
- package/dist-renderer/assets/{index-4dEMStJj.js → index-h17WuEyf.js} +1 -1
- package/dist-renderer/assets/{infoDiagram-HS3SLOUP-CyqtElLq.js → infoDiagram-HS3SLOUP-hMGmNojH.js} +1 -1
- package/dist-renderer/assets/{journeyDiagram-XKPGCS4Q-BvjQ0Hm0.js → journeyDiagram-XKPGCS4Q-DXV2rBDl.js} +1 -1
- package/dist-renderer/assets/{kanban-definition-3W4ZIXB7-CJJ-k0zT.js → kanban-definition-3W4ZIXB7-Bf99WLRy.js} +1 -1
- package/dist-renderer/assets/{layout-CnV6rQAG.js → layout-C3XWrpwo.js} +1 -1
- package/dist-renderer/assets/{linear-Cw3UQgyX.js → linear-OEEcn8KN.js} +1 -1
- package/dist-renderer/assets/{mindmap-definition-VGOIOE7T-C5tDaGSK.js → mindmap-definition-VGOIOE7T-Dpi3S2x4.js} +1 -1
- package/dist-renderer/assets/{pieDiagram-ADFJNKIX-CiIpPsau.js → pieDiagram-ADFJNKIX-xTPPhtNx.js} +1 -1
- package/dist-renderer/assets/{quadrantDiagram-AYHSOK5B-C3gtowNj.js → quadrantDiagram-AYHSOK5B-euniyDlz.js} +1 -1
- package/dist-renderer/assets/{requirementDiagram-UZGBJVZJ-CXBTrAnU.js → requirementDiagram-UZGBJVZJ-D9Uiw4kF.js} +1 -1
- package/dist-renderer/assets/{sankeyDiagram-TZEHDZUN-wziX77xG.js → sankeyDiagram-TZEHDZUN-CySU4nED.js} +1 -1
- package/dist-renderer/assets/{sequenceDiagram-WL72ISMW-sYqopcrj.js → sequenceDiagram-WL72ISMW-JVGpET6V.js} +1 -1
- package/dist-renderer/assets/splashScene-D0YB9uxm.js +17 -0
- package/dist-renderer/assets/{stateDiagram-FKZM4ZOC-Bl1-0_Cp.js → stateDiagram-FKZM4ZOC-B2FY5qqi.js} +1 -1
- package/dist-renderer/assets/stateDiagram-v2-4FDKWEC3-DcoMiR8H.js +1 -0
- package/dist-renderer/assets/{timeline-definition-IT6M3QCI-CIRjJUBo.js → timeline-definition-IT6M3QCI-DmycNUUe.js} +1 -1
- package/dist-renderer/assets/{treemap-GDKQZRPO-CVPuNe1n.js → treemap-GDKQZRPO-DPq4gZuB.js} +1 -1
- package/dist-renderer/assets/{xychartDiagram-PRI3JC2R-3nT9yHwp.js → xychartDiagram-PRI3JC2R-J6VVJzRq.js} +1 -1
- package/dist-renderer/index.html +20 -53
- package/package.json +25 -18
- package/src/main/ipc/extensions.ts +30 -50
- package/src/main/server.ts +890 -247
- package/src/main/services/extensions/ExtensionFacadeService.ts +4 -56
- package/src/main/services/extensions/catalog/PluginCatalogService.ts +4 -2
- package/src/main/services/extensions/library/McpLibraryService.ts +243 -0
- package/src/main/services/session-intelligence/ConversationTelemetryService.ts +1101 -0
- package/src/main/services/session-intelligence/LocalSessionScanner.ts +512 -0
- package/src/main/services/session-intelligence/SessionUsageParser.ts +4 -4
- package/src/main/services/session-intelligence/UsageTelemetryService.ts +14 -1
- package/src/main/services/system-manager/SystemManagerConfigService.ts +122 -0
- package/src/main/services/system-manager/SystemManagerPtyService.ts +233 -0
- package/src/main/services/system-manager/WorkflowPromptService.ts +75 -0
- package/src/main/services/teams-mvp/TaskDispatchService.ts +32 -8
- package/src/main/services/teams-mvp/TeamProvisioningService.ts +39 -2
- package/src/main/services/teams-mvp/TeamWorkspaceService.ts +22 -4
- package/src/main/utils/teamProjectResolution.ts +15 -0
- package/src/renderer/App.tsx +8 -4
- package/src/renderer/api/httpClient.ts +174 -38
- package/src/renderer/api/providers.ts +23 -2
- package/src/renderer/assets/participant-avatars/01.svg +3 -0
- package/src/renderer/assets/participant-avatars/02.svg +3 -0
- package/src/renderer/assets/participant-avatars/03.svg +3 -0
- package/src/renderer/assets/participant-avatars/04.svg +3 -0
- package/src/renderer/assets/participant-avatars/05.svg +3 -0
- package/src/renderer/assets/participant-avatars/06.svg +3 -0
- package/src/renderer/assets/participant-avatars/07.svg +3 -0
- package/src/renderer/assets/participant-avatars/08.svg +3 -0
- package/src/renderer/assets/participant-avatars/09.svg +3 -0
- package/src/renderer/assets/participant-avatars/10.svg +3 -0
- package/src/renderer/assets/participant-avatars/11.svg +3 -0
- package/src/renderer/assets/participant-avatars/12.svg +3 -0
- package/src/renderer/assets/participant-avatars/13.svg +3 -0
- package/src/renderer/components/common/TerminalPane.tsx +213 -0
- package/src/renderer/components/dashboard/DashboardView.tsx +9 -36
- package/src/renderer/components/extensions/ExtensionStoreView.tsx +12 -221
- package/src/renderer/components/extensions/ExtensionsSubTabTrigger.tsx +1 -1
- package/src/renderer/components/extensions/mcp/McpLibraryEnableDialog.tsx +305 -0
- package/src/renderer/components/extensions/mcp/McpLibraryEntryDialog.tsx +418 -0
- package/src/renderer/components/extensions/mcp/McpLibraryPanel.tsx +404 -0
- package/src/renderer/components/extensions/plugins/PluginCard.tsx +10 -2
- package/src/renderer/components/extensions/plugins/PluginsPanel.tsx +40 -22
- package/src/renderer/components/extensions/skills/SkillsLibraryPanel.tsx +335 -0
- package/src/renderer/components/layout/PaneContent.tsx +8 -1
- package/src/renderer/components/layout/Sidebar.tsx +11 -54
- package/src/renderer/components/layout/SortableTab.tsx +20 -31
- package/src/renderer/components/layout/TabBar.tsx +1 -1
- package/src/renderer/components/layout/TabContextMenu.tsx +1 -1
- package/src/renderer/components/runtime/ProviderRuntimeSettingsDialog.tsx +768 -157
- package/src/renderer/components/schedules/SchedulesView.tsx +51 -462
- package/src/renderer/components/schedules/calendar/CalendarDayView.tsx +173 -0
- package/src/renderer/components/schedules/calendar/CalendarEventBlock.tsx +113 -0
- package/src/renderer/components/schedules/calendar/CalendarHeader.tsx +148 -0
- package/src/renderer/components/schedules/calendar/CalendarMonthView.tsx +142 -0
- package/src/renderer/components/schedules/calendar/CalendarWeekView.tsx +219 -0
- package/src/renderer/components/schedules/calendar/ScheduleCalendarBoard.tsx +41 -0
- package/src/renderer/components/schedules/calendar/TeamGanttView.tsx +405 -0
- package/src/renderer/components/schedules/calendar/computeOccurrences.ts +234 -0
- package/src/renderer/components/schedules/calendar/index.ts +2 -0
- package/src/renderer/components/schedules/calendar/types.ts +44 -0
- package/src/renderer/components/settings/SettingsTabs.tsx +50 -55
- package/src/renderer/components/settings/SettingsView.tsx +30 -35
- package/src/renderer/components/settings/components/SettingsSectionHeader.tsx +5 -1
- package/src/renderer/components/settings/components/SettingsSelect.tsx +5 -3
- package/src/renderer/components/settings/components/SettingsToggle.tsx +2 -2
- package/src/renderer/components/settings/sections/AdvancedSection.tsx +11 -42
- package/src/renderer/components/settings/sections/CliStatusSection.tsx +71 -112
- package/src/renderer/components/settings/sections/ConfigEditorDialog.tsx +1 -1
- package/src/renderer/components/settings/sections/GeneralSection.tsx +11 -3
- package/src/renderer/components/settings/sections/HarnessSection.tsx +18 -14
- package/src/renderer/components/settings/sections/PlatformsSection.tsx +3 -3
- package/src/renderer/components/settings/sections/TaskBusSection.tsx +33 -40
- package/src/renderer/components/settings/sections/index.ts +0 -1
- package/src/renderer/components/sidebar/SidebarSessions.tsx +182 -4
- package/src/renderer/components/sidebar/SidebarTaskItem.tsx +4 -4
- package/src/renderer/components/sidebar/WorkspaceBrowser.tsx +39 -4
- package/src/renderer/components/splash/splashScene.ts +121 -929
- package/src/renderer/components/system-manager/FolderBrowser.tsx +163 -0
- package/src/renderer/components/system-manager/SystemManagerView.tsx +351 -0
- package/src/renderer/components/tasks/TasksView.tsx +112 -134
- package/src/renderer/components/team/CcSessionsSection.tsx +431 -89
- package/src/renderer/components/team/CollapsibleTeamSection.tsx +17 -32
- package/src/renderer/components/team/TeamDetailView.tsx +325 -114
- package/src/renderer/components/team/TeamListView.tsx +108 -123
- package/src/renderer/components/team/dialogs/CreateTaskDialog.tsx +2 -2
- package/src/renderer/components/team/dialogs/CreateTeamDialog.tsx +84 -306
- package/src/renderer/components/team/dialogs/EditTeamDialog.tsx +259 -342
- package/src/renderer/components/team/dialogs/GlobalTaskDetailDialog.tsx +1 -1
- package/src/renderer/components/team/dialogs/LaunchTeamDialog.tsx +17 -15
- package/src/renderer/components/team/dialogs/PlatformBindingDialog.tsx +221 -0
- package/src/renderer/components/team/dialogs/PlatformManualForm.tsx +7 -0
- package/src/renderer/components/team/dialogs/PlatformSetupQR.tsx +1 -1
- package/src/renderer/components/team/dialogs/RuntimeConfigDialog.tsx +361 -0
- package/src/renderer/components/team/dialogs/platformMeta.ts +122 -11
- package/src/renderer/components/team/dialogs/useTeamEditForm.ts +17 -5
- package/src/renderer/components/team/kanban/KanbanBoard.tsx +9 -9
- package/src/renderer/components/team/members/MemberCard.tsx +14 -47
- package/src/renderer/components/team/members/MemberDetailDialog.tsx +3 -95
- package/src/renderer/components/team/members/MemberDetailStats.tsx +50 -65
- package/src/renderer/components/team/messages/MessageComposer.tsx +8 -110
- package/src/renderer/components/team/messages/MessagesPanel.tsx +131 -114
- package/src/renderer/components/team/schedule/ScheduleStatusBadge.tsx +2 -2
- package/src/renderer/components/team/tools/AddMcpInline.tsx +57 -0
- package/src/renderer/components/team/tools/AddSkillInline.tsx +61 -0
- package/src/renderer/components/team/tools/McpChip.tsx +45 -0
- package/src/renderer/components/team/tools/SkillChip.tsx +35 -0
- package/src/renderer/components/team/tools/ToolsSection.tsx +556 -0
- package/src/renderer/hooks/useExtensionsTabState.ts +3 -114
- package/src/renderer/index.css +39 -22
- package/src/renderer/index.html +17 -50
- package/src/renderer/store/index.ts +2 -1
- package/src/renderer/store/slices/scheduleSlice.ts +1 -1
- package/src/renderer/store/slices/teamSlice.ts +45 -168
- package/src/renderer/utils/claudeCodeOnlyProviders.ts +3 -10
- package/src/renderer/utils/memberHelpers.ts +5 -17
- package/src/renderer/utils/openCodeRuntimeDeliveryDiagnostics.ts +4 -2
- package/src/renderer/utils/providerSlashCommands.ts +0 -5
- package/src/renderer/utils/scheduleFormatters.ts +3 -1
- package/src/renderer/utils/teamMessageFiltering.ts +14 -1
- package/src/renderer/utils/teamModelAvailability.ts +18 -2
- package/src/shared/types/api.ts +121 -2
- package/src/shared/types/ccConnect.ts +2 -0
- package/src/shared/types/extensions/api.ts +9 -0
- package/src/shared/types/extensions/index.ts +4 -0
- package/src/shared/types/extensions/mcp.ts +41 -0
- package/src/shared/types/index.ts +3 -0
- package/src/shared/types/systemManager.ts +49 -0
- package/src/shared/types/team.ts +29 -0
- package/src/shared/types/terminal.ts +4 -2
- package/src/shared/utils/extensionNormalizers.ts +29 -0
- package/src/shared/utils/providerExtensionCapabilities.ts +2 -2
- package/dist-renderer/assets/ProjectEditorOverlay-Va_Vz-zz.js +0 -52
- package/dist-renderer/assets/channel-5dJIx68e.js +0 -1
- package/dist-renderer/assets/classDiagram-2ON5EDUG-BMGXWJ2d.js +0 -1
- package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-BMGXWJ2d.js +0 -1
- package/dist-renderer/assets/clone-D7FWfGY9.js +0 -1
- package/dist-renderer/assets/index-B2z_IyRH.css +0 -1
- package/dist-renderer/assets/splashScene-C8lWNnm4.js +0 -1
- package/dist-renderer/assets/stateDiagram-v2-4FDKWEC3-DOYYvDbi.js +0 -1
- package/src/main/services/extensions/catalog/GlamaMcpEnrichmentService.ts +0 -190
- package/src/main/services/extensions/catalog/McpCatalogAggregator.ts +0 -150
- package/src/main/services/extensions/catalog/OfficialMcpRegistryService.ts +0 -381
- package/src/main/services/extensions/install/McpInstallService.ts +0 -407
- package/src/main/services/extensions/state/McpInstallationStateService.ts +0 -42
- package/src/renderer/components/extensions/mcp/McpServerCard.tsx +0 -314
- package/src/renderer/components/extensions/mcp/McpServerDetailDialog.tsx +0 -765
- package/src/renderer/components/extensions/mcp/McpServersPanel.tsx +0 -593
- package/src/renderer/components/extensions/skills/SkillDetailDialog.tsx +0 -372
- package/src/renderer/components/extensions/skills/SkillImportDialog.tsx +0 -343
- package/src/renderer/components/extensions/skills/SkillsPanel.tsx +0 -659
|
@@ -1,34 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Facade service that combines plugin catalog +
|
|
2
|
+
* Facade service that combines plugin catalog + installation state
|
|
3
3
|
* into enriched data ready for the renderer.
|
|
4
4
|
*
|
|
5
5
|
* Also provides install target resolution for the security model
|
|
6
|
-
* (main-side re-resolution: renderer sends pluginId
|
|
6
|
+
* (main-side re-resolution: renderer sends pluginId, main resolves from catalog).
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { createLogger } from '@shared/utils/logger';
|
|
10
10
|
|
|
11
|
-
import { type McpCatalogAggregator } from './catalog/McpCatalogAggregator';
|
|
12
11
|
import { type PluginCatalogService } from './catalog/PluginCatalogService';
|
|
13
|
-
import { type McpInstallationStateService } from './state/McpInstallationStateService';
|
|
14
12
|
import { type PluginInstallationStateService } from './state/PluginInstallationStateService';
|
|
15
13
|
|
|
16
|
-
import type {
|
|
17
|
-
EnrichedPlugin,
|
|
18
|
-
InstalledMcpEntry,
|
|
19
|
-
McpCatalogItem,
|
|
20
|
-
McpSearchResult,
|
|
21
|
-
PluginCatalogItem,
|
|
22
|
-
} from '@shared/types/extensions';
|
|
14
|
+
import type { EnrichedPlugin, PluginCatalogItem } from '@shared/types/extensions';
|
|
23
15
|
|
|
24
16
|
const logger = createLogger('Extensions:Facade');
|
|
25
17
|
|
|
26
18
|
export class ExtensionFacadeService {
|
|
27
19
|
constructor(
|
|
28
20
|
private readonly pluginCatalog: PluginCatalogService,
|
|
29
|
-
private readonly pluginState: PluginInstallationStateService
|
|
30
|
-
private readonly mcpAggregator: McpCatalogAggregator | null = null,
|
|
31
|
-
private readonly mcpState: McpInstallationStateService | null = null
|
|
21
|
+
private readonly pluginState: PluginInstallationStateService
|
|
32
22
|
) {}
|
|
33
23
|
|
|
34
24
|
// ── Plugin methods ───────────────────────────────────────────────────
|
|
@@ -85,51 +75,9 @@ export class ExtensionFacadeService {
|
|
|
85
75
|
return { qualifiedName: plugin.qualifiedName, plugin };
|
|
86
76
|
}
|
|
87
77
|
|
|
88
|
-
// ── MCP methods ──────────────────────────────────────────────────────
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Search MCP servers across both registries.
|
|
92
|
-
*/
|
|
93
|
-
async searchMcp(query: string, limit?: number): Promise<McpSearchResult> {
|
|
94
|
-
if (!this.mcpAggregator) {
|
|
95
|
-
return { servers: [], warnings: ['MCP catalog not configured'] };
|
|
96
|
-
}
|
|
97
|
-
return this.mcpAggregator.search(query, limit);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Browse MCP catalog with pagination.
|
|
102
|
-
*/
|
|
103
|
-
async browseMcp(
|
|
104
|
-
cursor?: string,
|
|
105
|
-
limit?: number
|
|
106
|
-
): Promise<{ servers: McpCatalogItem[]; nextCursor?: string }> {
|
|
107
|
-
if (!this.mcpAggregator) {
|
|
108
|
-
return { servers: [] };
|
|
109
|
-
}
|
|
110
|
-
return this.mcpAggregator.browse(cursor, limit);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Get a single MCP server by registry ID (for install flow).
|
|
115
|
-
*/
|
|
116
|
-
async getMcpById(registryId: string): Promise<McpCatalogItem | null> {
|
|
117
|
-
if (!this.mcpAggregator) return null;
|
|
118
|
-
return this.mcpAggregator.getById(registryId);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Get installed MCP servers.
|
|
123
|
-
*/
|
|
124
|
-
async getInstalledMcp(projectPath?: string): Promise<InstalledMcpEntry[]> {
|
|
125
|
-
if (!this.mcpState) return [];
|
|
126
|
-
return this.mcpState.getInstalled(projectPath);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
78
|
// ── Cache invalidation ───────────────────────────────────────────────
|
|
130
79
|
|
|
131
80
|
invalidateInstalledCache(): void {
|
|
132
81
|
this.pluginState.invalidateCache();
|
|
133
|
-
this.mcpState?.invalidateCache();
|
|
134
82
|
}
|
|
135
83
|
}
|
|
@@ -14,7 +14,7 @@ import https from 'node:https';
|
|
|
14
14
|
import path from 'node:path';
|
|
15
15
|
|
|
16
16
|
import { getClaudeBasePath } from '@main/utils/pathDecoder';
|
|
17
|
-
import { buildPluginId } from '@shared/utils/extensionNormalizers';
|
|
17
|
+
import { buildPluginId, isHiddenPluginFromStore } from '@shared/utils/extensionNormalizers';
|
|
18
18
|
import { createLogger } from '@shared/utils/logger';
|
|
19
19
|
|
|
20
20
|
import type { PluginCatalogItem } from '@shared/types/extensions';
|
|
@@ -174,7 +174,9 @@ export class PluginCatalogService {
|
|
|
174
174
|
|
|
175
175
|
// Official wins on pluginId collisions; append local plugins not already present.
|
|
176
176
|
const seen = new Set(official.map((p) => p.pluginId));
|
|
177
|
-
return [...official, ...local.filter((p) => !seen.has(p.pluginId))]
|
|
177
|
+
return [...official, ...local.filter((p) => !seen.has(p.pluginId))].filter(
|
|
178
|
+
(plugin) => !isHiddenPluginFromStore(plugin)
|
|
179
|
+
);
|
|
178
180
|
}
|
|
179
181
|
|
|
180
182
|
/**
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* McpLibraryService — a reusable, global library of MCP server definitions.
|
|
3
|
+
*
|
|
4
|
+
* cc-switch model: a server is defined once and can be enabled for any worker
|
|
5
|
+
* (= installed into that worker's project config) without re-entering the
|
|
6
|
+
* command / URL / env each time. The "enable/disable for a worker" action is
|
|
7
|
+
* handled by the existing install/uninstall path; this service only owns the
|
|
8
|
+
* persisted library of definitions.
|
|
9
|
+
*
|
|
10
|
+
* Storage: ~/.hermit/mcp-library.json (HERMIT_HOME override respected).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import * as fs from 'node:fs';
|
|
14
|
+
import * as os from 'node:os';
|
|
15
|
+
import * as path from 'node:path';
|
|
16
|
+
import { randomUUID } from 'node:crypto';
|
|
17
|
+
|
|
18
|
+
import { createLogger } from '@shared/utils/logger';
|
|
19
|
+
import { getErrorMessage } from '@shared/utils/errorHandling';
|
|
20
|
+
|
|
21
|
+
import { McpConfigStateReader } from '../runtime/McpConfigStateReader';
|
|
22
|
+
|
|
23
|
+
import type {
|
|
24
|
+
McpHeaderDef,
|
|
25
|
+
McpInstallSpec,
|
|
26
|
+
McpLibraryEntry,
|
|
27
|
+
McpLibraryImportRequest,
|
|
28
|
+
McpLibraryImportResult,
|
|
29
|
+
McpLibraryUpsertRequest,
|
|
30
|
+
} from '@shared/types/extensions';
|
|
31
|
+
|
|
32
|
+
const logger = createLogger('Extensions:McpLibrary');
|
|
33
|
+
|
|
34
|
+
function getHermitHome(): string {
|
|
35
|
+
return process.env.HERMIT_HOME ?? path.join(os.homedir(), '.hermit');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Best-effort mapping of a raw MCP server config (from ~/.claude.json /
|
|
40
|
+
* .mcp.json) into the install spec the current install path understands.
|
|
41
|
+
* Returns null when the config cannot be represented (e.g. an arbitrary stdio
|
|
42
|
+
* command rather than an npm package) — the caller skips those.
|
|
43
|
+
*/
|
|
44
|
+
function rawConfigToInstallSpec(config: Record<string, unknown>): McpInstallSpec | null {
|
|
45
|
+
const url = typeof config.url === 'string' ? config.url : null;
|
|
46
|
+
if (url) {
|
|
47
|
+
const rawType = typeof config.type === 'string' ? config.type : 'http';
|
|
48
|
+
const transportType =
|
|
49
|
+
rawType === 'sse' ? 'sse' : rawType === 'streamable-http' ? 'streamable-http' : 'http';
|
|
50
|
+
return { type: 'http', url, transportType };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const command = typeof config.command === 'string' ? config.command : null;
|
|
54
|
+
if (command) {
|
|
55
|
+
const args = Array.isArray(config.args) ? config.args.map(String) : [];
|
|
56
|
+
const npmPackage = extractNpmPackage(command, args);
|
|
57
|
+
if (npmPackage) {
|
|
58
|
+
return { type: 'stdio', npmPackage };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Pull the package spec out of `npx -y <pkg>` / `npm exec <pkg>` style commands. */
|
|
66
|
+
function extractNpmPackage(command: string, args: string[]): string | null {
|
|
67
|
+
const base = path.basename(command);
|
|
68
|
+
if (base !== 'npx' && base !== 'npm' && base !== 'pnpm' && base !== 'bunx') {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
// Skip flags (-y, --yes, exec, ...) and take the first package-looking token.
|
|
72
|
+
for (const arg of args) {
|
|
73
|
+
if (arg.startsWith('-')) continue;
|
|
74
|
+
if (arg === 'exec' || arg === 'dlx') continue;
|
|
75
|
+
return arg;
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function extractEnvValues(config: Record<string, unknown>): Record<string, string> | undefined {
|
|
81
|
+
const env = config.env;
|
|
82
|
+
if (!env || typeof env !== 'object' || Array.isArray(env)) return undefined;
|
|
83
|
+
const out: Record<string, string> = {};
|
|
84
|
+
for (const [key, value] of Object.entries(env as Record<string, unknown>)) {
|
|
85
|
+
if (typeof value === 'string') out[key] = value;
|
|
86
|
+
}
|
|
87
|
+
return Object.keys(out).length > 0 ? out : undefined;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function extractHeaders(config: Record<string, unknown>): McpHeaderDef[] | undefined {
|
|
91
|
+
const headers = config.headers;
|
|
92
|
+
if (!headers || typeof headers !== 'object' || Array.isArray(headers)) return undefined;
|
|
93
|
+
const out: McpHeaderDef[] = [];
|
|
94
|
+
for (const [key, value] of Object.entries(headers as Record<string, unknown>)) {
|
|
95
|
+
if (typeof value === 'string') out.push({ key, value });
|
|
96
|
+
}
|
|
97
|
+
return out.length > 0 ? out : undefined;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export class McpLibraryService {
|
|
101
|
+
private readonly filePath: string;
|
|
102
|
+
private entries: McpLibraryEntry[] | null = null;
|
|
103
|
+
|
|
104
|
+
constructor(
|
|
105
|
+
dataDir: string = getHermitHome(),
|
|
106
|
+
private readonly stateReader = new McpConfigStateReader()
|
|
107
|
+
) {
|
|
108
|
+
this.filePath = path.join(dataDir, 'mcp-library.json');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ── Read ─────────────────────────────────────────────────────────────────
|
|
112
|
+
|
|
113
|
+
list(): McpLibraryEntry[] {
|
|
114
|
+
return [...this.load()].sort((a, b) => a.name.localeCompare(b.name));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ── Write ──────────────────────────────────────────────────────────────────
|
|
118
|
+
|
|
119
|
+
upsert(request: McpLibraryUpsertRequest): McpLibraryEntry {
|
|
120
|
+
const name = request.name.trim();
|
|
121
|
+
if (!name) throw new Error('MCP 名称不能为空');
|
|
122
|
+
|
|
123
|
+
const entries = this.load();
|
|
124
|
+
const now = Date.now();
|
|
125
|
+
|
|
126
|
+
if (request.id) {
|
|
127
|
+
const existing = entries.find((e) => e.id === request.id);
|
|
128
|
+
if (!existing) throw new Error(`未找到库条目: ${request.id}`);
|
|
129
|
+
this.assertNameAvailable(entries, name, request.id);
|
|
130
|
+
existing.name = name;
|
|
131
|
+
existing.description = request.description?.trim() || undefined;
|
|
132
|
+
existing.installSpec = request.installSpec;
|
|
133
|
+
existing.envValues = request.envValues;
|
|
134
|
+
existing.headers = request.headers;
|
|
135
|
+
existing.updatedAt = now;
|
|
136
|
+
this.save(entries);
|
|
137
|
+
return existing;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
this.assertNameAvailable(entries, name, null);
|
|
141
|
+
const entry: McpLibraryEntry = {
|
|
142
|
+
id: randomUUID(),
|
|
143
|
+
name,
|
|
144
|
+
description: request.description?.trim() || undefined,
|
|
145
|
+
installSpec: request.installSpec,
|
|
146
|
+
envValues: request.envValues,
|
|
147
|
+
headers: request.headers,
|
|
148
|
+
createdAt: now,
|
|
149
|
+
updatedAt: now,
|
|
150
|
+
};
|
|
151
|
+
entries.push(entry);
|
|
152
|
+
this.save(entries);
|
|
153
|
+
return entry;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
remove(id: string): void {
|
|
157
|
+
const entries = this.load();
|
|
158
|
+
const next = entries.filter((e) => e.id !== id);
|
|
159
|
+
if (next.length !== entries.length) this.save(next);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Import MCP servers already present in live config into the library.
|
|
164
|
+
* Skips entries whose name already exists, and ones whose config can't be
|
|
165
|
+
* represented by the current install spec (e.g. arbitrary stdio commands).
|
|
166
|
+
*/
|
|
167
|
+
async importFromLive(request: McpLibraryImportRequest): Promise<McpLibraryImportResult> {
|
|
168
|
+
const configured = await this.stateReader.readConfigured(request.projectPath);
|
|
169
|
+
const entries = this.load();
|
|
170
|
+
const existingNames = new Set(entries.map((e) => e.name.toLowerCase()));
|
|
171
|
+
|
|
172
|
+
const imported: string[] = [];
|
|
173
|
+
const skipped: string[] = [];
|
|
174
|
+
const now = Date.now();
|
|
175
|
+
|
|
176
|
+
for (const entry of configured) {
|
|
177
|
+
if (existingNames.has(entry.name.toLowerCase())) {
|
|
178
|
+
skipped.push(entry.name);
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
const installSpec = rawConfigToInstallSpec(entry.config);
|
|
182
|
+
if (!installSpec) {
|
|
183
|
+
skipped.push(entry.name);
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
entries.push({
|
|
187
|
+
id: randomUUID(),
|
|
188
|
+
name: entry.name,
|
|
189
|
+
installSpec,
|
|
190
|
+
envValues: extractEnvValues(entry.config),
|
|
191
|
+
headers: extractHeaders(entry.config),
|
|
192
|
+
createdAt: now,
|
|
193
|
+
updatedAt: now,
|
|
194
|
+
});
|
|
195
|
+
existingNames.add(entry.name.toLowerCase());
|
|
196
|
+
imported.push(entry.name);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (imported.length > 0) this.save(entries);
|
|
200
|
+
return { imported, skipped };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// ── Private ──────────────────────────────────────────────────────────────
|
|
204
|
+
|
|
205
|
+
private assertNameAvailable(
|
|
206
|
+
entries: McpLibraryEntry[],
|
|
207
|
+
name: string,
|
|
208
|
+
ignoreId: string | null
|
|
209
|
+
): void {
|
|
210
|
+
const clash = entries.find(
|
|
211
|
+
(e) => e.name.toLowerCase() === name.toLowerCase() && e.id !== ignoreId
|
|
212
|
+
);
|
|
213
|
+
if (clash) throw new Error(`库中已存在同名 MCP: ${name}`);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
private load(): McpLibraryEntry[] {
|
|
217
|
+
if (this.entries) return this.entries;
|
|
218
|
+
try {
|
|
219
|
+
if (fs.existsSync(this.filePath)) {
|
|
220
|
+
const raw = fs.readFileSync(this.filePath, 'utf-8');
|
|
221
|
+
const parsed = JSON.parse(raw) as McpLibraryEntry[];
|
|
222
|
+
this.entries = Array.isArray(parsed) ? parsed : [];
|
|
223
|
+
} else {
|
|
224
|
+
this.entries = [];
|
|
225
|
+
}
|
|
226
|
+
} catch (error) {
|
|
227
|
+
logger.warn(`Failed to load MCP library from ${this.filePath}: ${getErrorMessage(error)}`);
|
|
228
|
+
this.entries = [];
|
|
229
|
+
}
|
|
230
|
+
return this.entries;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
private save(entries: McpLibraryEntry[]): void {
|
|
234
|
+
this.entries = entries;
|
|
235
|
+
try {
|
|
236
|
+
const dir = path.dirname(this.filePath);
|
|
237
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
238
|
+
fs.writeFileSync(this.filePath, JSON.stringify(entries, null, 2), 'utf-8');
|
|
239
|
+
} catch (error) {
|
|
240
|
+
logger.warn(`Failed to save MCP library to ${this.filePath}: ${getErrorMessage(error)}`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|