@yancyyu/openhermit 1.6.36 → 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.
Files changed (91) hide show
  1. package/dist-renderer/assets/{ProjectEditorOverlay-B_QAoeaA.js → ProjectEditorOverlay-lJZi-9Hp.js} +1 -1
  2. package/dist-renderer/assets/{TeamGraphOverlay-PB9luAZU.js → TeamGraphOverlay-ZEDfZyHb.js} +1 -1
  3. package/dist-renderer/assets/{_basePickBy-Dfzcp_Ry.js → _basePickBy-CIhniz70.js} +1 -1
  4. package/dist-renderer/assets/{_baseUniq-B5u2Yiq2.js → _baseUniq-cKAW4Q8I.js} +1 -1
  5. package/dist-renderer/assets/{arc-DElOI7qz.js → arc-YmNsoDXW.js} +1 -1
  6. package/dist-renderer/assets/{architectureDiagram-VXUJARFQ-Cf6f4tCu.js → architectureDiagram-VXUJARFQ-DHEls2sX.js} +1 -1
  7. package/dist-renderer/assets/{blockDiagram-VD42YOAC-FJUdo9Ry.js → blockDiagram-VD42YOAC-Bpwf1Sbg.js} +1 -1
  8. package/dist-renderer/assets/{c4Diagram-YG6GDRKO-BvJQS9lb.js → c4Diagram-YG6GDRKO-B0IaQ4w5.js} +1 -1
  9. package/dist-renderer/assets/channel-yIlSKy0e.js +1 -0
  10. package/dist-renderer/assets/{chunk-4BX2VUAB-n-SGLbin.js → chunk-4BX2VUAB-DLk-hcFc.js} +1 -1
  11. package/dist-renderer/assets/{chunk-55IACEB6-Dwle9tlA.js → chunk-55IACEB6-1XRmX_Zm.js} +1 -1
  12. package/dist-renderer/assets/{chunk-B4BG7PRW-Dic8YxQz.js → chunk-B4BG7PRW-1waH1DAD.js} +1 -1
  13. package/dist-renderer/assets/{chunk-DI55MBZ5-3n5jC1jk.js → chunk-DI55MBZ5-BqpZBtrN.js} +1 -1
  14. package/dist-renderer/assets/{chunk-FMBD7UC4-BqizUB3O.js → chunk-FMBD7UC4-Bly7vVym.js} +1 -1
  15. package/dist-renderer/assets/{chunk-QN33PNHL-JRDmD8o9.js → chunk-QN33PNHL-Ci2QWBAs.js} +1 -1
  16. package/dist-renderer/assets/{chunk-QZHKN3VN-BxFpQw92.js → chunk-QZHKN3VN-YCqFW7d-.js} +1 -1
  17. package/dist-renderer/assets/{chunk-TZMSLE5B-ByqPwtW9.js → chunk-TZMSLE5B-B0xGXInl.js} +1 -1
  18. package/dist-renderer/assets/classDiagram-2ON5EDUG-24fHez0s.js +1 -0
  19. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-24fHez0s.js +1 -0
  20. package/dist-renderer/assets/clone-BTNuUva-.js +1 -0
  21. package/dist-renderer/assets/{cose-bilkent-S5V4N54A-CVztr86T.js → cose-bilkent-S5V4N54A-DxcFNQKT.js} +1 -1
  22. package/dist-renderer/assets/{dagre-6UL2VRFP-CIui920O.js → dagre-6UL2VRFP-DPo_RfZY.js} +1 -1
  23. package/dist-renderer/assets/{diagram-PSM6KHXK-CyL8-bgb.js → diagram-PSM6KHXK-U3hQsFe4.js} +1 -1
  24. package/dist-renderer/assets/{diagram-QEK2KX5R-CM_67YoY.js → diagram-QEK2KX5R-OrwrAy0V.js} +1 -1
  25. package/dist-renderer/assets/{diagram-S2PKOQOG-DtrtPSGg.js → diagram-S2PKOQOG-CXATPWVw.js} +1 -1
  26. package/dist-renderer/assets/{erDiagram-Q2GNP2WA-bOICzF9d.js → erDiagram-Q2GNP2WA-B0e8AfMF.js} +1 -1
  27. package/dist-renderer/assets/{flowDiagram-NV44I4VS-CJV1g9Hr.js → flowDiagram-NV44I4VS-CXfzA4jJ.js} +1 -1
  28. package/dist-renderer/assets/{ganttDiagram-JELNMOA3-CXbhDo09.js → ganttDiagram-JELNMOA3-CMr08qVl.js} +1 -1
  29. package/dist-renderer/assets/{gitGraphDiagram-V2S2FVAM-vbXopTpS.js → gitGraphDiagram-V2S2FVAM-vYFHpPmy.js} +1 -1
  30. package/dist-renderer/assets/{graph-CY2T-j4q.js → graph-DOe5j8dH.js} +1 -1
  31. package/dist-renderer/assets/{index-Dv7q-OB0.js → index-B2Dy7M2G.js} +1 -1
  32. package/dist-renderer/assets/index-Bi6nrZ4z.css +1 -0
  33. package/dist-renderer/assets/{index-CpyChjme.js → index-BySQS7AB.js} +1 -1
  34. package/dist-renderer/assets/{index-S-i9egm8.js → index-C_okzZXP.js} +1 -1
  35. package/dist-renderer/assets/{index-Mrh4pTHw.js → index-CzWxVCRL.js} +1 -1
  36. package/dist-renderer/assets/{index-Dn-BpzSm.js → index-V7dAKPqd.js} +571 -607
  37. package/dist-renderer/assets/{index-C9ONRXVI.js → index-VJ-MM9xa.js} +1 -1
  38. package/dist-renderer/assets/{infoDiagram-HS3SLOUP-BNg14AdU.js → infoDiagram-HS3SLOUP-D_WubR0B.js} +1 -1
  39. package/dist-renderer/assets/{journeyDiagram-XKPGCS4Q-2_PkPCiu.js → journeyDiagram-XKPGCS4Q-w9ca-1TI.js} +1 -1
  40. package/dist-renderer/assets/{kanban-definition-3W4ZIXB7-DjTx7qoU.js → kanban-definition-3W4ZIXB7-Jg9p6_pN.js} +1 -1
  41. package/dist-renderer/assets/{layout-DZlHGGN0.js → layout-B-z3y17c.js} +1 -1
  42. package/dist-renderer/assets/{linear-DnlOm48z.js → linear-D-RTX5UW.js} +1 -1
  43. package/dist-renderer/assets/{mindmap-definition-VGOIOE7T-B-nrgt7V.js → mindmap-definition-VGOIOE7T-CDQmHOYP.js} +1 -1
  44. package/dist-renderer/assets/{pieDiagram-ADFJNKIX-BToJFvWR.js → pieDiagram-ADFJNKIX-D_odsQL7.js} +1 -1
  45. package/dist-renderer/assets/{quadrantDiagram-AYHSOK5B-C0qoxXvH.js → quadrantDiagram-AYHSOK5B-BRsmYWSA.js} +1 -1
  46. package/dist-renderer/assets/{requirementDiagram-UZGBJVZJ-DCKGwsGN.js → requirementDiagram-UZGBJVZJ-ChNE_BOV.js} +1 -1
  47. package/dist-renderer/assets/{sankeyDiagram-TZEHDZUN-CS6JCcu7.js → sankeyDiagram-TZEHDZUN-C8FtpwKc.js} +1 -1
  48. package/dist-renderer/assets/{sequenceDiagram-WL72ISMW-C9pAWoSR.js → sequenceDiagram-WL72ISMW-DmLCzNcc.js} +1 -1
  49. package/dist-renderer/assets/{stateDiagram-FKZM4ZOC-BTTX_v1m.js → stateDiagram-FKZM4ZOC-WJBm4bhu.js} +1 -1
  50. package/dist-renderer/assets/{stateDiagram-v2-4FDKWEC3-BHk97lQJ.js → stateDiagram-v2-4FDKWEC3-_m6iPPUR.js} +1 -1
  51. package/dist-renderer/assets/{timeline-definition-IT6M3QCI-CSWCEzCQ.js → timeline-definition-IT6M3QCI-BXs_hOJs.js} +1 -1
  52. package/dist-renderer/assets/{treemap-GDKQZRPO-CmiIc68g.js → treemap-GDKQZRPO-o04MA0G9.js} +1 -1
  53. package/dist-renderer/assets/{xychartDiagram-PRI3JC2R-DhwSTphI.js → xychartDiagram-PRI3JC2R-Czj69XRd.js} +1 -1
  54. package/dist-renderer/index.html +2 -2
  55. package/package.json +2 -2
  56. package/src/main/ipc/extensions.ts +29 -50
  57. package/src/main/server.ts +17 -26
  58. package/src/main/services/extensions/ExtensionFacadeService.ts +2 -51
  59. package/src/main/services/extensions/library/McpLibraryService.ts +243 -0
  60. package/src/main/services/session-intelligence/UsageTelemetryService.ts +14 -1
  61. package/src/main/services/teams-mvp/TaskDispatchService.ts +32 -7
  62. package/src/renderer/api/httpClient.ts +108 -22
  63. package/src/renderer/components/extensions/ExtensionStoreView.tsx +6 -96
  64. package/src/renderer/components/extensions/plugins/PluginCard.tsx +8 -0
  65. package/src/renderer/components/extensions/plugins/PluginsPanel.tsx +13 -8
  66. package/src/renderer/components/team/TeamDetailView.tsx +15 -0
  67. package/src/renderer/components/team/tools/AddMcpInline.tsx +47 -0
  68. package/src/renderer/components/team/tools/AddSkillInline.tsx +61 -0
  69. package/src/renderer/components/team/tools/McpChip.tsx +42 -0
  70. package/src/renderer/components/team/tools/SkillChip.tsx +35 -0
  71. package/src/renderer/components/team/tools/ToolsSection.tsx +208 -0
  72. package/src/shared/types/extensions/api.ts +9 -0
  73. package/src/shared/types/extensions/index.ts +4 -0
  74. package/src/shared/types/extensions/mcp.ts +41 -0
  75. package/src/shared/utils/extensionNormalizers.ts +22 -0
  76. package/dist-renderer/assets/channel-DnbgZg0A.js +0 -1
  77. package/dist-renderer/assets/classDiagram-2ON5EDUG-BAD4p014.js +0 -1
  78. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-BAD4p014.js +0 -1
  79. package/dist-renderer/assets/clone-CRX5ZTPd.js +0 -1
  80. package/dist-renderer/assets/index-B2z_IyRH.css +0 -1
  81. package/src/main/services/extensions/catalog/GlamaMcpEnrichmentService.ts +0 -190
  82. package/src/main/services/extensions/catalog/McpCatalogAggregator.ts +0 -150
  83. package/src/main/services/extensions/catalog/OfficialMcpRegistryService.ts +0 -381
  84. package/src/main/services/extensions/install/McpInstallService.ts +0 -407
  85. package/src/main/services/extensions/state/McpInstallationStateService.ts +0 -42
  86. package/src/renderer/components/extensions/mcp/McpServerCard.tsx +0 -314
  87. package/src/renderer/components/extensions/mcp/McpServerDetailDialog.tsx +0 -765
  88. package/src/renderer/components/extensions/mcp/McpServersPanel.tsx +0 -593
  89. package/src/renderer/components/extensions/skills/SkillDetailDialog.tsx +0 -372
  90. package/src/renderer/components/extensions/skills/SkillImportDialog.tsx +0 -343
  91. 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 ─────────────────────────────────────────────────────────────
@@ -21,6 +21,10 @@ export type {
21
21
  McpHttpInstallSpec,
22
22
  McpInstallRequest,
23
23
  McpInstallSpec,
24
+ McpLibraryEntry,
25
+ McpLibraryImportRequest,
26
+ McpLibraryImportResult,
27
+ McpLibraryUpsertRequest,
24
28
  McpSearchResult,
25
29
  McpServerDiagnostic,
26
30
  McpServerHealthStatus,
@@ -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-Dn-BpzSm.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-Dic8YxQz.js";import{_ as i}from"./index-Dn-BpzSm.js";import"./chunk-FMBD7UC4-BqizUB3O.js";import"./chunk-55IACEB6-Dwle9tlA.js";import"./chunk-QN33PNHL-JRDmD8o9.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-Dic8YxQz.js";import{_ as i}from"./index-Dn-BpzSm.js";import"./chunk-FMBD7UC4-BqizUB3O.js";import"./chunk-55IACEB6-Dwle9tlA.js";import"./chunk-QN33PNHL-JRDmD8o9.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-B5u2Yiq2.js";var e=4;function a(o){return r(o,e)}export{a as c};