@yancyyu/openhermit 1.6.38 → 1.6.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/dist-renderer/assets/ProjectEditorOverlay-krO5vQxX.js +58 -0
  2. package/dist-renderer/assets/{TeamGraphOverlay-ZEDfZyHb.js → TeamGraphOverlay-DqhQzcTr.js} +1 -1
  3. package/dist-renderer/assets/{_basePickBy-CIhniz70.js → _basePickBy-B7kSYPxr.js} +1 -1
  4. package/dist-renderer/assets/{_baseUniq-cKAW4Q8I.js → _baseUniq-CnjxqwAk.js} +1 -1
  5. package/dist-renderer/assets/{arc-YmNsoDXW.js → arc-CLeZuINP.js} +1 -1
  6. package/dist-renderer/assets/{architectureDiagram-VXUJARFQ-DHEls2sX.js → architectureDiagram-VXUJARFQ-QKtqaqdY.js} +1 -1
  7. package/dist-renderer/assets/{blockDiagram-VD42YOAC-Bpwf1Sbg.js → blockDiagram-VD42YOAC-BqdrzO_f.js} +1 -1
  8. package/dist-renderer/assets/{c4Diagram-YG6GDRKO-B0IaQ4w5.js → c4Diagram-YG6GDRKO-gwPlCxDC.js} +1 -1
  9. package/dist-renderer/assets/channel-DpMHF50r.js +1 -0
  10. package/dist-renderer/assets/{chunk-4BX2VUAB-DLk-hcFc.js → chunk-4BX2VUAB-C6XLurL4.js} +1 -1
  11. package/dist-renderer/assets/{chunk-55IACEB6-1XRmX_Zm.js → chunk-55IACEB6-Ds6quhEP.js} +1 -1
  12. package/dist-renderer/assets/{chunk-B4BG7PRW-1waH1DAD.js → chunk-B4BG7PRW-5UlA1_e9.js} +1 -1
  13. package/dist-renderer/assets/{chunk-DI55MBZ5-BqpZBtrN.js → chunk-DI55MBZ5-ywFrqIsY.js} +1 -1
  14. package/dist-renderer/assets/{chunk-FMBD7UC4-Bly7vVym.js → chunk-FMBD7UC4-C7ifUA17.js} +1 -1
  15. package/dist-renderer/assets/{chunk-QN33PNHL-Ci2QWBAs.js → chunk-QN33PNHL-BxGCo80U.js} +1 -1
  16. package/dist-renderer/assets/{chunk-QZHKN3VN-YCqFW7d-.js → chunk-QZHKN3VN-B2CuaZs6.js} +1 -1
  17. package/dist-renderer/assets/{chunk-TZMSLE5B-B0xGXInl.js → chunk-TZMSLE5B-Ds1hInvp.js} +1 -1
  18. package/dist-renderer/assets/classDiagram-2ON5EDUG-CBYCBVRl.js +1 -0
  19. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-CBYCBVRl.js +1 -0
  20. package/dist-renderer/assets/clone-DcMF6Psb.js +1 -0
  21. package/dist-renderer/assets/{cose-bilkent-S5V4N54A-DxcFNQKT.js → cose-bilkent-S5V4N54A-Cz1GVtLp.js} +1 -1
  22. package/dist-renderer/assets/{dagre-6UL2VRFP-DPo_RfZY.js → dagre-6UL2VRFP-BrmR-P4h.js} +1 -1
  23. package/dist-renderer/assets/{diagram-PSM6KHXK-U3hQsFe4.js → diagram-PSM6KHXK-DbNjC5Rg.js} +1 -1
  24. package/dist-renderer/assets/{diagram-QEK2KX5R-OrwrAy0V.js → diagram-QEK2KX5R-qkRX5_Mq.js} +1 -1
  25. package/dist-renderer/assets/{diagram-S2PKOQOG-CXATPWVw.js → diagram-S2PKOQOG-CyL5rCv2.js} +1 -1
  26. package/dist-renderer/assets/{erDiagram-Q2GNP2WA-B0e8AfMF.js → erDiagram-Q2GNP2WA-Dox3-bA5.js} +1 -1
  27. package/dist-renderer/assets/{flowDiagram-NV44I4VS-CXfzA4jJ.js → flowDiagram-NV44I4VS-BtkaxlDL.js} +1 -1
  28. package/dist-renderer/assets/{ganttDiagram-JELNMOA3-CMr08qVl.js → ganttDiagram-JELNMOA3-Dhy_d9GK.js} +1 -1
  29. package/dist-renderer/assets/{gitGraphDiagram-V2S2FVAM-vYFHpPmy.js → gitGraphDiagram-V2S2FVAM-B5XRhIQA.js} +1 -1
  30. package/dist-renderer/assets/{graph-DOe5j8dH.js → graph-CsoEwUhS.js} +1 -1
  31. package/dist-renderer/assets/{index-BySQS7AB.js → index-BWPWmJNo.js} +1 -1
  32. package/dist-renderer/assets/{index-V7dAKPqd.js → index-Bu2R-Se7.js} +587 -705
  33. package/dist-renderer/assets/index-CnWV3BhG.css +32 -0
  34. package/dist-renderer/assets/{index-CzWxVCRL.js → index-D-3KgskL.js} +1 -1
  35. package/dist-renderer/assets/{index-VJ-MM9xa.js → index-DGEBzLNT.js} +1 -1
  36. package/dist-renderer/assets/{index-B2Dy7M2G.js → index-NhHNs2Oo.js} +1 -1
  37. package/dist-renderer/assets/{index-C_okzZXP.js → index-h17WuEyf.js} +1 -1
  38. package/dist-renderer/assets/{infoDiagram-HS3SLOUP-D_WubR0B.js → infoDiagram-HS3SLOUP-hMGmNojH.js} +1 -1
  39. package/dist-renderer/assets/{journeyDiagram-XKPGCS4Q-w9ca-1TI.js → journeyDiagram-XKPGCS4Q-DXV2rBDl.js} +1 -1
  40. package/dist-renderer/assets/{kanban-definition-3W4ZIXB7-Jg9p6_pN.js → kanban-definition-3W4ZIXB7-Bf99WLRy.js} +1 -1
  41. package/dist-renderer/assets/{layout-B-z3y17c.js → layout-C3XWrpwo.js} +1 -1
  42. package/dist-renderer/assets/{linear-D-RTX5UW.js → linear-OEEcn8KN.js} +1 -1
  43. package/dist-renderer/assets/{mindmap-definition-VGOIOE7T-CDQmHOYP.js → mindmap-definition-VGOIOE7T-Dpi3S2x4.js} +1 -1
  44. package/dist-renderer/assets/{pieDiagram-ADFJNKIX-D_odsQL7.js → pieDiagram-ADFJNKIX-xTPPhtNx.js} +1 -1
  45. package/dist-renderer/assets/{quadrantDiagram-AYHSOK5B-BRsmYWSA.js → quadrantDiagram-AYHSOK5B-euniyDlz.js} +1 -1
  46. package/dist-renderer/assets/{requirementDiagram-UZGBJVZJ-ChNE_BOV.js → requirementDiagram-UZGBJVZJ-D9Uiw4kF.js} +1 -1
  47. package/dist-renderer/assets/{sankeyDiagram-TZEHDZUN-C8FtpwKc.js → sankeyDiagram-TZEHDZUN-CySU4nED.js} +1 -1
  48. package/dist-renderer/assets/{sequenceDiagram-WL72ISMW-DmLCzNcc.js → sequenceDiagram-WL72ISMW-JVGpET6V.js} +1 -1
  49. package/dist-renderer/assets/splashScene-D0YB9uxm.js +17 -0
  50. package/dist-renderer/assets/{stateDiagram-FKZM4ZOC-WJBm4bhu.js → stateDiagram-FKZM4ZOC-B2FY5qqi.js} +1 -1
  51. package/dist-renderer/assets/stateDiagram-v2-4FDKWEC3-DcoMiR8H.js +1 -0
  52. package/dist-renderer/assets/{timeline-definition-IT6M3QCI-BXs_hOJs.js → timeline-definition-IT6M3QCI-DmycNUUe.js} +1 -1
  53. package/dist-renderer/assets/{treemap-GDKQZRPO-o04MA0G9.js → treemap-GDKQZRPO-DPq4gZuB.js} +1 -1
  54. package/dist-renderer/assets/{xychartDiagram-PRI3JC2R-Czj69XRd.js → xychartDiagram-PRI3JC2R-J6VVJzRq.js} +1 -1
  55. package/dist-renderer/index.html +20 -53
  56. package/package.json +25 -18
  57. package/src/main/ipc/extensions.ts +2 -1
  58. package/src/main/server.ts +873 -221
  59. package/src/main/services/extensions/ExtensionFacadeService.ts +2 -5
  60. package/src/main/services/extensions/catalog/PluginCatalogService.ts +4 -2
  61. package/src/main/services/session-intelligence/ConversationTelemetryService.ts +1101 -0
  62. package/src/main/services/session-intelligence/LocalSessionScanner.ts +512 -0
  63. package/src/main/services/session-intelligence/SessionUsageParser.ts +4 -4
  64. package/src/main/services/system-manager/SystemManagerConfigService.ts +122 -0
  65. package/src/main/services/system-manager/SystemManagerPtyService.ts +233 -0
  66. package/src/main/services/system-manager/WorkflowPromptService.ts +75 -0
  67. package/src/main/services/teams-mvp/TaskDispatchService.ts +5 -6
  68. package/src/main/services/teams-mvp/TeamProvisioningService.ts +39 -2
  69. package/src/main/services/teams-mvp/TeamWorkspaceService.ts +22 -4
  70. package/src/main/utils/teamProjectResolution.ts +15 -0
  71. package/src/renderer/App.tsx +8 -4
  72. package/src/renderer/api/httpClient.ts +68 -18
  73. package/src/renderer/api/providers.ts +23 -2
  74. package/src/renderer/assets/participant-avatars/01.svg +3 -0
  75. package/src/renderer/assets/participant-avatars/02.svg +3 -0
  76. package/src/renderer/assets/participant-avatars/03.svg +3 -0
  77. package/src/renderer/assets/participant-avatars/04.svg +3 -0
  78. package/src/renderer/assets/participant-avatars/05.svg +3 -0
  79. package/src/renderer/assets/participant-avatars/06.svg +3 -0
  80. package/src/renderer/assets/participant-avatars/07.svg +3 -0
  81. package/src/renderer/assets/participant-avatars/08.svg +3 -0
  82. package/src/renderer/assets/participant-avatars/09.svg +3 -0
  83. package/src/renderer/assets/participant-avatars/10.svg +3 -0
  84. package/src/renderer/assets/participant-avatars/11.svg +3 -0
  85. package/src/renderer/assets/participant-avatars/12.svg +3 -0
  86. package/src/renderer/assets/participant-avatars/13.svg +3 -0
  87. package/src/renderer/components/common/TerminalPane.tsx +213 -0
  88. package/src/renderer/components/dashboard/DashboardView.tsx +9 -36
  89. package/src/renderer/components/extensions/ExtensionStoreView.tsx +6 -125
  90. package/src/renderer/components/extensions/ExtensionsSubTabTrigger.tsx +1 -1
  91. package/src/renderer/components/extensions/mcp/McpLibraryEnableDialog.tsx +305 -0
  92. package/src/renderer/components/extensions/mcp/McpLibraryEntryDialog.tsx +418 -0
  93. package/src/renderer/components/extensions/mcp/McpLibraryPanel.tsx +404 -0
  94. package/src/renderer/components/extensions/plugins/PluginCard.tsx +6 -6
  95. package/src/renderer/components/extensions/plugins/PluginsPanel.tsx +34 -21
  96. package/src/renderer/components/extensions/skills/SkillsLibraryPanel.tsx +335 -0
  97. package/src/renderer/components/layout/PaneContent.tsx +8 -1
  98. package/src/renderer/components/layout/Sidebar.tsx +11 -54
  99. package/src/renderer/components/layout/SortableTab.tsx +20 -31
  100. package/src/renderer/components/layout/TabBar.tsx +1 -1
  101. package/src/renderer/components/layout/TabContextMenu.tsx +1 -1
  102. package/src/renderer/components/runtime/ProviderRuntimeSettingsDialog.tsx +768 -157
  103. package/src/renderer/components/schedules/SchedulesView.tsx +51 -462
  104. package/src/renderer/components/schedules/calendar/CalendarDayView.tsx +173 -0
  105. package/src/renderer/components/schedules/calendar/CalendarEventBlock.tsx +113 -0
  106. package/src/renderer/components/schedules/calendar/CalendarHeader.tsx +148 -0
  107. package/src/renderer/components/schedules/calendar/CalendarMonthView.tsx +142 -0
  108. package/src/renderer/components/schedules/calendar/CalendarWeekView.tsx +219 -0
  109. package/src/renderer/components/schedules/calendar/ScheduleCalendarBoard.tsx +41 -0
  110. package/src/renderer/components/schedules/calendar/TeamGanttView.tsx +405 -0
  111. package/src/renderer/components/schedules/calendar/computeOccurrences.ts +234 -0
  112. package/src/renderer/components/schedules/calendar/index.ts +2 -0
  113. package/src/renderer/components/schedules/calendar/types.ts +44 -0
  114. package/src/renderer/components/settings/SettingsTabs.tsx +50 -55
  115. package/src/renderer/components/settings/SettingsView.tsx +30 -35
  116. package/src/renderer/components/settings/components/SettingsSectionHeader.tsx +5 -1
  117. package/src/renderer/components/settings/components/SettingsSelect.tsx +5 -3
  118. package/src/renderer/components/settings/components/SettingsToggle.tsx +2 -2
  119. package/src/renderer/components/settings/sections/AdvancedSection.tsx +11 -42
  120. package/src/renderer/components/settings/sections/CliStatusSection.tsx +71 -112
  121. package/src/renderer/components/settings/sections/ConfigEditorDialog.tsx +1 -1
  122. package/src/renderer/components/settings/sections/GeneralSection.tsx +11 -3
  123. package/src/renderer/components/settings/sections/HarnessSection.tsx +18 -14
  124. package/src/renderer/components/settings/sections/PlatformsSection.tsx +3 -3
  125. package/src/renderer/components/settings/sections/TaskBusSection.tsx +33 -40
  126. package/src/renderer/components/settings/sections/index.ts +0 -1
  127. package/src/renderer/components/sidebar/SidebarSessions.tsx +182 -4
  128. package/src/renderer/components/sidebar/SidebarTaskItem.tsx +4 -4
  129. package/src/renderer/components/sidebar/WorkspaceBrowser.tsx +39 -4
  130. package/src/renderer/components/splash/splashScene.ts +121 -929
  131. package/src/renderer/components/system-manager/FolderBrowser.tsx +163 -0
  132. package/src/renderer/components/system-manager/SystemManagerView.tsx +351 -0
  133. package/src/renderer/components/tasks/TasksView.tsx +112 -134
  134. package/src/renderer/components/team/CcSessionsSection.tsx +431 -89
  135. package/src/renderer/components/team/CollapsibleTeamSection.tsx +17 -32
  136. package/src/renderer/components/team/TeamDetailView.tsx +319 -123
  137. package/src/renderer/components/team/TeamListView.tsx +108 -123
  138. package/src/renderer/components/team/dialogs/CreateTaskDialog.tsx +2 -2
  139. package/src/renderer/components/team/dialogs/CreateTeamDialog.tsx +84 -306
  140. package/src/renderer/components/team/dialogs/EditTeamDialog.tsx +259 -342
  141. package/src/renderer/components/team/dialogs/GlobalTaskDetailDialog.tsx +1 -1
  142. package/src/renderer/components/team/dialogs/LaunchTeamDialog.tsx +17 -15
  143. package/src/renderer/components/team/dialogs/PlatformBindingDialog.tsx +221 -0
  144. package/src/renderer/components/team/dialogs/PlatformManualForm.tsx +7 -0
  145. package/src/renderer/components/team/dialogs/PlatformSetupQR.tsx +1 -1
  146. package/src/renderer/components/team/dialogs/RuntimeConfigDialog.tsx +361 -0
  147. package/src/renderer/components/team/dialogs/platformMeta.ts +122 -11
  148. package/src/renderer/components/team/dialogs/useTeamEditForm.ts +17 -5
  149. package/src/renderer/components/team/kanban/KanbanBoard.tsx +9 -9
  150. package/src/renderer/components/team/members/MemberCard.tsx +14 -47
  151. package/src/renderer/components/team/members/MemberDetailDialog.tsx +3 -95
  152. package/src/renderer/components/team/members/MemberDetailStats.tsx +50 -65
  153. package/src/renderer/components/team/messages/MessageComposer.tsx +8 -110
  154. package/src/renderer/components/team/messages/MessagesPanel.tsx +131 -114
  155. package/src/renderer/components/team/schedule/ScheduleStatusBadge.tsx +2 -2
  156. package/src/renderer/components/team/tools/AddMcpInline.tsx +27 -17
  157. package/src/renderer/components/team/tools/McpChip.tsx +6 -3
  158. package/src/renderer/components/team/tools/SkillChip.tsx +2 -2
  159. package/src/renderer/components/team/tools/ToolsSection.tsx +418 -70
  160. package/src/renderer/hooks/useExtensionsTabState.ts +3 -114
  161. package/src/renderer/index.css +39 -22
  162. package/src/renderer/index.html +17 -50
  163. package/src/renderer/store/index.ts +2 -1
  164. package/src/renderer/store/slices/scheduleSlice.ts +1 -1
  165. package/src/renderer/store/slices/teamSlice.ts +45 -168
  166. package/src/renderer/utils/claudeCodeOnlyProviders.ts +3 -10
  167. package/src/renderer/utils/memberHelpers.ts +5 -17
  168. package/src/renderer/utils/openCodeRuntimeDeliveryDiagnostics.ts +4 -2
  169. package/src/renderer/utils/providerSlashCommands.ts +0 -5
  170. package/src/renderer/utils/scheduleFormatters.ts +3 -1
  171. package/src/renderer/utils/teamMessageFiltering.ts +14 -1
  172. package/src/renderer/utils/teamModelAvailability.ts +18 -2
  173. package/src/shared/types/api.ts +121 -2
  174. package/src/shared/types/ccConnect.ts +2 -0
  175. package/src/shared/types/index.ts +3 -0
  176. package/src/shared/types/systemManager.ts +49 -0
  177. package/src/shared/types/team.ts +29 -0
  178. package/src/shared/types/terminal.ts +4 -2
  179. package/src/shared/utils/extensionNormalizers.ts +15 -8
  180. package/src/shared/utils/providerExtensionCapabilities.ts +2 -2
  181. package/dist-renderer/assets/ProjectEditorOverlay-lJZi-9Hp.js +0 -52
  182. package/dist-renderer/assets/channel-yIlSKy0e.js +0 -1
  183. package/dist-renderer/assets/classDiagram-2ON5EDUG-24fHez0s.js +0 -1
  184. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-24fHez0s.js +0 -1
  185. package/dist-renderer/assets/clone-BTNuUva-.js +0 -1
  186. package/dist-renderer/assets/index-Bi6nrZ4z.css +0 -1
  187. package/dist-renderer/assets/splashScene-C8lWNnm4.js +0 -1
  188. package/dist-renderer/assets/stateDiagram-v2-4FDKWEC3-_m6iPPUR.js +0 -1
