@yancyyu/openhermit 1.6.38 → 1.6.40
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-CemDOX-3.js +58 -0
- package/dist-renderer/assets/{TeamGraphOverlay-ZEDfZyHb.js → TeamGraphOverlay-hPY770Db.js} +1 -1
- package/dist-renderer/assets/{_basePickBy-CIhniz70.js → _basePickBy-BHHrJT1i.js} +1 -1
- package/dist-renderer/assets/{_baseUniq-cKAW4Q8I.js → _baseUniq-CWErBtke.js} +1 -1
- package/dist-renderer/assets/{arc-YmNsoDXW.js → arc-C_o2_Uv8.js} +1 -1
- package/dist-renderer/assets/{architectureDiagram-VXUJARFQ-DHEls2sX.js → architectureDiagram-VXUJARFQ-DUW0LI3t.js} +1 -1
- package/dist-renderer/assets/{blockDiagram-VD42YOAC-Bpwf1Sbg.js → blockDiagram-VD42YOAC-CWbCE9hQ.js} +1 -1
- package/dist-renderer/assets/{c4Diagram-YG6GDRKO-B0IaQ4w5.js → c4Diagram-YG6GDRKO-BjLadrfV.js} +1 -1
- package/dist-renderer/assets/channel-DyP9YlCF.js +1 -0
- package/dist-renderer/assets/{chunk-4BX2VUAB-DLk-hcFc.js → chunk-4BX2VUAB-CPnvjZl9.js} +1 -1
- package/dist-renderer/assets/{chunk-55IACEB6-1XRmX_Zm.js → chunk-55IACEB6-OlL47yXQ.js} +1 -1
- package/dist-renderer/assets/{chunk-B4BG7PRW-1waH1DAD.js → chunk-B4BG7PRW-DTasjbm8.js} +1 -1
- package/dist-renderer/assets/{chunk-DI55MBZ5-BqpZBtrN.js → chunk-DI55MBZ5-C5_Xaqkk.js} +1 -1
- package/dist-renderer/assets/{chunk-FMBD7UC4-Bly7vVym.js → chunk-FMBD7UC4-NdoM4DMR.js} +1 -1
- package/dist-renderer/assets/{chunk-QN33PNHL-Ci2QWBAs.js → chunk-QN33PNHL-C8Fybejy.js} +1 -1
- package/dist-renderer/assets/{chunk-QZHKN3VN-YCqFW7d-.js → chunk-QZHKN3VN-E98TYFXJ.js} +1 -1
- package/dist-renderer/assets/{chunk-TZMSLE5B-B0xGXInl.js → chunk-TZMSLE5B-h4lFgkIq.js} +1 -1
- package/dist-renderer/assets/classDiagram-2ON5EDUG-BqffFTae.js +1 -0
- package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-BqffFTae.js +1 -0
- package/dist-renderer/assets/clone-MPcKWs2O.js +1 -0
- package/dist-renderer/assets/{cose-bilkent-S5V4N54A-DxcFNQKT.js → cose-bilkent-S5V4N54A-DtQ7fkrs.js} +1 -1
- package/dist-renderer/assets/{dagre-6UL2VRFP-DPo_RfZY.js → dagre-6UL2VRFP-CN-nL_z4.js} +1 -1
- package/dist-renderer/assets/{diagram-PSM6KHXK-U3hQsFe4.js → diagram-PSM6KHXK-DVJtqmm-.js} +1 -1
- package/dist-renderer/assets/{diagram-QEK2KX5R-OrwrAy0V.js → diagram-QEK2KX5R-DlxHxyXh.js} +1 -1
- package/dist-renderer/assets/{diagram-S2PKOQOG-CXATPWVw.js → diagram-S2PKOQOG-7dpzO6x6.js} +1 -1
- package/dist-renderer/assets/{erDiagram-Q2GNP2WA-B0e8AfMF.js → erDiagram-Q2GNP2WA-GP1TqsHi.js} +1 -1
- package/dist-renderer/assets/{flowDiagram-NV44I4VS-CXfzA4jJ.js → flowDiagram-NV44I4VS-C7ZLETuH.js} +1 -1
- package/dist-renderer/assets/{ganttDiagram-JELNMOA3-CMr08qVl.js → ganttDiagram-JELNMOA3-CvPB68dH.js} +1 -1
- package/dist-renderer/assets/{gitGraphDiagram-V2S2FVAM-vYFHpPmy.js → gitGraphDiagram-V2S2FVAM-B5yOm3w7.js} +1 -1
- package/dist-renderer/assets/{graph-DOe5j8dH.js → graph-smeyY1YZ.js} +1 -1
- package/dist-renderer/assets/{index-BySQS7AB.js → index-BJx8XvG1.js} +1 -1
- package/dist-renderer/assets/{index-C_okzZXP.js → index-CQaXUAua.js} +1 -1
- package/dist-renderer/assets/{index-VJ-MM9xa.js → index-CajRpxO2.js} +1 -1
- package/dist-renderer/assets/{index-V7dAKPqd.js → index-ChG4rE-E.js} +587 -705
- package/dist-renderer/assets/index-DUd0uw9C.css +32 -0
- package/dist-renderer/assets/{index-CzWxVCRL.js → index-IhmXZWqf.js} +1 -1
- package/dist-renderer/assets/{index-B2Dy7M2G.js → index-x_JkoDRH.js} +1 -1
- package/dist-renderer/assets/{infoDiagram-HS3SLOUP-D_WubR0B.js → infoDiagram-HS3SLOUP-D-hWRQGY.js} +1 -1
- package/dist-renderer/assets/{journeyDiagram-XKPGCS4Q-w9ca-1TI.js → journeyDiagram-XKPGCS4Q-Bb6W8rUG.js} +1 -1
- package/dist-renderer/assets/{kanban-definition-3W4ZIXB7-Jg9p6_pN.js → kanban-definition-3W4ZIXB7-CnHdUX0q.js} +1 -1
- package/dist-renderer/assets/{layout-B-z3y17c.js → layout-pqss_zkI.js} +1 -1
- package/dist-renderer/assets/{linear-D-RTX5UW.js → linear-B1mFITNh.js} +1 -1
- package/dist-renderer/assets/{mindmap-definition-VGOIOE7T-CDQmHOYP.js → mindmap-definition-VGOIOE7T-DTD9q7-D.js} +1 -1
- package/dist-renderer/assets/{pieDiagram-ADFJNKIX-D_odsQL7.js → pieDiagram-ADFJNKIX-Df3mhrn7.js} +1 -1
- package/dist-renderer/assets/{quadrantDiagram-AYHSOK5B-BRsmYWSA.js → quadrantDiagram-AYHSOK5B-B1FZ09vH.js} +1 -1
- package/dist-renderer/assets/{requirementDiagram-UZGBJVZJ-ChNE_BOV.js → requirementDiagram-UZGBJVZJ-aEO78thZ.js} +1 -1
- package/dist-renderer/assets/{sankeyDiagram-TZEHDZUN-C8FtpwKc.js → sankeyDiagram-TZEHDZUN-6Ui--jp-.js} +1 -1
- package/dist-renderer/assets/{sequenceDiagram-WL72ISMW-DmLCzNcc.js → sequenceDiagram-WL72ISMW-DF4Q1cAM.js} +1 -1
- package/dist-renderer/assets/splashScene-D0YB9uxm.js +17 -0
- package/dist-renderer/assets/{stateDiagram-FKZM4ZOC-WJBm4bhu.js → stateDiagram-FKZM4ZOC-BqA2BI8C.js} +1 -1
- package/dist-renderer/assets/stateDiagram-v2-4FDKWEC3-Cs2ZtUD2.js +1 -0
- package/dist-renderer/assets/{timeline-definition-IT6M3QCI-BXs_hOJs.js → timeline-definition-IT6M3QCI-DoOkw_A8.js} +1 -1
- package/dist-renderer/assets/{treemap-GDKQZRPO-o04MA0G9.js → treemap-GDKQZRPO-DUe26QdD.js} +1 -1
- package/dist-renderer/assets/{xychartDiagram-PRI3JC2R-Czj69XRd.js → xychartDiagram-PRI3JC2R-BKCnj5Xn.js} +1 -1
- package/dist-renderer/index.html +20 -53
- package/package.json +25 -18
- package/src/main/ipc/extensions.ts +2 -1
- package/src/main/server.ts +873 -221
- package/src/main/services/extensions/ExtensionFacadeService.ts +2 -5
- package/src/main/services/extensions/catalog/PluginCatalogService.ts +4 -2
- 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/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 +5 -6
- 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 +68 -18
- 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/chat/ChatHistoryItem.tsx +1 -1
- package/src/renderer/components/chat/items/SubagentItem.tsx +2 -2
- package/src/renderer/components/chat/viewers/MermaidDiagram.tsx +2 -2
- package/src/renderer/components/common/ErrorBoundary.tsx +1 -1
- package/src/renderer/components/common/TerminalPane.tsx +213 -0
- package/src/renderer/components/dashboard/CliStatusBanner.tsx +7 -7
- package/src/renderer/components/dashboard/DashboardView.tsx +9 -36
- package/src/renderer/components/extensions/ExtensionStoreView.tsx +7 -126
- package/src/renderer/components/extensions/ExtensionsSubTabTrigger.tsx +1 -1
- package/src/renderer/components/extensions/common/ExtensionToast.tsx +3 -3
- package/src/renderer/components/extensions/common/SourceBadge.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/CategoryChips.tsx +1 -1
- package/src/renderer/components/extensions/plugins/PluginCard.tsx +6 -6
- package/src/renderer/components/extensions/plugins/PluginDetailDialog.tsx +2 -2
- package/src/renderer/components/extensions/plugins/PluginsPanel.tsx +34 -21
- package/src/renderer/components/extensions/skills/SkillEditorDialog.tsx +1 -1
- package/src/renderer/components/extensions/skills/SkillsLibraryPanel.tsx +335 -0
- package/src/renderer/components/layout/PaneContent.tsx +8 -1
- package/src/renderer/components/layout/PaneResizeHandle.tsx +2 -2
- package/src/renderer/components/layout/Sidebar.tsx +13 -56
- package/src/renderer/components/layout/SortableTab.tsx +22 -33
- package/src/renderer/components/layout/TabBar.tsx +1 -1
- package/src/renderer/components/layout/TabContextMenu.tsx +1 -1
- package/src/renderer/components/report/sections/CostSection.tsx +2 -2
- package/src/renderer/components/report/sections/InsightsSection.tsx +1 -1
- package/src/renderer/components/runtime/ProviderRuntimeBackendSelector.tsx +2 -2
- 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/search/CommandPalette.tsx +4 -4
- 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 +72 -113
- 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/SessionFiltersPopover.tsx +1 -1
- package/src/renderer/components/sidebar/SessionItem.tsx +3 -3
- package/src/renderer/components/sidebar/SidebarSessions.tsx +184 -6
- package/src/renderer/components/sidebar/SidebarTaskItem.tsx +4 -4
- package/src/renderer/components/sidebar/WorkspaceBrowser.tsx +40 -5
- 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/ClaudeLogsFilterPopover.tsx +1 -1
- package/src/renderer/components/team/ClaudeLogsPanel.tsx +1 -1
- package/src/renderer/components/team/CollapsibleTeamSection.tsx +17 -32
- package/src/renderer/components/team/ProcessesSection.tsx +2 -2
- package/src/renderer/components/team/TaskTooltip.tsx +2 -2
- package/src/renderer/components/team/TeamDetailView.tsx +319 -123
- package/src/renderer/components/team/TeamListFilterPopover.tsx +1 -1
- package/src/renderer/components/team/TeamListView.tsx +109 -124
- package/src/renderer/components/team/TeamSessionsSection.tsx +6 -6
- package/src/renderer/components/team/UnreadCommentsBadge.tsx +1 -1
- package/src/renderer/components/team/activity/ActivityItem.tsx +9 -9
- package/src/renderer/components/team/activity/ActivityTimeline.tsx +5 -5
- package/src/renderer/components/team/activity/LeadThoughtsGroup.tsx +3 -3
- package/src/renderer/components/team/activity/ReplyQuoteBlock.tsx +4 -4
- package/src/renderer/components/team/dialogs/CreateTaskDialog.tsx +4 -4
- 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 +18 -16
- package/src/renderer/components/team/dialogs/PlatformBindingDialog.tsx +221 -0
- package/src/renderer/components/team/dialogs/PlatformManualForm.tsx +8 -1
- package/src/renderer/components/team/dialogs/PlatformSetupQR.tsx +5 -5
- package/src/renderer/components/team/dialogs/RuntimeConfigDialog.tsx +361 -0
- package/src/renderer/components/team/dialogs/SendMessageDialog.tsx +6 -6
- package/src/renderer/components/team/dialogs/SkipPermissionsCheckbox.tsx +6 -6
- package/src/renderer/components/team/dialogs/StatusHistoryTimeline.tsx +1 -1
- package/src/renderer/components/team/dialogs/TaskAttachments.tsx +1 -1
- package/src/renderer/components/team/dialogs/TaskCommentInput.tsx +6 -6
- package/src/renderer/components/team/dialogs/TaskCommentsSection.tsx +4 -4
- package/src/renderer/components/team/dialogs/TaskDetailDialog.tsx +3 -3
- 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/editor/EditorFileTree.tsx +4 -4
- package/src/renderer/components/team/editor/EditorSearchPanel.tsx +1 -1
- package/src/renderer/components/team/editor/MarkdownSplitView.tsx +1 -1
- package/src/renderer/components/team/editor/NewFileDialog.tsx +1 -1
- package/src/renderer/components/team/editor/ProjectEditorOverlay.tsx +1 -1
- package/src/renderer/components/team/editor/SearchInFilesPanel.tsx +1 -1
- package/src/renderer/components/team/kanban/KanbanBoard.tsx +9 -9
- package/src/renderer/components/team/kanban/KanbanFilterPopover.tsx +4 -4
- package/src/renderer/components/team/kanban/KanbanSearchInput.tsx +1 -1
- package/src/renderer/components/team/kanban/KanbanSortPopover.tsx +5 -5
- package/src/renderer/components/team/kanban/KanbanTaskCard.tsx +4 -4
- 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/members/MemberDraftRow.tsx +1 -1
- package/src/renderer/components/team/members/MemberStatsTab.tsx +2 -2
- package/src/renderer/components/team/members/MemberWorkspaceTab.tsx +1 -1
- package/src/renderer/components/team/messages/MessageComposer.tsx +10 -112
- package/src/renderer/components/team/messages/MessagesFilterPopover.tsx +1 -1
- package/src/renderer/components/team/messages/MessagesPanel.tsx +136 -119
- package/src/renderer/components/team/review/ChangeReviewDialog.tsx +1 -1
- package/src/renderer/components/team/schedule/ScheduleStatusBadge.tsx +3 -3
- package/src/renderer/components/team/sidebar/TeamSidebarRail.tsx +4 -4
- package/src/renderer/components/team/tasks/TaskRow.tsx +1 -1
- package/src/renderer/components/team/tools/AddMcpInline.tsx +27 -17
- package/src/renderer/components/team/tools/McpChip.tsx +6 -3
- package/src/renderer/components/team/tools/SkillChip.tsx +3 -3
- package/src/renderer/components/team/tools/ToolsSection.tsx +418 -70
- package/src/renderer/components/ui/MemberSelect.tsx +2 -2
- package/src/renderer/components/ui/MentionSuggestionList.tsx +2 -2
- package/src/renderer/components/ui/MentionableTextarea.tsx +3 -3
- package/src/renderer/hooks/useExtensionsTabState.ts +3 -114
- package/src/renderer/index.css +56 -39
- 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/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 +15 -8
- package/src/shared/utils/providerExtensionCapabilities.ts +2 -2
- package/dist-renderer/assets/ProjectEditorOverlay-lJZi-9Hp.js +0 -52
- package/dist-renderer/assets/channel-yIlSKy0e.js +0 -1
- package/dist-renderer/assets/classDiagram-2ON5EDUG-24fHez0s.js +0 -1
- package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-24fHez0s.js +0 -1
- package/dist-renderer/assets/clone-BTNuUva-.js +0 -1
- package/dist-renderer/assets/index-Bi6nrZ4z.css +0 -1
- package/dist-renderer/assets/splashScene-C8lWNnm4.js +0 -1
- package/dist-renderer/assets/stateDiagram-v2-4FDKWEC3-_m6iPPUR.js +0 -1
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { spawn as spawnChild } from 'node:child_process';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { EventEmitter } from 'node:events';
|
|
4
|
+
import { stat } from 'node:fs/promises';
|
|
5
|
+
|
|
6
|
+
import type { PtySpawnOptions } from '@shared/types/terminal';
|
|
7
|
+
import * as pty from 'node-pty';
|
|
8
|
+
|
|
9
|
+
const PYTHON_PTY_BRIDGE = String.raw`
|
|
10
|
+
import errno
|
|
11
|
+
import os
|
|
12
|
+
import pty
|
|
13
|
+
import select
|
|
14
|
+
import signal
|
|
15
|
+
import sys
|
|
16
|
+
|
|
17
|
+
cmd = sys.argv[1:]
|
|
18
|
+
child_pid = None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def terminate(signum, _frame):
|
|
22
|
+
if child_pid:
|
|
23
|
+
try:
|
|
24
|
+
os.kill(child_pid, signum)
|
|
25
|
+
except OSError:
|
|
26
|
+
pass
|
|
27
|
+
sys.exit(128 + signum)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
signal.signal(signal.SIGTERM, terminate)
|
|
31
|
+
signal.signal(signal.SIGINT, terminate)
|
|
32
|
+
|
|
33
|
+
child_pid, fd = pty.fork()
|
|
34
|
+
if child_pid == 0:
|
|
35
|
+
os.execvpe(cmd[0], cmd, os.environ)
|
|
36
|
+
|
|
37
|
+
while True:
|
|
38
|
+
try:
|
|
39
|
+
readable, _, _ = select.select([fd, sys.stdin.fileno()], [], [])
|
|
40
|
+
except OSError:
|
|
41
|
+
break
|
|
42
|
+
|
|
43
|
+
if fd in readable:
|
|
44
|
+
try:
|
|
45
|
+
data = os.read(fd, 4096)
|
|
46
|
+
except OSError:
|
|
47
|
+
break
|
|
48
|
+
if not data:
|
|
49
|
+
break
|
|
50
|
+
os.write(sys.stdout.fileno(), data)
|
|
51
|
+
|
|
52
|
+
if sys.stdin.fileno() in readable:
|
|
53
|
+
try:
|
|
54
|
+
data = os.read(sys.stdin.fileno(), 4096)
|
|
55
|
+
except OSError as err:
|
|
56
|
+
if err.errno == errno.EIO:
|
|
57
|
+
data = b''
|
|
58
|
+
else:
|
|
59
|
+
raise
|
|
60
|
+
if data:
|
|
61
|
+
os.write(fd, data)
|
|
62
|
+
|
|
63
|
+
try:
|
|
64
|
+
_, status = os.waitpid(child_pid, 0)
|
|
65
|
+
except ChildProcessError:
|
|
66
|
+
sys.exit(0)
|
|
67
|
+
sys.exit(os.waitstatus_to_exitcode(status))
|
|
68
|
+
`;
|
|
69
|
+
|
|
70
|
+
interface ManagedProcess {
|
|
71
|
+
id: string;
|
|
72
|
+
pid: number;
|
|
73
|
+
command: string;
|
|
74
|
+
args: string[];
|
|
75
|
+
cwd: string;
|
|
76
|
+
createdAt: string;
|
|
77
|
+
write(data: string): void;
|
|
78
|
+
resize(cols: number, rows: number): void;
|
|
79
|
+
kill(signal?: NodeJS.Signals): void;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const TERMINAL_KILL_TIMEOUT_MS = 1_500;
|
|
83
|
+
const TERMINAL_FORCE_KILL_TIMEOUT_MS = 1_500;
|
|
84
|
+
|
|
85
|
+
export type TerminalDataEvent = { ptyId: string; data: string };
|
|
86
|
+
export type TerminalExitEvent = { ptyId: string; exitCode: number };
|
|
87
|
+
|
|
88
|
+
export class SystemManagerPtyService extends EventEmitter {
|
|
89
|
+
private readonly sessions = new Map<string, ManagedProcess>();
|
|
90
|
+
|
|
91
|
+
async spawn(options: PtySpawnOptions = {}): Promise<string> {
|
|
92
|
+
const command = options.command || 'claude';
|
|
93
|
+
const args = options.args ?? [];
|
|
94
|
+
const cwd = options.cwd || process.cwd();
|
|
95
|
+
const cwdStat = await stat(cwd);
|
|
96
|
+
if (!cwdStat.isDirectory()) {
|
|
97
|
+
throw new Error(`cwd 不是有效目录: ${cwd}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const id = `pty-${randomUUID()}`;
|
|
101
|
+
const env = {
|
|
102
|
+
...process.env,
|
|
103
|
+
TERM: 'xterm-256color',
|
|
104
|
+
COLORTERM: 'truecolor',
|
|
105
|
+
...(options.env ?? {}),
|
|
106
|
+
} as Record<string, string>;
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
const proc = pty.spawn(command, args, {
|
|
110
|
+
name: 'xterm-256color',
|
|
111
|
+
cols: options.cols ?? 120,
|
|
112
|
+
rows: options.rows ?? 34,
|
|
113
|
+
cwd,
|
|
114
|
+
env,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
this.sessions.set(id, {
|
|
118
|
+
id,
|
|
119
|
+
pid: proc.pid,
|
|
120
|
+
command,
|
|
121
|
+
args,
|
|
122
|
+
cwd,
|
|
123
|
+
createdAt: new Date().toISOString(),
|
|
124
|
+
write: (data) => proc.write(data),
|
|
125
|
+
resize: (cols, rows) => proc.resize(Math.max(20, cols), Math.max(5, rows)),
|
|
126
|
+
kill: (signal) => proc.kill(signal),
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
proc.onData((data) => {
|
|
130
|
+
this.emit('data', { ptyId: id, data } satisfies TerminalDataEvent);
|
|
131
|
+
});
|
|
132
|
+
proc.onExit(({ exitCode }) => {
|
|
133
|
+
if (!this.sessions.delete(id)) return;
|
|
134
|
+
this.emit('exit', { ptyId: id, exitCode } satisfies TerminalExitEvent);
|
|
135
|
+
});
|
|
136
|
+
} catch (err) {
|
|
137
|
+
const child = spawnChild('python3', ['-u', '-c', PYTHON_PTY_BRIDGE, command, ...args], {
|
|
138
|
+
cwd,
|
|
139
|
+
env,
|
|
140
|
+
stdio: 'pipe',
|
|
141
|
+
});
|
|
142
|
+
this.sessions.set(id, {
|
|
143
|
+
id,
|
|
144
|
+
pid: child.pid ?? -1,
|
|
145
|
+
command,
|
|
146
|
+
args,
|
|
147
|
+
cwd,
|
|
148
|
+
createdAt: new Date().toISOString(),
|
|
149
|
+
write: (data) => child.stdin.write(data),
|
|
150
|
+
resize: () => {},
|
|
151
|
+
kill: (signal) => child.kill(signal),
|
|
152
|
+
});
|
|
153
|
+
this.emit('data', {
|
|
154
|
+
ptyId: id,
|
|
155
|
+
data: `[33m[Hermit] node-pty unavailable (${err instanceof Error ? err.message : String(err)}); using python PTY fallback.[0m\r\n`,
|
|
156
|
+
} satisfies TerminalDataEvent);
|
|
157
|
+
child.stdout.on('data', (data) => {
|
|
158
|
+
this.emit('data', { ptyId: id, data: data.toString() } satisfies TerminalDataEvent);
|
|
159
|
+
});
|
|
160
|
+
child.stderr.on('data', (data) => {
|
|
161
|
+
this.emit('data', { ptyId: id, data: data.toString() } satisfies TerminalDataEvent);
|
|
162
|
+
});
|
|
163
|
+
child.on('error', (error) => {
|
|
164
|
+
if (!this.sessions.delete(id)) return;
|
|
165
|
+
this.emit('data', {
|
|
166
|
+
ptyId: id,
|
|
167
|
+
data: `[31m[Hermit] failed to start process: ${error.message}[0m\r\n`,
|
|
168
|
+
} satisfies TerminalDataEvent);
|
|
169
|
+
this.emit('exit', { ptyId: id, exitCode: 1 } satisfies TerminalExitEvent);
|
|
170
|
+
});
|
|
171
|
+
child.on('exit', (exitCode) => {
|
|
172
|
+
if (!this.sessions.delete(id)) return;
|
|
173
|
+
this.emit('exit', { ptyId: id, exitCode: exitCode ?? 0 } satisfies TerminalExitEvent);
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return id;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
write(ptyId: string, data: string): void {
|
|
181
|
+
const session = this.sessions.get(ptyId);
|
|
182
|
+
if (!session) throw new Error(`PTY 不存在: ${ptyId}`);
|
|
183
|
+
session.write(data);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
resize(ptyId: string, cols: number, rows: number): void {
|
|
187
|
+
const session = this.sessions.get(ptyId);
|
|
188
|
+
if (!session) return;
|
|
189
|
+
session.resize(cols, rows);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async kill(ptyId: string): Promise<void> {
|
|
193
|
+
const session = this.sessions.get(ptyId);
|
|
194
|
+
if (!session) return;
|
|
195
|
+
|
|
196
|
+
await new Promise<void>((resolve) => {
|
|
197
|
+
let settled = false;
|
|
198
|
+
let forceTimer: ReturnType<typeof setTimeout> | null = null;
|
|
199
|
+
const finish = (): void => {
|
|
200
|
+
if (settled) return;
|
|
201
|
+
settled = true;
|
|
202
|
+
if (forceTimer) clearTimeout(forceTimer);
|
|
203
|
+
this.off('exit', onExit);
|
|
204
|
+
resolve();
|
|
205
|
+
};
|
|
206
|
+
const onExit = (event: TerminalExitEvent): void => {
|
|
207
|
+
if (event.ptyId === ptyId) finish();
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
this.on('exit', onExit);
|
|
211
|
+
session.kill();
|
|
212
|
+
forceTimer = setTimeout(() => {
|
|
213
|
+
if (!this.sessions.has(ptyId)) {
|
|
214
|
+
finish();
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
session.kill('SIGKILL');
|
|
218
|
+
}, TERMINAL_KILL_TIMEOUT_MS);
|
|
219
|
+
setTimeout(() => {
|
|
220
|
+
if (this.sessions.delete(ptyId)) {
|
|
221
|
+
this.emit('exit', { ptyId, exitCode: 0 } satisfies TerminalExitEvent);
|
|
222
|
+
}
|
|
223
|
+
finish();
|
|
224
|
+
}, TERMINAL_KILL_TIMEOUT_MS + TERMINAL_FORCE_KILL_TIMEOUT_MS);
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
killAll(): void {
|
|
229
|
+
for (const id of [...this.sessions.keys()]) {
|
|
230
|
+
void this.kill(id);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { readFile, readdir, stat } from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
WorkflowPromptContentResponse,
|
|
8
|
+
WorkflowPromptListResponse,
|
|
9
|
+
WorkflowPromptSummary,
|
|
10
|
+
} from '@shared/types/systemManager';
|
|
11
|
+
|
|
12
|
+
const SUPPORTED_EXTENSIONS = new Set(['.md', '.txt', '.prompt', '.workflow']);
|
|
13
|
+
const MAX_PROMPT_BYTES = 256 * 1024;
|
|
14
|
+
|
|
15
|
+
function expandHome(input: string): string {
|
|
16
|
+
const normalized = input.trim().replace(/^~/, '~');
|
|
17
|
+
if (normalized === '~') return os.homedir();
|
|
18
|
+
if (normalized.startsWith('~/')) return path.join(os.homedir(), normalized.slice(2));
|
|
19
|
+
return normalized;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function promptId(filePath: string): string {
|
|
23
|
+
return createHash('sha256').update(filePath).digest('hex').slice(0, 16);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function labelFromFilename(filename: string): string {
|
|
27
|
+
return path.basename(filename, path.extname(filename)).replace(/[-_]+/g, ' ').trim() || filename;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class WorkflowPromptService {
|
|
31
|
+
async list(folderInput: string): Promise<WorkflowPromptListResponse> {
|
|
32
|
+
const folder = path.resolve(expandHome(folderInput));
|
|
33
|
+
const folderStat = await stat(folder);
|
|
34
|
+
if (!folderStat.isDirectory()) {
|
|
35
|
+
throw new Error(`workflow folder 不是有效目录: ${folder}`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const warnings: string[] = [];
|
|
39
|
+
const prompts: WorkflowPromptSummary[] = [];
|
|
40
|
+
const entries = await readdir(folder, { withFileTypes: true });
|
|
41
|
+
|
|
42
|
+
for (const entry of entries) {
|
|
43
|
+
if (!entry.isFile() || entry.name.startsWith('.')) continue;
|
|
44
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
45
|
+
if (!SUPPORTED_EXTENSIONS.has(ext)) continue;
|
|
46
|
+
const filePath = path.join(folder, entry.name);
|
|
47
|
+
const fileStat = await stat(filePath);
|
|
48
|
+
if (fileStat.size > MAX_PROMPT_BYTES) {
|
|
49
|
+
warnings.push(`${entry.name} 超过 256 KiB,已跳过`);
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
prompts.push({
|
|
53
|
+
id: promptId(filePath),
|
|
54
|
+
label: labelFromFilename(entry.name),
|
|
55
|
+
filename: entry.name,
|
|
56
|
+
path: filePath,
|
|
57
|
+
sizeBytes: fileStat.size,
|
|
58
|
+
updatedAt: fileStat.mtime.toISOString(),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
prompts.sort((a, b) => a.filename.localeCompare(b.filename));
|
|
63
|
+
return { folder, prompts, warnings };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async read(folderInput: string, id: string): Promise<WorkflowPromptContentResponse> {
|
|
67
|
+
const list = await this.list(folderInput);
|
|
68
|
+
const prompt = list.prompts.find((item) => item.id === id || item.filename === id);
|
|
69
|
+
if (!prompt) {
|
|
70
|
+
throw new Error(`未找到 workflow: ${id}`);
|
|
71
|
+
}
|
|
72
|
+
const content = await readFile(prompt.path, 'utf-8');
|
|
73
|
+
return { prompt, content };
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -776,11 +776,11 @@ export class TaskDispatchService {
|
|
|
776
776
|
redis = new ioredis.default(opts);
|
|
777
777
|
redisSub = new ioredis.default(opts);
|
|
778
778
|
|
|
779
|
-
redis.on('error', (
|
|
780
|
-
|
|
779
|
+
redis.on('error', () => {
|
|
780
|
+
/* handled by task bus connection status */
|
|
781
781
|
});
|
|
782
|
-
redisSub.on('error', (
|
|
783
|
-
|
|
782
|
+
redisSub.on('error', () => {
|
|
783
|
+
/* handled by task bus connection status */
|
|
784
784
|
});
|
|
785
785
|
|
|
786
786
|
await redis.connect();
|
|
@@ -796,8 +796,7 @@ export class TaskDispatchService {
|
|
|
796
796
|
this.startConsumers();
|
|
797
797
|
this.startResponseConsumers();
|
|
798
798
|
this.subscribeStatus();
|
|
799
|
-
} catch
|
|
800
|
-
console.warn('[TaskDispatchService] Redis connect failed:', err);
|
|
799
|
+
} catch {
|
|
801
800
|
this.collabBoard.setRedis(null);
|
|
802
801
|
redis?.disconnect();
|
|
803
802
|
redisSub?.disconnect();
|
|
@@ -45,6 +45,35 @@ function removeManagedTeamInstructions(content: string): string {
|
|
|
45
45
|
return next.replace(/\n{3,}/g, '\n\n').trimEnd();
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
async function injectHermitTasksMcpConfig(workDir: string): Promise<void> {
|
|
49
|
+
const settingsPath = path.join(workDir, '.claude', 'settings.json');
|
|
50
|
+
let settings: Record<string, unknown> = {};
|
|
51
|
+
try {
|
|
52
|
+
const raw = await fs.promises.readFile(settingsPath, 'utf8');
|
|
53
|
+
settings = JSON.parse(raw) as Record<string, unknown>;
|
|
54
|
+
} catch (err) {
|
|
55
|
+
if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {
|
|
56
|
+
throw err;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const existingMcpServers =
|
|
61
|
+
settings.mcpServers && typeof settings.mcpServers === 'object'
|
|
62
|
+
? (settings.mcpServers as Record<string, unknown>)
|
|
63
|
+
: {};
|
|
64
|
+
const port = process.env.PORT ?? '5680';
|
|
65
|
+
settings.mcpServers = {
|
|
66
|
+
...existingMcpServers,
|
|
67
|
+
'hermit-tasks': {
|
|
68
|
+
type: 'sse',
|
|
69
|
+
url: `http://127.0.0.1:${port}/mcp`,
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
await fs.promises.mkdir(path.dirname(settingsPath), { recursive: true });
|
|
74
|
+
await fs.promises.writeFile(settingsPath, JSON.stringify(settings, null, 2));
|
|
75
|
+
}
|
|
76
|
+
|
|
48
77
|
export class TeamProvisioningService {
|
|
49
78
|
private readonly workspace: TeamWorkspaceService;
|
|
50
79
|
|
|
@@ -68,12 +97,16 @@ export class TeamProvisioningService {
|
|
|
68
97
|
* 4. 触发 cc-connect restart 激活 project
|
|
69
98
|
*/
|
|
70
99
|
async createTeam(
|
|
71
|
-
input: CreateTeamInput & { createCcProject?: boolean }
|
|
100
|
+
input: CreateTeamInput & { createCcProject?: boolean; injectInstructions?: boolean }
|
|
72
101
|
): Promise<{ slug: string; manifest: TeamManifest }> {
|
|
73
|
-
const { createCcProject = true, ...workspaceInput } = input;
|
|
102
|
+
const { createCcProject = true, injectInstructions = true, ...workspaceInput } = input;
|
|
74
103
|
|
|
75
104
|
const { slug, manifest } = await this.workspace.createTeam(workspaceInput);
|
|
76
105
|
|
|
106
|
+
if (injectInstructions && manifest.harness === 'claudecode') {
|
|
107
|
+
await injectHermitTasksMcpConfig(manifest.workDir);
|
|
108
|
+
}
|
|
109
|
+
|
|
77
110
|
if (createCcProject) {
|
|
78
111
|
try {
|
|
79
112
|
const platformType = manifest.platform ?? 'bridge';
|
|
@@ -118,6 +151,7 @@ export class TeamProvisioningService {
|
|
|
118
151
|
Pick<
|
|
119
152
|
TeamManifest,
|
|
120
153
|
| 'displayName'
|
|
154
|
+
| 'bindProject'
|
|
121
155
|
| 'color'
|
|
122
156
|
| 'description'
|
|
123
157
|
| 'collaboration'
|
|
@@ -130,7 +164,10 @@ export class TeamProvisioningService {
|
|
|
130
164
|
| 'injectSender'
|
|
131
165
|
| 'managedSources'
|
|
132
166
|
| 'disabledCommands'
|
|
167
|
+
| 'platform'
|
|
168
|
+
| 'platformOptions'
|
|
133
169
|
| 'platformAllowFrom'
|
|
170
|
+
| 'platformAllowChat'
|
|
134
171
|
>
|
|
135
172
|
>
|
|
136
173
|
): Promise<TeamManifest> {
|
|
@@ -45,6 +45,8 @@ export interface TeamManifest {
|
|
|
45
45
|
managedSources?: string;
|
|
46
46
|
disabledCommands?: string[];
|
|
47
47
|
platformAllowFrom?: Record<string, string>;
|
|
48
|
+
/** 群聊允许的 chat ID(按平台) */
|
|
49
|
+
platformAllowChat?: Record<string, string>;
|
|
48
50
|
pendingDelete?: boolean;
|
|
49
51
|
restartRequired?: boolean;
|
|
50
52
|
/**
|
|
@@ -159,9 +161,9 @@ async function readJson<T>(p: string, fallback: T): Promise<T> {
|
|
|
159
161
|
try {
|
|
160
162
|
const raw = await fs.promises.readFile(p, 'utf8');
|
|
161
163
|
return JSON.parse(raw) as T;
|
|
162
|
-
} catch
|
|
163
|
-
|
|
164
|
-
|
|
164
|
+
} catch {
|
|
165
|
+
// File missing or corrupted JSON — return fallback silently
|
|
166
|
+
return fallback;
|
|
165
167
|
}
|
|
166
168
|
}
|
|
167
169
|
|
|
@@ -185,6 +187,17 @@ export class TeamWorkspaceService {
|
|
|
185
187
|
return match?.slug ?? teamSlug;
|
|
186
188
|
}
|
|
187
189
|
|
|
190
|
+
private async createUniqueStorageSlug(displayName: string): Promise<string> {
|
|
191
|
+
const baseSlug = toSlug(displayName);
|
|
192
|
+
let slug = baseSlug;
|
|
193
|
+
let suffix = 2;
|
|
194
|
+
while (await pathExists(path.join(teamRoot(slug), 'team.json'))) {
|
|
195
|
+
slug = `${baseSlug}-${suffix}`;
|
|
196
|
+
suffix += 1;
|
|
197
|
+
}
|
|
198
|
+
return slug;
|
|
199
|
+
}
|
|
200
|
+
|
|
188
201
|
async createTeam(
|
|
189
202
|
input: CreateTeamInput
|
|
190
203
|
): Promise<{ slug: string; root: string; manifest: TeamManifest }> {
|
|
@@ -192,7 +205,7 @@ export class TeamWorkspaceService {
|
|
|
192
205
|
if (!input.bindProject) throw new Error('bindProject is required');
|
|
193
206
|
if (!input.workDir) throw new Error('workDir is required');
|
|
194
207
|
|
|
195
|
-
const slug =
|
|
208
|
+
const slug = await this.createUniqueStorageSlug(input.displayName);
|
|
196
209
|
const root = teamRoot(slug);
|
|
197
210
|
|
|
198
211
|
await fs.promises.mkdir(root, { recursive: true });
|
|
@@ -263,6 +276,7 @@ export class TeamWorkspaceService {
|
|
|
263
276
|
const out: TeamManifest[] = [];
|
|
264
277
|
for (const e of entries) {
|
|
265
278
|
if (!e.isDirectory()) continue;
|
|
279
|
+
if (e.name.startsWith('.')) continue;
|
|
266
280
|
try {
|
|
267
281
|
out.push(await this.readTeamManifest(e.name));
|
|
268
282
|
} catch {
|
|
@@ -278,6 +292,7 @@ export class TeamWorkspaceService {
|
|
|
278
292
|
Pick<
|
|
279
293
|
TeamManifest,
|
|
280
294
|
| 'displayName'
|
|
295
|
+
| 'bindProject'
|
|
281
296
|
| 'color'
|
|
282
297
|
| 'description'
|
|
283
298
|
| 'collaboration'
|
|
@@ -290,7 +305,10 @@ export class TeamWorkspaceService {
|
|
|
290
305
|
| 'injectSender'
|
|
291
306
|
| 'managedSources'
|
|
292
307
|
| 'disabledCommands'
|
|
308
|
+
| 'platform'
|
|
309
|
+
| 'platformOptions'
|
|
293
310
|
| 'platformAllowFrom'
|
|
311
|
+
| 'platformAllowChat'
|
|
294
312
|
| 'pendingDelete'
|
|
295
313
|
| 'restartRequired'
|
|
296
314
|
>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { TeamManifest } from '@main/services/teams-mvp/TeamWorkspaceService';
|
|
2
|
+
|
|
3
|
+
export async function resolveCcProjectName(
|
|
4
|
+
routeTeamName: string,
|
|
5
|
+
readTeamManifest: (teamName: string) => Promise<TeamManifest>
|
|
6
|
+
): Promise<string> {
|
|
7
|
+
try {
|
|
8
|
+
const manifest = await readTeamManifest(routeTeamName);
|
|
9
|
+
const bindProject = manifest.bindProject?.trim();
|
|
10
|
+
if (bindProject) return bindProject;
|
|
11
|
+
} catch {
|
|
12
|
+
// routeTeamName may already be a cc-connect project name.
|
|
13
|
+
}
|
|
14
|
+
return routeTeamName;
|
|
15
|
+
}
|
package/src/renderer/App.tsx
CHANGED
|
@@ -41,6 +41,7 @@ function buildPathForTab(activeTab: Tab | null): string {
|
|
|
41
41
|
}
|
|
42
42
|
switch (activeTab.type) {
|
|
43
43
|
case 'team':
|
|
44
|
+
if (activeTab.teamName === 'system-manager') return '/system-manager';
|
|
44
45
|
return activeTab.teamName
|
|
45
46
|
? `/team/${encodeURIComponent(activeTab.teamName)}`
|
|
46
47
|
: DEFAULT_APP_PATH;
|
|
@@ -142,6 +143,9 @@ function useTabPathPersistence() {
|
|
|
142
143
|
case 'teams':
|
|
143
144
|
state.openTeamsTab();
|
|
144
145
|
break;
|
|
146
|
+
case 'system-manager':
|
|
147
|
+
void state.openSystemManager();
|
|
148
|
+
break;
|
|
145
149
|
case 'settings':
|
|
146
150
|
state.openSettingsTab();
|
|
147
151
|
break;
|
|
@@ -208,11 +212,11 @@ declare global {
|
|
|
208
212
|
}
|
|
209
213
|
}
|
|
210
214
|
|
|
211
|
-
const SPLASH_MIN_DURATION_MS =
|
|
212
|
-
const SPLASH_ENHANCED_HOLD_MS =
|
|
215
|
+
const SPLASH_MIN_DURATION_MS = 2800;
|
|
216
|
+
const SPLASH_ENHANCED_HOLD_MS = 800;
|
|
213
217
|
const SPLASH_FADE_MS = 480;
|
|
214
|
-
const SPLASH_REDUCED_MIN_DURATION_MS =
|
|
215
|
-
const SPLASH_REDUCED_HOLD_MS =
|
|
218
|
+
const SPLASH_REDUCED_MIN_DURATION_MS = 600;
|
|
219
|
+
const SPLASH_REDUCED_HOLD_MS = 200;
|
|
216
220
|
const SPLASH_REDUCED_FADE_MS = 180;
|
|
217
221
|
const SPLASH_AVATAR_READY_MAX_WAIT_MS = 900;
|
|
218
222
|
const SPLASH_REDUCED_AVATAR_READY_MAX_WAIT_MS = 160;
|
|
@@ -82,6 +82,7 @@ import type {
|
|
|
82
82
|
TeamLaunchRequest,
|
|
83
83
|
TeamLaunchResponse,
|
|
84
84
|
TeamMemberActivityMeta,
|
|
85
|
+
SystemManagerSummary,
|
|
85
86
|
TeamProvisioningModelVerificationMode,
|
|
86
87
|
TeamProvisioningPrepareResult,
|
|
87
88
|
TeamProvisioningProgress,
|
|
@@ -141,6 +142,7 @@ import type {
|
|
|
141
142
|
TaskChangeSetV2,
|
|
142
143
|
} from '@shared/types/review';
|
|
143
144
|
import type { ApplyReviewRequest } from '@shared/types/review';
|
|
145
|
+
import type { SystemManagerAPI } from '@shared/types/systemManager';
|
|
144
146
|
import type { TerminalAPI } from '@shared/types/terminal';
|
|
145
147
|
import type { CliArgsValidationResult } from '@shared/utils/cliArgsParser';
|
|
146
148
|
|
|
@@ -171,7 +173,6 @@ export class HttpAPIClient implements ElectronAPI {
|
|
|
171
173
|
};
|
|
172
174
|
}
|
|
173
175
|
|
|
174
|
-
|
|
175
176
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- event callbacks have varying signatures
|
|
176
177
|
private addEventListener(channel: string, callback: (...args: any[]) => void): () => void {
|
|
177
178
|
this.initEventSource();
|
|
@@ -409,7 +410,7 @@ export class HttpAPIClient implements ElectronAPI {
|
|
|
409
410
|
await this.patch('/api/cc-settings', patch);
|
|
410
411
|
},
|
|
411
412
|
restart: async (): Promise<void> => {
|
|
412
|
-
await this.
|
|
413
|
+
await this.postLong('/api/cc-restart', {});
|
|
413
414
|
},
|
|
414
415
|
reload: async (): Promise<void> => {
|
|
415
416
|
await this.post('/api/cc-reload', {});
|
|
@@ -1093,6 +1094,8 @@ export class HttpAPIClient implements ElectronAPI {
|
|
|
1093
1094
|
|
|
1094
1095
|
teams: TeamsAPI = {
|
|
1095
1096
|
list: async (): Promise<TeamSummary[]> => this.get<TeamSummary[]>('/api/teams'),
|
|
1097
|
+
ensureSystemManager: async (): Promise<SystemManagerSummary> =>
|
|
1098
|
+
this.post<SystemManagerSummary>('/api/system-manager/ensure'),
|
|
1096
1099
|
getData: async (teamName: string): Promise<TeamViewSnapshot> =>
|
|
1097
1100
|
this.get<TeamViewSnapshot>(`/api/teams/${encodeURIComponent(teamName)}/data`),
|
|
1098
1101
|
getTaskChangePresence: async (): Promise<
|
|
@@ -1450,14 +1453,27 @@ export class HttpAPIClient implements ElectronAPI {
|
|
|
1450
1453
|
return this.get(`/api/teams/${encodeURIComponent(teamName)}/lead-context`);
|
|
1451
1454
|
},
|
|
1452
1455
|
getMemberSpawnStatuses: async (teamName: string) => {
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
+
try {
|
|
1457
|
+
return await this.get<MemberSpawnStatusesSnapshot>(
|
|
1458
|
+
`/api/teams/${encodeURIComponent(teamName)}/member-spawn-statuses`
|
|
1459
|
+
);
|
|
1460
|
+
} catch {
|
|
1461
|
+
return { statuses: {}, runId: null };
|
|
1462
|
+
}
|
|
1456
1463
|
},
|
|
1457
1464
|
getTeamAgentRuntime: async (teamName: string) => {
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1465
|
+
try {
|
|
1466
|
+
return await this.get<TeamAgentRuntimeSnapshot>(
|
|
1467
|
+
`/api/teams/${encodeURIComponent(teamName)}/agent-runtime`
|
|
1468
|
+
);
|
|
1469
|
+
} catch {
|
|
1470
|
+
return {
|
|
1471
|
+
teamName,
|
|
1472
|
+
updatedAt: new Date().toISOString(),
|
|
1473
|
+
runId: null,
|
|
1474
|
+
members: {},
|
|
1475
|
+
};
|
|
1476
|
+
}
|
|
1461
1477
|
},
|
|
1462
1478
|
restartMember: async (teamName: string, memberName: string): Promise<void> => {
|
|
1463
1479
|
await this.post(
|
|
@@ -1886,7 +1902,7 @@ export class HttpAPIClient implements ElectronAPI {
|
|
|
1886
1902
|
p != null &&
|
|
1887
1903
|
typeof p === 'object' &&
|
|
1888
1904
|
!Array.isArray(p) &&
|
|
1889
|
-
typeof
|
|
1905
|
+
typeof p.providerId === 'string'
|
|
1890
1906
|
);
|
|
1891
1907
|
|
|
1892
1908
|
// When the backend reports no provider capability data, fall back to a
|
|
@@ -2281,18 +2297,52 @@ export class HttpAPIClient implements ElectronAPI {
|
|
|
2281
2297
|
};
|
|
2282
2298
|
|
|
2283
2299
|
// ---------------------------------------------------------------------------
|
|
2284
|
-
//
|
|
2300
|
+
// System Manager / Control Console
|
|
2301
|
+
// ---------------------------------------------------------------------------
|
|
2302
|
+
|
|
2303
|
+
systemManager: SystemManagerAPI = {
|
|
2304
|
+
getStatus: () => this.get('/api/system-manager/status'),
|
|
2305
|
+
getConfig: () => this.get('/api/system-manager/config'),
|
|
2306
|
+
updateConfig: (patch) => this.put('/api/system-manager/config', patch),
|
|
2307
|
+
listWorkflowPrompts: (folder) => this.post('/api/system-manager/workflows/list', { folder }),
|
|
2308
|
+
readWorkflowPrompt: (_folder, id) => this.post('/api/system-manager/workflows/read', { id }),
|
|
2309
|
+
};
|
|
2310
|
+
|
|
2311
|
+
// ---------------------------------------------------------------------------
|
|
2312
|
+
// Terminal (HTTP/SSE-backed browser mode)
|
|
2285
2313
|
// ---------------------------------------------------------------------------
|
|
2286
2314
|
|
|
2287
2315
|
terminal: TerminalAPI = {
|
|
2288
|
-
spawn: async (): Promise<string> => {
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2316
|
+
spawn: async (options = {}): Promise<string> => {
|
|
2317
|
+
const result = await this.post<{ ptyId: string }>('/api/terminal/spawn', options);
|
|
2318
|
+
return result.ptyId;
|
|
2319
|
+
},
|
|
2320
|
+
write: (ptyId: string, data: string): void => {
|
|
2321
|
+
void this.post(`/api/terminal/${encodeURIComponent(ptyId)}/write`, { data });
|
|
2322
|
+
},
|
|
2323
|
+
resize: (ptyId: string, cols: number, rows: number): void => {
|
|
2324
|
+
void this.post(`/api/terminal/${encodeURIComponent(ptyId)}/resize`, { cols, rows });
|
|
2325
|
+
},
|
|
2326
|
+
kill: async (ptyId: string): Promise<void> => {
|
|
2327
|
+
await this.del(`/api/terminal/${encodeURIComponent(ptyId)}`);
|
|
2328
|
+
},
|
|
2329
|
+
openExternal: async (options: { command: string; args?: string[]; cwd?: string }): Promise<void> => {
|
|
2330
|
+
await this.post('/api/terminal/open-external', options);
|
|
2331
|
+
},
|
|
2332
|
+
onData: (callback): (() => void) =>
|
|
2333
|
+
this.addEventListener('terminal:data', (data: unknown) => {
|
|
2334
|
+
const event = data as { ptyId?: string; data?: string };
|
|
2335
|
+
if (event.ptyId && typeof event.data === 'string') {
|
|
2336
|
+
callback(null, event.ptyId, event.data);
|
|
2337
|
+
}
|
|
2338
|
+
}),
|
|
2339
|
+
onExit: (callback): (() => void) =>
|
|
2340
|
+
this.addEventListener('terminal:exit', (data: unknown) => {
|
|
2341
|
+
const event = data as { ptyId?: string; exitCode?: number };
|
|
2342
|
+
if (event.ptyId && typeof event.exitCode === 'number') {
|
|
2343
|
+
callback(null, event.ptyId, event.exitCode);
|
|
2344
|
+
}
|
|
2345
|
+
}),
|
|
2296
2346
|
};
|
|
2297
2347
|
|
|
2298
2348
|
// ---------------------------------------------------------------------------
|