@yancyyu/openhermit 1.6.29 → 1.6.30
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-CQm6jUR1.js → ProjectEditorOverlay-DsQt4FHy.js} +1 -1
- package/dist-renderer/assets/{TeamGraphOverlay-h0WDfifv.js → TeamGraphOverlay-BjZC53xf.js} +1 -1
- package/dist-renderer/assets/{_basePickBy-CgG_tjgX.js → _basePickBy-CrWocIjq.js} +1 -1
- package/dist-renderer/assets/{_baseUniq-DwPTU9lP.js → _baseUniq-B6d8ysWi.js} +1 -1
- package/dist-renderer/assets/{arc-7nIrGRzY.js → arc-DAIYCFP8.js} +1 -1
- package/dist-renderer/assets/{architectureDiagram-VXUJARFQ-BYhA6Ev2.js → architectureDiagram-VXUJARFQ-B3UudXJh.js} +1 -1
- package/dist-renderer/assets/{blockDiagram-VD42YOAC-BVpZUGDg.js → blockDiagram-VD42YOAC-DbptKQ4W.js} +1 -1
- package/dist-renderer/assets/{c4Diagram-YG6GDRKO-DsdreMQ9.js → c4Diagram-YG6GDRKO-C4WQuZpV.js} +1 -1
- package/dist-renderer/assets/channel-DbjZvWii.js +1 -0
- package/dist-renderer/assets/{chunk-4BX2VUAB-CcoAs7Jd.js → chunk-4BX2VUAB-Dp7fVpI_.js} +1 -1
- package/dist-renderer/assets/{chunk-55IACEB6-CGGAOoXd.js → chunk-55IACEB6-B8KGfbAy.js} +1 -1
- package/dist-renderer/assets/{chunk-B4BG7PRW-FhpTEPvD.js → chunk-B4BG7PRW-BG1oJrjA.js} +1 -1
- package/dist-renderer/assets/{chunk-DI55MBZ5-DoYySbm1.js → chunk-DI55MBZ5-DRmxNjht.js} +1 -1
- package/dist-renderer/assets/{chunk-FMBD7UC4-e9l2tGHG.js → chunk-FMBD7UC4-D6VLvy16.js} +1 -1
- package/dist-renderer/assets/{chunk-QN33PNHL-DeiXVTCy.js → chunk-QN33PNHL-DZou1667.js} +1 -1
- package/dist-renderer/assets/{chunk-QZHKN3VN-DC2UJLJM.js → chunk-QZHKN3VN-CghmasSh.js} +1 -1
- package/dist-renderer/assets/{chunk-TZMSLE5B-BHFD9eZI.js → chunk-TZMSLE5B-B7apcMPK.js} +1 -1
- package/dist-renderer/assets/classDiagram-2ON5EDUG-D_FGxxsl.js +1 -0
- package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-D_FGxxsl.js +1 -0
- package/dist-renderer/assets/clone-CJ1kxO2J.js +1 -0
- package/dist-renderer/assets/{cose-bilkent-S5V4N54A-BdybQraU.js → cose-bilkent-S5V4N54A-05e5uQDp.js} +1 -1
- package/dist-renderer/assets/{dagre-6UL2VRFP-DdF3pwM3.js → dagre-6UL2VRFP-B06bRykF.js} +1 -1
- package/dist-renderer/assets/{diagram-PSM6KHXK-B9Ldd3nh.js → diagram-PSM6KHXK-CY7VYQ7c.js} +1 -1
- package/dist-renderer/assets/{diagram-QEK2KX5R-XEqkrbpu.js → diagram-QEK2KX5R-BjKEH7dD.js} +1 -1
- package/dist-renderer/assets/{diagram-S2PKOQOG-CipwtY59.js → diagram-S2PKOQOG-Bf4ELS1_.js} +1 -1
- package/dist-renderer/assets/{erDiagram-Q2GNP2WA-BB-2ISGo.js → erDiagram-Q2GNP2WA-DJ753_L9.js} +1 -1
- package/dist-renderer/assets/{flowDiagram-NV44I4VS-B8XmJ0u2.js → flowDiagram-NV44I4VS-B71S-lC-.js} +1 -1
- package/dist-renderer/assets/{ganttDiagram-JELNMOA3-D-8XglBb.js → ganttDiagram-JELNMOA3-C_U42mSZ.js} +1 -1
- package/dist-renderer/assets/{gitGraphDiagram-V2S2FVAM-DL4ChakD.js → gitGraphDiagram-V2S2FVAM-DKUJU4Ns.js} +1 -1
- package/dist-renderer/assets/{graph-BiFNoBjP.js → graph-DY3qbzqj.js} +1 -1
- package/dist-renderer/assets/{index-BowUl0Jb.js → index-BlOrAXp3.js} +542 -532
- package/dist-renderer/assets/{index-6m1ZAymG.js → index-Bs27J5gB.js} +1 -1
- package/dist-renderer/assets/{index-Dp3kJTEe.js → index-C8B_nKOF.js} +1 -1
- package/dist-renderer/assets/index-CmZPUEhS.css +1 -0
- package/dist-renderer/assets/{index-TOpt_T7A.js → index-DLKyDr4T.js} +1 -1
- package/dist-renderer/assets/{index-qNBNjW4K.js → index-Dhsk3_DD.js} +1 -1
- package/dist-renderer/assets/{index-vAykq1H1.js → index-GpUvV2xs.js} +1 -1
- package/dist-renderer/assets/{infoDiagram-HS3SLOUP-DRIBfHDi.js → infoDiagram-HS3SLOUP-BNs0y3IG.js} +1 -1
- package/dist-renderer/assets/{journeyDiagram-XKPGCS4Q-BOMiigU4.js → journeyDiagram-XKPGCS4Q-CqPnw4UV.js} +1 -1
- package/dist-renderer/assets/{kanban-definition-3W4ZIXB7-DDxeyjod.js → kanban-definition-3W4ZIXB7-SLlzcUJ2.js} +1 -1
- package/dist-renderer/assets/{layout-DNANbrI4.js → layout-BZLlNmbr.js} +1 -1
- package/dist-renderer/assets/{linear-DxEJi1yT.js → linear-qz6v45xy.js} +1 -1
- package/dist-renderer/assets/{mindmap-definition-VGOIOE7T-nBfGriW8.js → mindmap-definition-VGOIOE7T-B1-kmEWV.js} +1 -1
- package/dist-renderer/assets/{pieDiagram-ADFJNKIX-Din5j6sV.js → pieDiagram-ADFJNKIX-B8a02iNx.js} +1 -1
- package/dist-renderer/assets/{quadrantDiagram-AYHSOK5B-DMVK2BEQ.js → quadrantDiagram-AYHSOK5B-BKv1Xfou.js} +1 -1
- package/dist-renderer/assets/{requirementDiagram-UZGBJVZJ-6SC94Gg_.js → requirementDiagram-UZGBJVZJ-B3DUpZi2.js} +1 -1
- package/dist-renderer/assets/{sankeyDiagram-TZEHDZUN-CD2gghhu.js → sankeyDiagram-TZEHDZUN-DmPzuTsy.js} +1 -1
- package/dist-renderer/assets/{sequenceDiagram-WL72ISMW-BnhkN7nZ.js → sequenceDiagram-WL72ISMW-Bo7RelRb.js} +1 -1
- package/dist-renderer/assets/{stateDiagram-FKZM4ZOC-Bn8XdYX-.js → stateDiagram-FKZM4ZOC-1epX98gV.js} +1 -1
- package/dist-renderer/assets/{stateDiagram-v2-4FDKWEC3-1b6sI1_g.js → stateDiagram-v2-4FDKWEC3-03Ym9PTr.js} +1 -1
- package/dist-renderer/assets/{timeline-definition-IT6M3QCI-CNs3RPoa.js → timeline-definition-IT6M3QCI-r6isC62H.js} +1 -1
- package/dist-renderer/assets/treemap-GDKQZRPO-CGKpOUF2.js +162 -0
- package/dist-renderer/assets/{xychartDiagram-PRI3JC2R-B8o5J2f3.js → xychartDiagram-PRI3JC2R-t4-rwdAw.js} +1 -1
- package/dist-renderer/index.html +2 -2
- package/package.json +4 -1
- package/src/main/ipc/extensions.ts +353 -0
- package/src/main/server.ts +209 -6
- package/src/main/services/extensions/ExtensionFacadeService.ts +135 -0
- package/src/main/services/extensions/catalog/GlamaMcpEnrichmentService.ts +190 -0
- package/src/main/services/extensions/catalog/McpCatalogAggregator.ts +150 -0
- package/src/main/services/extensions/catalog/OfficialMcpRegistryService.ts +381 -0
- package/src/main/services/extensions/catalog/PluginCatalogService.ts +392 -0
- package/src/main/services/extensions/credentials/CredentialService.ts +343 -0
- package/src/main/services/extensions/install/McpInstallService.ts +407 -0
- package/src/main/services/extensions/install/PluginInstallService.ts +198 -0
- package/src/main/services/extensions/runtime/ClaudeCodeAdapter.ts +199 -0
- package/src/main/services/extensions/runtime/CodexAdapter.ts +100 -0
- package/src/main/services/extensions/runtime/CursorAdapter.ts +154 -0
- package/src/main/services/extensions/runtime/ExtensionsRuntimeAdapter.ts +172 -0
- package/src/main/services/extensions/runtime/GeminiAdapter.ts +91 -0
- package/src/main/services/extensions/runtime/HarnessInstallAdapter.ts +49 -0
- package/src/main/services/extensions/runtime/McpConfigStateReader.ts +209 -0
- package/src/main/services/extensions/runtime/OpenCodeAdapter.ts +91 -0
- package/src/main/services/extensions/runtime/adapterRegistry.ts +54 -0
- package/src/main/services/extensions/runtime/mcpDiagnosticsParser.ts +214 -0
- package/src/main/services/extensions/runtime/mcpRuntimeJson.ts +45 -0
- package/src/main/services/extensions/skills/SkillImportService.ts +155 -0
- package/src/main/services/extensions/skills/SkillMetadataParser.ts +323 -0
- package/src/main/services/extensions/skills/SkillPlanService.ts +411 -0
- package/src/main/services/extensions/skills/SkillReviewService.ts +73 -0
- package/src/main/services/extensions/skills/SkillRootsResolver.ts +49 -0
- package/src/main/services/extensions/skills/SkillScaffoldService.ts +89 -0
- package/src/main/services/extensions/skills/SkillScanner.ts +117 -0
- package/src/main/services/extensions/skills/SkillValidator.ts +69 -0
- package/src/main/services/extensions/skills/SkillsCatalogService.ts +92 -0
- package/src/main/services/extensions/skills/SkillsMutationService.ts +146 -0
- package/src/main/services/extensions/skills/SkillsWatcherService.ts +134 -0
- package/src/main/services/extensions/state/McpInstallationStateService.ts +42 -0
- package/src/main/services/extensions/state/PluginInstallationStateService.ts +281 -0
- package/src/main/services/identity/AgentTeamsIdentityStore.ts +218 -0
- package/src/main/services/runtime/providerAwareCliEnv.ts +60 -0
- package/src/main/services/team/ClaudeBinaryResolver.ts +469 -0
- package/src/main/services/team/ClaudeDoctorProbe.ts +0 -0
- package/src/main/services/team/cliFlavor.ts +54 -0
- package/src/main/services/teams-mvp/TaskDispatchService.ts +3 -0
- package/src/main/utils/atomicWrite.ts +72 -0
- package/src/main/utils/childProcess.ts +554 -0
- package/src/main/utils/cliEnv.ts +54 -0
- package/src/main/utils/cliPathMerge.ts +97 -0
- package/src/main/utils/pathDecoder.ts +664 -0
- package/src/main/utils/pathValidation.ts +432 -0
- package/src/main/utils/shellEnv.ts +331 -0
- package/src/renderer/api/httpClient.ts +61 -0
- package/src/renderer/components/extensions/ExtensionStoreView.tsx +59 -34
- package/src/renderer/components/extensions/ExtensionsSubTabTrigger.tsx +1 -1
- package/src/renderer/components/extensions/common/ExtensionToast.tsx +141 -0
- package/src/renderer/components/extensions/common/HarnessSelector.tsx +71 -0
- package/src/renderer/components/extensions/env/EnvVarPanel.tsx +335 -0
- package/src/renderer/components/extensions/env/ProjectEnvPanel.tsx +239 -0
- package/src/renderer/components/extensions/mcp/CustomMcpServerDialog.tsx +14 -223
- package/src/renderer/components/extensions/mcp/McpServerDetailDialog.tsx +11 -0
- package/src/renderer/components/extensions/mcp/McpServersPanel.tsx +51 -1
- package/src/renderer/components/extensions/skills/SkillsPanel.tsx +1 -126
- package/src/renderer/components/settings/sections/HarnessSection.tsx +2 -6
- package/src/renderer/components/settings/sections/TaskBusSection.tsx +17 -7
- package/src/renderer/components/sidebar/SidebarSessions.tsx +23 -0
- package/src/renderer/components/sidebar/WorkspaceBrowser.tsx +1 -7
- package/src/renderer/components/team/HarnessSelect.tsx +71 -0
- package/src/renderer/components/team/TeamDetailView.tsx +35 -0
- package/src/renderer/components/team/dialogs/CreateTeamDialog.tsx +21 -12
- package/src/renderer/components/team/dialogs/EditTeamDialog.tsx +8 -13
- package/src/renderer/components/team/kanban/KanbanBoard.tsx +26 -64
- package/src/renderer/components/team/messages/MessagesPanel.tsx +28 -24
- package/src/renderer/components/terminal/TerminalPanel.tsx +156 -0
- package/src/renderer/hooks/useExtensionsTabState.ts +2 -2
- package/src/renderer/store/slices/extensionsSlice.ts +42 -107
- package/src/renderer/store/slices/teamSlice.ts +8 -2
- package/src/shared/types/api.ts +29 -0
- package/src/shared/types/extensions/index.ts +1 -0
- package/src/shared/types/extensions/mcp.ts +2 -0
- package/src/shared/types/extensions/plugin.ts +2 -1
- package/src/shared/types/extensions/skill.ts +7 -0
- package/src/shared/utils/providerExtensionCapabilities.ts +1 -1
- package/dist-renderer/assets/channel-C0SqeFU7.js +0 -1
- package/dist-renderer/assets/classDiagram-2ON5EDUG-DWew1HpM.js +0 -1
- package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-DWew1HpM.js +0 -1
- package/dist-renderer/assets/clone-Dm-k63Yr.js +0 -1
- package/dist-renderer/assets/index-BhellmRb.css +0 -1
- package/dist-renderer/assets/treemap-GDKQZRPO-DU_yr827.js +0 -162
- package/src/features/recent-projects/main/adapters/input/http/registerRecentProjectsHttp.ts +0 -30
- package/src/features/recent-projects/main/adapters/output/presenters/DashboardRecentProjectsPresenter.ts +0 -27
- package/src/features/recent-projects/main/adapters/output/sources/ClaudeRecentProjectsSourceAdapter.ts +0 -91
- package/src/features/recent-projects/main/adapters/output/sources/CodexRecentProjectsSourceAdapter.ts +0 -326
- package/src/features/recent-projects/main/composition/createRecentProjectsFeature.ts +0 -43
- package/src/features/recent-projects/main/index.ts +0 -3
- package/src/features/recent-projects/main/infrastructure/cache/InMemoryRecentProjectsCache.ts +0 -34
- package/src/features/recent-projects/main/infrastructure/codex/CodexAppServerClient.ts +0 -116
- package/src/features/recent-projects/main/infrastructure/identity/RecentProjectIdentityResolver.ts +0 -20
- package/src/features/recent-projects/main/infrastructure/identity/normalizeIdentityPath.ts +0 -10
- package/src/renderer/components/extensions/apikeys/ApiKeyCard.tsx +0 -143
- package/src/renderer/components/extensions/apikeys/ApiKeyFormDialog.tsx +0 -282
- package/src/renderer/components/extensions/apikeys/ApiKeysPanel.tsx +0 -280
|
@@ -208,7 +208,8 @@ export const MessagesPanel = memo(function MessagesPanel({
|
|
|
208
208
|
teams,
|
|
209
209
|
openTeamTab,
|
|
210
210
|
messages,
|
|
211
|
-
|
|
211
|
+
hasMore,
|
|
212
|
+
loadingOlderMessages,
|
|
212
213
|
loadOlderTeamMessages,
|
|
213
214
|
refreshTeamMessagesHead,
|
|
214
215
|
addOptimisticTeamMessage,
|
|
@@ -224,7 +225,13 @@ export const MessagesPanel = memo(function MessagesPanel({
|
|
|
224
225
|
teams: s.teams,
|
|
225
226
|
openTeamTab: s.openTeamTab,
|
|
226
227
|
messages: selectTeamMessages(s, teamName),
|
|
227
|
-
|
|
228
|
+
// Subscribe to only the primitive flags the panel renders. The full
|
|
229
|
+
// cache entry object is rebuilt on every (even no-op) head refresh —
|
|
230
|
+
// selecting it wholesale would re-render this heavy panel every poll.
|
|
231
|
+
hasMore: teamName ? (s.teamMessagesByName[teamName]?.hasMore ?? false) : false,
|
|
232
|
+
loadingOlderMessages: teamName
|
|
233
|
+
? (s.teamMessagesByName[teamName]?.loadingOlder ?? false)
|
|
234
|
+
: false,
|
|
228
235
|
loadOlderTeamMessages: s.loadOlderTeamMessages,
|
|
229
236
|
refreshTeamMessagesHead: s.refreshTeamMessagesHead,
|
|
230
237
|
addOptimisticTeamMessage: s.addOptimisticTeamMessage,
|
|
@@ -233,16 +240,15 @@ export const MessagesPanel = memo(function MessagesPanel({
|
|
|
233
240
|
const bootstrapHeadRefreshAttemptedForTeamRef = useRef<string | null>(null);
|
|
234
241
|
|
|
235
242
|
const loadOlderMessages = useCallback(async () => {
|
|
236
|
-
|
|
243
|
+
// Read the live cache entry instead of subscribing to it — loadingHead
|
|
244
|
+
// toggles on every background head refresh and must not re-render us.
|
|
245
|
+
const entry = useStore.getState().teamMessagesByName[teamName];
|
|
246
|
+
if (!entry?.hasMore || entry.loadingHead || entry.loadingOlder) {
|
|
237
247
|
return;
|
|
238
248
|
}
|
|
239
249
|
await loadOlderTeamMessages(teamName);
|
|
240
|
-
}, [loadOlderTeamMessages,
|
|
250
|
+
}, [loadOlderTeamMessages, teamName]);
|
|
241
251
|
|
|
242
|
-
const messagesLoading =
|
|
243
|
-
(messagesState?.loadingHead ?? false) || (messagesState?.loadingOlder ?? false);
|
|
244
|
-
const loadingOlderMessages = messagesState?.loadingOlder ?? false;
|
|
245
|
-
const hasMore = messagesState?.hasMore ?? false;
|
|
246
252
|
const effectiveMessages = messages;
|
|
247
253
|
const loadedMessageCount = effectiveMessages.length;
|
|
248
254
|
const autoLoadOlderLockRef = useRef(false);
|
|
@@ -271,12 +277,11 @@ export const MessagesPanel = memo(function MessagesPanel({
|
|
|
271
277
|
|
|
272
278
|
const maybeAutoLoadOlderMessages = useCallback(
|
|
273
279
|
(scrollTop: number) => {
|
|
274
|
-
if (
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
) {
|
|
280
|
+
if (scrollTop > AUTO_LOAD_OLDER_SCROLL_TOP_PX || !hasMore || loadingOlderMessages) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
// loadingHead is read live (not subscribed) to avoid per-poll re-renders.
|
|
284
|
+
if (useStore.getState().teamMessagesByName[teamName]?.loadingHead) {
|
|
280
285
|
return;
|
|
281
286
|
}
|
|
282
287
|
if (autoLoadOlderLockRef.current) {
|
|
@@ -285,7 +290,7 @@ export const MessagesPanel = memo(function MessagesPanel({
|
|
|
285
290
|
autoLoadOlderLockRef.current = true;
|
|
286
291
|
void loadOlderMessages();
|
|
287
292
|
},
|
|
288
|
-
[hasMore, loadOlderMessages, loadingOlderMessages,
|
|
293
|
+
[hasMore, loadOlderMessages, loadingOlderMessages, teamName]
|
|
289
294
|
);
|
|
290
295
|
|
|
291
296
|
useEffect(() => {
|
|
@@ -379,7 +384,9 @@ export const MessagesPanel = memo(function MessagesPanel({
|
|
|
379
384
|
return () => {
|
|
380
385
|
cancelled = true;
|
|
381
386
|
};
|
|
382
|
-
|
|
387
|
+
// Refetch when the lead session id changes (e.g. a new session is spawned)
|
|
388
|
+
// so the session list/selector reflects the updated id without a remount.
|
|
389
|
+
}, [teamName, currentLeadSessionId]);
|
|
383
390
|
|
|
384
391
|
const selectedSession = useMemo(
|
|
385
392
|
() => teamSessions.find((session) => session.sessionKey === selectedSessionKey) ?? null,
|
|
@@ -455,7 +462,10 @@ export const MessagesPanel = memo(function MessagesPanel({
|
|
|
455
462
|
bootstrapHeadRefreshAttemptedForTeamRef.current = null;
|
|
456
463
|
return;
|
|
457
464
|
}
|
|
458
|
-
|
|
465
|
+
// Read loading flags live rather than subscribing — they toggle on every
|
|
466
|
+
// background head refresh and must not drive this bootstrap effect.
|
|
467
|
+
const entry = useStore.getState().teamMessagesByName[teamName];
|
|
468
|
+
if (entry?.loadingHead || entry?.loadingOlder) {
|
|
459
469
|
return;
|
|
460
470
|
}
|
|
461
471
|
if (bootstrapHeadRefreshAttemptedForTeamRef.current === teamName) {
|
|
@@ -463,13 +473,7 @@ export const MessagesPanel = memo(function MessagesPanel({
|
|
|
463
473
|
}
|
|
464
474
|
bootstrapHeadRefreshAttemptedForTeamRef.current = teamName;
|
|
465
475
|
void refreshTeamMessagesHead(teamName).catch(() => undefined);
|
|
466
|
-
}, [
|
|
467
|
-
effectiveMessages.length,
|
|
468
|
-
messagesState?.loadingHead,
|
|
469
|
-
messagesState?.loadingOlder,
|
|
470
|
-
refreshTeamMessagesHead,
|
|
471
|
-
teamName,
|
|
472
|
-
]);
|
|
476
|
+
}, [effectiveMessages.length, refreshTeamMessagesHead, teamName]);
|
|
473
477
|
|
|
474
478
|
useLayoutEffect(() => {
|
|
475
479
|
if (position !== 'sidebar') return;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TerminalPanel — a good-looking, read-only terminal-style panel for rendering
|
|
3
|
+
* command / CLI output faithfully.
|
|
4
|
+
*
|
|
5
|
+
* Unlike the structured markdown renderers, this preserves the raw terminal feel:
|
|
6
|
+
* - full ANSI color / decoration fidelity (via `anser`)
|
|
7
|
+
* - monospace, whitespace-exact output
|
|
8
|
+
* - carriage-return (\r) overwrite handling so progress bars settle to their
|
|
9
|
+
* final frame instead of dumping every intermediate line
|
|
10
|
+
* - optional `$ command` prompt line so a Bash call reads like a real terminal
|
|
11
|
+
*
|
|
12
|
+
* It is intentionally lightweight (no xterm.js / node-pty): the session view is a
|
|
13
|
+
* read-only viewer of recorded output, so we only need faithful rendering, not a
|
|
14
|
+
* live PTY.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { useMemo, useState } from 'react';
|
|
18
|
+
|
|
19
|
+
import Anser, { type AnserJsonEntry } from 'anser';
|
|
20
|
+
import { Check, Copy } from 'lucide-react';
|
|
21
|
+
|
|
22
|
+
interface TerminalPanelProps {
|
|
23
|
+
/** Raw output text, may contain ANSI escape sequences. */
|
|
24
|
+
text: string;
|
|
25
|
+
/** Optional command to render as a `$ command` prompt line above the output. */
|
|
26
|
+
command?: string;
|
|
27
|
+
/** Optional label shown in the header bar (e.g. a short description). */
|
|
28
|
+
title?: string;
|
|
29
|
+
/** Max body height in px before scrolling. Defaults to 384. */
|
|
30
|
+
maxHeight?: number;
|
|
31
|
+
className?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Collapse carriage-return overwrites within each line and strip non-color
|
|
36
|
+
* escape sequences (cursor moves, screen clears, OSC) that would otherwise
|
|
37
|
+
* render as garbage. SGR color codes are left intact for `anser`.
|
|
38
|
+
*/
|
|
39
|
+
function normalizeTerminalText(raw: string): string {
|
|
40
|
+
// Strip OSC sequences: ESC ] ... BEL or ESC ] ... ESC \
|
|
41
|
+
let out = raw.replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, '');
|
|
42
|
+
// Strip CSI sequences that are NOT SGR ("m"): cursor movement, erase, etc.
|
|
43
|
+
out = out.replace(/\x1b\[[0-9;?]*[A-Za-ln-z]/g, '');
|
|
44
|
+
// Collapse \r overwrites per line: later segments overwrite earlier from col 0.
|
|
45
|
+
out = out
|
|
46
|
+
.split('\n')
|
|
47
|
+
.map((line) => {
|
|
48
|
+
if (!line.includes('\r')) return line;
|
|
49
|
+
let acc = '';
|
|
50
|
+
for (const seg of line.split('\r')) {
|
|
51
|
+
acc = seg.length >= acc.length ? seg : seg + acc.slice(seg.length);
|
|
52
|
+
}
|
|
53
|
+
return acc;
|
|
54
|
+
})
|
|
55
|
+
.join('\n');
|
|
56
|
+
return out;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function styleForSegment(seg: AnserJsonEntry): React.CSSProperties {
|
|
60
|
+
const style: React.CSSProperties = {};
|
|
61
|
+
if (seg.fg) style.color = `rgb(${seg.fg})`;
|
|
62
|
+
if (seg.bg) style.backgroundColor = `rgb(${seg.bg})`;
|
|
63
|
+
const decorations = seg.decorations ?? [];
|
|
64
|
+
if (decorations.includes('bold')) style.fontWeight = 600;
|
|
65
|
+
if (decorations.includes('italic')) style.fontStyle = 'italic';
|
|
66
|
+
if (decorations.includes('underline')) style.textDecoration = 'underline';
|
|
67
|
+
if (decorations.includes('dim')) style.opacity = 0.6;
|
|
68
|
+
return style;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const MONO_FONT =
|
|
72
|
+
'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace';
|
|
73
|
+
|
|
74
|
+
export const TerminalPanel = ({
|
|
75
|
+
text,
|
|
76
|
+
command,
|
|
77
|
+
title,
|
|
78
|
+
maxHeight = 384,
|
|
79
|
+
className,
|
|
80
|
+
}: TerminalPanelProps): React.JSX.Element => {
|
|
81
|
+
const [copied, setCopied] = useState(false);
|
|
82
|
+
|
|
83
|
+
const segments = useMemo<AnserJsonEntry[]>(
|
|
84
|
+
() =>
|
|
85
|
+
Anser.ansiToJson(normalizeTerminalText(text ?? ''), {
|
|
86
|
+
json: true,
|
|
87
|
+
use_classes: false,
|
|
88
|
+
remove_empty: false,
|
|
89
|
+
}),
|
|
90
|
+
[text]
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const handleCopy = (): void => {
|
|
94
|
+
const payload = command ? `$ ${command}\n${text ?? ''}` : (text ?? '');
|
|
95
|
+
void navigator.clipboard?.writeText(payload).then(() => {
|
|
96
|
+
setCopied(true);
|
|
97
|
+
setTimeout(() => setCopied(false), 1500);
|
|
98
|
+
});
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<div
|
|
103
|
+
className={`overflow-hidden rounded-lg border ${className ?? ''}`}
|
|
104
|
+
style={{ borderColor: 'rgba(255,255,255,0.08)', backgroundColor: '#0c0c0f' }}
|
|
105
|
+
>
|
|
106
|
+
{/* Header / window chrome */}
|
|
107
|
+
<div
|
|
108
|
+
className="flex items-center gap-2 px-3 py-1.5"
|
|
109
|
+
style={{
|
|
110
|
+
backgroundColor: '#16161b',
|
|
111
|
+
borderBottom: '1px solid rgba(255,255,255,0.06)',
|
|
112
|
+
}}
|
|
113
|
+
>
|
|
114
|
+
<span className="flex items-center gap-1.5">
|
|
115
|
+
<span className="size-2.5 rounded-full" style={{ backgroundColor: '#ff5f57' }} />
|
|
116
|
+
<span className="size-2.5 rounded-full" style={{ backgroundColor: '#febc2e' }} />
|
|
117
|
+
<span className="size-2.5 rounded-full" style={{ backgroundColor: '#28c840' }} />
|
|
118
|
+
</span>
|
|
119
|
+
<span
|
|
120
|
+
className="ml-1 flex-1 truncate text-[11px]"
|
|
121
|
+
style={{ color: 'rgba(255,255,255,0.45)', fontFamily: MONO_FONT }}
|
|
122
|
+
>
|
|
123
|
+
{title ?? (command ? command : 'terminal')}
|
|
124
|
+
</span>
|
|
125
|
+
<button
|
|
126
|
+
type="button"
|
|
127
|
+
onClick={handleCopy}
|
|
128
|
+
className="flex items-center gap-1 rounded px-1.5 py-0.5 text-[10px] transition-colors"
|
|
129
|
+
style={{ color: 'rgba(255,255,255,0.45)' }}
|
|
130
|
+
title="复制"
|
|
131
|
+
>
|
|
132
|
+
{copied ? <Check className="size-3" /> : <Copy className="size-3" />}
|
|
133
|
+
{copied ? '已复制' : '复制'}
|
|
134
|
+
</button>
|
|
135
|
+
</div>
|
|
136
|
+
|
|
137
|
+
{/* Body */}
|
|
138
|
+
<pre
|
|
139
|
+
className="overflow-auto whitespace-pre-wrap break-all px-3 py-2.5 text-xs leading-relaxed"
|
|
140
|
+
style={{ maxHeight, fontFamily: MONO_FONT, color: '#d4d4d4', margin: 0 }}
|
|
141
|
+
>
|
|
142
|
+
{command && (
|
|
143
|
+
<div className="mb-1">
|
|
144
|
+
<span style={{ color: '#28c840' }}>$ </span>
|
|
145
|
+
<span style={{ color: '#e8e8e8' }}>{command}</span>
|
|
146
|
+
</div>
|
|
147
|
+
)}
|
|
148
|
+
{segments.map((seg, i) => (
|
|
149
|
+
<span key={i} style={styleForSegment(seg)}>
|
|
150
|
+
{seg.content}
|
|
151
|
+
</span>
|
|
152
|
+
))}
|
|
153
|
+
</pre>
|
|
154
|
+
</div>
|
|
155
|
+
);
|
|
156
|
+
};
|
|
@@ -16,7 +16,7 @@ import type {
|
|
|
16
16
|
PluginSortField,
|
|
17
17
|
} from '@shared/types/extensions';
|
|
18
18
|
|
|
19
|
-
export type ExtensionsSubTab = 'plugins' | 'mcp-servers' | 'skills' | '
|
|
19
|
+
export type ExtensionsSubTab = 'plugins' | 'mcp-servers' | 'skills' | 'env-vars';
|
|
20
20
|
export type SkillsSortState = 'name-asc' | 'recent-desc';
|
|
21
21
|
|
|
22
22
|
interface PluginSortState {
|
|
@@ -33,7 +33,7 @@ const DEFAULT_FILTERS: PluginFilters = {
|
|
|
33
33
|
|
|
34
34
|
export function useExtensionsTabState() {
|
|
35
35
|
// ── Sub-tab navigation ──
|
|
36
|
-
const [activeSubTab, setActiveSubTab] = useState<ExtensionsSubTab>('
|
|
36
|
+
const [activeSubTab, setActiveSubTab] = useState<ExtensionsSubTab>('plugins');
|
|
37
37
|
|
|
38
38
|
// ── Plugin filters & sort ──
|
|
39
39
|
const [pluginFilters, setPluginFilters] = useState<PluginFilters>(DEFAULT_FILTERS);
|
|
@@ -18,9 +18,6 @@ import { findPaneByTabId, updatePane } from '../utils/paneHelpers';
|
|
|
18
18
|
|
|
19
19
|
import type { AppState } from '../types';
|
|
20
20
|
import type {
|
|
21
|
-
ApiKeyEntry,
|
|
22
|
-
ApiKeySaveRequest,
|
|
23
|
-
ApiKeyStorageStatus,
|
|
24
21
|
EnrichedPlugin,
|
|
25
22
|
ExtensionOperationState,
|
|
26
23
|
InstalledMcpEntry,
|
|
@@ -74,12 +71,13 @@ export interface ExtensionsSlice {
|
|
|
74
71
|
mcpInstallProgress: Record<string, ExtensionOperationState>;
|
|
75
72
|
installErrors: Record<string, string>; // keyed by scoped operation key
|
|
76
73
|
|
|
77
|
-
// ──
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
74
|
+
// ── Toast notifications ──
|
|
75
|
+
extensionToasts: Array<{
|
|
76
|
+
id: string;
|
|
77
|
+
type: 'success' | 'error' | 'warning' | 'info';
|
|
78
|
+
title: string;
|
|
79
|
+
message?: string;
|
|
80
|
+
}>;
|
|
83
81
|
|
|
84
82
|
// ── Skills catalog cache ──
|
|
85
83
|
skillsUserCatalog: SkillCatalogItem[];
|
|
@@ -111,6 +109,14 @@ export interface ExtensionsSlice {
|
|
|
111
109
|
applySkillImport: (request: SkillImportRequest) => Promise<SkillDetail | null>;
|
|
112
110
|
deleteSkill: (request: SkillDeleteRequest) => Promise<void>;
|
|
113
111
|
|
|
112
|
+
// ── Toast actions ──
|
|
113
|
+
addExtensionToast: (
|
|
114
|
+
type: 'success' | 'error' | 'warning' | 'info',
|
|
115
|
+
title: string,
|
|
116
|
+
message?: string
|
|
117
|
+
) => void;
|
|
118
|
+
dismissExtensionToast: (id: string) => void;
|
|
119
|
+
|
|
114
120
|
// ── Mutation actions ──
|
|
115
121
|
installPlugin: (request: PluginInstallRequest) => Promise<void>;
|
|
116
122
|
uninstallPlugin: (pluginId: string, scope?: InstallScope, projectPath?: string) => Promise<void>;
|
|
@@ -123,12 +129,6 @@ export interface ExtensionsSlice {
|
|
|
123
129
|
projectPath?: string
|
|
124
130
|
) => Promise<void>;
|
|
125
131
|
|
|
126
|
-
// ── API Keys actions ──
|
|
127
|
-
fetchApiKeys: () => Promise<void>;
|
|
128
|
-
fetchApiKeyStorageStatus: () => Promise<void>;
|
|
129
|
-
saveApiKey: (request: ApiKeySaveRequest) => Promise<void>;
|
|
130
|
-
deleteApiKey: (id: string) => Promise<void>;
|
|
131
|
-
|
|
132
132
|
// ── Tab opener ──
|
|
133
133
|
openExtensionsTab: () => void;
|
|
134
134
|
|
|
@@ -337,11 +337,6 @@ function getSkillsCatalogKey(projectPath?: string): string {
|
|
|
337
337
|
return projectPath ?? USER_SKILLS_CATALOG_KEY;
|
|
338
338
|
}
|
|
339
339
|
|
|
340
|
-
function upsertApiKeyEntry(entries: ApiKeyEntry[], entry: ApiKeyEntry): ApiKeyEntry[] {
|
|
341
|
-
const nextEntries = entries.filter((candidate) => candidate.id !== entry.id);
|
|
342
|
-
return [entry, ...nextEntries];
|
|
343
|
-
}
|
|
344
|
-
|
|
345
340
|
/** Duration to show "success" state before returning to idle */
|
|
346
341
|
const SUCCESS_DISPLAY_MS = 2_000;
|
|
347
342
|
const PROJECT_SCOPE_REQUIRED_MESSAGE =
|
|
@@ -397,12 +392,7 @@ export const createExtensionsSlice: StateCreator<AppState, [], [], ExtensionsSli
|
|
|
397
392
|
pluginInstallProgress: {},
|
|
398
393
|
mcpInstallProgress: {},
|
|
399
394
|
installErrors: {},
|
|
400
|
-
|
|
401
|
-
apiKeys: [],
|
|
402
|
-
apiKeysLoading: false,
|
|
403
|
-
apiKeysError: null,
|
|
404
|
-
apiKeySaving: false,
|
|
405
|
-
apiKeyStorageStatus: null,
|
|
395
|
+
extensionToasts: [],
|
|
406
396
|
|
|
407
397
|
skillsUserCatalog: [],
|
|
408
398
|
skillsProjectCatalogByProjectPath: {},
|
|
@@ -631,6 +621,11 @@ export const createExtensionsSlice: StateCreator<AppState, [], [], ExtensionsSli
|
|
|
631
621
|
const diagnosticsRecord = Object.fromEntries(
|
|
632
622
|
diagnostics.map((entry) => [getMcpDiagnosticKey(entry.name, entry.scope), entry] as const)
|
|
633
623
|
);
|
|
624
|
+
const failedServers = diagnostics.filter((d) => d.status === 'failed');
|
|
625
|
+
if (failedServers.length > 0) {
|
|
626
|
+
const names = failedServers.map((s) => s.name).join(', ');
|
|
627
|
+
get().addExtensionToast('warning', 'MCP 连接异常', `${names} 连接失败`);
|
|
628
|
+
}
|
|
634
629
|
const checkedAt = Date.now();
|
|
635
630
|
set({
|
|
636
631
|
mcpDiagnostics: diagnosticsRecord,
|
|
@@ -956,6 +951,11 @@ export const createExtensionsSlice: StateCreator<AppState, [], [], ExtensionsSli
|
|
|
956
951
|
set((prev) => ({
|
|
957
952
|
pluginInstallProgress: { ...prev.pluginInstallProgress, [operationKey]: 'success' },
|
|
958
953
|
}));
|
|
954
|
+
get().addExtensionToast(
|
|
955
|
+
'success',
|
|
956
|
+
'插件已安装',
|
|
957
|
+
`已安装到 claudecode (${effectiveRequest.scope ?? 'user'})`
|
|
958
|
+
);
|
|
959
959
|
|
|
960
960
|
// Refresh catalog to pick up new installed state
|
|
961
961
|
void get().fetchPluginCatalog(
|
|
@@ -1183,6 +1183,7 @@ export const createExtensionsSlice: StateCreator<AppState, [], [], ExtensionsSli
|
|
|
1183
1183
|
set((prev) => ({
|
|
1184
1184
|
mcpInstallProgress: { ...prev.mcpInstallProgress, [progressKey]: 'success' },
|
|
1185
1185
|
}));
|
|
1186
|
+
get().addExtensionToast('success', 'MCP 服务器已安装', `已安装 ${request.serverName}`);
|
|
1186
1187
|
|
|
1187
1188
|
scheduleMcpSuccessReset(progressKey, set);
|
|
1188
1189
|
} catch (err) {
|
|
@@ -1264,6 +1265,7 @@ export const createExtensionsSlice: StateCreator<AppState, [], [], ExtensionsSli
|
|
|
1264
1265
|
set((prev) => ({
|
|
1265
1266
|
mcpInstallProgress: { ...prev.mcpInstallProgress, [operationKey]: 'success' },
|
|
1266
1267
|
}));
|
|
1268
|
+
get().addExtensionToast('success', 'MCP 服务器已卸载');
|
|
1267
1269
|
|
|
1268
1270
|
scheduleMcpSuccessReset(operationKey, set);
|
|
1269
1271
|
} catch (err) {
|
|
@@ -1276,92 +1278,25 @@ export const createExtensionsSlice: StateCreator<AppState, [], [], ExtensionsSli
|
|
|
1276
1278
|
}
|
|
1277
1279
|
},
|
|
1278
1280
|
|
|
1279
|
-
// ──
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
} catch (err) {
|
|
1288
|
-
set({
|
|
1289
|
-
apiKeysLoading: false,
|
|
1290
|
-
apiKeysError: err instanceof Error ? err.message : 'Failed to load API keys',
|
|
1291
|
-
});
|
|
1292
|
-
}
|
|
1293
|
-
},
|
|
1294
|
-
|
|
1295
|
-
fetchApiKeyStorageStatus: async () => {
|
|
1296
|
-
if (!api.apiKeys) return;
|
|
1297
|
-
try {
|
|
1298
|
-
const status = await api.apiKeys.getStorageStatus();
|
|
1299
|
-
set({ apiKeyStorageStatus: status });
|
|
1300
|
-
} catch {
|
|
1301
|
-
// Non-critical — UI will just not show the info icon
|
|
1302
|
-
}
|
|
1303
|
-
},
|
|
1304
|
-
|
|
1305
|
-
// ── API Key save ──
|
|
1306
|
-
saveApiKey: async (request: ApiKeySaveRequest) => {
|
|
1307
|
-
if (!api.apiKeys) return;
|
|
1308
|
-
|
|
1309
|
-
set({ apiKeySaving: true, apiKeysError: null });
|
|
1310
|
-
try {
|
|
1311
|
-
const savedKey = await api.apiKeys.save(request);
|
|
1312
|
-
const warnings: string[] = [];
|
|
1313
|
-
|
|
1314
|
-
try {
|
|
1315
|
-
const keys = await api.apiKeys.list();
|
|
1316
|
-
set({ apiKeys: keys });
|
|
1317
|
-
} catch (listError) {
|
|
1318
|
-
warnings.push(
|
|
1319
|
-
listError instanceof Error
|
|
1320
|
-
? `API key saved, but failed to refresh key list. ${listError.message}`
|
|
1321
|
-
: 'API key saved, but failed to refresh key list.'
|
|
1322
|
-
);
|
|
1281
|
+
// ── Toast notifications ──
|
|
1282
|
+
addExtensionToast: (type, title, message) => {
|
|
1283
|
+
const id = `toast-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
1284
|
+
set((prev) => ({
|
|
1285
|
+
extensionToasts: [...prev.extensionToasts, { id, type, title, message }],
|
|
1286
|
+
}));
|
|
1287
|
+
if (type === 'success') {
|
|
1288
|
+
setTimeout(() => {
|
|
1323
1289
|
set((prev) => ({
|
|
1324
|
-
|
|
1290
|
+
extensionToasts: prev.extensionToasts.filter((t) => t.id !== id),
|
|
1325
1291
|
}));
|
|
1326
|
-
}
|
|
1327
|
-
|
|
1328
|
-
await refreshConfiguredCliStatus(get());
|
|
1329
|
-
const refreshError = get().cliStatusError;
|
|
1330
|
-
if (refreshError) {
|
|
1331
|
-
warnings.push(`API key saved, but failed to refresh provider status. ${refreshError}`);
|
|
1332
|
-
}
|
|
1333
|
-
set({ apiKeySaving: false, apiKeysError: warnings.length > 0 ? warnings.join(' ') : null });
|
|
1334
|
-
} catch (err) {
|
|
1335
|
-
set({
|
|
1336
|
-
apiKeySaving: false,
|
|
1337
|
-
apiKeysError: err instanceof Error ? err.message : 'Failed to save API key',
|
|
1338
|
-
});
|
|
1339
|
-
throw err; // Re-throw so the dialog can show the error
|
|
1292
|
+
}, 3000);
|
|
1340
1293
|
}
|
|
1341
1294
|
},
|
|
1342
1295
|
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
try {
|
|
1348
|
-
await api.apiKeys.delete(id);
|
|
1349
|
-
set((prev) => ({
|
|
1350
|
-
apiKeys: prev.apiKeys.filter((k) => k.id !== id),
|
|
1351
|
-
}));
|
|
1352
|
-
await refreshConfiguredCliStatus(get());
|
|
1353
|
-
const refreshError = get().cliStatusError;
|
|
1354
|
-
set({
|
|
1355
|
-
apiKeysError: refreshError
|
|
1356
|
-
? `API key deleted, but failed to refresh provider status. ${refreshError}`
|
|
1357
|
-
: null,
|
|
1358
|
-
});
|
|
1359
|
-
} catch (err) {
|
|
1360
|
-
set({
|
|
1361
|
-
apiKeysError: err instanceof Error ? err.message : 'Failed to delete API key',
|
|
1362
|
-
});
|
|
1363
|
-
throw err;
|
|
1364
|
-
}
|
|
1296
|
+
dismissExtensionToast: (id) => {
|
|
1297
|
+
set((prev) => ({
|
|
1298
|
+
extensionToasts: prev.extensionToasts.filter((t) => t.id !== id),
|
|
1299
|
+
}));
|
|
1365
1300
|
},
|
|
1366
1301
|
|
|
1367
1302
|
// ── Tab opener ──
|
|
@@ -904,7 +904,9 @@ function pruneOptimisticMessages(
|
|
|
904
904
|
canonical: readonly InboxMessage[]
|
|
905
905
|
): InboxMessage[] {
|
|
906
906
|
if (optimistic.length === 0) {
|
|
907
|
-
|
|
907
|
+
// Preserve the input reference so selectTeamMessages' identity cache stays
|
|
908
|
+
// warm across no-op head refreshes (otherwise every poll churns `messages`).
|
|
909
|
+
return optimistic as InboxMessage[];
|
|
908
910
|
}
|
|
909
911
|
|
|
910
912
|
const canonicalIds = new Set(
|
|
@@ -913,10 +915,14 @@ function pruneOptimisticMessages(
|
|
|
913
915
|
.filter((messageId) => messageId.length > 0)
|
|
914
916
|
);
|
|
915
917
|
|
|
916
|
-
|
|
918
|
+
const pruned = optimistic.filter((message) => {
|
|
917
919
|
const messageId = typeof message.messageId === 'string' ? message.messageId.trim() : '';
|
|
918
920
|
return !messageId || !canonicalIds.has(messageId);
|
|
919
921
|
});
|
|
922
|
+
|
|
923
|
+
// Nothing was actually pruned — return the original reference so downstream
|
|
924
|
+
// identity checks (merged-messages selector) can short-circuit re-renders.
|
|
925
|
+
return pruned.length === optimistic.length ? (optimistic as InboxMessage[]) : pruned;
|
|
920
926
|
}
|
|
921
927
|
|
|
922
928
|
function clearPendingReplyRefreshTimer(teamName: string): void {
|
package/src/shared/types/api.ts
CHANGED
|
@@ -1122,6 +1122,35 @@ export interface ElectronAPI extends RecentProjectsElectronApi {
|
|
|
1122
1122
|
// Extension Store — Skills Catalog API (Electron-only, optional)
|
|
1123
1123
|
skills?: SkillsCatalogAPI;
|
|
1124
1124
|
|
|
1125
|
+
// Extension Store — Credentials (project env, MCP credentials)
|
|
1126
|
+
credentials?: {
|
|
1127
|
+
getStatus: () => Promise<{ encryption: string; storagePath: string } | null>;
|
|
1128
|
+
getProjectEnv: (projectPath: string) => Promise<Record<string, string>>;
|
|
1129
|
+
saveProjectEnv: (projectPath: string, vars: Record<string, string>) => Promise<void>;
|
|
1130
|
+
scanRequired: (
|
|
1131
|
+
projectPath: string,
|
|
1132
|
+
mcpServers: {
|
|
1133
|
+
name: string;
|
|
1134
|
+
envVars?: { name: string; isRequired: boolean; description?: string };
|
|
1135
|
+
}[],
|
|
1136
|
+
skillReqs: {
|
|
1137
|
+
name: string;
|
|
1138
|
+
envVars: { name: string; isRequired?: boolean; description?: string }[];
|
|
1139
|
+
}[]
|
|
1140
|
+
) => Promise<{
|
|
1141
|
+
required: {
|
|
1142
|
+
name: string;
|
|
1143
|
+
isRequired: boolean;
|
|
1144
|
+
description?: string;
|
|
1145
|
+
source: string;
|
|
1146
|
+
value?: string;
|
|
1147
|
+
}[];
|
|
1148
|
+
}>;
|
|
1149
|
+
resolveAgentEnv: (projectPath: string) => Promise<Record<string, string>>;
|
|
1150
|
+
getSkillGlobalEnv: (skillFolderName: string) => Promise<Record<string, string>>;
|
|
1151
|
+
saveSkillGlobalEnv: (skillFolderName: string, vars: Record<string, string>) => Promise<void>;
|
|
1152
|
+
};
|
|
1153
|
+
|
|
1125
1154
|
// Extension Store — API Keys Management (Electron-only, optional)
|
|
1126
1155
|
apiKeys?: ApiKeysAPI;
|
|
1127
1156
|
|
|
@@ -111,6 +111,7 @@ export interface McpInstallRequest {
|
|
|
111
111
|
projectPath?: string; // required for 'project' scope
|
|
112
112
|
envValues: Record<string, string>;
|
|
113
113
|
headers: McpHeaderDef[]; // for HTTP/SSE servers (CLI --header flag)
|
|
114
|
+
harnessType?: string; // which harness to install to (defaults to claudecode)
|
|
114
115
|
}
|
|
115
116
|
|
|
116
117
|
// ── Custom install request (bypasses registry, user provides spec) ──────────
|
|
@@ -122,6 +123,7 @@ export interface McpCustomInstallRequest {
|
|
|
122
123
|
installSpec: McpInstallSpec; // user provides directly
|
|
123
124
|
envValues: Record<string, string>;
|
|
124
125
|
headers: McpHeaderDef[];
|
|
126
|
+
harnessType?: string; // which harness to install to
|
|
125
127
|
}
|
|
126
128
|
|
|
127
129
|
// ── Search result wrapper ──────────────────────────────────────────────────
|
|
@@ -14,7 +14,7 @@ export interface PluginCatalogItem {
|
|
|
14
14
|
name: string; // display name only
|
|
15
15
|
|
|
16
16
|
// Metadata
|
|
17
|
-
source: 'official';
|
|
17
|
+
source: 'official' | 'local'; // 'local' = discovered from a user-registered custom marketplace
|
|
18
18
|
description: string;
|
|
19
19
|
category: string; // open-ended string, derived from marketplace.json
|
|
20
20
|
author?: { name: string; email?: string };
|
|
@@ -71,6 +71,7 @@ export interface PluginInstallRequest {
|
|
|
71
71
|
pluginId: string; // canonical key — main resolves qualifiedName from catalog
|
|
72
72
|
scope: InstallScope;
|
|
73
73
|
projectPath?: string; // required for repo-scoped installs ('project' or 'local')
|
|
74
|
+
harnessType?: string; // which harness to install to (defaults to claudecode)
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
// ── Filters (renderer-only concern) ────────────────────────────────────────
|
|
@@ -12,6 +12,12 @@ export type SkillInvocationMode = 'auto' | 'manual-only';
|
|
|
12
12
|
|
|
13
13
|
export type SkillIssueSeverity = 'info' | 'warning' | 'error';
|
|
14
14
|
|
|
15
|
+
export interface SkillEnvVarDef {
|
|
16
|
+
name: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
isRequired?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
15
21
|
export interface SkillDirectoryFlags {
|
|
16
22
|
hasScripts: boolean;
|
|
17
23
|
hasReferences: boolean;
|
|
@@ -57,6 +63,7 @@ export interface SkillCatalogItem {
|
|
|
57
63
|
isValid: boolean;
|
|
58
64
|
issues: SkillValidationIssue[];
|
|
59
65
|
modifiedAt: number;
|
|
66
|
+
requiredEnv?: SkillEnvVarDef[];
|
|
60
67
|
}
|
|
61
68
|
|
|
62
69
|
export interface SkillDetail {
|
|
@@ -58,7 +58,7 @@ export function createLegacyRuntimeFallbackCliExtensionCapabilities(
|
|
|
58
58
|
export function getCliProviderExtensionCapabilities(
|
|
59
59
|
provider: Pick<CliProviderStatus, 'capabilities'> | null | undefined
|
|
60
60
|
): CliExtensionCapabilities {
|
|
61
|
-
const fallback =
|
|
61
|
+
const fallback = createDefaultCliExtensionCapabilities();
|
|
62
62
|
const extensions = provider?.capabilities?.extensions;
|
|
63
63
|
if (!extensions) {
|
|
64
64
|
return fallback;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a6 as o,a7 as n}from"./index-BowUl0Jb.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-FhpTEPvD.js";import{_ as i}from"./index-BowUl0Jb.js";import"./chunk-FMBD7UC4-e9l2tGHG.js";import"./chunk-55IACEB6-CGGAOoXd.js";import"./chunk-QN33PNHL-DeiXVTCy.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-FhpTEPvD.js";import{_ as i}from"./index-BowUl0Jb.js";import"./chunk-FMBD7UC4-e9l2tGHG.js";import"./chunk-55IACEB6-CGGAOoXd.js";import"./chunk-QN33PNHL-DeiXVTCy.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-DwPTU9lP.js";var e=4;function a(o){return r(o,e)}export{a as c};
|