@@ -1,4 +1,4 @@
1
- import { useEffect } from 'react';
1
+ import { useEffect, useMemo, useRef, useState } from 'react';
2
2
 
3
3
  import { Button } from '@renderer/components/ui/button';
4
4
  import { Checkbox } from '@renderer/components/ui/checkbox';
@@ -10,46 +10,16 @@ import {
10
10
  DialogHeader,
11
11
  DialogTitle,
12
12
  } from '@renderer/components/ui/dialog';
13
- import { AGENT_TYPE_LABELS } from '@renderer/components/team/HarnessCards';
14
- import { HarnessSelect } from '@renderer/components/team/HarnessSelect';
15
- import { Loader2, Trash2 } from 'lucide-react';
13
+ import { Loader2 } from 'lucide-react';
16
14
 
17
- import type { CcAgentType } from '@shared/types/ccConnect';
18
-
19
- import { PERMISSION_MODE_OPTIONS, useTeamEditForm } from './useTeamEditForm';
15
+ import { api } from '@renderer/api';
16
+ import { useStore } from '@renderer/store';
20
17
 
21
18
  interface EditTeamDialogProps {
22
19
  open: boolean;
23
20
  teamName: string;
24
21
  onClose: () => void;
25
- onDeleteTeam?: () => void;
26
- }
27
-
28
- // ── Section wrapper ──────────────────────────────────────────
29
- function FormSection({
30
- title,
31
- description,
32
- variant = 'default',
33
- children,
34
- }: {
35
- title: string;
36
- description?: string;
37
- variant?: 'default' | 'danger';
38
- children: React.ReactNode;
39
- }): React.JSX.Element {
40
- const border = variant === 'danger' ? 'border-red-500/40' : 'border-[var(--color-border)]';
41
- const bg = variant === 'danger' ? 'bg-red-500/5' : '';
42
- const titleColor = variant === 'danger' ? 'text-red-300' : 'text-[var(--color-text)]';
43
-
44
- return (
45
- <div className={`rounded-md border ${border} ${bg} p-3`}>
46
- <h3 className={`text-sm font-medium ${titleColor}`}>{title}</h3>
47
- {description && (
48
- <p className="mt-0.5 text-xs text-[var(--color-text-muted)]">{description}</p>
49
- )}
50
- <div className="mt-3 space-y-3">{children}</div>
51
- </div>
52
- );
22
+ onDeleteTeam?: (() => void) | undefined;
53
23
  }
