@yancyyu/openhermit 1.5.8 → 1.5.10

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 (107) hide show
  1. package/README.md +10 -1
  2. package/bin/alias-loader.mjs +51 -0
  3. package/bin/hermit.mjs +14 -5
  4. package/dist-renderer/assets/{ProjectEditorOverlay-BNoDw9T1.js → ProjectEditorOverlay-C5D83Zxv.js} +1 -1
  5. package/dist-renderer/assets/{TeamGraphOverlay-CfGRKQIu.js → TeamGraphOverlay-ajzuM1-u.js} +1 -1
  6. package/dist-renderer/assets/{_basePickBy-Ct8Hm5_h.js → _basePickBy-C9H2zmVj.js} +1 -1
  7. package/dist-renderer/assets/{_baseUniq-BofrAFBx.js → _baseUniq-CpGZGemc.js} +1 -1
  8. package/dist-renderer/assets/{arc-AbJgatzR.js → arc-CbGBDw-m.js} +1 -1
  9. package/dist-renderer/assets/{architectureDiagram-VXUJARFQ-gpniCJVk.js → architectureDiagram-VXUJARFQ-nuKXUIpb.js} +1 -1
  10. package/dist-renderer/assets/{blockDiagram-VD42YOAC-aBbbmONC.js → blockDiagram-VD42YOAC-DHUUE7Jc.js} +1 -1
  11. package/dist-renderer/assets/{c4Diagram-YG6GDRKO-DJio1IsU.js → c4Diagram-YG6GDRKO-OILHhqLM.js} +1 -1
  12. package/dist-renderer/assets/channel-DpUKLLrj.js +1 -0
  13. package/dist-renderer/assets/{chunk-4BX2VUAB-D1_HKao2.js → chunk-4BX2VUAB-dqNpZaQ8.js} +1 -1
  14. package/dist-renderer/assets/{chunk-55IACEB6-NAmVxF4k.js → chunk-55IACEB6-BCoSJQM-.js} +1 -1
  15. package/dist-renderer/assets/{chunk-B4BG7PRW-Ce829laz.js → chunk-B4BG7PRW-BLbg8yVR.js} +1 -1
  16. package/dist-renderer/assets/{chunk-DI55MBZ5-Ct2Le12y.js → chunk-DI55MBZ5-CUUWOs1Q.js} +1 -1
  17. package/dist-renderer/assets/{chunk-FMBD7UC4-Cie3DzKk.js → chunk-FMBD7UC4-D9geTN5P.js} +1 -1
  18. package/dist-renderer/assets/{chunk-QN33PNHL-4f5Yb50e.js → chunk-QN33PNHL-BGT8-BRX.js} +1 -1
  19. package/dist-renderer/assets/{chunk-QZHKN3VN-D9ranl9c.js → chunk-QZHKN3VN-CC8ebGaM.js} +1 -1
  20. package/dist-renderer/assets/{chunk-TZMSLE5B-bdGZWlEy.js → chunk-TZMSLE5B-CajekcT6.js} +1 -1
  21. package/dist-renderer/assets/classDiagram-2ON5EDUG-LL85aSlz.js +1 -0
  22. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-LL85aSlz.js +1 -0
  23. package/dist-renderer/assets/clone-BHWsFzFA.js +1 -0
  24. package/dist-renderer/assets/{cose-bilkent-S5V4N54A-C6tvfcVi.js → cose-bilkent-S5V4N54A-C_x7hSy3.js} +1 -1
  25. package/dist-renderer/assets/{dagre-6UL2VRFP-B-4qcZam.js → dagre-6UL2VRFP-C4Y1k4DZ.js} +1 -1
  26. package/dist-renderer/assets/{diagram-PSM6KHXK-CwT3TLjx.js → diagram-PSM6KHXK-oRIeULoh.js} +1 -1
  27. package/dist-renderer/assets/{diagram-QEK2KX5R-BWH6-ZFd.js → diagram-QEK2KX5R-DwSqw5HF.js} +1 -1
  28. package/dist-renderer/assets/{diagram-S2PKOQOG-DfpPnfi1.js → diagram-S2PKOQOG-DqjGYje2.js} +1 -1
  29. package/dist-renderer/assets/{erDiagram-Q2GNP2WA-BFbEFR4x.js → erDiagram-Q2GNP2WA-CEV5bBgg.js} +1 -1
  30. package/dist-renderer/assets/{flowDiagram-NV44I4VS-Dg3cf5hW.js → flowDiagram-NV44I4VS-BQQkrRyu.js} +1 -1
  31. package/dist-renderer/assets/{ganttDiagram-JELNMOA3-B21y55W5.js → ganttDiagram-JELNMOA3-CLy4WR1G.js} +1 -1
  32. package/dist-renderer/assets/{gitGraphDiagram-V2S2FVAM-BDV3BJzn.js → gitGraphDiagram-V2S2FVAM-6W3ioQu_.js} +1 -1
  33. package/dist-renderer/assets/{graph-BfaZ4hZt.js → graph-BnLKQvhH.js} +1 -1
  34. package/dist-renderer/assets/{index-CCqtDawH.js → index-B4aiRxoU.js} +1 -1
  35. package/dist-renderer/assets/{index-pMg_LlsS.js → index-B8lKqPVq.js} +1 -1
  36. package/dist-renderer/assets/{index-CZltVMDP.js → index-BRuhNKyU.js} +12 -12
  37. package/dist-renderer/assets/{index-BMXHMpkG.js → index-BufvLVIl.js} +1 -1
  38. package/dist-renderer/assets/{index-Ct0-y9TF.js → index-C1xShqKH.js} +1 -1
  39. package/dist-renderer/assets/{index-CVMSpK8C.js → index-zIOLLI7O.js} +1 -1
  40. package/dist-renderer/assets/{infoDiagram-HS3SLOUP-DvMlS0CL.js → infoDiagram-HS3SLOUP-BoBweEEY.js} +1 -1
  41. package/dist-renderer/assets/{journeyDiagram-XKPGCS4Q-DIyMluRv.js → journeyDiagram-XKPGCS4Q-DLL0V5oP.js} +1 -1
  42. package/dist-renderer/assets/{kanban-definition-3W4ZIXB7-CVOx8f-7.js → kanban-definition-3W4ZIXB7-HsFtEDG3.js} +1 -1
  43. package/dist-renderer/assets/{layout-BPKIXUf4.js → layout-ClIooAAq.js} +1 -1
  44. package/dist-renderer/assets/{linear-CScZGLr2.js → linear-r3RJcj8y.js} +1 -1
  45. package/dist-renderer/assets/{mindmap-definition-VGOIOE7T-CmDQ7Wo6.js → mindmap-definition-VGOIOE7T-BA_P1U4V.js} +1 -1
  46. package/dist-renderer/assets/{pieDiagram-ADFJNKIX-DbVClin-.js → pieDiagram-ADFJNKIX-CzPAfkTB.js} +1 -1
  47. package/dist-renderer/assets/{quadrantDiagram-AYHSOK5B-CAB0MYcW.js → quadrantDiagram-AYHSOK5B-PvdPWzFJ.js} +1 -1
  48. package/dist-renderer/assets/{requirementDiagram-UZGBJVZJ-w2Lfpg3T.js → requirementDiagram-UZGBJVZJ-CHqIL_Od.js} +1 -1
  49. package/dist-renderer/assets/{sankeyDiagram-TZEHDZUN-kvG1QoKY.js → sankeyDiagram-TZEHDZUN-ConzpACM.js} +1 -1
  50. package/dist-renderer/assets/{sequenceDiagram-WL72ISMW-DCVBQ23J.js → sequenceDiagram-WL72ISMW-Zryq4oxP.js} +1 -1
  51. package/dist-renderer/assets/{stateDiagram-FKZM4ZOC-ItZ0JBvq.js → stateDiagram-FKZM4ZOC-BA9V7NHF.js} +1 -1
  52. package/dist-renderer/assets/{stateDiagram-v2-4FDKWEC3-Hpmw4dMm.js → stateDiagram-v2-4FDKWEC3-CGnaujD-.js} +1 -1
  53. package/dist-renderer/assets/{timeline-definition-IT6M3QCI-BzSFaAjV.js → timeline-definition-IT6M3QCI-DPs2ZjMm.js} +1 -1
  54. package/dist-renderer/assets/{treemap-GDKQZRPO-fSz4hQn0.js → treemap-GDKQZRPO-B0lzrLxb.js} +1 -1
  55. package/dist-renderer/assets/{xychartDiagram-PRI3JC2R-CT1kaGlv.js → xychartDiagram-PRI3JC2R-CINGmMxX.js} +1 -1
  56. package/dist-renderer/index.html +1 -1
  57. package/package.json +2 -1
  58. package/src/main/server.ts +993 -764
  59. package/src/main/services/UpdateService.ts +4 -1
  60. package/src/main/services/ccConnect/CcConnectBridge.ts +1 -8
  61. package/src/main/services/ccConnect/CcConnectClient.ts +7 -2
  62. package/src/main/services/teams-mvp/TeamProvisioningService.ts +14 -4
  63. package/src/main/services/teams-mvp/TeamWorkspaceService.ts +11 -6
  64. package/src/renderer/App.tsx +18 -7
  65. package/src/renderer/api/httpClient.ts +136 -42
  66. package/src/renderer/components/chat/ChatHistory.tsx +11 -8
  67. package/src/renderer/components/dashboard/DashboardView.tsx +4 -2
  68. package/src/renderer/components/extensions/ExtensionStoreView.tsx +2 -7
  69. package/src/renderer/components/layout/Sidebar.tsx +3 -1
  70. package/src/renderer/components/schedules/SchedulesView.tsx +15 -13
  71. package/src/renderer/components/settings/SettingsTabs.tsx +2 -1
  72. package/src/renderer/components/settings/hooks/useSettingsHandlers.ts +4 -5
  73. package/src/renderer/components/settings/sections/AdvancedSection.tsx +19 -4
  74. package/src/renderer/components/settings/sections/CliStatusSection.tsx +63 -59
  75. package/src/renderer/components/settings/sections/GeneralSection.tsx +5 -11
  76. package/src/renderer/components/settings/sections/HarnessSection.tsx +30 -15
  77. package/src/renderer/components/settings/sections/PlatformsSection.tsx +110 -51
  78. package/src/renderer/components/sidebar/SidebarSessions.tsx +100 -67
  79. package/src/renderer/components/sidebar/WorkspaceBrowser.tsx +26 -43
  80. package/src/renderer/components/team/CcSessionsSection.tsx +34 -14
  81. package/src/renderer/components/team/TeamDetailView.tsx +150 -148
  82. package/src/renderer/components/team/TeamEmptyState.tsx +27 -16
  83. package/src/renderer/components/team/TeamListView.tsx +4 -2
  84. package/src/renderer/components/team/activity/ActivityItem.tsx +6 -1
  85. package/src/renderer/components/team/dialogs/CreateTeamDialog.tsx +282 -75
  86. package/src/renderer/components/team/dialogs/EditTeamDialog.tsx +2 -1
  87. package/src/renderer/components/team/dialogs/PlatformManualForm.tsx +64 -21
  88. package/src/renderer/components/team/dialogs/PlatformSetupQR.tsx +68 -19
  89. package/src/renderer/components/team/dialogs/ProjectPathSelector.tsx +20 -16
  90. package/src/renderer/components/team/dialogs/platformMeta.ts +66 -11
  91. package/src/renderer/components/team/editor/EditorFileTree.tsx +9 -7
  92. package/src/renderer/components/team/kanban/KanbanBoard.tsx +7 -10
  93. package/src/renderer/components/team/kanban/KanbanTaskCard.tsx +1 -3
  94. package/src/renderer/components/team/members/MemberDetailDialog.tsx +1 -5
  95. package/src/renderer/components/team/messages/MessageComposer.tsx +3 -1
  96. package/src/renderer/components/team/messages/MessagesPanel.tsx +34 -26
  97. package/src/renderer/components/team/schedule/CcCronScheduleDialog.tsx +1 -3
  98. package/src/renderer/components/team/schedule/ScheduleSection.tsx +9 -10
  99. package/src/renderer/store/slices/scheduleSlice.ts +4 -1
  100. package/src/renderer/store/slices/teamSlice.ts +3 -1
  101. package/src/shared/types/api.ts +70 -21
  102. package/src/shared/utils/leadDetection.ts +5 -1
  103. package/tsconfig.json +26 -0
  104. package/dist-renderer/assets/channel-CZ8sd5Xf.js +0 -1
  105. package/dist-renderer/assets/classDiagram-2ON5EDUG-CMcfSKj5.js +0 -1
  106. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-CMcfSKj5.js +0 -1
  107. package/dist-renderer/assets/clone-CMuwA8RV.js +0 -1
