@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.
- package/README.md +10 -1
- package/dist-renderer/assets/{ProjectEditorOverlay-BNoDw9T1.js → ProjectEditorOverlay-C5D83Zxv.js} +1 -1
- package/dist-renderer/assets/{TeamGraphOverlay-CfGRKQIu.js → TeamGraphOverlay-ajzuM1-u.js} +1 -1
- package/dist-renderer/assets/{_basePickBy-Ct8Hm5_h.js → _basePickBy-C9H2zmVj.js} +1 -1
- package/dist-renderer/assets/{_baseUniq-BofrAFBx.js → _baseUniq-CpGZGemc.js} +1 -1
- package/dist-renderer/assets/{arc-AbJgatzR.js → arc-CbGBDw-m.js} +1 -1
- package/dist-renderer/assets/{architectureDiagram-VXUJARFQ-gpniCJVk.js → architectureDiagram-VXUJARFQ-nuKXUIpb.js} +1 -1
- package/dist-renderer/assets/{blockDiagram-VD42YOAC-aBbbmONC.js → blockDiagram-VD42YOAC-DHUUE7Jc.js} +1 -1
- package/dist-renderer/assets/{c4Diagram-YG6GDRKO-DJio1IsU.js → c4Diagram-YG6GDRKO-OILHhqLM.js} +1 -1
- package/dist-renderer/assets/channel-DpUKLLrj.js +1 -0
- package/dist-renderer/assets/{chunk-4BX2VUAB-D1_HKao2.js → chunk-4BX2VUAB-dqNpZaQ8.js} +1 -1
- package/dist-renderer/assets/{chunk-55IACEB6-NAmVxF4k.js → chunk-55IACEB6-BCoSJQM-.js} +1 -1
- package/dist-renderer/assets/{chunk-B4BG7PRW-Ce829laz.js → chunk-B4BG7PRW-BLbg8yVR.js} +1 -1
- package/dist-renderer/assets/{chunk-DI55MBZ5-Ct2Le12y.js → chunk-DI55MBZ5-CUUWOs1Q.js} +1 -1
- package/dist-renderer/assets/{chunk-FMBD7UC4-Cie3DzKk.js → chunk-FMBD7UC4-D9geTN5P.js} +1 -1
- package/dist-renderer/assets/{chunk-QN33PNHL-4f5Yb50e.js → chunk-QN33PNHL-BGT8-BRX.js} +1 -1
- package/dist-renderer/assets/{chunk-QZHKN3VN-D9ranl9c.js → chunk-QZHKN3VN-CC8ebGaM.js} +1 -1
- package/dist-renderer/assets/{chunk-TZMSLE5B-bdGZWlEy.js → chunk-TZMSLE5B-CajekcT6.js} +1 -1
- package/dist-renderer/assets/classDiagram-2ON5EDUG-LL85aSlz.js +1 -0
- package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-LL85aSlz.js +1 -0
- package/dist-renderer/assets/clone-BHWsFzFA.js +1 -0
- package/dist-renderer/assets/{cose-bilkent-S5V4N54A-C6tvfcVi.js → cose-bilkent-S5V4N54A-C_x7hSy3.js} +1 -1
- package/dist-renderer/assets/{dagre-6UL2VRFP-B-4qcZam.js → dagre-6UL2VRFP-C4Y1k4DZ.js} +1 -1
- package/dist-renderer/assets/{diagram-PSM6KHXK-CwT3TLjx.js → diagram-PSM6KHXK-oRIeULoh.js} +1 -1
- package/dist-renderer/assets/{diagram-QEK2KX5R-BWH6-ZFd.js → diagram-QEK2KX5R-DwSqw5HF.js} +1 -1
- package/dist-renderer/assets/{diagram-S2PKOQOG-DfpPnfi1.js → diagram-S2PKOQOG-DqjGYje2.js} +1 -1
- package/dist-renderer/assets/{erDiagram-Q2GNP2WA-BFbEFR4x.js → erDiagram-Q2GNP2WA-CEV5bBgg.js} +1 -1
- package/dist-renderer/assets/{flowDiagram-NV44I4VS-Dg3cf5hW.js → flowDiagram-NV44I4VS-BQQkrRyu.js} +1 -1
- package/dist-renderer/assets/{ganttDiagram-JELNMOA3-B21y55W5.js → ganttDiagram-JELNMOA3-CLy4WR1G.js} +1 -1
- package/dist-renderer/assets/{gitGraphDiagram-V2S2FVAM-BDV3BJzn.js → gitGraphDiagram-V2S2FVAM-6W3ioQu_.js} +1 -1
- package/dist-renderer/assets/{graph-BfaZ4hZt.js → graph-BnLKQvhH.js} +1 -1
- package/dist-renderer/assets/{index-CCqtDawH.js → index-B4aiRxoU.js} +1 -1
- package/dist-renderer/assets/{index-pMg_LlsS.js → index-B8lKqPVq.js} +1 -1
- package/dist-renderer/assets/{index-CZltVMDP.js → index-BRuhNKyU.js} +12 -12
- package/dist-renderer/assets/{index-BMXHMpkG.js → index-BufvLVIl.js} +1 -1
- package/dist-renderer/assets/{index-Ct0-y9TF.js → index-C1xShqKH.js} +1 -1
- package/dist-renderer/assets/{index-CVMSpK8C.js → index-zIOLLI7O.js} +1 -1
- package/dist-renderer/assets/{infoDiagram-HS3SLOUP-DvMlS0CL.js → infoDiagram-HS3SLOUP-BoBweEEY.js} +1 -1
- package/dist-renderer/assets/{journeyDiagram-XKPGCS4Q-DIyMluRv.js → journeyDiagram-XKPGCS4Q-DLL0V5oP.js} +1 -1
- package/dist-renderer/assets/{kanban-definition-3W4ZIXB7-CVOx8f-7.js → kanban-definition-3W4ZIXB7-HsFtEDG3.js} +1 -1
- package/dist-renderer/assets/{layout-BPKIXUf4.js → layout-ClIooAAq.js} +1 -1
- package/dist-renderer/assets/{linear-CScZGLr2.js → linear-r3RJcj8y.js} +1 -1
- package/dist-renderer/assets/{mindmap-definition-VGOIOE7T-CmDQ7Wo6.js → mindmap-definition-VGOIOE7T-BA_P1U4V.js} +1 -1
- package/dist-renderer/assets/{pieDiagram-ADFJNKIX-DbVClin-.js → pieDiagram-ADFJNKIX-CzPAfkTB.js} +1 -1
- package/dist-renderer/assets/{quadrantDiagram-AYHSOK5B-CAB0MYcW.js → quadrantDiagram-AYHSOK5B-PvdPWzFJ.js} +1 -1
- package/dist-renderer/assets/{requirementDiagram-UZGBJVZJ-w2Lfpg3T.js → requirementDiagram-UZGBJVZJ-CHqIL_Od.js} +1 -1
- package/dist-renderer/assets/{sankeyDiagram-TZEHDZUN-kvG1QoKY.js → sankeyDiagram-TZEHDZUN-ConzpACM.js} +1 -1
- package/dist-renderer/assets/{sequenceDiagram-WL72ISMW-DCVBQ23J.js → sequenceDiagram-WL72ISMW-Zryq4oxP.js} +1 -1
- package/dist-renderer/assets/{stateDiagram-FKZM4ZOC-ItZ0JBvq.js → stateDiagram-FKZM4ZOC-BA9V7NHF.js} +1 -1
- package/dist-renderer/assets/{stateDiagram-v2-4FDKWEC3-Hpmw4dMm.js → stateDiagram-v2-4FDKWEC3-CGnaujD-.js} +1 -1
- package/dist-renderer/assets/{timeline-definition-IT6M3QCI-BzSFaAjV.js → timeline-definition-IT6M3QCI-DPs2ZjMm.js} +1 -1
- package/dist-renderer/assets/{treemap-GDKQZRPO-fSz4hQn0.js → treemap-GDKQZRPO-B0lzrLxb.js} +1 -1
- package/dist-renderer/assets/{xychartDiagram-PRI3JC2R-CT1kaGlv.js → xychartDiagram-PRI3JC2R-CINGmMxX.js} +1 -1
- package/dist-renderer/index.html +1 -1
- package/package.json +2 -1
- package/src/main/server.ts +993 -764
- package/src/main/services/UpdateService.ts +4 -1
- package/src/main/services/ccConnect/CcConnectBridge.ts +1 -8
- package/src/main/services/ccConnect/CcConnectClient.ts +7 -2
- package/src/main/services/teams-mvp/TeamProvisioningService.ts +14 -4
- package/src/main/services/teams-mvp/TeamWorkspaceService.ts +11 -6
- package/src/renderer/App.tsx +18 -7
- package/src/renderer/api/httpClient.ts +136 -42
- package/src/renderer/components/chat/ChatHistory.tsx +11 -8
- package/src/renderer/components/dashboard/DashboardView.tsx +4 -2
- package/src/renderer/components/extensions/ExtensionStoreView.tsx +2 -7
- package/src/renderer/components/layout/Sidebar.tsx +3 -1
- package/src/renderer/components/schedules/SchedulesView.tsx +15 -13
- package/src/renderer/components/settings/SettingsTabs.tsx +2 -1
- package/src/renderer/components/settings/hooks/useSettingsHandlers.ts +4 -5
- package/src/renderer/components/settings/sections/AdvancedSection.tsx +19 -4
- package/src/renderer/components/settings/sections/CliStatusSection.tsx +63 -59
- package/src/renderer/components/settings/sections/GeneralSection.tsx +5 -11
- package/src/renderer/components/settings/sections/HarnessSection.tsx +30 -15
- package/src/renderer/components/settings/sections/PlatformsSection.tsx +110 -51
- package/src/renderer/components/sidebar/SidebarSessions.tsx +100 -67
- package/src/renderer/components/sidebar/WorkspaceBrowser.tsx +26 -43
- package/src/renderer/components/team/CcSessionsSection.tsx +34 -14
- package/src/renderer/components/team/TeamDetailView.tsx +150 -148
- package/src/renderer/components/team/TeamEmptyState.tsx +27 -16
- package/src/renderer/components/team/TeamListView.tsx +4 -2
- package/src/renderer/components/team/activity/ActivityItem.tsx +6 -1
- package/src/renderer/components/team/dialogs/CreateTeamDialog.tsx +282 -75
- package/src/renderer/components/team/dialogs/EditTeamDialog.tsx +2 -1
- package/src/renderer/components/team/dialogs/PlatformManualForm.tsx +64 -21
- package/src/renderer/components/team/dialogs/PlatformSetupQR.tsx +68 -19
- package/src/renderer/components/team/dialogs/ProjectPathSelector.tsx +20 -16
- package/src/renderer/components/team/dialogs/platformMeta.ts +66 -11
- package/src/renderer/components/team/editor/EditorFileTree.tsx +9 -7
- package/src/renderer/components/team/kanban/KanbanBoard.tsx +7 -10
- package/src/renderer/components/team/kanban/KanbanTaskCard.tsx +1 -3
- package/src/renderer/components/team/members/MemberDetailDialog.tsx +1 -5
- package/src/renderer/components/team/messages/MessageComposer.tsx +3 -1
- package/src/renderer/components/team/messages/MessagesPanel.tsx +34 -26
- package/src/renderer/components/team/schedule/CcCronScheduleDialog.tsx +1 -3
- package/src/renderer/components/team/schedule/ScheduleSection.tsx +9 -10
- package/src/renderer/store/slices/scheduleSlice.ts +4 -1
- package/src/renderer/store/slices/teamSlice.ts +3 -1
- package/src/shared/types/api.ts +70 -21
- package/src/shared/utils/leadDetection.ts +5 -1
- package/tsconfig.json +26 -0
- package/dist-renderer/assets/channel-CZ8sd5Xf.js +0 -1
- package/dist-renderer/assets/classDiagram-2ON5EDUG-CMcfSKj5.js +0 -1
- package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-CMcfSKj5.js +0 -1
- 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({
|
|
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,
|
|
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
|
|
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
|
|
90
|
+
<ChevronDown
|
|
91
|
+
size={12}
|
|
92
|
+
className={cn('transition-transform', showAdvanced && 'rotate-180')}
|
|
93
|
+
/>
|
|
75
94
|
高级选项 ({advancedFields.length})
|
|
76
95
|
</button>
|
|
77
|
-
{showAdvanced &&
|
|
78
|
-
|
|
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
|
|
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}
|
|
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({
|
|
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
|
|
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="
|
|
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={
|
|
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
|
|
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 {
|
|
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 =
|
|
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({
|
|
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 () => {
|
|
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 = {
|
|
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(
|
|
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,
|
|
69
|
-
|
|
70
|
-
|
|
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,
|
|
134
|
-
|
|
135
|
-
|
|
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
|
|
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="
|
|
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
|
|
196
|
-
{phase === 'scanned'
|
|
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
|
|
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
|
|
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
|
|
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)]
|
|
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
|
|
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)}
|
|
324
|
-
|
|
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
|
-
{
|
|
26
|
-
|
|
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
|
-
{
|
|
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
|
-
{
|
|
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
|
-
{
|
|
46
|
-
|
|
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
|
-
{
|
|
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
|
-
{
|
|
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
|
-
{
|
|
70
|
-
|
|
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
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
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
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
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={{
|
|
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}
|