@yancyyu/openhermit 1.5.8 → 1.5.9

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 (105) hide show
  1. package/README.md +10 -1
  2. package/dist-renderer/assets/{ProjectEditorOverlay-BNoDw9T1.js → ProjectEditorOverlay-C5D83Zxv.js} +1 -1
  3. package/dist-renderer/assets/{TeamGraphOverlay-CfGRKQIu.js → TeamGraphOverlay-ajzuM1-u.js} +1 -1
  4. package/dist-renderer/assets/{_basePickBy-Ct8Hm5_h.js → _basePickBy-C9H2zmVj.js} +1 -1
  5. package/dist-renderer/assets/{_baseUniq-BofrAFBx.js → _baseUniq-CpGZGemc.js} +1 -1
  6. package/dist-renderer/assets/{arc-AbJgatzR.js → arc-CbGBDw-m.js} +1 -1
  7. package/dist-renderer/assets/{architectureDiagram-VXUJARFQ-gpniCJVk.js → architectureDiagram-VXUJARFQ-nuKXUIpb.js} +1 -1
  8. package/dist-renderer/assets/{blockDiagram-VD42YOAC-aBbbmONC.js → blockDiagram-VD42YOAC-DHUUE7Jc.js} +1 -1
  9. package/dist-renderer/assets/{c4Diagram-YG6GDRKO-DJio1IsU.js → c4Diagram-YG6GDRKO-OILHhqLM.js} +1 -1
  10. package/dist-renderer/assets/channel-DpUKLLrj.js +1 -0
  11. package/dist-renderer/assets/{chunk-4BX2VUAB-D1_HKao2.js → chunk-4BX2VUAB-dqNpZaQ8.js} +1 -1
  12. package/dist-renderer/assets/{chunk-55IACEB6-NAmVxF4k.js → chunk-55IACEB6-BCoSJQM-.js} +1 -1
  13. package/dist-renderer/assets/{chunk-B4BG7PRW-Ce829laz.js → chunk-B4BG7PRW-BLbg8yVR.js} +1 -1
  14. package/dist-renderer/assets/{chunk-DI55MBZ5-Ct2Le12y.js → chunk-DI55MBZ5-CUUWOs1Q.js} +1 -1
  15. package/dist-renderer/assets/{chunk-FMBD7UC4-Cie3DzKk.js → chunk-FMBD7UC4-D9geTN5P.js} +1 -1
  16. package/dist-renderer/assets/{chunk-QN33PNHL-4f5Yb50e.js → chunk-QN33PNHL-BGT8-BRX.js} +1 -1
  17. package/dist-renderer/assets/{chunk-QZHKN3VN-D9ranl9c.js → chunk-QZHKN3VN-CC8ebGaM.js} +1 -1
  18. package/dist-renderer/assets/{chunk-TZMSLE5B-bdGZWlEy.js → chunk-TZMSLE5B-CajekcT6.js} +1 -1
  19. package/dist-renderer/assets/classDiagram-2ON5EDUG-LL85aSlz.js +1 -0
  20. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-LL85aSlz.js +1 -0
  21. package/dist-renderer/assets/clone-BHWsFzFA.js +1 -0
  22. package/dist-renderer/assets/{cose-bilkent-S5V4N54A-C6tvfcVi.js → cose-bilkent-S5V4N54A-C_x7hSy3.js} +1 -1
  23. package/dist-renderer/assets/{dagre-6UL2VRFP-B-4qcZam.js → dagre-6UL2VRFP-C4Y1k4DZ.js} +1 -1
  24. package/dist-renderer/assets/{diagram-PSM6KHXK-CwT3TLjx.js → diagram-PSM6KHXK-oRIeULoh.js} +1 -1
  25. package/dist-renderer/assets/{diagram-QEK2KX5R-BWH6-ZFd.js → diagram-QEK2KX5R-DwSqw5HF.js} +1 -1
  26. package/dist-renderer/assets/{diagram-S2PKOQOG-DfpPnfi1.js → diagram-S2PKOQOG-DqjGYje2.js} +1 -1
  27. package/dist-renderer/assets/{erDiagram-Q2GNP2WA-BFbEFR4x.js → erDiagram-Q2GNP2WA-CEV5bBgg.js} +1 -1
  28. package/dist-renderer/assets/{flowDiagram-NV44I4VS-Dg3cf5hW.js → flowDiagram-NV44I4VS-BQQkrRyu.js} +1 -1
  29. package/dist-renderer/assets/{ganttDiagram-JELNMOA3-B21y55W5.js → ganttDiagram-JELNMOA3-CLy4WR1G.js} +1 -1
  30. package/dist-renderer/assets/{gitGraphDiagram-V2S2FVAM-BDV3BJzn.js → gitGraphDiagram-V2S2FVAM-6W3ioQu_.js} +1 -1
  31. package/dist-renderer/assets/{graph-BfaZ4hZt.js → graph-BnLKQvhH.js} +1 -1
  32. package/dist-renderer/assets/{index-CCqtDawH.js → index-B4aiRxoU.js} +1 -1
  33. package/dist-renderer/assets/{index-pMg_LlsS.js → index-B8lKqPVq.js} +1 -1
  34. package/dist-renderer/assets/{index-CZltVMDP.js → index-BRuhNKyU.js} +12 -12
  35. package/dist-renderer/assets/{index-BMXHMpkG.js → index-BufvLVIl.js} +1 -1
  36. package/dist-renderer/assets/{index-Ct0-y9TF.js → index-C1xShqKH.js} +1 -1
  37. package/dist-renderer/assets/{index-CVMSpK8C.js → index-zIOLLI7O.js} +1 -1
  38. package/dist-renderer/assets/{infoDiagram-HS3SLOUP-DvMlS0CL.js → infoDiagram-HS3SLOUP-BoBweEEY.js} +1 -1
  39. package/dist-renderer/assets/{journeyDiagram-XKPGCS4Q-DIyMluRv.js → journeyDiagram-XKPGCS4Q-DLL0V5oP.js} +1 -1
  40. package/dist-renderer/assets/{kanban-definition-3W4ZIXB7-CVOx8f-7.js → kanban-definition-3W4ZIXB7-HsFtEDG3.js} +1 -1
  41. package/dist-renderer/assets/{layout-BPKIXUf4.js → layout-ClIooAAq.js} +1 -1
  42. package/dist-renderer/assets/{linear-CScZGLr2.js → linear-r3RJcj8y.js} +1 -1
  43. package/dist-renderer/assets/{mindmap-definition-VGOIOE7T-CmDQ7Wo6.js → mindmap-definition-VGOIOE7T-BA_P1U4V.js} +1 -1
  44. package/dist-renderer/assets/{pieDiagram-ADFJNKIX-DbVClin-.js → pieDiagram-ADFJNKIX-CzPAfkTB.js} +1 -1
  45. package/dist-renderer/assets/{quadrantDiagram-AYHSOK5B-CAB0MYcW.js → quadrantDiagram-AYHSOK5B-PvdPWzFJ.js} +1 -1
  46. package/dist-renderer/assets/{requirementDiagram-UZGBJVZJ-w2Lfpg3T.js → requirementDiagram-UZGBJVZJ-CHqIL_Od.js} +1 -1
  47. package/dist-renderer/assets/{sankeyDiagram-TZEHDZUN-kvG1QoKY.js → sankeyDiagram-TZEHDZUN-ConzpACM.js} +1 -1
  48. package/dist-renderer/assets/{sequenceDiagram-WL72ISMW-DCVBQ23J.js → sequenceDiagram-WL72ISMW-Zryq4oxP.js} +1 -1
  49. package/dist-renderer/assets/{stateDiagram-FKZM4ZOC-ItZ0JBvq.js → stateDiagram-FKZM4ZOC-BA9V7NHF.js} +1 -1
  50. package/dist-renderer/assets/{stateDiagram-v2-4FDKWEC3-Hpmw4dMm.js → stateDiagram-v2-4FDKWEC3-CGnaujD-.js} +1 -1
  51. package/dist-renderer/assets/{timeline-definition-IT6M3QCI-BzSFaAjV.js → timeline-definition-IT6M3QCI-DPs2ZjMm.js} +1 -1
  52. package/dist-renderer/assets/{treemap-GDKQZRPO-fSz4hQn0.js → treemap-GDKQZRPO-B0lzrLxb.js} +1 -1
  53. package/dist-renderer/assets/{xychartDiagram-PRI3JC2R-CT1kaGlv.js → xychartDiagram-PRI3JC2R-CINGmMxX.js} +1 -1
  54. package/dist-renderer/index.html +1 -1
  55. package/package.json +2 -1
  56. package/src/main/server.ts +993 -764
  57. package/src/main/services/UpdateService.ts +4 -1
  58. package/src/main/services/ccConnect/CcConnectBridge.ts +1 -8
  59. package/src/main/services/ccConnect/CcConnectClient.ts +7 -2
  60. package/src/main/services/teams-mvp/TeamProvisioningService.ts +14 -4
  61. package/src/main/services/teams-mvp/TeamWorkspaceService.ts +11 -6
  62. package/src/renderer/App.tsx +18 -7
  63. package/src/renderer/api/httpClient.ts +136 -42
  64. package/src/renderer/components/chat/ChatHistory.tsx +11 -8
  65. package/src/renderer/components/dashboard/DashboardView.tsx +4 -2
  66. package/src/renderer/components/extensions/ExtensionStoreView.tsx +2 -7
  67. package/src/renderer/components/layout/Sidebar.tsx +3 -1
  68. package/src/renderer/components/schedules/SchedulesView.tsx +15 -13
  69. package/src/renderer/components/settings/SettingsTabs.tsx +2 -1
  70. package/src/renderer/components/settings/hooks/useSettingsHandlers.ts +4 -5
  71. package/src/renderer/components/settings/sections/AdvancedSection.tsx +19 -4
  72. package/src/renderer/components/settings/sections/CliStatusSection.tsx +63 -59
  73. package/src/renderer/components/settings/sections/GeneralSection.tsx +5 -11
  74. package/src/renderer/components/settings/sections/HarnessSection.tsx +30 -15
  75. package/src/renderer/components/settings/sections/PlatformsSection.tsx +110 -51
  76. package/src/renderer/components/sidebar/SidebarSessions.tsx +100 -67
  77. package/src/renderer/components/sidebar/WorkspaceBrowser.tsx +26 -43
  78. package/src/renderer/components/team/CcSessionsSection.tsx +34 -14
  79. package/src/renderer/components/team/TeamDetailView.tsx +150 -148
  80. package/src/renderer/components/team/TeamEmptyState.tsx +27 -16
  81. package/src/renderer/components/team/TeamListView.tsx +4 -2
  82. package/src/renderer/components/team/activity/ActivityItem.tsx +6 -1
  83. package/src/renderer/components/team/dialogs/CreateTeamDialog.tsx +282 -75
  84. package/src/renderer/components/team/dialogs/EditTeamDialog.tsx +2 -1
  85. package/src/renderer/components/team/dialogs/PlatformManualForm.tsx +64 -21
  86. package/src/renderer/components/team/dialogs/PlatformSetupQR.tsx +68 -19
  87. package/src/renderer/components/team/dialogs/ProjectPathSelector.tsx +20 -16
  88. package/src/renderer/components/team/dialogs/platformMeta.ts +66 -11
  89. package/src/renderer/components/team/editor/EditorFileTree.tsx +9 -7
  90. package/src/renderer/components/team/kanban/KanbanBoard.tsx +7 -10
  91. package/src/renderer/components/team/kanban/KanbanTaskCard.tsx +1 -3
  92. package/src/renderer/components/team/members/MemberDetailDialog.tsx +1 -5
  93. package/src/renderer/components/team/messages/MessageComposer.tsx +3 -1
  94. package/src/renderer/components/team/messages/MessagesPanel.tsx +34 -26
  95. package/src/renderer/components/team/schedule/CcCronScheduleDialog.tsx +1 -3
  96. package/src/renderer/components/team/schedule/ScheduleSection.tsx +9 -10
  97. package/src/renderer/store/slices/scheduleSlice.ts +4 -1
  98. package/src/renderer/store/slices/teamSlice.ts +3 -1
  99. package/src/shared/types/api.ts +70 -21
  100. package/src/shared/utils/leadDetection.ts +5 -1
  101. package/tsconfig.json +26 -0
  102. package/dist-renderer/assets/channel-CZ8sd5Xf.js +0 -1
  103. package/dist-renderer/assets/classDiagram-2ON5EDUG-CMcfSKj5.js +0 -1
  104. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-CMcfSKj5.js +0 -1
  105. package/dist-renderer/assets/clone-CMuwA8RV.js +0 -1