@@ -17,19 +17,27 @@ interface Props {
17
17
  onCancel: () => void;
18
18
  }
19
19
 
20
- export default function PlatformManualForm({ platformType, platformMeta: meta, projectName, workDir, agentType, onComplete, onCancel }: Props) {
20
+ export default function PlatformManualForm({
21
+ platformType,
22
+ platformMeta: meta,
23
+ projectName,
24
+ workDir,
25
+ agentType,
26
+ onComplete,
27
+ onCancel,
28
+ }: Props) {
21
29
  const [values, setValues] = useState<Record<string, unknown>>({});
22
30
  const [showAdvanced, setShowAdvanced] = useState(false);
23
31
  const [saving, setSaving] = useState(false);
24
32
  const [error, setError] = useState('');
25
33
 
26
- const basicFields = meta.fields.filter(f => f.group !== 'advanced');
27
- const advancedFields = meta.fields.filter(f => f.group === 'advanced');
34
+ const basicFields = meta.fields.filter((f) => f.group !== 'advanced');
35
+ const advancedFields = meta.fields.filter((f) => f.group === 'advanced');
28
36
 
29
37
  const handleSave = async () => {
30
- const missing = meta.fields.filter(f => f.required && !values[f.key]);
38
+ const missing = meta.fields.filter((f) => f.required && !values[f.key]);
31
39
  if (missing.length > 0) {
32
- setError(missing.map(f => f.label).join(', ') + ' 为必填项');
40
+ setError(missing.map((f) => f.label).join(', ') + ' 为必填项');
33
41
  return;
34
42
  }
35
43
 
@@ -44,7 +52,10 @@ export default function PlatformManualForm({ platformType, platformMeta: meta, p
44
52
  }
45
53
  }
46
54
  await api.ccSetup.addPlatform(projectName, {
47
- type: platformType, options: opts, work_dir: workDir, agent_type: agentType,
55
+ type: platformType,
56
+ options: opts,
57
+ work_dir: workDir,
58
+ agent_type: agentType,
48
59
  });
49
60
  onComplete();
50
61
  } catch (e: unknown) {
@@ -54,14 +65,19 @@ export default function PlatformManualForm({ platformType, platformMeta: meta, p
54
65
  }
55
66
  };
56
67
 
57
- const set = (key: string, val: unknown) => setValues(prev => ({ ...prev, [key]: val }));
68
+ const set = (key: string, val: unknown) => setValues((prev) => ({ ...prev, [key]: val }));
58
69
 
59
70
  return (
60
71
  <div className="space-y-4 py-2">
61
72
  <p className="text-sm font-medium text-gray-900 dark:text-white">{meta.label}</p>
62
73
 
63
- {basicFields.map(f => (
64
- <FieldInput key={f.key} field={f} value={values[f.key] as string | boolean | undefined} onChange={v => set(f.key, v)} />
74
+ {basicFields.map((f) => (
75
+ <FieldInput
76
+ key={f.key}
77
+ field={f}
78
+ value={values[f.key] as string | boolean | undefined}
79
+ onChange={(v) => set(f.key, v)}
80
+ />
65
81
  ))}
66
82
 
67
83
  {advancedFields.length > 0 && (
@@ -71,23 +87,34 @@ export default function PlatformManualForm({ platformType, platformMeta: meta, p
71
87
  onClick={() => setShowAdvanced(!showAdvanced)}
72
88
  className="flex items-center gap-1 text-xs text-gray-500 hover:text-gray-700 dark:hover:text-gray-300"
73
89
  >
74
- <ChevronDown size={12} className={cn('transition-transform', showAdvanced && 'rotate-180')} />
90
+ <ChevronDown
91
+ size={12}
92
+ className={cn('transition-transform', showAdvanced && 'rotate-180')}
93
+ />
75
94
  高级选项 ({advancedFields.length})
76
95
  </button>
77
- {showAdvanced && advancedFields.map(f => (
78
- <FieldInput key={f.key} field={f} value={values[f.key] as string | boolean | undefined} onChange={v => set(f.key, v)} />
79
- ))}
96
+ {showAdvanced &&
97
+ advancedFields.map((f) => (
98
+ <FieldInput
99
+ key={f.key}
100
+ field={f}
101
+ value={values[f.key] as string | boolean | undefined}
102
+ onChange={(v) => set(f.key, v)}
103
+ />
104
+ ))}
80
105
  </>
81
106
  )}
82
107
 
83
108
  {error && (
84
- <div className="flex items-center gap-2 text-sm text-red-500 bg-red-50 dark:bg-red-900/20 rounded-lg p-3">
109
+ <div className="flex items-center gap-2 rounded-lg bg-red-50 p-3 text-sm text-red-500 dark:bg-red-900/20">
85
110
  <AlertCircle size={14} className="shrink-0" /> {error}
86
111
  </div>
87
112
  )}
88
113
 
89
114
  <div className="flex justify-between pt-2">
90
- <Button variant="outline" size="sm" onClick={onCancel}>返回</Button>
115
+ <Button variant="outline" size="sm" onClick={onCancel}>
116
+ 返回
117
+ </Button>
91
118
  <Button size="sm" onClick={handleSave} disabled={saving}>
92
119
  {saving ? '保存中...' : '绑定平台'}
93
120
  </Button>
@@ -96,17 +123,25 @@ export default function PlatformManualForm({ platformType, platformMeta: meta, p
96
123
  );
97
124
  }
98
125
 
99
- function FieldInput({ field, value, onChange }: { field: FieldDef; value: string | boolean | undefined; onChange: (v: unknown) => void }) {
126
+ function FieldInput({
127
+ field,
128
+ value,
129
+ onChange,
130
+ }: {
131
+ field: FieldDef;
132
+ value: string | boolean | undefined;
133
+ onChange: (v: unknown) => void;
134
+ }) {
100
135
  const [showPwd, setShowPwd] = useState(false);
101
136
 
102
137
  if (field.type === 'boolean') {
103
138
  return (
104
- <label className="flex items-center gap-2 cursor-pointer">
139
+ <label className="flex cursor-pointer items-center gap-2">
105
140
  <input
106
141
  type="checkbox"
107
142
  checked={!!value}
108
- onChange={e => onChange(e.target.checked)}
109
- className="w-4 h-4 rounded border-gray-300 dark:border-gray-600 text-blue-600 focus:ring-blue-500"
143
+ onChange={(e) => onChange(e.target.checked)}
144
+ className="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500 dark:border-gray-600"
110
145
  />
111
146
  <span className="text-sm text-gray-700 dark:text-gray-300">{field.label}</span>
112
147
  {field.hint && <span className="text-[11px] text-gray-400">({field.hint})</span>}
@@ -125,7 +160,15 @@ function FieldInput({ field, value, onChange }: { field: FieldDef; value: string
125
160
  <Input
126
161
  type={isPassword && !showPwd ? 'password' : field.type === 'number' ? 'number' : 'text'}
127
162
  value={typeof value === 'string' ? value : ''}
128
- onChange={e => onChange(field.type === 'number' ? (e.target.value ? Number(e.target.value) : '') : e.target.value)}
163
+ onChange={(e) =>
164
+ onChange(
165
+ field.type === 'number'
166
+ ? e.target.value
167
+ ? Number(e.target.value)
168
+ : ''
169
+ : e.target.value
170
+ )
171
+ }
129
172
  placeholder={field.placeholder}
130
173
  className="text-sm"
131
174
  />
@@ -139,7 +182,7 @@ function FieldInput({ field, value, onChange }: { field: FieldDef; value: string
139
182
  </button>
140
183
  )}
141
184
  </div>
142
- {field.hint && <p className="text-[11px] text-gray-400 mt-1">{field.hint}</p>}
185
+ {field.hint && <p className="mt-1 text-[11px] text-gray-400">{field.hint}</p>}
143
186
  </div>
144
187
  );
145
188
  }
@@ -2,10 +2,28 @@ import { useCallback, useEffect, useRef, useState } from 'react';
2
2
  import { QRCodeSVG } from 'qrcode.react';
3
3
  import { Button } from '@renderer/components/ui/button';
4
4
  import { api } from '@renderer/api';
5
- import { AlertCircle, CheckCircle2, Loader2, RefreshCw, RotateCcw, Smartphone, XCircle } from 'lucide-react';
5
+ import {
6
+ AlertCircle,
7
+ CheckCircle2,
8
+ Loader2,
9
+ RefreshCw,
10
+ RotateCcw,
11
+ Smartphone,
12
+ XCircle,
13
+ } from 'lucide-react';
6
14
 
7
15
  type PlatformKind = 'feishu' | 'lark' | 'weixin';
8
- type Phase = 'idle' | 'loading' | 'scanning' | 'scanned' | 'completed' | 'expired' | 'denied' | 'error' | 'saving' | 'restarting';
16
+ type Phase =
17
+ | 'idle'
18
+ | 'loading'
19
+ | 'scanning'
20
+ | 'scanned'
21
+ | 'completed'
22
+ | 'expired'
23
+ | 'denied'
24
+ | 'error'
25
+ | 'saving'
26
+ | 'restarting';
9
27
 
10
28
  interface Props {
11
29
  platformType: PlatformKind;
@@ -16,7 +34,14 @@ interface Props {
16
34
  onCancel: () => void;
17
35
  }
18
36
 
19
- export default function PlatformSetupQR({ platformType, projectName, workDir, agentType, onComplete, onCancel }: Props) {
37
+ export default function PlatformSetupQR({
38
+ platformType,
39
+ projectName,
40
+ workDir,
41
+ agentType,
42
+ onComplete,
43
+ onCancel,
44
+ }: Props) {
20
45
  const [phase, setPhase] = useState<Phase>('idle');
21
46
  const [qrUrl, setQrUrl] = useState('');
22
47
  const [error, setError] = useState('');
@@ -27,7 +52,9 @@ export default function PlatformSetupQR({ platformType, projectName, workDir, ag
27
52
  const weixinRef = useRef({ qrKey: '' });
28
53
 
29
54
  useEffect(() => {
30
- return () => { cancelledRef.current = true; };
55
+ return () => {
56
+ cancelledRef.current = true;
57
+ };
31
58
  }, []);
32
59
 
33
60
  const isFeishu = platformType === 'feishu' || platformType === 'lark';
@@ -39,7 +66,11 @@ export default function PlatformSetupQR({ platformType, projectName, workDir, ag
39
66
  pollingRef.current = false;
40
67
  try {
41
68
  const res = await api.ccSetup.feishuBegin();
42
- feishuRef.current = { deviceCode: res.device_code, baseUrl: res.base_url ?? '', interval: res.interval || 5 };
69
+ feishuRef.current = {
70
+ deviceCode: res.device_code,
71
+ baseUrl: res.base_url ?? '',
72
+ interval: res.interval || 5,
73
+ };
43
74
  setQrUrl(res.qr_url);
44
75
  setPhase('scanning');
45
76
  pollFeishu();
@@ -56,7 +87,10 @@ export default function PlatformSetupQR({ platformType, projectName, workDir, ag
56
87
  const poll = async () => {
57
88
  while (!cancelledRef.current) {
58
89
  try {
59
- const res = await api.ccSetup.feishuPoll(feishuRef.current.deviceCode, feishuRef.current.baseUrl || undefined);
90
+ const res = await api.ccSetup.feishuPoll(
91
+ feishuRef.current.deviceCode,
92
+ feishuRef.current.baseUrl || undefined
93
+ );
60
94
  if (cancelledRef.current) break;
61
95
  if (res.base_url) feishuRef.current.baseUrl = res.base_url;
62
96
  if (res.slow_down) feishuRef.current.interval += 5;
@@ -65,9 +99,13 @@ export default function PlatformSetupQR({ platformType, projectName, workDir, ag
65
99
  case 'completed':
66
100
  setPhase('saving');
67
101
  await api.ccSetup.feishuSave({
68
- project: projectName, app_id: res.app_id!, app_secret: res.app_secret!,
69
- platform_type: res.platform, owner_open_id: res.owner_open_id,
70
- work_dir: workDir, agent_type: agentType,
102
+ project: projectName,
103
+ app_id: res.app_id!,
104
+ app_secret: res.app_secret!,
105
+ platform_type: res.platform,
106
+ owner_open_id: res.owner_open_id,
107
+ work_dir: workDir,
108
+ agent_type: agentType,
71
109
  });
72
110
  setPhase('completed');
73
111
  pollingRef.current = false;
@@ -130,9 +168,13 @@ export default function PlatformSetupQR({ platformType, projectName, workDir, ag
130
168
  case 'confirmed':
131
169
  setPhase('saving');
132
170
  await api.ccSetup.weixinSave({
133
- project: projectName, token: pollRes.bot_token!,
134
- base_url: pollRes.base_url, ilink_bot_id: pollRes.ilink_bot_id,
135
- ilink_user_id: pollRes.ilink_user_id, work_dir: workDir, agent_type: agentType,
171
+ project: projectName,
172
+ token: pollRes.bot_token!,
173
+ base_url: pollRes.base_url,
174
+ ilink_bot_id: pollRes.ilink_bot_id,
175
+ ilink_user_id: pollRes.ilink_user_id,
176
+ work_dir: workDir,
177
+ agent_type: agentType,
136
178
  });
137
179
  setPhase('completed');
138
180
  return;
@@ -173,7 +215,7 @@ export default function PlatformSetupQR({ platformType, projectName, workDir, ag
173
215
  {phase === 'idle' && (
174
216
  <>
175
217
  <Smartphone size={48} className="text-gray-400 dark:text-gray-500" />
176
- <p className="text-sm text-gray-600 dark:text-gray-400 text-center">
218
+ <p className="text-center text-sm text-gray-600 dark:text-gray-400">
177
219
  使用手机扫描 {platformLabel} 二维码,快速绑定渠道
178
220
  </p>
179
221
  <Button onClick={startFlow}>开始扫码绑定</Button>
@@ -189,11 +231,15 @@ export default function PlatformSetupQR({ platformType, projectName, workDir, ag
189
231
 
190
232
  {(phase === 'scanning' || phase === 'scanned' || phase === 'saving') && (
191
233
  <>
192
- <div className="p-4 bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700">
234
+ <div className="rounded-xl border border-gray-200 bg-white p-4 shadow-sm dark:border-gray-700 dark:bg-gray-800">
193
235
  <QRCodeSVG value={qrUrl} size={200} level="M" />
194
236
  </div>
195
- <p className="text-sm text-gray-600 dark:text-gray-400 text-center max-w-xs">
196
- {phase === 'scanned' ? '已扫描!请在手机上确认...' : phase === 'saving' ? '正在保存配置...' : scanHint}
237
+ <p className="max-w-xs text-center text-sm text-gray-600 dark:text-gray-400">
238
+ {phase === 'scanned'
239
+ ? '已扫描!请在手机上确认...'
240
+ : phase === 'saving'
241
+ ? '正在保存配置...'
242
+ : scanHint}
197
243
  </p>
198
244
  {phase === 'scanning' && (
199
245
  <div className="flex items-center gap-2 text-xs text-gray-400">
@@ -217,7 +263,7 @@ export default function PlatformSetupQR({ platformType, projectName, workDir, ag
217
263
  <div className="flex flex-col items-center gap-3 py-4">
218
264
  <CheckCircle2 size={48} className="text-green-500" />
219
265
  <p className="text-sm font-medium text-green-700 dark:text-green-400">平台绑定成功!</p>
220
- <p className="text-xs text-gray-500 text-center">重启服务使新平台生效</p>
266
+ <p className="text-center text-xs text-gray-500">重启服务使新平台生效</p>
221
267
  <div className="flex gap-2">
222
268
  <Button
223
269
  variant="outline"
@@ -276,7 +322,10 @@ export default function PlatformSetupQR({ platformType, projectName, workDir, ag
276
322
  )}
277
323
 
278
324
  {phase !== 'completed' && phase !== 'restarting' && (
279
- <button onClick={onCancel} className="text-xs text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 mt-2">
325
+ <button
326
+ onClick={onCancel}
327
+ className="mt-2 text-xs text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
328
+ >
280
329
  取消
281
330
  </button>
282
331
  )}
@@ -285,5 +334,5 @@ export default function PlatformSetupQR({ platformType, projectName, workDir, ag
285
334
  }
286
335
 
287
336
  function sleep(ms: number): Promise<void> {
288
- return new Promise(resolve => setTimeout(resolve, ms));
337
+ return new Promise((resolve) => setTimeout(resolve, ms));
289
338
  }
@@ -190,11 +190,7 @@ interface FolderBrowserProps {
190
190
  fieldError?: string | null;
191
191
  }
192
192
 
193
- const FolderBrowser = ({
194
- value,
195
- onChange,
196
- fieldError,
197
- }: FolderBrowserProps): React.JSX.Element => {
193
+ const FolderBrowser = ({ value, onChange, fieldError }: FolderBrowserProps): React.JSX.Element => {
198
194
  const [open, setOpen] = useState(false);
199
195
  const [currentPath, setCurrentPath] = useState(value || '');
200
196
  const [dirs, setDirs] = useState<string[]>([]);
@@ -255,23 +251,26 @@ const FolderBrowser = ({
255
251
  浏览
256
252
  </Button>
257
253
  </div>
258
- <p className="text-[11px] text-[var(--color-text-muted)]">
259
- 如果目录不存在,将自动创建。
260
- </p>
254
+ <p className="text-[11px] text-[var(--color-text-muted)]">如果目录不存在,将自动创建。</p>
261
255
  {fieldError && (
262
256
  <p className="text-[11px]" style={{ color: 'var(--field-error-text)' }}>
263
257
  {fieldError}
264
258
  </p>
265
259
  )}
266
260
 
267
- <Dialog open={open} onOpenChange={(o: boolean) => { if (!o) setOpen(false); }}>
261
+ <Dialog
262
+ open={open}
263
+ onOpenChange={(o: boolean) => {
264
+ if (!o) setOpen(false);
265
+ }}
266
+ >
268
267
  <DialogContent className="max-w-md">
269
268
  <DialogHeader>
270
269
  <DialogTitle>选择目录</DialogTitle>
271
270
  </DialogHeader>
272
271
 
273
272
  {/* Path breadcrumb */}
274
- <div className="flex items-center gap-1 text-xs text-[var(--color-text-muted)] truncate">
273
+ <div className="flex items-center gap-1 truncate text-xs text-[var(--color-text-muted)]">
275
274
  <button
276
275
  type="button"
277
276
  className="shrink-0 hover:text-[var(--color-text)]"
@@ -291,9 +290,7 @@ const FolderBrowser = ({
291
290
  加载中…
292
291
  </div>
293
292
  )}
294
- {error && (
295
- <div className="px-3 py-4 text-xs text-red-400">{error}</div>
296
- )}
293
+ {error && <div className="px-3 py-4 text-xs text-red-400">{error}</div>}
297
294
  {!loading && !error && dirs.length === 0 && (
298
295
  <div className="px-3 py-4 text-xs text-[var(--color-text-muted)]">
299
296
  此目录下没有子目录。可手动输入路径。
@@ -311,7 +308,10 @@ const FolderBrowser = ({
311
308
  >
312
309
  <Folder size={14} className="shrink-0 text-[var(--color-text-muted)]" />
313
310
  <span className="truncate">{dir}</span>
314
- <ChevronRight size={14} className="ml-auto shrink-0 text-[var(--color-text-muted)]" />
311
+ <ChevronRight
312
+ size={14}
313
+ className="ml-auto shrink-0 text-[var(--color-text-muted)]"
314
+ />
315
315
  </button>
316
316
  </li>
317
317
  ))}
@@ -320,8 +320,12 @@ const FolderBrowser = ({
320
320
  </div>
321
321
 
322
322
  <DialogFooter>
323
- <Button variant="outline" onClick={() => setOpen(false)}>取消</Button>
324
- <Button onClick={handleConfirm} disabled={!currentPath}>选择</Button>
323
+ <Button variant="outline" onClick={() => setOpen(false)}>
324
+ 取消
325
+ </Button>
326
+ <Button onClick={handleConfirm} disabled={!currentPath}>
327
+ 选择
328
+ </Button>
325
329
  </DialogFooter>
326
330
  </DialogContent>
327
331
  </Dialog>
@@ -22,8 +22,20 @@ export const platformMeta: Record<string, PlatformMeta> = {
22
22
  telegram: {
23
23
  label: 'Telegram',
24
24
  fields: [
25
- { key: 'token', label: 'Bot Token', required: true, type: 'password', placeholder: '123456:ABC-DEF...' },
26
- { key: 'allow_from', label: 'Allow From', placeholder: '* (all)', group: 'advanced', hint: '限制可交互的用户/群,* 表示所有' },
25
+ {
26
+ key: 'token',
27
+ label: 'Bot Token',
28
+ required: true,
29
+ type: 'password',
30
+ placeholder: '123456:ABC-DEF...',
31
+ },
32
+ {
33
+ key: 'allow_from',
34
+ label: 'Allow From',
35
+ placeholder: '* (all)',
36
+ group: 'advanced',
37
+ hint: '限制可交互的用户/群,* 表示所有',
38
+ },
27
39
  { key: 'group_reply_all', label: '群聊回复全部', type: 'boolean', group: 'advanced' },
28
40
  { key: 'share_session_in_channel', label: '共享群会话', type: 'boolean', group: 'advanced' },
29
41
  ],
@@ -33,19 +45,47 @@ export const platformMeta: Record<string, PlatformMeta> = {
33
45
  fields: [
34
46
  { key: 'token', label: 'Bot Token', required: true, type: 'password' },
35
47
  { key: 'allow_from', label: 'Allow From', placeholder: '* (all)', group: 'advanced' },
36
- { key: 'guild_id', label: 'Guild ID', placeholder: '', group: 'advanced', hint: '限定特定服务器' },
48
+ {
49
+ key: 'guild_id',
50
+ label: 'Guild ID',
51
+ placeholder: '',
52
+ group: 'advanced',
53
+ hint: '限定特定服务器',
54
+ },
37
55
  { key: 'group_reply_all', label: '群聊回复全部', type: 'boolean', group: 'advanced' },
38
- { key: 'share_session_in_channel', label: '共享频道会话', type: 'boolean', group: 'advanced' },
56
+ {
57
+ key: 'share_session_in_channel',
58
+ label: '共享频道会话',
59
+ type: 'boolean',
60
+ group: 'advanced',
61
+ },
39
62
  { key: 'thread_isolation', label: 'Thread 隔离', type: 'boolean', group: 'advanced' },
40
63
  ],
41
64
  },
42
65
  slack: {
43
66
  label: 'Slack',
44
67
  fields: [
45
- { key: 'bot_token', label: 'Bot Token', required: true, type: 'password', placeholder: 'xoxb-...' },
46
- { key: 'app_token', label: 'App Token', required: true, type: 'password', placeholder: 'xapp-...' },
68
+ {
69
+ key: 'bot_token',
70
+ label: 'Bot Token',
71
+ required: true,
72
+ type: 'password',
73
+ placeholder: 'xoxb-...',
74
+ },
75
+ {
76
+ key: 'app_token',
77
+ label: 'App Token',
78
+ required: true,
79
+ type: 'password',
80
+ placeholder: 'xapp-...',
81
+ },
47
82
  { key: 'allow_from', label: 'Allow From', placeholder: '* (all)', group: 'advanced' },
48
- { key: 'share_session_in_channel', label: '共享频道会话', type: 'boolean', group: 'advanced' },
83
+ {
84
+ key: 'share_session_in_channel',
85
+ label: '共享频道会话',
86
+ type: 'boolean',
87
+ group: 'advanced',
88
+ },
49
89
  ],
50
90
  },
51
91
  dingtalk: {
@@ -64,10 +104,25 @@ export const platformMeta: Record<string, PlatformMeta> = {
64
104
  { key: 'corp_secret', label: 'Corp Secret', required: true, type: 'password' },
65
105
  { key: 'agent_id', label: 'Agent ID', required: true, placeholder: '1000002' },
66
106
  { key: 'callback_token', label: 'Callback Token', required: true },
67
- { key: 'callback_aes_key', label: 'Callback AES Key', required: true, hint: '43 位 AES 密钥' },
107
+ {
108
+ key: 'callback_aes_key',
109
+ label: 'Callback AES Key',
110
+ required: true,
111
+ hint: '43 位 AES 密钥',
112
+ },
68
113
  { key: 'port', label: '端口', required: true, placeholder: '8081' },
69
- { key: 'callback_path', label: 'Callback Path', placeholder: '/wecom/callback', group: 'advanced' },
70
- { key: 'api_base_url', label: 'API Base URL', placeholder: 'https://qyapi.weixin.qq.com', group: 'advanced' },
114
+ {
115
+ key: 'callback_path',
116
+ label: 'Callback Path',
117
+ placeholder: '/wecom/callback',
118
+ group: 'advanced',
119
+ },
120
+ {
121
+ key: 'api_base_url',
122
+ label: 'API Base URL',
123
+ placeholder: 'https://qyapi.weixin.qq.com',
124
+ group: 'advanced',
125
+ },
71
126
  { key: 'allow_from', label: 'Allow From', placeholder: '* (all)', group: 'advanced' },
72
127
  ],
73
128
  },
@@ -114,5 +169,5 @@ export const platformMeta: Record<string, PlatformMeta> = {
114
169
  export const QR_PLATFORMS = ['feishu', 'lark', 'weixin'] as const;
115
170
 
116
171
  export function isQRPlatform(type: string): boolean {
117
- return QR_PLATFORMS.includes(type as typeof QR_PLATFORMS[number]);
172
+ return QR_PLATFORMS.includes(type as (typeof QR_PLATFORMS)[number]);
118
173
  }
@@ -876,13 +876,15 @@ function convertEntriesToNodes(entries: unknown): TreeNode<FileTreeEntry>[] {
876
876
  children: Array.isArray(entry.children) ? entry.children : undefined,
877
877
  } as FileTreeEntry;
878
878
 
879
- return [{
880
- name: normalizedEntry.name,
881
- fullPath: normalizedEntry.path, // absolute path — matches expandedDirs keys
882
- isFile: normalizedEntry.type === 'file',
883
- data: normalizedEntry,
884
- children: convertEntriesToNodes(normalizedEntry.children),
885
- }];
879
+ return [
880
+ {
881
+ name: normalizedEntry.name,
882
+ fullPath: normalizedEntry.path, // absolute path — matches expandedDirs keys
883
+ isFile: normalizedEntry.type === 'file',
884
+ data: normalizedEntry,
885
+ children: convertEntriesToNodes(normalizedEntry.children),
886
+ },
887
+ ];
886
888
  });
887
889
  }
888
890
 
@@ -433,16 +433,13 @@ export const KanbanBoard = ({
433
433
  );
434
434
  };
435
435
 
436
- const visibleColumns = useMemo(
437
- () => {
438
- if (filter.columns.size === 0) {
439
- return COLUMNS;
440
- }
441
- const narrowed = COLUMNS.filter((c) => filter.columns.has(c.id));
442
- return narrowed.length > 0 ? narrowed : COLUMNS;
443
- },
444
- [filter.columns]
445
- );
436
+ const visibleColumns = useMemo(() => {
437
+ if (filter.columns.size === 0) {
438
+ return COLUMNS;
439
+ }
440
+ const narrowed = COLUMNS.filter((c) => filter.columns.has(c.id));
441
+ return narrowed.length > 0 ? narrowed : COLUMNS;
442
+ }, [filter.columns]);
446
443
  const primaryVisibleColumnId = visibleColumns[0]?.id ?? null;
447
444
 
448
445
  const resizableColumnIds = useMemo(() => visibleColumns.map((c) => c.id), [visibleColumns]);
@@ -373,9 +373,7 @@ export const KanbanTaskCard = memo(
373
373
  <div className="flex items-center justify-between gap-2">
374
374
  <div className="flex min-w-0 flex-nowrap gap-2">
375
375
  {columnId === 'todo' ? (
376
- isScheduleTask ? (
377
- null
378
- ) : (
376
+ isScheduleTask ? null : (
379
377
  <>
380
378
  <TaskActionIconButton
381
379
  label="开始"
@@ -17,11 +17,7 @@ import {
17
17
  resolveMemberRuntimeSummary,
18
18
  } from '@renderer/utils/memberRuntimeSummary';
19
19
  import { isLeadMember } from '@shared/utils/leadDetection';
20
- import {
21
- BarChart3,
22
- FolderOpen,
23
- Loader2,
24
- } from 'lucide-react';
20
+ import { BarChart3, FolderOpen, Loader2 } from 'lucide-react';
25
21
 
26
22
  import { buildMemberActivityEntries } from './memberActivityEntries';
27
23
  import { MemberDetailHeader } from './MemberDetailHeader';
@@ -611,7 +611,9 @@ export const MessageComposer = ({
611
611
  'inline-block size-2 shrink-0 rounded-full',
612
612
  session.live && 'animate-pulse'
613
613
  )}
614
- style={{ backgroundColor: session.live ? '#22c55e' : currentTeamColor }}
614
+ style={{
615
+ backgroundColor: session.live ? '#22c55e' : currentTeamColor,
616
+ }}
615
617
  />
616
618
  <span className="min-w-0 flex-1 truncate text-[var(--color-text)]">
617
619
  {label}