@swarmclawai/swarmclaw 0.7.2 → 0.7.3
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 +81 -22
- package/package.json +1 -1
- package/src/app/api/agents/[id]/route.ts +26 -0
- package/src/app/api/agents/[id]/thread/route.ts +36 -7
- package/src/app/api/agents/route.ts +12 -1
- package/src/app/api/auth/route.ts +76 -7
- package/src/app/api/chatrooms/[id]/chat/route.ts +7 -2
- package/src/app/api/chats/[id]/browser/route.ts +5 -1
- package/src/app/api/chats/[id]/chat/route.ts +7 -3
- package/src/app/api/chats/[id]/main-loop/route.ts +7 -88
- package/src/app/api/chats/[id]/messages/route.ts +19 -13
- package/src/app/api/chats/[id]/route.ts +18 -0
- package/src/app/api/chats/[id]/stop/route.ts +6 -1
- package/src/app/api/chats/route.ts +16 -0
- package/src/app/api/connectors/[id]/doctor/route.ts +26 -0
- package/src/app/api/connectors/doctor/route.ts +13 -0
- package/src/app/api/files/open/route.ts +16 -14
- package/src/app/api/memory/maintenance/route.ts +11 -1
- package/src/app/api/openclaw/agent-files/route.ts +27 -4
- package/src/app/api/openclaw/skills/route.ts +11 -3
- package/src/app/api/plugins/dependencies/route.ts +24 -0
- package/src/app/api/plugins/install/route.ts +15 -92
- package/src/app/api/plugins/route.ts +3 -26
- package/src/app/api/plugins/settings/route.ts +17 -12
- package/src/app/api/plugins/ui/route.ts +1 -0
- package/src/app/api/settings/route.ts +49 -7
- package/src/app/api/tasks/[id]/route.ts +15 -6
- package/src/app/api/tasks/bulk/route.ts +2 -2
- package/src/app/api/tasks/route.ts +9 -4
- package/src/app/api/webhooks/[id]/route.ts +8 -1
- package/src/app/page.tsx +9 -2
- package/src/cli/index.js +4 -0
- package/src/cli/index.ts +3 -10
- package/src/components/agents/agent-card.tsx +15 -12
- package/src/components/agents/agent-chat-list.tsx +101 -1
- package/src/components/agents/agent-list.tsx +46 -9
- package/src/components/agents/agent-sheet.tsx +207 -16
- package/src/components/agents/inspector-panel.tsx +108 -48
- package/src/components/auth/access-key-gate.tsx +36 -97
- package/src/components/chat/chat-area.tsx +29 -13
- package/src/components/chat/chat-card.tsx +4 -20
- package/src/components/chat/chat-header.tsx +255 -353
- package/src/components/chat/chat-list.tsx +7 -9
- package/src/components/chat/checkpoint-timeline.tsx +1 -1
- package/src/components/chat/message-list.tsx +3 -1
- package/src/components/chatrooms/chatroom-view.tsx +347 -205
- package/src/components/connectors/connector-list.tsx +265 -127
- package/src/components/connectors/connector-sheet.tsx +217 -0
- package/src/components/home/home-view.tsx +128 -4
- package/src/components/layout/app-layout.tsx +383 -194
- package/src/components/layout/mobile-header.tsx +26 -8
- package/src/components/plugins/plugin-list.tsx +15 -3
- package/src/components/plugins/plugin-sheet.tsx +118 -9
- package/src/components/projects/project-detail.tsx +183 -0
- package/src/components/shared/agent-picker-list.tsx +2 -2
- package/src/components/shared/command-palette.tsx +111 -24
- package/src/components/shared/settings/plugin-manager.tsx +20 -4
- package/src/components/shared/settings/section-capability-policy.tsx +105 -0
- package/src/components/shared/settings/section-heartbeat.tsx +77 -0
- package/src/components/shared/settings/section-orchestrator.tsx +3 -3
- package/src/components/shared/settings/section-runtime-loop.tsx +5 -5
- package/src/components/shared/settings/section-secrets.tsx +6 -6
- package/src/components/shared/settings/section-user-preferences.tsx +1 -1
- package/src/components/shared/settings/section-voice.tsx +5 -1
- package/src/components/shared/settings/section-web-search.tsx +10 -2
- package/src/components/shared/settings/settings-page.tsx +245 -46
- package/src/components/tasks/approvals-panel.tsx +205 -18
- package/src/components/tasks/task-board.tsx +242 -46
- package/src/components/usage/metrics-dashboard.tsx +74 -1
- package/src/components/wallets/wallet-panel.tsx +17 -5
- package/src/components/webhooks/webhook-sheet.tsx +7 -7
- package/src/lib/auth.ts +17 -0
- package/src/lib/chat-streaming-state.test.ts +108 -0
- package/src/lib/chat-streaming-state.ts +108 -0
- package/src/lib/openclaw-agent-id.test.ts +14 -0
- package/src/lib/openclaw-agent-id.ts +31 -0
- package/src/lib/server/agent-assignment.test.ts +112 -0
- package/src/lib/server/agent-assignment.ts +169 -0
- package/src/lib/server/approval-connector-notify.test.ts +253 -0
- package/src/lib/server/approvals-auto-approve.test.ts +205 -0
- package/src/lib/server/approvals.ts +483 -75
- package/src/lib/server/autonomy-runtime.test.ts +341 -0
- package/src/lib/server/browser-state.test.ts +118 -0
- package/src/lib/server/browser-state.ts +123 -0
- package/src/lib/server/build-llm.test.ts +36 -0
- package/src/lib/server/build-llm.ts +11 -4
- package/src/lib/server/builtin-plugins.ts +34 -0
- package/src/lib/server/chat-execution-heartbeat.test.ts +40 -0
- package/src/lib/server/chat-execution-tool-events.test.ts +134 -0
- package/src/lib/server/chat-execution.ts +250 -61
- package/src/lib/server/chatroom-health.test.ts +26 -0
- package/src/lib/server/chatroom-health.ts +2 -3
- package/src/lib/server/chatroom-helpers.test.ts +67 -2
- package/src/lib/server/chatroom-helpers.ts +45 -5
- package/src/lib/server/connectors/discord.ts +175 -11
- package/src/lib/server/connectors/doctor.test.ts +80 -0
- package/src/lib/server/connectors/doctor.ts +116 -0
- package/src/lib/server/connectors/manager.ts +946 -110
- package/src/lib/server/connectors/policy.test.ts +222 -0
- package/src/lib/server/connectors/policy.ts +452 -0
- package/src/lib/server/connectors/slack.ts +188 -9
- package/src/lib/server/connectors/telegram.ts +65 -15
- package/src/lib/server/connectors/thread-context.test.ts +44 -0
- package/src/lib/server/connectors/thread-context.ts +72 -0
- package/src/lib/server/connectors/types.ts +41 -11
- package/src/lib/server/daemon-state.ts +59 -1
- package/src/lib/server/data-dir.ts +13 -0
- package/src/lib/server/delegation-jobs.test.ts +140 -0
- package/src/lib/server/delegation-jobs.ts +248 -0
- package/src/lib/server/document-utils.test.ts +47 -0
- package/src/lib/server/document-utils.ts +397 -0
- package/src/lib/server/heartbeat-service.ts +13 -39
- package/src/lib/server/heartbeat-source.test.ts +22 -0
- package/src/lib/server/heartbeat-source.ts +7 -0
- package/src/lib/server/identity-continuity.test.ts +77 -0
- package/src/lib/server/identity-continuity.ts +127 -0
- package/src/lib/server/mailbox-utils.ts +347 -0
- package/src/lib/server/main-agent-loop.ts +27 -967
- package/src/lib/server/memory-db.ts +4 -6
- package/src/lib/server/memory-tiers.ts +40 -0
- package/src/lib/server/openclaw-agent-resolver.test.ts +70 -0
- package/src/lib/server/openclaw-agent-resolver.ts +128 -0
- package/src/lib/server/openclaw-exec-config.ts +5 -6
- package/src/lib/server/openclaw-skills-normalize.test.ts +56 -0
- package/src/lib/server/openclaw-skills-normalize.ts +136 -0
- package/src/lib/server/openclaw-sync.ts +3 -2
- package/src/lib/server/orchestrator-lg.ts +17 -6
- package/src/lib/server/orchestrator.ts +2 -2
- package/src/lib/server/playwright-proxy.mjs +27 -3
- package/src/lib/server/plugins.test.ts +207 -0
- package/src/lib/server/plugins.ts +822 -69
- package/src/lib/server/provider-health.ts +33 -3
- package/src/lib/server/queue.ts +3 -20
- package/src/lib/server/scheduler.ts +2 -0
- package/src/lib/server/session-archive-memory.test.ts +85 -0
- package/src/lib/server/session-archive-memory.ts +230 -0
- package/src/lib/server/session-mailbox.ts +8 -18
- package/src/lib/server/session-reset-policy.test.ts +99 -0
- package/src/lib/server/session-reset-policy.ts +311 -0
- package/src/lib/server/session-run-manager.ts +33 -80
- package/src/lib/server/session-tools/autonomy-tools.test.ts +105 -0
- package/src/lib/server/session-tools/calendar.ts +2 -12
- package/src/lib/server/session-tools/connector.ts +109 -8
- package/src/lib/server/session-tools/context.ts +14 -2
- package/src/lib/server/session-tools/crawl.ts +447 -0
- package/src/lib/server/session-tools/crud.ts +70 -32
- package/src/lib/server/session-tools/delegate-fallback.test.ts +219 -0
- package/src/lib/server/session-tools/delegate.ts +406 -20
- package/src/lib/server/session-tools/discovery.ts +22 -4
- package/src/lib/server/session-tools/document.ts +283 -0
- package/src/lib/server/session-tools/email.ts +1 -3
- package/src/lib/server/session-tools/extract.ts +137 -0
- package/src/lib/server/session-tools/file-normalize.test.ts +93 -0
- package/src/lib/server/session-tools/file-send.test.ts +84 -1
- package/src/lib/server/session-tools/file.ts +237 -24
- package/src/lib/server/session-tools/human-loop.ts +227 -0
- package/src/lib/server/session-tools/image-gen.ts +1 -3
- package/src/lib/server/session-tools/index.ts +56 -1
- package/src/lib/server/session-tools/mailbox.ts +276 -0
- package/src/lib/server/session-tools/memory.ts +35 -3
- package/src/lib/server/session-tools/monitor.ts +150 -7
- package/src/lib/server/session-tools/normalize-tool-args.ts +17 -14
- package/src/lib/server/session-tools/platform-normalize.test.ts +142 -0
- package/src/lib/server/session-tools/platform.ts +142 -4
- package/src/lib/server/session-tools/plugin-creator.ts +86 -23
- package/src/lib/server/session-tools/primitive-tools.test.ts +257 -0
- package/src/lib/server/session-tools/replicate.ts +1 -3
- package/src/lib/server/session-tools/schedule.ts +20 -10
- package/src/lib/server/session-tools/session-info.ts +36 -3
- package/src/lib/server/session-tools/session-tools-wiring.test.ts +31 -17
- package/src/lib/server/session-tools/subagent.ts +193 -27
- package/src/lib/server/session-tools/table.ts +587 -0
- package/src/lib/server/session-tools/wallet.ts +13 -10
- package/src/lib/server/session-tools/web-browser-config.test.ts +39 -0
- package/src/lib/server/session-tools/web.ts +896 -100
- package/src/lib/server/storage.ts +226 -7
- package/src/lib/server/stream-agent-chat.ts +46 -21
- package/src/lib/server/structured-extract.test.ts +72 -0
- package/src/lib/server/structured-extract.ts +373 -0
- package/src/lib/server/task-mention.test.ts +16 -2
- package/src/lib/server/task-mention.ts +61 -10
- package/src/lib/server/tool-aliases.ts +44 -7
- package/src/lib/server/tool-capability-policy.ts +6 -0
- package/src/lib/server/tool-retry.ts +2 -0
- package/src/lib/server/watch-jobs.test.ts +173 -0
- package/src/lib/server/watch-jobs.ts +532 -0
- package/src/lib/server/ws-hub.ts +5 -3
- package/src/lib/validation/schemas.test.ts +26 -0
- package/src/lib/validation/schemas.ts +7 -0
- package/src/lib/ws-client.ts +14 -12
- package/src/proxy.ts +5 -5
- package/src/stores/use-app-store.ts +0 -6
- package/src/stores/use-chat-store.ts +31 -2
- package/src/types/index.ts +287 -44
- package/src/components/chat/new-chat-sheet.tsx +0 -253
- package/src/lib/server/main-session.ts +0 -17
- package/src/lib/server/session-run-manager.test.ts +0 -26
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { useCallback, useEffect, useState } from 'react'
|
|
3
|
+
import { useCallback, useEffect, useMemo, useState } from 'react'
|
|
4
4
|
import { useAppStore } from '@/stores/use-app-store'
|
|
5
5
|
import { useChatroomStore } from '@/stores/use-chatroom-store'
|
|
6
6
|
import { useWs } from '@/hooks/use-ws'
|
|
@@ -18,6 +18,24 @@ function relativeTime(ts: number): string {
|
|
|
18
18
|
return d.toLocaleDateString([], { month: 'short', day: 'numeric' })
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
type ConnectorGroup = 'needs-setup' | 'attention' | 'healthy'
|
|
22
|
+
|
|
23
|
+
function hasConnectorCredentials(connector: Connector): boolean {
|
|
24
|
+
return connector.platform === 'whatsapp'
|
|
25
|
+
|| connector.platform === 'openclaw'
|
|
26
|
+
|| connector.platform === 'signal'
|
|
27
|
+
|| (connector.platform === 'bluebubbles' && (!!connector.credentialId || !!connector.config?.password))
|
|
28
|
+
|| !!connector.credentialId
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function getConnectorGroup(connector: Connector): ConnectorGroup {
|
|
32
|
+
const missingRoute = !connector.agentId && !connector.chatroomId
|
|
33
|
+
const needsSetup = !hasConnectorCredentials(connector) || !!connector.qrDataUrl || missingRoute
|
|
34
|
+
if (needsSetup) return 'needs-setup'
|
|
35
|
+
if (connector.status === 'running' && !connector.lastError) return 'healthy'
|
|
36
|
+
return 'attention'
|
|
37
|
+
}
|
|
38
|
+
|
|
21
39
|
export function ConnectorList({ inSidebar }: { inSidebar?: boolean }) {
|
|
22
40
|
const connectors = useAppStore((s) => s.connectors)
|
|
23
41
|
const loadConnectors = useAppStore((s) => s.loadConnectors)
|
|
@@ -31,6 +49,7 @@ export function ConnectorList({ inSidebar }: { inSidebar?: boolean }) {
|
|
|
31
49
|
const [reconnecting, setReconnecting] = useState<string | null>(null)
|
|
32
50
|
const [loaded, setLoaded] = useState(false)
|
|
33
51
|
const [error, setError] = useState<string | null>(null)
|
|
52
|
+
const [groupFilter, setGroupFilter] = useState<'all' | ConnectorGroup>('all')
|
|
34
53
|
const openConnector = useCallback((id: string | null) => {
|
|
35
54
|
setEditingConnectorId(id)
|
|
36
55
|
setConnectorSheetOpen(true)
|
|
@@ -84,7 +103,48 @@ export function ConnectorList({ inSidebar }: { inSidebar?: boolean }) {
|
|
|
84
103
|
}
|
|
85
104
|
}
|
|
86
105
|
|
|
87
|
-
const list =
|
|
106
|
+
const list = useMemo(() => (
|
|
107
|
+
(Object.values(connectors) as Connector[]).sort((a, b) => {
|
|
108
|
+
const groupOrder: Record<ConnectorGroup, number> = {
|
|
109
|
+
'needs-setup': 0,
|
|
110
|
+
attention: 1,
|
|
111
|
+
healthy: 2,
|
|
112
|
+
}
|
|
113
|
+
const diff = groupOrder[getConnectorGroup(a)] - groupOrder[getConnectorGroup(b)]
|
|
114
|
+
if (diff !== 0) return diff
|
|
115
|
+
return a.name.localeCompare(b.name)
|
|
116
|
+
})
|
|
117
|
+
), [connectors])
|
|
118
|
+
|
|
119
|
+
const groupedConnectors = useMemo(() => {
|
|
120
|
+
const groups: Record<ConnectorGroup, Connector[]> = {
|
|
121
|
+
'needs-setup': [],
|
|
122
|
+
attention: [],
|
|
123
|
+
healthy: [],
|
|
124
|
+
}
|
|
125
|
+
for (const connector of list) {
|
|
126
|
+
groups[getConnectorGroup(connector)].push(connector)
|
|
127
|
+
}
|
|
128
|
+
return groups
|
|
129
|
+
}, [list])
|
|
130
|
+
|
|
131
|
+
const groupMeta: Record<ConnectorGroup, { label: string; description: string; tone: string }> = {
|
|
132
|
+
'needs-setup': {
|
|
133
|
+
label: 'Needs Setup',
|
|
134
|
+
description: 'Missing credentials, QR scan, or routing target',
|
|
135
|
+
tone: 'text-amber-400',
|
|
136
|
+
},
|
|
137
|
+
attention: {
|
|
138
|
+
label: 'Attention',
|
|
139
|
+
description: 'Configured, but stopped or reporting errors',
|
|
140
|
+
tone: 'text-red-400',
|
|
141
|
+
},
|
|
142
|
+
healthy: {
|
|
143
|
+
label: 'Healthy',
|
|
144
|
+
description: 'Connected and routed correctly',
|
|
145
|
+
tone: 'text-emerald-400',
|
|
146
|
+
},
|
|
147
|
+
}
|
|
88
148
|
|
|
89
149
|
if (!loaded) {
|
|
90
150
|
return (
|
|
@@ -122,6 +182,7 @@ export function ConnectorList({ inSidebar }: { inSidebar?: boolean }) {
|
|
|
122
182
|
const chatroom = c.chatroomId ? chatrooms[c.chatroomId] : null
|
|
123
183
|
const isRunning = c.status === 'running'
|
|
124
184
|
const meta = CONNECTOR_PLATFORM_META[c.platform]
|
|
185
|
+
const group = getConnectorGroup(c)
|
|
125
186
|
return (
|
|
126
187
|
<button
|
|
127
188
|
key={c.id}
|
|
@@ -140,7 +201,7 @@ export function ConnectorList({ inSidebar }: { inSidebar?: boolean }) {
|
|
|
140
201
|
</span>
|
|
141
202
|
</div>
|
|
142
203
|
<span className={`shrink-0 w-2 h-2 rounded-full ${
|
|
143
|
-
|
|
204
|
+
group === 'healthy' ? 'bg-green-400' : group === 'attention' ? 'bg-red-400' : 'bg-amber-400'
|
|
144
205
|
}`}
|
|
145
206
|
style={isRunning ? { animation: 'pulse-subtle 2s infinite' } : undefined} />
|
|
146
207
|
</button>
|
|
@@ -158,137 +219,214 @@ export function ConnectorList({ inSidebar }: { inSidebar?: boolean }) {
|
|
|
158
219
|
{error}
|
|
159
220
|
</div>
|
|
160
221
|
)}
|
|
161
|
-
<div className="grid grid-cols-1 sm:grid-cols-
|
|
162
|
-
{
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
222
|
+
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3 mb-4">
|
|
223
|
+
{(Object.entries(groupMeta) as Array<[ConnectorGroup, { label: string; description: string; tone: string }]>).map(([group, meta]) => (
|
|
224
|
+
<button
|
|
225
|
+
key={group}
|
|
226
|
+
onClick={() => setGroupFilter((current) => (current === group ? 'all' : group))}
|
|
227
|
+
className={`rounded-[14px] border px-4 py-3 text-left transition-all cursor-pointer ${
|
|
228
|
+
groupFilter === group
|
|
229
|
+
? 'border-white/[0.12] bg-white/[0.05]'
|
|
230
|
+
: 'border-white/[0.06] bg-white/[0.02] hover:bg-white/[0.04]'
|
|
231
|
+
}`}
|
|
232
|
+
style={{ fontFamily: 'inherit' }}
|
|
233
|
+
>
|
|
234
|
+
<div className={`text-[11px] font-700 uppercase tracking-[0.08em] ${meta.tone}`}>{meta.label}</div>
|
|
235
|
+
<div className={`mt-2 text-[24px] font-display font-700 tracking-[-0.03em] ${meta.tone}`}>{groupedConnectors[group].length}</div>
|
|
236
|
+
<p className="text-[11px] text-text-3/55 mt-1 leading-relaxed">{meta.description}</p>
|
|
237
|
+
</button>
|
|
238
|
+
))}
|
|
239
|
+
</div>
|
|
174
240
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
}`}
|
|
203
|
-
|
|
241
|
+
<div className="flex flex-wrap gap-2 mb-4">
|
|
242
|
+
{(['all', 'needs-setup', 'attention', 'healthy'] as const).map((group) => (
|
|
243
|
+
<button
|
|
244
|
+
key={group}
|
|
245
|
+
onClick={() => setGroupFilter(group)}
|
|
246
|
+
className={`px-3 py-1.5 rounded-[8px] text-[11px] font-600 transition-all cursor-pointer border-none ${
|
|
247
|
+
groupFilter === group
|
|
248
|
+
? 'bg-accent-soft text-accent-bright'
|
|
249
|
+
: 'bg-white/[0.04] text-text-3 hover:bg-white/[0.08] hover:text-text-2'
|
|
250
|
+
}`}
|
|
251
|
+
style={{ fontFamily: 'inherit' }}
|
|
252
|
+
>
|
|
253
|
+
{group === 'all' ? 'All connectors' : groupMeta[group].label}
|
|
254
|
+
</button>
|
|
255
|
+
))}
|
|
256
|
+
</div>
|
|
257
|
+
|
|
258
|
+
<div className="flex flex-col gap-6">
|
|
259
|
+
{(Object.entries(groupMeta) as Array<[ConnectorGroup, { label: string; description: string; tone: string }]>)
|
|
260
|
+
.filter(([group]) => groupFilter === 'all' || groupFilter === group)
|
|
261
|
+
.map(([group, meta]) => {
|
|
262
|
+
const connectorsForGroup = groupedConnectors[group]
|
|
263
|
+
if (connectorsForGroup.length === 0) return null
|
|
264
|
+
return (
|
|
265
|
+
<section key={group}>
|
|
266
|
+
<div className="flex items-end justify-between gap-3 mb-3">
|
|
267
|
+
<div>
|
|
268
|
+
<h2 className={`text-[12px] font-700 uppercase tracking-[0.1em] ${meta.tone}`}>{meta.label}</h2>
|
|
269
|
+
<p className="text-[12px] text-text-3/55 mt-1">{meta.description}</p>
|
|
204
270
|
</div>
|
|
205
|
-
<span className="text-[11px] text-text-3
|
|
206
|
-
{isRunning ? 'Connected' : c.status === 'error' ? 'Error' : 'Stopped'}
|
|
207
|
-
{c.qrDataUrl && ' · QR ready'}
|
|
208
|
-
</span>
|
|
271
|
+
<span className="text-[11px] text-text-3/45">{connectorsForGroup.length} connector{connectorsForGroup.length === 1 ? '' : 's'}</span>
|
|
209
272
|
</div>
|
|
210
|
-
</div>
|
|
211
273
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
274
|
+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
|
|
275
|
+
{connectorsForGroup.map((c, idx) => {
|
|
276
|
+
const platformLabel = getConnectorPlatformLabel(c.platform)
|
|
277
|
+
const agent = c.agentId ? agents[c.agentId] : null
|
|
278
|
+
const chatroom = c.chatroomId ? chatrooms[c.chatroomId] : null
|
|
279
|
+
const isRunning = c.status === 'running'
|
|
280
|
+
const isToggling = toggling === c.id
|
|
281
|
+
const hasCredentials = hasConnectorCredentials(c)
|
|
282
|
+
const lastMsg = c.presence?.lastMessageAt
|
|
283
|
+
const missingRoute = !chatroom && !agent
|
|
284
|
+
const issues = [
|
|
285
|
+
!hasCredentials ? { label: 'Credentials missing', tone: 'text-red-400 bg-red-500/10' } : null,
|
|
286
|
+
c.qrDataUrl ? { label: 'QR required', tone: 'text-amber-400 bg-amber-500/10' } : null,
|
|
287
|
+
missingRoute ? { label: 'Routing missing', tone: 'text-amber-300 bg-amber-500/10' } : null,
|
|
288
|
+
].filter(Boolean) as Array<{ label: string; tone: string }>
|
|
289
|
+
|
|
290
|
+
return (
|
|
291
|
+
<div
|
|
292
|
+
key={c.id}
|
|
293
|
+
role="button"
|
|
294
|
+
tabIndex={0}
|
|
295
|
+
onClick={() => openConnector(c.id)}
|
|
296
|
+
onKeyDown={(e) => {
|
|
297
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
298
|
+
e.preventDefault()
|
|
299
|
+
openConnector(c.id)
|
|
300
|
+
}
|
|
301
|
+
}}
|
|
302
|
+
className={`group relative flex flex-col rounded-[14px] border p-4 cursor-pointer transition-all hover:border-white/[0.12] hover:bg-white/[0.02] hover:scale-[1.01] text-left w-full ${
|
|
303
|
+
group === 'healthy'
|
|
304
|
+
? 'border-emerald-500/15 bg-emerald-500/[0.03]'
|
|
305
|
+
: group === 'attention'
|
|
306
|
+
? 'border-red-500/15 bg-red-500/[0.03]'
|
|
307
|
+
: 'border-amber-500/15 bg-amber-500/[0.03]'
|
|
308
|
+
}`}
|
|
309
|
+
style={{
|
|
310
|
+
fontFamily: 'inherit',
|
|
311
|
+
animation: 'spring-in 0.5s var(--ease-spring) both',
|
|
312
|
+
animationDelay: `${idx * 0.05}s`
|
|
313
|
+
}}
|
|
314
|
+
>
|
|
315
|
+
<div className="flex items-start gap-3 mb-3">
|
|
316
|
+
<ConnectorPlatformBadge platform={c.platform} size={40} iconSize={20} roundedClassName="rounded-[10px]" />
|
|
317
|
+
<div className="flex-1 min-w-0">
|
|
318
|
+
<div className="flex items-center gap-2">
|
|
319
|
+
<span className="text-[14px] font-600 text-text truncate">{c.name}</span>
|
|
320
|
+
<span className={`shrink-0 w-2 h-2 rounded-full ${
|
|
321
|
+
group === 'healthy' ? 'bg-green-400' : group === 'attention' ? 'bg-red-400' : 'bg-amber-400'
|
|
322
|
+
}`}
|
|
323
|
+
style={isRunning ? { animation: 'pulse-subtle 2s infinite' } : c.status === 'error' ? { animation: 'ai-shake 0.5s' } : undefined} />
|
|
324
|
+
</div>
|
|
325
|
+
<div className="flex flex-wrap items-center gap-1.5 mt-1">
|
|
326
|
+
<span className={`px-1.5 py-0.5 rounded-[5px] text-[10px] font-700 uppercase tracking-[0.08em] ${meta.tone} bg-white/[0.05]`}>
|
|
327
|
+
{meta.label}
|
|
328
|
+
</span>
|
|
329
|
+
<span className="text-[11px] text-text-3">
|
|
330
|
+
{isRunning ? 'Connected' : c.status === 'error' ? 'Error' : 'Stopped'}
|
|
331
|
+
</span>
|
|
332
|
+
</div>
|
|
333
|
+
</div>
|
|
334
|
+
</div>
|
|
335
|
+
|
|
336
|
+
<div className="flex items-center gap-2.5 mb-3 px-0.5">
|
|
337
|
+
{chatroom ? (
|
|
338
|
+
<>
|
|
339
|
+
<div className="w-6 h-6 rounded-full bg-white/[0.06] flex items-center justify-center shrink-0">
|
|
340
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" className="text-text-3">
|
|
341
|
+
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
|
|
342
|
+
</svg>
|
|
343
|
+
</div>
|
|
344
|
+
<div className="flex-1 min-w-0">
|
|
345
|
+
<span className="text-[12px] font-600 text-text-2 block truncate">{chatroom.name}</span>
|
|
346
|
+
<span className="text-[10px] text-text-3/60 block">
|
|
347
|
+
Room · {chatroom.agentIds.length} agent{chatroom.agentIds.length !== 1 ? 's' : ''}
|
|
348
|
+
</span>
|
|
349
|
+
</div>
|
|
350
|
+
</>
|
|
351
|
+
) : agent ? (
|
|
352
|
+
<>
|
|
353
|
+
<AgentAvatar seed={agent.avatarSeed || null} avatarUrl={agent.avatarUrl} name={agent.name} size={24} />
|
|
354
|
+
<div className="flex-1 min-w-0">
|
|
355
|
+
<span className="text-[12px] font-600 text-text-2 block truncate">{agent.name}</span>
|
|
356
|
+
<span className="text-[10px] text-text-3/60 block">Agent route</span>
|
|
357
|
+
</div>
|
|
358
|
+
</>
|
|
359
|
+
) : (
|
|
360
|
+
<span className="text-[11px] text-amber-300">No routing target yet</span>
|
|
361
|
+
)}
|
|
362
|
+
</div>
|
|
241
363
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
364
|
+
{issues.length > 0 ? (
|
|
365
|
+
<div className="flex flex-wrap gap-1.5 mb-3">
|
|
366
|
+
{issues.map((issue) => (
|
|
367
|
+
<span key={issue.label} className={`px-2 py-1 rounded-[7px] text-[10px] font-700 ${issue.tone}`}>
|
|
368
|
+
{issue.label}
|
|
369
|
+
</span>
|
|
370
|
+
))}
|
|
371
|
+
</div>
|
|
372
|
+
) : (
|
|
373
|
+
<div className="text-[11px] text-text-3/55 mb-3">
|
|
374
|
+
{platformLabel} routed to {chatroom ? 'chatroom' : agent ? 'agent' : 'connector'}.
|
|
375
|
+
</div>
|
|
376
|
+
)}
|
|
253
377
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
378
|
+
<div className="flex items-center gap-2 mt-auto pt-2 border-t border-white/[0.04]">
|
|
379
|
+
{c.lastError ? (
|
|
380
|
+
<span className="text-[10px] text-red-400 truncate flex-1">
|
|
381
|
+
{c.lastError.slice(0, 50)}{c.lastError.length > 50 ? '...' : ''}
|
|
382
|
+
</span>
|
|
383
|
+
) : lastMsg ? (
|
|
384
|
+
<span className="text-[10px] text-text-3/60 flex-1">Last message {relativeTime(lastMsg)}</span>
|
|
385
|
+
) : (
|
|
386
|
+
<span className="text-[10px] text-text-3/40 flex-1">No messages yet</span>
|
|
387
|
+
)}
|
|
388
|
+
|
|
389
|
+
<div className="flex gap-1 shrink-0" onClick={(e) => e.stopPropagation()}>
|
|
390
|
+
{c.status === 'error' && hasCredentials && (
|
|
391
|
+
<button
|
|
392
|
+
onClick={(e) => handleReconnect(e, c)}
|
|
393
|
+
disabled={reconnecting === c.id}
|
|
394
|
+
title="Reconnect"
|
|
395
|
+
className="px-2 py-1 rounded-[6px] text-[10px] font-600 transition-all cursor-pointer border-none opacity-0 group-hover:opacity-100 bg-amber-500/10 text-amber-400 hover:bg-amber-500/20 disabled:opacity-50"
|
|
396
|
+
>
|
|
397
|
+
{reconnecting === c.id ? '...' : 'Reconnect'}
|
|
398
|
+
</button>
|
|
399
|
+
)}
|
|
400
|
+
{hasCredentials && (
|
|
401
|
+
<button
|
|
402
|
+
onClick={(e) => handleToggle(e, c)}
|
|
403
|
+
disabled={isToggling}
|
|
404
|
+
title={isRunning ? 'Stop' : 'Start'}
|
|
405
|
+
className={`w-7 h-7 rounded-[6px] flex items-center justify-center transition-all cursor-pointer border-none ${
|
|
406
|
+
isToggling ? 'opacity-100' : 'opacity-0 group-hover:opacity-100'
|
|
407
|
+
} ${isRunning
|
|
408
|
+
? 'bg-red-500/10 text-red-400 hover:bg-red-500/20'
|
|
409
|
+
: 'bg-green-500/10 text-green-400 hover:bg-green-500/20'
|
|
410
|
+
} disabled:opacity-50`}
|
|
411
|
+
>
|
|
412
|
+
{isToggling ? (
|
|
413
|
+
<span className="w-3 h-3 rounded-full border-2 border-current border-t-transparent animate-spin" />
|
|
414
|
+
) : isRunning ? (
|
|
415
|
+
<svg width="10" height="10" viewBox="0 0 24 24" fill="currentColor"><rect x="4" y="4" width="16" height="16" rx="2" /></svg>
|
|
416
|
+
) : (
|
|
417
|
+
<svg width="10" height="10" viewBox="0 0 24 24" fill="currentColor"><polygon points="6,3 21,12 6,21" /></svg>
|
|
418
|
+
)}
|
|
419
|
+
</button>
|
|
420
|
+
)}
|
|
421
|
+
</div>
|
|
422
|
+
</div>
|
|
423
|
+
</div>
|
|
424
|
+
)
|
|
425
|
+
})}
|
|
287
426
|
</div>
|
|
288
|
-
</
|
|
289
|
-
|
|
290
|
-
)
|
|
291
|
-
})}
|
|
427
|
+
</section>
|
|
428
|
+
)
|
|
429
|
+
})}
|
|
292
430
|
</div>
|
|
293
431
|
</div>
|
|
294
432
|
)
|