@@ -420,63 +420,63 @@ export const CliStatusSection = ({
420
420
  </p>
421
421
  )}
422
422
  <div className="ml-6 mt-3 space-y-2">
423
- {ALL_AGENT_TYPES.map((agentType) => {
424
- const provider = cliProviderStatusByAgentType.get(agentType) ?? null;
425
- const harnessProviders = globalProvidersByAgentType.get(agentType) ?? [];
426
- if (!provider) {
427
- return (
428
- <div
429
- key={agentType}
430
- className="grid grid-cols-[minmax(0,1fr)_auto] gap-x-3 gap-y-2 rounded-md border px-3 py-2"
431
- style={{
432
- borderColor: 'var(--color-border-subtle)',
433
- backgroundColor: 'rgba(255, 255, 255, 0.02)',
434
- }}
435
- >
436
- <div className="min-w-0">
437
- <div className="flex items-center gap-2 text-xs">
438
- <Terminal
439
- className="size-4 shrink-0"
440
- style={{ color: 'var(--color-text-muted)' }}
441
- />
442
- <span
443
- className="font-medium"
444
- style={{ color: 'var(--color-text-secondary)' }}
445
- >
446
- {AGENT_TYPE_LABELS[agentType]}
447
- </span>
448
- <span style={{ color: 'var(--color-text-muted)' }}>
449
- {harnessProviders.length > 0
450
- ? `${harnessProviders.length} 个 Provider`
451
- : '未配置 Provider'}
452
- </span>
453
- </div>
454
- <div
455
- className="mt-1 flex flex-wrap gap-x-3 gap-y-1 text-[11px]"
423
+ {ALL_AGENT_TYPES.map((agentType) => {
424
+ const provider = cliProviderStatusByAgentType.get(agentType) ?? null;
425
+ const harnessProviders = globalProvidersByAgentType.get(agentType) ?? [];
426
+ if (!provider) {
427
+ return (
428
+ <div
429
+ key={agentType}
430
+ className="grid grid-cols-[minmax(0,1fr)_auto] gap-x-3 gap-y-2 rounded-md border px-3 py-2"
431
+ style={{
432
+ borderColor: 'var(--color-border-subtle)',
433
+ backgroundColor: 'rgba(255, 255, 255, 0.02)',
434
+ }}
435
+ >
436
+ <div className="min-w-0">
437
+ <div className="flex items-center gap-2 text-xs">
438
+ <Terminal
439
+ className="size-4 shrink-0"
456
440
  style={{ color: 'var(--color-text-muted)' }}
441
+ />
442
+ <span
443
+ className="font-medium"
444
+ style={{ color: 'var(--color-text-secondary)' }}
457
445
  >
458
- <span>Agent 类型:{agentType}</span>
459
- <span>Provider:{harnessProviders.length}</span>
460
- </div>
446
+ {AGENT_TYPE_LABELS[agentType]}
447
+ </span>
448
+ <span style={{ color: 'var(--color-text-muted)' }}>
449
+ {harnessProviders.length > 0
450
+ ? `${harnessProviders.length} 个 Provider`
451
+ : '未配置 Provider'}
452
+ </span>
461
453
  </div>
462
- <div className="flex shrink-0 items-start gap-2">
463
- <button
464
- type="button"
465
- onClick={() => handleHarnessManage(agentType)}
466
- className="flex items-center gap-1 rounded-md border px-2 py-[3px] text-[10px] font-medium transition-colors hover:bg-white/5"
467
- style={{
468
- borderColor: 'var(--color-border)',
469
- color: 'var(--color-text-secondary)',
470
- }}
471
- >
472
- <SlidersHorizontal className="size-3" />
473
- 配置
474
- </button>
454
+ <div
455
+ className="mt-1 flex flex-wrap gap-x-3 gap-y-1 text-[11px]"
456
+ style={{ color: 'var(--color-text-muted)' }}
457
+ >
458
+ <span>Agent 类型:{agentType}</span>
459
+ <span>Provider:{harnessProviders.length}</span>
475
460
  </div>
476
461
  </div>
477
- );
478
- }
479
- return (
462
+ <div className="flex shrink-0 items-start gap-2">
463
+ <button
464
+ type="button"
465
+ onClick={() => handleHarnessManage(agentType)}
466
+ className="flex items-center gap-1 rounded-md border px-2 py-[3px] text-[10px] font-medium transition-colors hover:bg-white/5"
467
+ style={{
468
+ borderColor: 'var(--color-border)',
469
+ color: 'var(--color-text-secondary)',
470
+ }}
471
+ >
472
+ <SlidersHorizontal className="size-3" />
473
+ 配置
474
+ </button>
475
+ </div>
476
+ </div>
477
+ );
478
+ }
479
+ return (
480
480
  <div
481
481
  key={agentType}
482
482
  className="grid grid-cols-[minmax(0,1fr)_auto] gap-x-3 gap-y-2 rounded-md border px-3 py-2"
@@ -526,9 +526,9 @@ export const CliStatusSection = ({
526
526
  </div>
527
527
  </div>
528
528
  </div>
529
- );
530
- })}
531
- </div>
529
+ );
530
+ })}
531
+ </div>
532
532
  </div>
533
533
  ) : (
534
534
  <div className="space-y-2 text-sm" style={{ color: 'var(--color-text-secondary)' }}>
@@ -683,7 +683,9 @@ export const CliStatusSection = ({
683
683
  <GenericHarnessProviderDialog
684
684
  agentType={genericHarnessAgentType}
685
685
  providers={
686
- genericHarnessAgentType ? (globalProvidersByAgentType.get(genericHarnessAgentType) ?? []) : []
686
+ genericHarnessAgentType
687
+ ? (globalProvidersByAgentType.get(genericHarnessAgentType) ?? [])
688
+ : []
687
689
  }
688
690
  loading={globalProvidersLoading}
689
691
  error={globalProvidersError}
@@ -793,7 +795,9 @@ const GenericHarnessProviderDialog = ({
793
795
  placeholder="API Key(可选)"
794
796
  />
795
797
  </div>
796
- {addProviderError ? <div className="text-xs text-red-400">{addProviderError}</div> : null}
798
+ {addProviderError ? (
799
+ <div className="text-xs text-red-400">{addProviderError}</div>
800
+ ) : null}
797
801
  <div className="flex justify-end">
798
802
  <button
799
803
  type="button"
@@ -919,8 +923,8 @@ const GenericHarnessProviderDialog = ({
919
923
  color: 'var(--color-text-muted)',
920
924
  }}
921
925
  >
922
- 当前还没有绑定到 {title} 的 Provider。请在 Hermit 配置中添加
923
- `agent_types` 包含 `{agentType ?? 'agent'}` 的 Provider。
926
+ 当前还没有绑定到 {title} 的 Provider。请在 Hermit 配置中添加 `agent_types` 包含 `
927
+ {agentType ?? 'agent'}` 的 Provider。
924
928
  </div>
925
929
  )}
926
930
  </div>
@@ -451,11 +451,7 @@ export const GeneralSection = ({
451
451
  {copied ? '已复制' : '复制链接'}
452
452
  </button>
453
453
  </div>
454
- {serverError && (
455
- <p className="mb-2 text-xs text-red-400">
456
- 服务状态获取失败:{serverError}
457
- </p>
458
- )}
454
+ {serverError && <p className="mb-2 text-xs text-red-400">服务状态获取失败:{serverError}</p>}
459
455
  <p className="text-xs" style={{ color: 'var(--color-text-muted)' }}>
460
456
  当前为 Web 控制台模式。服务由 Hermit 后端托管,不能在浏览器内启动或关闭。
461
457
  </p>
@@ -590,7 +586,9 @@ export const GeneralSection = ({
590
586
  min={100}
591
587
  value={ccSettings.stream_preview_interval_ms}
592
588
  onChange={(event) =>
593
- patchCcSettings({ stream_preview_interval_ms: Number(event.target.value) || 100 })
589
+ patchCcSettings({
590
+ stream_preview_interval_ms: Number(event.target.value) || 100,
591
+ })
594
592
  }
595
593
  className="h-8 w-full rounded-md border border-[var(--color-border)] bg-[var(--color-surface)] px-2 text-xs text-[var(--color-text)]"
596
594
  />
@@ -625,11 +623,7 @@ export const GeneralSection = ({
625
623
  </div>
626
624
 
627
625
  <div className="flex items-center gap-3">
628
- <Button
629
- size="sm"
630
- onClick={() => void saveCcSettings()}
631
- disabled={ccSettingsSaving}
632
- >
626
+ <Button size="sm" onClick={() => void saveCcSettings()} disabled={ccSettingsSaving}>
633
627
  {ccSettingsSaving ? <Loader2 className="mr-1.5 size-3.5 animate-spin" /> : null}
634
628
  保存运行设置
635
629
  </Button>
@@ -30,7 +30,8 @@ export const HarnessSection = (): React.JSX.Element => {
30
30
  fetch('/api/v1/projects'),
31
31
  ]);
32
32
 
33
- const providerData = providerRes.status === 'fulfilled' ? providerRes.value : { providers: [] };
33
+ const providerData =
34
+ providerRes.status === 'fulfilled' ? providerRes.value : { providers: [] };
34
35
  const providerCoverage = new Map<CcAgentType, string[]>(
35
36
  ALL_AGENT_TYPES.map((type) => [type, []])
36
37
  );
@@ -50,10 +51,12 @@ export const HarnessSection = (): React.JSX.Element => {
50
51
  if (projectsRes.status === 'fulfilled' && projectsRes.value.ok) {
51
52
  try {
52
53
  const json = await projectsRes.value.json();
53
- for (const proj of (json.data?.projects ?? [])) {
54
+ for (const proj of json.data?.projects ?? []) {
54
55
  if (proj.agent_type) projectAgentTypes.add(proj.agent_type);
55
56
  }
56
- } catch { /* ignore parse errors */ }
57
+ } catch {
58
+ /* ignore parse errors */
59
+ }
57
60
  }
58
61
 
59
62
  setUsedAgentTypes(projectAgentTypes);
@@ -83,11 +86,11 @@ export const HarnessSection = (): React.JSX.Element => {
83
86
  {/* Supported agent types reference */}
84
87
  <div>
85
88
  <SettingsSectionHeader title="支持的 Agent 类型" />
86
- <p className="text-xs mb-3" style={{ color: 'var(--color-text-muted)' }}>
89
+ <p className="mb-3 text-xs" style={{ color: 'var(--color-text-muted)' }}>
87
90
  hermit 支持的全部 Agent CLI 类型。绿色表示已配置对应 Provider。
88
91
  </p>
89
92
 
90
- <div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-2">
93
+ <div className="grid grid-cols-2 gap-2 sm:grid-cols-3 md:grid-cols-4">
91
94
  {ALL_AGENT_TYPES.map((type) => {
92
95
  const covering = coveredTypes.get(type) ?? [];
93
96
  const covered = covering.length > 0;
@@ -96,23 +99,34 @@ export const HarnessSection = (): React.JSX.Element => {
96
99
  <div
97
100
  key={type}
98
101
  className="flex items-center gap-2 rounded-lg border px-3 py-2.5"
99
- style={{ borderColor: 'var(--color-border)', background: 'var(--color-surface-raised)' }}
102
+ style={{
103
+ borderColor: 'var(--color-border)',
104
+ background: 'var(--color-surface-raised)',
105
+ }}
100
106
  >
101
- <div className={cn(
102
- 'w-2 h-2 rounded-full shrink-0',
103
- covered ? 'bg-green-500' : 'bg-gray-500'
104
- )} />
107
+ <div
108
+ className={cn(
109
+ 'h-2 w-2 shrink-0 rounded-full',
110
+ covered ? 'bg-green-500' : 'bg-gray-500'
111
+ )}
112
+ />
105
113
  <div className="min-w-0 flex-1">
106
- <p className="text-xs font-medium truncate" style={{ color: 'var(--color-text)' }}>
114
+ <p
115
+ className="truncate text-xs font-medium"
116
+ style={{ color: 'var(--color-text)' }}
117
+ >
107
118
  {AGENT_TYPE_LABELS[type]}
108
119
  </p>
109
120
  {covered && (
110
- <p className="text-[10px] truncate" style={{ color: 'var(--color-text-muted)' }}>
121
+ <p
122
+ className="truncate text-[10px]"
123
+ style={{ color: 'var(--color-text-muted)' }}
124
+ >
111
125
  {covering.join(', ')}
112
126
  </p>
113
127
  )}
114
128
  </div>
115
- {covered && <CheckCircle2 size={12} className="text-green-500 shrink-0" />}
129
+ {covered && <CheckCircle2 size={12} className="shrink-0 text-green-500" />}
116
130
  </div>
117
131
  );
118
132
  })}
@@ -122,8 +136,9 @@ export const HarnessSection = (): React.JSX.Element => {
122
136
  {/* Unified harness configuration: runtime + providers */}
123
137
  <div>
124
138
  <SettingsSectionHeader title="Harness 配置" />
125
- <p className="text-xs mb-3" style={{ color: 'var(--color-text-muted)' }}>
126
- 点击对应 Harness 的“配置”,安装、检查运行时,并配置 settings.json、API Key、端点和认证方式。
139
+ <p className="mb-3 text-xs" style={{ color: 'var(--color-text-muted)' }}>
140
+ 点击对应 Harness 的“配置”,安装、检查运行时,并配置 settings.json、API
141
+ Key、端点和认证方式。
127
142
  </p>
128
143
 
129
144
  <CliStatusSection showSectionHeader={false} />
@@ -53,7 +53,9 @@ const PLATFORM_META: Record<PlatformType, { label: string; fields: PlatformField
53
53
  { key: 'app_secret', label: 'App Secret', placeholder: '...', required: true, secret: true },
54
54
  { key: 'allow_from', label: '允许来源(用户 ID 或 *)', placeholder: '* 或 ou_xxxx' },
55
55
  {
56
- key: 'share_session_in_channel', label: '群内共享记忆', placeholder: '',
56
+ key: 'share_session_in_channel',
57
+ label: '群内共享记忆',
58
+ placeholder: '',
57
59
  type: 'select',
58
60
  options: [
59
61
  { value: 'false', label: '关闭(默认)— 每人独立上下文' },
@@ -61,7 +63,9 @@ const PLATFORM_META: Record<PlatformType, { label: string; fields: PlatformField
61
63
  ],
62
64
  },
63
65
  {
64
- key: 'thread_isolation', label: '按回复串隔离', placeholder: '',
66
+ key: 'thread_isolation',
67
+ label: '按回复串隔离',
68
+ placeholder: '',
65
69
  type: 'select',
66
70
  options: [
67
71
  { value: 'false', label: '关闭(默认)' },
@@ -77,7 +81,9 @@ const PLATFORM_META: Record<PlatformType, { label: string; fields: PlatformField
77
81
  { key: 'app_secret', label: 'App Secret', placeholder: '...', required: true, secret: true },
78
82
  { key: 'allow_from', label: '允许来源', placeholder: '* 或 ou_xxxx' },
79
83
  {
80
- key: 'share_session_in_channel', label: '群内共享记忆', placeholder: '',
84
+ key: 'share_session_in_channel',
85
+ label: '群内共享记忆',
86
+ placeholder: '',
81
87
  type: 'select',
82
88
  options: [
83
89
  { value: 'false', label: '关闭(默认)— 每人独立上下文' },
@@ -98,10 +104,18 @@ const PLATFORM_META: Record<PlatformType, { label: string; fields: PlatformField
98
104
  telegram: {
99
105
  label: 'Telegram',
100
106
  fields: [
101
- { key: 'bot_token', label: 'Bot Token', placeholder: '1234567890:AAH...', required: true, secret: true },
107
+ {
108
+ key: 'bot_token',
109
+ label: 'Bot Token',
110
+ placeholder: '1234567890:AAH...',
111
+ required: true,
112
+ secret: true,
113
+ },
102
114
  { key: 'allow_from', label: '允许的 Chat ID', placeholder: '* 或 123456789' },
103
115
  {
104
- key: 'share_session_in_channel', label: '群内共享记忆', placeholder: '',
116
+ key: 'share_session_in_channel',
117
+ label: '群内共享记忆',
118
+ placeholder: '',
105
119
  type: 'select',
106
120
  options: [
107
121
  { value: 'false', label: '关闭(默认)— 每人独立上下文' },
@@ -117,7 +131,9 @@ const PLATFORM_META: Record<PlatformType, { label: string; fields: PlatformField
117
131
  { key: 'app_secret', label: 'App Secret', placeholder: '...', required: true, secret: true },
118
132
  { key: 'allow_from', label: '允许来源', placeholder: '*' },
119
133
  {
120
- key: 'share_session_in_channel', label: '群内共享记忆', placeholder: '',
134
+ key: 'share_session_in_channel',
135
+ label: '群内共享记忆',
136
+ placeholder: '',
121
137
  type: 'select',
122
138
  options: [
123
139
  { value: 'false', label: '关闭(默认)— 每人独立上下文' },
@@ -129,15 +145,19 @@ const PLATFORM_META: Record<PlatformType, { label: string; fields: PlatformField
129
145
  slack: {
130
146
  label: 'Slack',
131
147
  fields: [
132
- { key: 'bot_token', label: 'Bot Token', placeholder: 'xoxb-...', required: true, secret: true },
148
+ {
149
+ key: 'bot_token',
150
+ label: 'Bot Token',
151
+ placeholder: 'xoxb-...',
152
+ required: true,
153
+ secret: true,
154
+ },
133
155
  { key: 'app_token', label: 'App Token', placeholder: 'xapp-...', secret: true },
134
156
  ],
135
157
  },
136
158
  bridge: {
137
159
  label: 'Bridge(内部)',
138
- fields: [
139
- { key: 'allow_from', label: '允许来源', placeholder: '* 或平台名' },
140
- ],
160
+ fields: [{ key: 'allow_from', label: '允许来源', placeholder: '* 或平台名' }],
141
161
  },
142
162
  };
143
163
 
@@ -177,7 +197,7 @@ async function fetchProjectDetail(name: string): Promise<ProjectDetail> {
177
197
  async function addPlatform(
178
198
  projectName: string,
179
199
  type: string,
180
- options: Record<string, string>,
200
+ options: Record<string, string>
181
201
  ): Promise<void> {
182
202
  const res = await fetch(`/api/v1/projects/${encodeURIComponent(projectName)}/add-platform`, {
183
203
  method: 'POST',
@@ -213,16 +233,23 @@ export const PlatformsSection = (): React.JSX.Element => {
213
233
  }
214
234
  }, []);
215
235
 
216
- useEffect(() => { void refresh(); }, [refresh]);
236
+ useEffect(() => {
237
+ void refresh();
238
+ }, [refresh]);
217
239
 
218
240
  const toggleExpand = async (name: string) => {
219
- if (expanded === name) { setExpanded(null); return; }
241
+ if (expanded === name) {
242
+ setExpanded(null);
243
+ return;
244
+ }
220
245
  setExpanded(name);
221
246
  if (!details[name]) {
222
247
  try {
223
248
  const d = await fetchProjectDetail(name);
224
249
  setDetails((prev) => ({ ...prev, [name]: d }));
225
- } catch { /* ignore */ }
250
+ } catch {
251
+ /* ignore */
252
+ }
226
253
  }
227
254
  };
228
255
 
@@ -232,7 +259,9 @@ export const PlatformsSection = (): React.JSX.Element => {
232
259
  try {
233
260
  const d = await fetchProjectDetail(projectName);
234
261
  setDetails((prev) => ({ ...prev, [projectName]: d }));
235
- } catch { /* ignore */ }
262
+ } catch {
263
+ /* ignore */
264
+ }
236
265
  void refresh();
237
266
  };
238
267
 
@@ -257,7 +286,9 @@ export const PlatformsSection = (): React.JSX.Element => {
257
286
  </div>
258
287
 
259
288
  {loading && (
260
- <p className="text-sm" style={{ color: 'var(--color-text-muted)' }}>加载中…</p>
289
+ <p className="text-sm" style={{ color: 'var(--color-text-muted)' }}>
290
+ 加载中…
291
+ </p>
261
292
  )}
262
293
 
263
294
  <div className="space-y-2">
@@ -277,11 +308,17 @@ export const PlatformsSection = (): React.JSX.Element => {
277
308
  onClick={() => void toggleExpand(proj.name)}
278
309
  >
279
310
  {isExpanded ? (
280
- <ChevronDown className="size-4 shrink-0" style={{ color: 'var(--color-text-muted)' }} />
311
+ <ChevronDown
312
+ className="size-4 shrink-0"
313
+ style={{ color: 'var(--color-text-muted)' }}
314
+ />
281
315
  ) : (
282
- <ChevronRight className="size-4 shrink-0" style={{ color: 'var(--color-text-muted)' }} />
316
+ <ChevronRight
317
+ className="size-4 shrink-0"
318
+ style={{ color: 'var(--color-text-muted)' }}
319
+ />
283
320
  )}
284
- <span className="flex-1 font-medium text-sm" style={{ color: 'var(--color-text)' }}>
321
+ <span className="flex-1 text-sm font-medium" style={{ color: 'var(--color-text)' }}>
285
322
  {proj.name}
286
323
  </span>
287
324
  <span className="text-xs" style={{ color: 'var(--color-text-muted)' }}>
@@ -308,29 +345,33 @@ export const PlatformsSection = (): React.JSX.Element => {
308
345
  {/* Expanded detail */}
309
346
  {isExpanded && (
310
347
  <div
311
- className="border-t px-4 py-3 space-y-3"
348
+ className="space-y-3 border-t px-4 py-3"
312
349
  style={{ borderColor: 'var(--color-border)', background: 'var(--color-surface)' }}
313
350
  >
314
351
  {detail ? (
315
352
  <>
316
353
  {detail.platforms.map((p) => {
317
354
  const meta = PLATFORM_META[p.type as PlatformType];
318
- const cfg = detail.platform_configs.find(
319
- (c) => c.type === p.type,
320
- );
355
+ const cfg = detail.platform_configs.find((c) => c.type === p.type);
321
356
  return (
322
357
  <div
323
358
  key={p.type}
324
359
  className="rounded-lg border p-3"
325
- style={{ borderColor: 'var(--color-border)', background: 'var(--color-surface-raised)' }}
360
+ style={{
361
+ borderColor: 'var(--color-border)',
362
+ background: 'var(--color-surface-raised)',
363
+ }}
326
364
  >
327
- <div className="flex items-center gap-2 mb-2">
365
+ <div className="mb-2 flex items-center gap-2">
328
366
  {p.connected ? (
329
367
  <Wifi className="size-3.5 text-green-400" />
330
368
  ) : (
331
369
  <WifiOff className="size-3.5 text-red-400" />
332
370
  )}
333
- <span className="font-medium text-sm" style={{ color: 'var(--color-text)' }}>
371
+ <span
372
+ className="text-sm font-medium"
373
+ style={{ color: 'var(--color-text)' }}
374
+ >
334
375
  {meta?.label ?? p.type}
335
376
  </span>
336
377
  <span
@@ -340,31 +381,40 @@ export const PlatformsSection = (): React.JSX.Element => {
340
381
  {p.connected ? '已连接' : '未连接'}
341
382
  </span>
342
383
  </div>
343
- {cfg && Object.entries(cfg)
344
- .filter(([k]) => k !== 'type')
345
- .map(([k, v]) => (
346
- <div key={k} className="flex gap-2 text-xs mt-1">
347
- <span className="w-28 shrink-0" style={{ color: 'var(--color-text-muted)' }}>{k}</span>
348
- <span className="truncate font-mono" style={{ color: 'var(--color-text-secondary)' }}>
349
- {k.toLowerCase().includes('secret') || k.toLowerCase().includes('token') || k.toLowerCase().includes('key')
350
- ? `${String(v).slice(0, 6)}••••`
351
- : String(v)}
352
- </span>
353
- </div>
354
- ))}
384
+ {cfg &&
385
+ Object.entries(cfg)
386
+ .filter(([k]) => k !== 'type')
387
+ .map(([k, v]) => (
388
+ <div key={k} className="mt-1 flex gap-2 text-xs">
389
+ <span
390
+ className="w-28 shrink-0"
391
+ style={{ color: 'var(--color-text-muted)' }}
392
+ >
393
+ {k}
394
+ </span>
395
+ <span
396
+ className="truncate font-mono"
397
+ style={{ color: 'var(--color-text-secondary)' }}
398
+ >
399
+ {k.toLowerCase().includes('secret') ||
400
+ k.toLowerCase().includes('token') ||
401
+ k.toLowerCase().includes('key')
402
+ ? `${String(v).slice(0, 6)}••••`
403
+ : String(v)}
404
+ </span>
405
+ </div>
406
+ ))}
355
407
  </div>
356
408
  );
357
409
  })}
358
410
  </>
359
411
  ) : (
360
- <p className="text-xs" style={{ color: 'var(--color-text-muted)' }}>加载中…</p>
412
+ <p className="text-xs" style={{ color: 'var(--color-text-muted)' }}>
413
+ 加载中…
414
+ </p>
361
415
  )}
362
416
 
363
- <Button
364
- size="sm"
365
- variant="outline"
366
- onClick={() => setAddOpen(proj.name)}
367
- >
417
+ <Button size="sm" variant="outline" onClick={() => setAddOpen(proj.name)}>
368
418
  <Plus className="mr-1.5 size-3.5" />
369
419
  添加渠道
370
420
  </Button>
@@ -407,7 +457,11 @@ function AddPlatformDialog({
407
457
  const [error, setError] = useState<string | null>(null);
408
458
 
409
459
  useEffect(() => {
410
- if (open) { setPlatformType('feishu'); setFields({}); setError(null); }
460
+ if (open) {
461
+ setPlatformType('feishu');
462
+ setFields({});
463
+ setError(null);
464
+ }
411
465
  }, [open]);
412
466
 
413
467
  const meta = PLATFORM_META[platformType];
@@ -449,7 +503,10 @@ function AddPlatformDialog({
449
503
  <Label>平台类型</Label>
450
504
  <Select
451
505
  value={platformType}
452
- onValueChange={(v) => { setPlatformType(v as PlatformType); setFields({}); }}
506
+ onValueChange={(v) => {
507
+ setPlatformType(v as PlatformType);
508
+ setFields({});
509
+ }}
453
510
  >
454
511
  <SelectTrigger className="mt-1">
455
512
  <SelectValue />
@@ -480,7 +537,9 @@ function AddPlatformDialog({
480
537
  </SelectTrigger>
481
538
  <SelectContent>
482
539
  {f.options.map((o) => (
483
- <SelectItem key={o.value} value={o.value}>{o.label}</SelectItem>
540
+ <SelectItem key={o.value} value={o.value}>
541
+ {o.label}
542
+ </SelectItem>
484
543
  ))}
485
544
  </SelectContent>
486
545
  </Select>
@@ -496,9 +555,7 @@ function AddPlatformDialog({
496
555
  </div>
497
556
  ))}
498
557
 
499
- {error && (
500
- <p className="text-sm text-red-400">{error}</p>
501
- )}
558
+ {error && <p className="text-sm text-red-400">{error}</p>}
502
559
 
503
560
  <p className="text-xs" style={{ color: 'var(--color-text-muted)' }}>
504
561
  添加后需要重启 cc-connect 才能生效。
@@ -506,7 +563,9 @@ function AddPlatformDialog({
506
563
  </div>
507
564
 
508
565
  <DialogFooter>
509
- <Button variant="outline" onClick={onClose}>取消</Button>
566
+ <Button variant="outline" onClick={onClose}>
567
+ 取消
568
+ </Button>
510
569
  <Button onClick={() => void handleSave()} disabled={saving}>
511
570
  {saving ? '添加中…' : '添加'}
512
571
  </Button>