54
24
 
55
25
  // ── Shared input class ───────────────────────────────────────
@@ -65,346 +35,293 @@ export const EditTeamDialog = ({
65
35
  onClose,
66
36
  onDeleteTeam,
67
37
  }: EditTeamDialogProps): React.JSX.Element => {
68
- const form = useTeamEditForm(teamName, open);
38
+ const { data, fetchTeams, selectTeam } = useStore((s) => ({
39
+ data: s.selectedTeamName === teamName ? s.selectedTeamData : null,
40
+ fetchTeams: s.fetchTeams,
41
+ selectTeam: s.selectTeam,
42
+ }));
43
+
44
+ // ── Derived defaults ─────────────────────────────────────────
45
+ const rawSettings = useMemo(
46
+ () => (data?.settings ?? {}) as Record<string, unknown>,
47
+ [data?.settings]
48
+ );
49
+
50
+ const defaults = useMemo(() => {
51
+ const cfg = data?.config;
52
+ return {
53
+ name: cfg?.name ?? '',
54
+ description: cfg?.description ?? '',
55
+ color: cfg?.color ?? '',
56
+ language:
57
+ cfg?.language ?? (typeof rawSettings.language === 'string' ? rawSettings.language : 'zh'),
58
+ managedSources:
59
+ cfg?.managedSources ??
60
+ (typeof rawSettings.admin_from === 'string' ? rawSettings.admin_from : '*'),
61
+ platformAllowFrom:
62
+ cfg?.platformAllowFrom ??
63
+ (typeof rawSettings.platform_allow_from === 'object' &&
64
+ rawSettings.platform_allow_from !== null &&
65
+ !Array.isArray(rawSettings.platform_allow_from)
66
+ ? (rawSettings.platform_allow_from as Record<string, string>)
67
+ : {}),
68
+ platformAllowChat:
69
+ cfg?.platformAllowChat ??
70
+ (typeof (rawSettings as Record<string, unknown>).platform_allow_chat === 'object' &&
71
+ (rawSettings as Record<string, unknown>).platform_allow_chat !== null &&
72
+ !Array.isArray((rawSettings as Record<string, unknown>).platform_allow_chat)
73
+ ? ((rawSettings as Record<string, unknown>).platform_allow_chat as Record<string, string>)
74
+ : {}),
75
+ showContextIndicator:
76
+ cfg?.showContextIndicator ??
77
+ (typeof rawSettings.show_context_indicator === 'boolean'
78
+ ? rawSettings.show_context_indicator
79
+ : true),
80
+ replyFooter:
81
+ cfg?.replyFooter ??
82
+ (typeof rawSettings.reply_footer === 'boolean' ? rawSettings.reply_footer : true),
83
+ injectSender:
84
+ cfg?.injectSender ??
85
+ (typeof rawSettings.inject_sender === 'boolean' ? rawSettings.inject_sender : false),
86
+ };
87
+ }, [data, rawSettings]);
88
+
89
+ // ── Local form state ─────────────────────────────────────────
90
+ const [name, setName] = useState(defaults.name);
91
+ const [description, setDescription] = useState(defaults.description);
92
+ const [language, setLanguage] = useState(defaults.language);
93
+ const [managedSources, setManagedSources] = useState(defaults.managedSources);
94
+ const [feishuAllowFrom, setFeishuAllowFrom] = useState(defaults.platformAllowFrom.feishu ?? '*');
95
+ const [feishuAllowChat, setFeishuAllowChat] = useState(defaults.platformAllowChat.feishu ?? '*');
96
+ const [showContextIndicator, setShowContextIndicator] = useState(defaults.showContextIndicator);
97
+ const [replyFooter, setReplyFooter] = useState(defaults.replyFooter);
98
+ const [injectSender, setInjectSender] = useState(defaults.injectSender);
99
+ const [savePhase, setSavePhase] = useState<'idle' | 'saving' | 'done'>('idle');
100
+ const [error, setError] = useState<string | null>(null);
101
+ const saving = savePhase === 'saving';
69
102
 
70
- // No auto-close — user closes manually after seeing "保存成功"
103
+ const defaultsRef = useRef(defaults);
104
+ if (defaults.name) defaultsRef.current = defaults;
71
105
 
72
- const toggleProviderRef = (providerName: string): void => {
73
- form.clearError();
74
- const next = form.providerRef === providerName ? '' : providerName;
75
- form.setProviderRef(next);
106
+ const prevOpenRef = useRef(false);
107
+ useEffect(() => {
108
+ if (!open || prevOpenRef.current) {
109
+ prevOpenRef.current = open;
110
+ return;
111
+ }
112
+ prevOpenRef.current = true;
113
+ const d = defaultsRef.current;
114
+ setSavePhase('idle');
115
+ setError(null);
116
+ setName(d.name);
117
+ setDescription(d.description);
118
+ setLanguage(d.language);
119
+ setManagedSources(d.managedSources);
120
+ setFeishuAllowFrom(d.platformAllowFrom.feishu ?? '*');
121
+ setFeishuAllowChat(d.platformAllowChat.feishu ?? '*');
122
+ setShowContextIndicator(d.showContextIndicator);
123
+ setReplyFooter(d.replyFooter);
124
+ setInjectSender(d.injectSender);
125
+ }, [open]);
126
+
127
+ const handleSave = (): void => {
128
+ if (!name.trim()) {
129
+ setError('团队名称不能为空');
130
+ return;
131
+ }
132
+ if (savePhase !== 'idle') return;
133
+ setSavePhase('saving');
134
+ setError(null);
135
+
136
+ const feishu = feishuAllowFrom.trim();
137
+ const feishuChat = feishuAllowChat.trim();
138
+
139
+ void (async () => {
140
+ try {
141
+ await api.teams.updateConfig(teamName, {
142
+ name: name.trim(),
143
+ description: description.trim(),
144
+ color: defaultsRef.current.color,
145
+ language: language.trim() || undefined,
146
+ managedSources: managedSources.trim() || undefined,
147
+ platformAllowFrom: feishu ? { feishu } : {},
148
+ platformAllowChat: feishuChat ? { feishu: feishuChat } : {},
149
+ showContextIndicator,
150
+ replyFooter,
151
+ injectSender,
152
+ });
153
+ await Promise.all([fetchTeams(), selectTeam(teamName)]);
154
+ setSavePhase('done');
155
+ } catch (err) {
156
+ setError(err instanceof Error ? err.message : '保存失败');
157
+ setSavePhase('idle');
158
+ }
159
+ })();
76
160
  };
77
161
 
78
162
  const saveLabel =
79
- form.savePhase === 'done'
80
- ? '保存成功'
81
- : form.savePhase === 'restarting'
82
- ? '重启服务中...'
83
- : form.savePhase === 'saving'
84
- ? '保存中...'
85
- : '保存并重启';
163
+ savePhase === 'done' ? '保存成功' : savePhase === 'saving' ? '保存中...' : '保存';
86
164
 
87
165
  return (
88
166
  <Dialog
89
- open={form.saving ? true : open}
167
+ open={saving ? true : open}
90
168
  onOpenChange={(nextOpen) => {
91
- if (form.saving) return;
169
+ if (saving) return;
92
170
  if (!nextOpen) onClose();
93
171
  }}
94
172
  >
95
- <DialogContent className="max-w-2xl">
173
+ <DialogContent className="max-w-lg">
96
174
  <DialogHeader>
97
175
  <DialogTitle>编辑团队</DialogTitle>
98
- <DialogDescription>修改团队名称、描述和运行参数</DialogDescription>
176
+ <DialogDescription>修改团队信息和消息设置(无需重启)</DialogDescription>
99
177
  </DialogHeader>
100
178
 
101
179
  <div className="space-y-4">
102
- {/* ── Section 1: 基本信息 ─────────────────────────── */}
103
- <FormSection title="基本信息">
104
- <div>
105
- <label htmlFor="edit-team-name" className={labelCls}>
106
- 名称
107
- </label>
108
- <input
109
- id="edit-team-name"
110
- type="text"
111
- value={form.name}
112
- onChange={(e) => {
113
- form.clearError();
114
- form.setName(e.target.value);
115
- }}
116
- onKeyDown={(e) => {
117
- if (e.key === 'Enter' && !form.saving && form.name.trim()) form.handleSave();
118
- }}
119
- className={inputCls}
120
- placeholder="团队名称"
121
- />
122
- </div>
123
- <div>
124
- <label htmlFor="edit-team-description" className={labelCls}>
125
- 描述
126
- </label>
127
- <textarea
128
- id="edit-team-description"
129
- value={form.description}
130
- onChange={(e) => {
131
- form.clearError();
132
- form.setDescription(e.target.value);
133
- }}
134
- rows={2}
135
- className={`${inputCls} resize-none`}
136
- placeholder="团队描述(可选)"
137
- />
138
- </div>
139
- </FormSection>
140
-
141
- {/* ── Section 2: Agent 配置 ───────────────────────── */}
142
- <FormSection
143
- title="Agent 配置"
144
- description="运行参数配置。运行时模型和 Provider 请到 Harness 配置中管理。"
145
- >
146
- <div className="grid gap-3 md:grid-cols-2">
147
- <div>
148
- <label className={labelCls}>Agent 类型</label>
149
- <HarnessSelect
150
- value={form.agentType as CcAgentType}
151
- onChange={(v) => {
152
- form.clearError();
153
- form.setAgentType(v);
154
- }}
155
- className="w-full"
156
- />
157
- </div>
158
- <div>
159
- <label className={labelCls}>权限模式</label>
160
- <select
161
- value={form.permissionMode}
162
- onChange={(e) => {
163
- form.clearError();
164
- form.setPermissionMode(e.target.value);
165
- }}
166
- className={inputCls}
167
- >
168
- {PERMISSION_MODE_OPTIONS.map((opt) => (
169
- <option key={opt.value} value={opt.value}>
170
- {opt.label}
171
- </option>
172
- ))}
173
- </select>
174
- </div>
175
- </div>
176
- <div>
177
- <label className={labelCls}>工作目录</label>
178
- <input
179
- type="text"
180
- value={form.workDir}
181
- onChange={(e) => {
182
- form.clearError();
183
- form.setWorkDir(e.target.value);
184
- }}
185
- className={`${inputCls} font-mono`}
186
- placeholder="/Users/you/code/project"
187
- />
188
- </div>
189
- </FormSection>
190
-
191
- {/* ── Section 3: 通信与平台 ───────────────────────── */}
192
- <FormSection title="通信与平台">
193
- <div className="grid gap-3 md:grid-cols-2">
194
- <div>
195
- <label className={labelCls}>语言</label>
196
- <input
197
- type="text"
198
- value={form.language}
199
- onChange={(e) => {
200
- form.clearError();
201
- form.setLanguage(e.target.value);
202
- }}
203
- className={inputCls}
204
- placeholder="zh"
205
- />
206
- </div>
207
- <div>
208
- <label className={labelCls}>管理来源</label>
209
- <input
210
- type="text"
211
- value={form.managedSources}
212
- onChange={(e) => {
213
- form.clearError();
214
- form.setManagedSources(e.target.value);
215
- }}
216
- className={inputCls}
217
- placeholder="user1,user2 或 *"
218
- />
219
- </div>
220
- </div>
221
- <div>
222
- <label className={labelCls}>平台访问控制(Feishu 允许的用户)</label>
223
- <input
224
- type="text"
225
- value={form.feishuAllowFrom}
226
- onChange={(e) => {
227
- form.clearError();
228
- form.setFeishuAllowFrom(e.target.value);
229
- }}
230
- className={inputCls}
231
- placeholder="*"
232
- />
233
- </div>
234
- </FormSection>
235
-
236
- {/* ── Section 4: 高级开关 ─────────────────────────── */}
237
- <div className="grid gap-2 md:grid-cols-3">
238
- <label
239
- htmlFor="edit-team-show-context-indicator"
240
- className="flex cursor-pointer items-center gap-2 rounded-md border border-[var(--color-border)] px-2 py-1.5 text-xs text-[var(--color-text-secondary)]"
241
- >
242
- <Checkbox
243
- id="edit-team-show-context-indicator"
244
- checked={form.showContextIndicator}
245
- onCheckedChange={(checked) => {
246
- form.clearError();
247
- form.setShowContextIndicator(checked === true);
248
- }}
249
- />
250
- 上下文指示
180
+ {/* Basic info */}
181
+ <div>
182
+ <label htmlFor="edit-team-name" className={labelCls}>
183
+ 名称
251
184
  </label>
252
- <label
253
- htmlFor="edit-team-reply-footer"
254
- className="flex cursor-pointer items-center gap-2 rounded-md border border-[var(--color-border)] px-2 py-1.5 text-xs text-[var(--color-text-secondary)]"
255
- >
256
- <Checkbox
257
- id="edit-team-reply-footer"
258
- checked={form.replyFooter}
259
- onCheckedChange={(checked) => {
260
- form.clearError();
261
- form.setReplyFooter(checked === true);
262
- }}
263
- />
264
- 回复尾部信息
265
- </label>
266
- <label
267
- htmlFor="edit-team-inject-sender"
268
- className="flex cursor-pointer items-center gap-2 rounded-md border border-[var(--color-border)] px-2 py-1.5 text-xs text-[var(--color-text-secondary)]"
269
- >
270
- <Checkbox
271
- id="edit-team-inject-sender"
272
- checked={form.injectSender}
273
- onCheckedChange={(checked) => {
274
- form.clearError();
275
- form.setInjectSender(checked === true);
276
- }}
277
- />
278
- 注入发送者
185
+ <input
186
+ id="edit-team-name"
187
+ type="text"
188
+ value={name}
189
+ onChange={(e) => {
190
+ setError(null);
191
+ setName(e.target.value);
192
+ }}
193
+ onKeyDown={(e) => {
194
+ if (e.key === 'Enter' && !saving && name.trim()) handleSave();
195
+ }}
196
+ className={inputCls}
197
+ placeholder="团队名称"
198
+ />
199
+ </div>
200
+ <div>
201
+ <label htmlFor="edit-team-description" className={labelCls}>
202
+ 描述
279
203
  </label>
204
+ <textarea
205
+ id="edit-team-description"
206
+ value={description}
207
+ onChange={(e) => {
208
+ setError(null);
209
+ setDescription(e.target.value);
210
+ }}
211
+ rows={2}
212
+ className={`${inputCls} resize-none`}
213
+ placeholder="团队描述(可选)"
214
+ />
280
215
  </div>
281
216
 
282
- {/* ── Section 5: Provider 绑定 ────────────────────── */}
283
- <div className="rounded-lg border border-[var(--color-border-subtle)] bg-white/[0.02] p-3">
284
- <div className="flex items-start justify-between gap-3">
285
- <div>
286
- <p className="text-xs font-medium text-[var(--color-text)]">Provider(可选)</p>
287
- <p className="mt-1 text-[11px] leading-relaxed text-[var(--color-text-muted)]">
288
- 留空时使用本机{' '}
289
- {AGENT_TYPE_LABELS[form.agentType as CcAgentType] ?? form.agentType}{' '}
290
- 默认配置和登录状态。只有需要给该团队指定模型供应商时,才绑定下面的全局 Provider。
291
- </p>
217
+ {/* Messaging settings */}
218
+ <div className="rounded-md border border-[var(--color-border)] p-3">
219
+ <h3 className="text-xs font-medium text-[var(--color-text)]">消息设置</h3>
220
+ <div className="mt-3 space-y-3">
221
+ <div className="grid gap-3 md:grid-cols-2">
222
+ <div>
223
+ <label className={labelCls}>语言</label>
224
+ <input
225
+ type="text"
226
+ value={language}
227
+ onChange={(e) => {
228
+ setError(null);
229
+ setLanguage(e.target.value);
230
+ }}
231
+ className={inputCls}
232
+ placeholder="zh"
233
+ />
234
+ </div>
235
+ <div>
236
+ <label className={labelCls}>管理来源</label>
237
+ <input
238
+ type="text"
239
+ value={managedSources}
240
+ onChange={(e) => {
241
+ setError(null);
242
+ setManagedSources(e.target.value);
243
+ }}
244
+ className={inputCls}
245
+ placeholder="user1,user2 或 *"
246
+ />
247
+ </div>
292
248
  </div>
293
- {form.providerRef ? (
294
- <button
295
- type="button"
296
- className="shrink-0 rounded-md border border-[var(--color-border)] px-2 py-1 text-[11px] text-[var(--color-text-muted)] hover:bg-white/5"
297
- onClick={() => form.setProviderRef('')}
298
- >
299
- 使用本机默认
300
- </button>
301
- ) : null}
302
- </div>
303
-
304
- <div className="mt-3 space-y-2">
305
- {form.compatibleProviders.length > 0 ? (
306
- form.compatibleProviders.map((provider) => {
307
- const checked = form.providerRef === provider.name;
308
- const at = form.agentType as CcAgentType;
309
- const endpoint = provider.endpoints?.[at] ?? provider.base_url ?? '默认端点';
310
- const model =
311
- provider.agent_models?.[at] ??
312
- provider.model ??
313
- provider.models?.[0]?.model ??
314
- '未指定模型';
315
- return (
316
- <button
317
- key={provider.name}
318
- type="button"
319
- onClick={() => toggleProviderRef(provider.name)}
320
- className={`w-full rounded-lg border px-3 py-2 text-left transition-colors ${
321
- checked
322
- ? 'border-indigo-400/60 bg-indigo-500/10'
323
- : 'border-[var(--color-border-subtle)] bg-black/10 hover:border-[var(--color-border)] hover:bg-white/[0.04]'
324
- }`}
325
- >
326
- <div className="flex items-center justify-between gap-3">
327
- <div className="min-w-0">
328
- <p className="truncate text-xs font-medium text-[var(--color-text)]">
329
- {provider.name}
330
- </p>
331
- <p className="mt-0.5 truncate text-[11px] text-[var(--color-text-muted)]">
332
- {model} · {endpoint}
333
- </p>
334
- </div>
335
- <span
336
- className={`shrink-0 rounded-full px-2 py-0.5 text-[10px] ${
337
- checked
338
- ? 'bg-indigo-400/20 text-indigo-200'
339
- : 'bg-white/5 text-[var(--color-text-muted)]'
340
- }`}
341
- >
342
- {checked ? '已绑定' : '可绑定'}
343
- </span>
344
- </div>
345
- </button>
346
- );
347
- })
348
- ) : (
349
- <div className="rounded-md border border-dashed border-[var(--color-border)] px-3 py-3 text-xs text-[var(--color-text-muted)]">
350
- 暂无适用于 {AGENT_TYPE_LABELS[form.agentType as CcAgentType] ?? form.agentType}{' '}
351
- 的全局 Provider。可先在「设置 → Harness
352
- 配置」中添加;不添加也会使用本机默认登录态。
249
+ <div className="grid gap-3 md:grid-cols-2">
250
+ <div>
251
+ <label className={labelCls}>飞书私聊权限</label>
252
+ <input
253
+ type="text"
254
+ value={feishuAllowFrom}
255
+ onChange={(e) => {
256
+ setError(null);
257
+ setFeishuAllowFrom(e.target.value);
258
+ }}
259
+ className={inputCls}
260
+ placeholder="ou_xxx 或 *"
261
+ />
262
+ </div>
263
+ <div>
264
+ <label className={labelCls}>飞书群聊权限</label>
265
+ <input
266
+ type="text"
267
+ value={feishuAllowChat}
268
+ onChange={(e) => {
269
+ setError(null);
270
+ setFeishuAllowChat(e.target.value);
271
+ }}
272
+ className={inputCls}
273
+ placeholder="oc_xxx 或 *"
274
+ />
353
275
  </div>
354
- )}
276
+ </div>
277
+ <div className="grid gap-2 md:grid-cols-3">
278
+ <label className="flex cursor-pointer items-center gap-2 rounded-md border border-[var(--color-border)] px-2 py-1.5 text-xs text-[var(--color-text-secondary)]">
279
+ <Checkbox
280
+ checked={showContextIndicator}
281
+ onCheckedChange={(c) => {
282
+ setError(null);
283
+ setShowContextIndicator(c === true);
284
+ }}
285
+ />
286
+ 上下文指示
287
+ </label>
288
+ <label className="flex cursor-pointer items-center gap-2 rounded-md border border-[var(--color-border)] px-2 py-1.5 text-xs text-[var(--color-text-secondary)]">
289
+ <Checkbox
290
+ checked={replyFooter}
291
+ onCheckedChange={(c) => {
292
+ setError(null);
293
+ setReplyFooter(c === true);
294
+ }}
295
+ />
296
+ 回复尾部信息
297
+ </label>
298
+ <label className="flex cursor-pointer items-center gap-2 rounded-md border border-[var(--color-border)] px-2 py-1.5 text-xs text-[var(--color-text-secondary)]">
299
+ <Checkbox
300
+ checked={injectSender}
301
+ onCheckedChange={(c) => {
302
+ setError(null);
303
+ setInjectSender(c === true);
304
+ }}
305
+ />
306
+ 注入发送者
307
+ </label>
308
+ </div>
355
309
  </div>
356
310
  </div>
357
311
 
358
- {/* ── Section 6: 危险操作 ─────────────────────────── */}
359
- <FormSection title="危险操作" variant="danger">
360
- <div>
361
- <label className={labelCls}>已禁用命令</label>
362
- <input
363
- type="text"
364
- value={form.disabledCommandsInput}
365
- onChange={(e) => {
366
- form.clearError();
367
- form.setDisabledCommandsInput(e.target.value);
368
- }}
369
- className={inputCls}
370
- placeholder="restart, upgrade, cron"
371
- />
372
- </div>
373
- {onDeleteTeam && form.canDelete && (
374
- <>
375
- <p className="text-xs text-[var(--color-text-muted)]">
376
- 删除项目会将团队从当前控制面板移除。
377
- </p>
378
- <Button
379
- type="button"
380
- variant="destructive"
381
- size="sm"
382
- onClick={() => {
383
- onClose();
384
- window.setTimeout(onDeleteTeam, 0);
385
- }}
386
- >
387
- <Trash2 size={14} className="mr-1.5" />
388
- 删除项目
389
- </Button>
390
- </>
391
- )}
392
- </FormSection>
393
-
394
- {/* ── Status messages ──────────────────────────────── */}
395
- {form.error && <p className="text-xs text-red-400">{form.error}</p>}
312
+ {error && <p className="text-xs text-red-400">{error}</p>}
396
313
  </div>
397
314
 
398
315
  <DialogFooter>
399
- <Button variant="outline" size="sm" onClick={onClose} disabled={form.saving}>
400
- {form.savePhase === 'done' ? '关闭' : '取消'}
316
+ <Button variant="outline" size="sm" onClick={onClose} disabled={saving}>
317
+ {savePhase === 'done' ? '关闭' : '取消'}
401
318
  </Button>
402
319
  <Button
403
320
  size="sm"
404
- onClick={form.handleSave}
405
- disabled={form.saving || form.savePhase === 'done' || !form.name.trim()}
321
+ onClick={handleSave}
322
+ disabled={saving || savePhase === 'done' || !name.trim()}
406
323
  >
407
- {form.saving && <Loader2 size={14} className="mr-1.5 animate-spin" />}
324
+ {saving && <Loader2 size={14} className="mr-1.5 animate-spin" />}
408
325
  {saveLabel}
409
326
  </Button>
410
327
  </DialogFooter>
@@ -103,7 +103,7 @@ export const GlobalTaskDetailDialog = (): React.JSX.Element | null => {
103
103
 
104
104
  const handleOpenTeam = useCallback((): void => {
105
105
  closeGlobalTaskDetail();
106
- openTeamTab(teamName, undefined, taskId);
106
+ openTeamTab(teamName, undefined, { taskId });
107
107
  }, [closeGlobalTaskDetail, openTeamTab, teamName, taskId]);
108
108
 
109
109
  const handleViewChanges = useCallback(