@yancyyu/openhermit 1.6.37 → 1.6.38
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-Va_Vz-zz.js → ProjectEditorOverlay-lJZi-9Hp.js} +1 -1
- package/dist-renderer/assets/{TeamGraphOverlay-DYT3bwFR.js → TeamGraphOverlay-ZEDfZyHb.js} +1 -1
- package/dist-renderer/assets/{_basePickBy-Dbt_EU-e.js → _basePickBy-CIhniz70.js} +1 -1
- package/dist-renderer/assets/{_baseUniq-DWo68sXI.js → _baseUniq-cKAW4Q8I.js} +1 -1
- package/dist-renderer/assets/{arc-DXH1iZQK.js → arc-YmNsoDXW.js} +1 -1
- package/dist-renderer/assets/{architectureDiagram-VXUJARFQ-cjffS2Qr.js → architectureDiagram-VXUJARFQ-DHEls2sX.js} +1 -1
- package/dist-renderer/assets/{blockDiagram-VD42YOAC-BKdZF02Y.js → blockDiagram-VD42YOAC-Bpwf1Sbg.js} +1 -1
- package/dist-renderer/assets/{c4Diagram-YG6GDRKO-CN27pqaI.js → c4Diagram-YG6GDRKO-B0IaQ4w5.js} +1 -1
- package/dist-renderer/assets/channel-yIlSKy0e.js +1 -0
- package/dist-renderer/assets/{chunk-4BX2VUAB-CXPCI7g_.js → chunk-4BX2VUAB-DLk-hcFc.js} +1 -1
- package/dist-renderer/assets/{chunk-55IACEB6-BGAXQZRC.js → chunk-55IACEB6-1XRmX_Zm.js} +1 -1
- package/dist-renderer/assets/{chunk-B4BG7PRW-TPDaA_KQ.js → chunk-B4BG7PRW-1waH1DAD.js} +1 -1
- package/dist-renderer/assets/{chunk-DI55MBZ5-D1ADe_tq.js → chunk-DI55MBZ5-BqpZBtrN.js} +1 -1
- package/dist-renderer/assets/{chunk-FMBD7UC4-Beimtg3a.js → chunk-FMBD7UC4-Bly7vVym.js} +1 -1
- package/dist-renderer/assets/{chunk-QN33PNHL-OjNBu854.js → chunk-QN33PNHL-Ci2QWBAs.js} +1 -1
- package/dist-renderer/assets/{chunk-QZHKN3VN-DinqvbH8.js → chunk-QZHKN3VN-YCqFW7d-.js} +1 -1
- package/dist-renderer/assets/{chunk-TZMSLE5B-BfFtlPSZ.js → chunk-TZMSLE5B-B0xGXInl.js} +1 -1
- package/dist-renderer/assets/classDiagram-2ON5EDUG-24fHez0s.js +1 -0
- package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-24fHez0s.js +1 -0
- package/dist-renderer/assets/clone-BTNuUva-.js +1 -0
- package/dist-renderer/assets/{cose-bilkent-S5V4N54A-D9z9Dgt7.js → cose-bilkent-S5V4N54A-DxcFNQKT.js} +1 -1
- package/dist-renderer/assets/{dagre-6UL2VRFP-n1g-DhEE.js → dagre-6UL2VRFP-DPo_RfZY.js} +1 -1
- package/dist-renderer/assets/{diagram-PSM6KHXK-BvxFq-BE.js → diagram-PSM6KHXK-U3hQsFe4.js} +1 -1
- package/dist-renderer/assets/{diagram-QEK2KX5R-wVnJuwza.js → diagram-QEK2KX5R-OrwrAy0V.js} +1 -1
- package/dist-renderer/assets/{diagram-S2PKOQOG-B707WJQw.js → diagram-S2PKOQOG-CXATPWVw.js} +1 -1
- package/dist-renderer/assets/{erDiagram-Q2GNP2WA-C-_1dGHs.js → erDiagram-Q2GNP2WA-B0e8AfMF.js} +1 -1
- package/dist-renderer/assets/{flowDiagram-NV44I4VS-CMTSi3H6.js → flowDiagram-NV44I4VS-CXfzA4jJ.js} +1 -1
- package/dist-renderer/assets/{ganttDiagram-JELNMOA3-DZ0bNrAA.js → ganttDiagram-JELNMOA3-CMr08qVl.js} +1 -1
- package/dist-renderer/assets/{gitGraphDiagram-V2S2FVAM-DNVfGooQ.js → gitGraphDiagram-V2S2FVAM-vYFHpPmy.js} +1 -1
- package/dist-renderer/assets/{graph-865j_tM_.js → graph-DOe5j8dH.js} +1 -1
- package/dist-renderer/assets/{index-2EW-eu3q.js → index-B2Dy7M2G.js} +1 -1
- package/dist-renderer/assets/index-Bi6nrZ4z.css +1 -0
- package/dist-renderer/assets/{index-C_F9N5x-.js → index-BySQS7AB.js} +1 -1
- package/dist-renderer/assets/{index-4dEMStJj.js → index-C_okzZXP.js} +1 -1
- package/dist-renderer/assets/{index-DuUaf8at.js → index-CzWxVCRL.js} +1 -1
- package/dist-renderer/assets/{index-LwDIsXJN.js → index-V7dAKPqd.js} +571 -607
- package/dist-renderer/assets/{index-BTx1nc4T.js → index-VJ-MM9xa.js} +1 -1
- package/dist-renderer/assets/{infoDiagram-HS3SLOUP-CyqtElLq.js → infoDiagram-HS3SLOUP-D_WubR0B.js} +1 -1
- package/dist-renderer/assets/{journeyDiagram-XKPGCS4Q-BvjQ0Hm0.js → journeyDiagram-XKPGCS4Q-w9ca-1TI.js} +1 -1
- package/dist-renderer/assets/{kanban-definition-3W4ZIXB7-CJJ-k0zT.js → kanban-definition-3W4ZIXB7-Jg9p6_pN.js} +1 -1
- package/dist-renderer/assets/{layout-CnV6rQAG.js → layout-B-z3y17c.js} +1 -1
- package/dist-renderer/assets/{linear-Cw3UQgyX.js → linear-D-RTX5UW.js} +1 -1
- package/dist-renderer/assets/{mindmap-definition-VGOIOE7T-C5tDaGSK.js → mindmap-definition-VGOIOE7T-CDQmHOYP.js} +1 -1
- package/dist-renderer/assets/{pieDiagram-ADFJNKIX-CiIpPsau.js → pieDiagram-ADFJNKIX-D_odsQL7.js} +1 -1
- package/dist-renderer/assets/{quadrantDiagram-AYHSOK5B-C3gtowNj.js → quadrantDiagram-AYHSOK5B-BRsmYWSA.js} +1 -1
- package/dist-renderer/assets/{requirementDiagram-UZGBJVZJ-CXBTrAnU.js → requirementDiagram-UZGBJVZJ-ChNE_BOV.js} +1 -1
- package/dist-renderer/assets/{sankeyDiagram-TZEHDZUN-wziX77xG.js → sankeyDiagram-TZEHDZUN-C8FtpwKc.js} +1 -1
- package/dist-renderer/assets/{sequenceDiagram-WL72ISMW-sYqopcrj.js → sequenceDiagram-WL72ISMW-DmLCzNcc.js} +1 -1
- package/dist-renderer/assets/{stateDiagram-FKZM4ZOC-Bl1-0_Cp.js → stateDiagram-FKZM4ZOC-WJBm4bhu.js} +1 -1
- package/dist-renderer/assets/{stateDiagram-v2-4FDKWEC3-DOYYvDbi.js → stateDiagram-v2-4FDKWEC3-_m6iPPUR.js} +1 -1
- package/dist-renderer/assets/{timeline-definition-IT6M3QCI-CIRjJUBo.js → timeline-definition-IT6M3QCI-BXs_hOJs.js} +1 -1
- package/dist-renderer/assets/{treemap-GDKQZRPO-CVPuNe1n.js → treemap-GDKQZRPO-o04MA0G9.js} +1 -1
- package/dist-renderer/assets/{xychartDiagram-PRI3JC2R-3nT9yHwp.js → xychartDiagram-PRI3JC2R-Czj69XRd.js} +1 -1
- package/dist-renderer/index.html +2 -2
- package/package.json +1 -1
- package/src/main/ipc/extensions.ts +29 -50
- package/src/main/server.ts +17 -26
- package/src/main/services/extensions/ExtensionFacadeService.ts +2 -51
- package/src/main/services/extensions/library/McpLibraryService.ts +243 -0
- package/src/main/services/session-intelligence/UsageTelemetryService.ts +14 -1
- package/src/main/services/teams-mvp/TaskDispatchService.ts +32 -7
- package/src/renderer/api/httpClient.ts +108 -22
- package/src/renderer/components/extensions/ExtensionStoreView.tsx +6 -96
- package/src/renderer/components/extensions/plugins/PluginCard.tsx +8 -0
- package/src/renderer/components/extensions/plugins/PluginsPanel.tsx +13 -8
- package/src/renderer/components/team/TeamDetailView.tsx +15 -0
- package/src/renderer/components/team/tools/AddMcpInline.tsx +47 -0
- package/src/renderer/components/team/tools/AddSkillInline.tsx +61 -0
- package/src/renderer/components/team/tools/McpChip.tsx +42 -0
- package/src/renderer/components/team/tools/SkillChip.tsx +35 -0
- package/src/renderer/components/team/tools/ToolsSection.tsx +208 -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/utils/extensionNormalizers.ts +22 -0
- 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/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
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SkillChip — compact chip for a skill.
|
|
3
|
+
* Shows skill name, scope badge, and remove button on hover.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { X } from 'lucide-react';
|
|
7
|
+
|
|
8
|
+
import type { SkillCatalogItem } from '@shared/types/extensions';
|
|
9
|
+
|
|
10
|
+
interface SkillChipProps {
|
|
11
|
+
skill: SkillCatalogItem;
|
|
12
|
+
onRemove: (skill: SkillCatalogItem) => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const SkillChip = ({ skill, onRemove }: SkillChipProps): React.JSX.Element => {
|
|
16
|
+
return (
|
|
17
|
+
<div className="group inline-flex items-center gap-1.5 rounded-full bg-[var(--color-bg-secondary)] px-2.5 py-1 text-xs transition-colors hover:bg-[var(--color-bg-secondary-hover)]">
|
|
18
|
+
<span className="max-w-[120px] truncate text-[var(--color-text)]">{skill.name}</span>
|
|
19
|
+
{skill.scope && (
|
|
20
|
+
<span className="rounded bg-blue-500/20 px-1 py-0.5 text-[10px] text-blue-400">
|
|
21
|
+
{skill.scope === 'project' ? '项目' : '用户'}
|
|
22
|
+
</span>
|
|
23
|
+
)}
|
|
24
|
+
<button
|
|
25
|
+
type="button"
|
|
26
|
+
className="shrink-0 rounded-full p-0.5 opacity-0 transition-opacity hover:bg-red-500/20 group-hover:opacity-100"
|
|
27
|
+
onClick={() => onRemove(skill)}
|
|
28
|
+
aria-label={`删除 ${skill.name}`}
|
|
29
|
+
title="点击删除"
|
|
30
|
+
>
|
|
31
|
+
<X size={10} className="text-[var(--color-text-muted)] hover:text-red-400" />
|
|
32
|
+
</button>
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ToolsSection — per-worker MCP and Skills management.
|
|
3
|
+
* Chip/tag UI: compact chips for installed tools, inline add forms.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
7
|
+
|
|
8
|
+
import { confirm } from '@renderer/components/common/ConfirmDialog';
|
|
9
|
+
import { useStore } from '@renderer/store';
|
|
10
|
+
import { Plus, Wrench } from 'lucide-react';
|
|
11
|
+
|
|
12
|
+
import { AddMcpInline } from './AddMcpInline';
|
|
13
|
+
import { AddSkillInline } from './AddSkillInline';
|
|
14
|
+
import { McpChip } from './McpChip';
|
|
15
|
+
import { SkillChip } from './SkillChip';
|
|
16
|
+
|
|
17
|
+
interface ToolsSectionProps {
|
|
18
|
+
teamName: string;
|
|
19
|
+
projectPath: string | null;
|
|
20
|
+
harnessType?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const ToolsSection = ({
|
|
24
|
+
teamName,
|
|
25
|
+
projectPath,
|
|
26
|
+
harnessType,
|
|
27
|
+
}: ToolsSectionProps): React.JSX.Element => {
|
|
28
|
+
// ── Store selectors ──
|
|
29
|
+
const mcpByPath = useStore((s) => s.mcpInstalledServersByProjectPath);
|
|
30
|
+
const diagnosticsByPath = useStore((s) => s.mcpDiagnosticsByProjectPath);
|
|
31
|
+
const skillsByPath = useStore((s) => s.skillsProjectCatalogByProjectPath);
|
|
32
|
+
const mcpFetchInstalled = useStore((s) => s.mcpFetchInstalled);
|
|
33
|
+
const runMcpDiagnostics = useStore((s) => s.runMcpDiagnostics);
|
|
34
|
+
const fetchSkillsCatalog = useStore((s) => s.fetchSkillsCatalog);
|
|
35
|
+
const uninstallMcpServer = useStore((s) => s.uninstallMcpServer);
|
|
36
|
+
const deleteSkill = useStore((s) => s.deleteSkill);
|
|
37
|
+
|
|
38
|
+
// ── Local state ──
|
|
39
|
+
const [addingMcp, setAddingMcp] = useState(false);
|
|
40
|
+
const [addingSkill, setAddingSkill] = useState(false);
|
|
41
|
+
|
|
42
|
+
// ── Derived data ──
|
|
43
|
+
const mcpServers = useMemo(
|
|
44
|
+
() => (projectPath ? mcpByPath[projectPath] ?? [] : []),
|
|
45
|
+
[mcpByPath, projectPath],
|
|
46
|
+
);
|
|
47
|
+
// Diagnostics are stored keyed by `getMcpDiagnosticKey(name, scope)`, but the
|
|
48
|
+
// scope is not always present (e.g. the text-mode CLI parser omits it), so the
|
|
49
|
+
// scoped key can't be reconstructed reliably from an installed entry. Index by
|
|
50
|
+
// server name instead — chip rows already assume names are unique per project.
|
|
51
|
+
const diagnosticByName = useMemo(() => {
|
|
52
|
+
const record = projectPath ? diagnosticsByPath[projectPath] ?? {} : {};
|
|
53
|
+
return Object.fromEntries(Object.values(record).map((d) => [d.name, d] as const));
|
|
54
|
+
}, [diagnosticsByPath, projectPath]);
|
|
55
|
+
|
|
56
|
+
// Deduplicate skills by name (same skill may appear from multiple roots)
|
|
57
|
+
const skills = useMemo(() => {
|
|
58
|
+
const raw = projectPath ? skillsByPath[projectPath] ?? [] : [];
|
|
59
|
+
const seen = new Set<string>();
|
|
60
|
+
return raw.filter((skill) => {
|
|
61
|
+
const key = skill.name.toLowerCase();
|
|
62
|
+
if (seen.has(key)) return false;
|
|
63
|
+
seen.add(key);
|
|
64
|
+
return true;
|
|
65
|
+
});
|
|
66
|
+
}, [skillsByPath, projectPath]);
|
|
67
|
+
|
|
68
|
+
// ── Fetch data on mount ──
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
if (projectPath) {
|
|
71
|
+
mcpFetchInstalled(projectPath).catch(() => {});
|
|
72
|
+
runMcpDiagnostics(projectPath).catch(() => {});
|
|
73
|
+
fetchSkillsCatalog(projectPath).catch(() => {});
|
|
74
|
+
}
|
|
75
|
+
}, [projectPath, mcpFetchInstalled, runMcpDiagnostics, fetchSkillsCatalog]);
|
|
76
|
+
|
|
77
|
+
// ── Handlers ──
|
|
78
|
+
const handleRemoveMcp = useCallback(
|
|
79
|
+
(entry: { name: string; scope: string }) => {
|
|
80
|
+
void (async () => {
|
|
81
|
+
const confirmed = await confirm({
|
|
82
|
+
title: '删除 MCP 服务器',
|
|
83
|
+
message: `确认删除 MCP 服务器「${entry.name}」?删除后该工具将不再可用。`,
|
|
84
|
+
confirmLabel: '删除',
|
|
85
|
+
cancelLabel: '取消',
|
|
86
|
+
variant: 'danger',
|
|
87
|
+
});
|
|
88
|
+
if (!confirmed) return;
|
|
89
|
+
uninstallMcpServer('', entry.name, entry.scope, projectPath ?? undefined).catch(() => {});
|
|
90
|
+
})();
|
|
91
|
+
},
|
|
92
|
+
[uninstallMcpServer, projectPath],
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
const handleRemoveSkill = useCallback(
|
|
96
|
+
(skill: { id: string; name: string }) => {
|
|
97
|
+
void (async () => {
|
|
98
|
+
const confirmed = await confirm({
|
|
99
|
+
title: '删除 Skill',
|
|
100
|
+
message: `确认删除 Skill「${skill.name}」?删除后该技能将不再可用。`,
|
|
101
|
+
confirmLabel: '删除',
|
|
102
|
+
cancelLabel: '取消',
|
|
103
|
+
variant: 'danger',
|
|
104
|
+
});
|
|
105
|
+
if (!confirmed) return;
|
|
106
|
+
deleteSkill({ skillId: skill.id, projectPath: projectPath ?? undefined }).catch(() => {});
|
|
107
|
+
})();
|
|
108
|
+
},
|
|
109
|
+
[deleteSkill, projectPath],
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const handleMcpAdded = useCallback(() => {
|
|
113
|
+
setAddingMcp(false);
|
|
114
|
+
if (projectPath) {
|
|
115
|
+
mcpFetchInstalled(projectPath).catch(() => {});
|
|
116
|
+
runMcpDiagnostics(projectPath).catch(() => {});
|
|
117
|
+
}
|
|
118
|
+
}, [projectPath, mcpFetchInstalled, runMcpDiagnostics]);
|
|
119
|
+
|
|
120
|
+
const handleSkillAdded = useCallback(() => {
|
|
121
|
+
setAddingSkill(false);
|
|
122
|
+
if (projectPath) {
|
|
123
|
+
fetchSkillsCatalog(projectPath).catch(() => {});
|
|
124
|
+
}
|
|
125
|
+
}, [projectPath, fetchSkillsCatalog]);
|
|
126
|
+
|
|
127
|
+
if (!projectPath) {
|
|
128
|
+
return (
|
|
129
|
+
<div className="flex items-center gap-2 px-1 py-2 text-xs text-[var(--color-text-muted)]">
|
|
130
|
+
<Wrench size={12} />
|
|
131
|
+
<span>需要关联项目目录才能管理工具</span>
|
|
132
|
+
</div>
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return (
|
|
137
|
+
<div className="flex flex-col gap-3 px-1 py-2">
|
|
138
|
+
{/* MCP 工具 */}
|
|
139
|
+
<div className="flex flex-col gap-1.5">
|
|
140
|
+
<div className="flex items-center gap-1.5 text-[11px] font-medium text-[var(--color-text-muted)]">
|
|
141
|
+
<span>MCP 工具</span>
|
|
142
|
+
<span className="text-[10px]">({mcpServers.length})</span>
|
|
143
|
+
</div>
|
|
144
|
+
<div className="flex flex-wrap items-center gap-1.5">
|
|
145
|
+
{mcpServers.map((entry) => (
|
|
146
|
+
<McpChip
|
|
147
|
+
key={entry.name}
|
|
148
|
+
entry={entry}
|
|
149
|
+
diagnostic={diagnosticByName[entry.name]}
|
|
150
|
+
onRemove={handleRemoveMcp}
|
|
151
|
+
/>
|
|
152
|
+
))}
|
|
153
|
+
{addingMcp ? (
|
|
154
|
+
<AddMcpInline
|
|
155
|
+
projectPath={projectPath}
|
|
156
|
+
harnessType={harnessType ?? 'claudecode'}
|
|
157
|
+
onAdded={handleMcpAdded}
|
|
158
|
+
onCancel={() => setAddingMcp(false)}
|
|
159
|
+
/>
|
|
160
|
+
) : (
|
|
161
|
+
<button
|
|
162
|
+
type="button"
|
|
163
|
+
onClick={() => setAddingMcp(true)}
|
|
164
|
+
className="inline-flex items-center gap-1 rounded-full border border-dashed border-[var(--color-border)] px-2 py-1 text-xs text-[var(--color-text-muted)] transition-colors hover:border-[var(--color-text-muted)] hover:text-[var(--color-text)]"
|
|
165
|
+
>
|
|
166
|
+
<Plus size={10} />
|
|
167
|
+
添加
|
|
168
|
+
</button>
|
|
169
|
+
)}
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
|
|
173
|
+
{/* Skills */}
|
|
174
|
+
<div className="flex flex-col gap-1.5">
|
|
175
|
+
<div className="flex items-center gap-1.5 text-[11px] font-medium text-[var(--color-text-muted)]">
|
|
176
|
+
<span>Skills</span>
|
|
177
|
+
<span className="text-[10px]">({skills.length})</span>
|
|
178
|
+
</div>
|
|
179
|
+
<div className="flex flex-wrap items-center gap-1.5">
|
|
180
|
+
{skills.map((skill) => (
|
|
181
|
+
<SkillChip
|
|
182
|
+
key={skill.id}
|
|
183
|
+
skill={skill}
|
|
184
|
+
onRemove={handleRemoveSkill}
|
|
185
|
+
/>
|
|
186
|
+
))}
|
|
187
|
+
{addingSkill ? (
|
|
188
|
+
<AddSkillInline
|
|
189
|
+
projectPath={projectPath}
|
|
190
|
+
projectLabel={teamName}
|
|
191
|
+
onAdded={handleSkillAdded}
|
|
192
|
+
onCancel={() => setAddingSkill(false)}
|
|
193
|
+
/>
|
|
194
|
+
) : (
|
|
195
|
+
<button
|
|
196
|
+
type="button"
|
|
197
|
+
onClick={() => setAddingSkill(true)}
|
|
198
|
+
className="inline-flex items-center gap-1 rounded-full border border-dashed border-[var(--color-border)] px-2 py-1 text-xs text-[var(--color-text-muted)] transition-colors hover:border-[var(--color-text-muted)] hover:text-[var(--color-text)]"
|
|
199
|
+
>
|
|
200
|
+
<Plus size={10} />
|
|
201
|
+
添加
|
|
202
|
+
</button>
|
|
203
|
+
)}
|
|
204
|
+
</div>
|
|
205
|
+
</div>
|
|
206
|
+
</div>
|
|
207
|
+
);
|
|
208
|
+
};
|
|
@@ -15,6 +15,10 @@ import type {
|
|
|
15
15
|
McpCatalogItem,
|
|
16
16
|
McpCustomInstallRequest,
|
|
17
17
|
McpInstallRequest,
|
|
18
|
+
McpLibraryEntry,
|
|
19
|
+
McpLibraryImportRequest,
|
|
20
|
+
McpLibraryImportResult,
|
|
21
|
+
McpLibraryUpsertRequest,
|
|
18
22
|
McpSearchResult,
|
|
19
23
|
McpServerDiagnostic,
|
|
20
24
|
} from './mcp';
|
|
@@ -59,6 +63,11 @@ export interface McpCatalogAPI {
|
|
|
59
63
|
installCustom: (request: McpCustomInstallRequest) => Promise<OperationResult>;
|
|
60
64
|
uninstall: (name: string, scope?: string, projectPath?: string) => Promise<OperationResult>;
|
|
61
65
|
githubStars: (repositoryUrls: string[]) => Promise<Record<string, number>>;
|
|
66
|
+
// ── Library (cc-switch style global library of server definitions) ──
|
|
67
|
+
libraryList: () => Promise<McpLibraryEntry[]>;
|
|
68
|
+
libraryUpsert: (request: McpLibraryUpsertRequest) => Promise<McpLibraryEntry>;
|
|
69
|
+
libraryDelete: (id: string) => Promise<void>;
|
|
70
|
+
libraryImport: (request: McpLibraryImportRequest) => Promise<McpLibraryImportResult>;
|
|
62
71
|
}
|
|
63
72
|
|
|
64
73
|
// ── Skills API ─────────────────────────────────────────────────────────────
|
|
@@ -132,3 +132,44 @@ export interface McpSearchResult {
|
|
|
132
132
|
servers: McpCatalogItem[];
|
|
133
133
|
warnings: string[]; // e.g. "Official registry unavailable"
|
|
134
134
|
}
|
|
135
|
+
|
|
136
|
+
// ── MCP Library (cc-switch style: a reusable global library of server defs) ──
|
|
137
|
+
//
|
|
138
|
+
// A saved server definition lives once in the library and can be enabled for
|
|
139
|
+
// any worker (= installed into that worker's project config) without re-typing
|
|
140
|
+
// the command / URL / env each time.
|
|
141
|
+
|
|
142
|
+
export interface McpLibraryEntry {
|
|
143
|
+
id: string; // stable uuid
|
|
144
|
+
name: string; // server name used when installing (`claude mcp add <name>`)
|
|
145
|
+
description?: string;
|
|
146
|
+
installSpec: McpInstallSpec;
|
|
147
|
+
envValues?: Record<string, string>; // saved non-secret defaults
|
|
148
|
+
headers?: McpHeaderDef[];
|
|
149
|
+
createdAt: number;
|
|
150
|
+
updatedAt: number;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Create (omit id) or update (provide id) a library entry. */
|
|
154
|
+
export interface McpLibraryUpsertRequest {
|
|
155
|
+
id?: string;
|
|
156
|
+
name: string;
|
|
157
|
+
description?: string;
|
|
158
|
+
installSpec: McpInstallSpec;
|
|
159
|
+
envValues?: Record<string, string>;
|
|
160
|
+
headers?: McpHeaderDef[];
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Import existing MCP servers from live config into the library.
|
|
165
|
+
* Pulls user-scope servers plus, when a projectPath is given, that worker's
|
|
166
|
+
* project-scope servers. Existing library entries (matched by name) are skipped.
|
|
167
|
+
*/
|
|
168
|
+
export interface McpLibraryImportRequest {
|
|
169
|
+
projectPath?: string;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export interface McpLibraryImportResult {
|
|
173
|
+
imported: string[];
|
|
174
|
+
skipped: string[];
|
|
175
|
+
}
|
|
@@ -106,6 +106,28 @@ export function buildPluginId(pluginName: string, marketplaceName: string): stri
|
|
|
106
106
|
return `${pluginName}@${marketplaceName}`;
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Substrings that mark a plugin as "essential" (必装) — recommended/required for
|
|
111
|
+
* the workbench. Matched against both the pluginId and homepage so it works
|
|
112
|
+
* regardless of the marketplace name a plugin is published under. Note the codex
|
|
113
|
+
* plugin ships as `codex@openai-codex` (repo: openai/codex-plugin-cc), so the
|
|
114
|
+
* pluginId alone does not contain "codex-plugin-cc" — the homepage does.
|
|
115
|
+
*/
|
|
116
|
+
export const ESSENTIAL_PLUGIN_PATTERNS = [
|
|
117
|
+
'oh-my-claudecode',
|
|
118
|
+
'codex@openai-codex',
|
|
119
|
+
'codex-plugin-cc',
|
|
120
|
+
] as const;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Whether a plugin is one of the essential plugins surfaced with a 必装 badge and
|
|
124
|
+
* the recommendation banner.
|
|
125
|
+
*/
|
|
126
|
+
export function isEssentialPlugin(plugin: { pluginId: string; homepage?: string }): boolean {
|
|
127
|
+
const haystack = `${plugin.pluginId} ${plugin.homepage ?? ''}`.toLowerCase();
|
|
128
|
+
return ESSENTIAL_PLUGIN_PATTERNS.some((pattern) => haystack.includes(pattern));
|
|
129
|
+
}
|
|
130
|
+
|
|
109
131
|
/**
|
|
110
132
|
* Namespaced operation-state key for plugin install/uninstall UI state.
|
|
111
133
|
*/
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a6 as o,a7 as n}from"./index-LwDIsXJN.js";const t=(a,r)=>o.lang.round(n.parse(a)[r]);export{t as c};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,c as s,a as e,C as t}from"./chunk-B4BG7PRW-TPDaA_KQ.js";import{_ as i}from"./index-LwDIsXJN.js";import"./chunk-FMBD7UC4-Beimtg3a.js";import"./chunk-55IACEB6-BGAXQZRC.js";import"./chunk-QN33PNHL-OjNBu854.js";import"./splashScene-C8lWNnm4.js";var u={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{u as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,c as s,a as e,C as t}from"./chunk-B4BG7PRW-TPDaA_KQ.js";import{_ as i}from"./index-LwDIsXJN.js";import"./chunk-FMBD7UC4-Beimtg3a.js";import"./chunk-55IACEB6-BGAXQZRC.js";import"./chunk-QN33PNHL-OjNBu854.js";import"./splashScene-C8lWNnm4.js";var u={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{u as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{b as r}from"./_baseUniq-DWo68sXI.js";var e=4;function a(o){return r(o,e)}export{a as c};
|