@swarmclawai/swarmclaw 0.3.0 → 0.4.0
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 +20 -11
- package/bin/server-cmd.js +14 -7
- package/bin/swarmclaw.js +3 -1
- package/bin/update-cmd.js +120 -0
- package/next.config.ts +2 -0
- package/package.json +3 -1
- package/src/app/api/agents/[id]/route.ts +3 -0
- package/src/app/api/agents/[id]/thread/route.ts +2 -1
- package/src/app/api/agents/route.ts +5 -1
- package/src/app/api/auth/route.ts +3 -1
- package/src/app/api/claude-skills/route.ts +3 -1
- package/src/app/api/connectors/[id]/route.ts +4 -0
- package/src/app/api/connectors/route.ts +6 -1
- package/src/app/api/credentials/route.ts +3 -1
- package/src/app/api/daemon/route.ts +6 -1
- package/src/app/api/ip/route.ts +3 -1
- package/src/app/api/mcp-servers/route.ts +3 -1
- package/src/app/api/orchestrator/graph/route.ts +25 -0
- package/src/app/api/plugins/marketplace/route.ts +3 -1
- package/src/app/api/plugins/route.ts +3 -1
- package/src/app/api/providers/[id]/route.ts +3 -0
- package/src/app/api/providers/configs/route.ts +3 -1
- package/src/app/api/providers/route.ts +5 -1
- package/src/app/api/schedules/[id]/route.ts +3 -0
- package/src/app/api/schedules/route.ts +6 -1
- package/src/app/api/secrets/route.ts +3 -1
- package/src/app/api/sessions/[id]/chat/route.ts +5 -2
- package/src/app/api/sessions/route.ts +9 -2
- package/src/app/api/settings/route.ts +3 -1
- package/src/app/api/setup/doctor/route.ts +1 -0
- package/src/app/api/setup/openclaw-device/route.ts +3 -1
- package/src/app/api/skills/route.ts +3 -1
- package/src/app/api/tasks/[id]/approve/route.ts +73 -0
- package/src/app/api/tasks/[id]/route.ts +3 -0
- package/src/app/api/tasks/route.ts +3 -0
- package/src/app/api/usage/route.ts +3 -1
- package/src/app/api/version/route.ts +3 -1
- package/src/app/api/webhooks/[id]/route.ts +2 -1
- package/src/app/api/webhooks/route.ts +3 -1
- package/src/app/icon.svg +58 -0
- package/src/app/page.tsx +8 -2
- package/src/cli/index.js +1 -9
- package/src/cli/index.ts +51 -1
- package/src/cli/spec.js +0 -8
- package/src/components/agents/agent-card.tsx +1 -1
- package/src/components/agents/agent-sheet.tsx +63 -80
- package/src/components/chat/chat-area.tsx +44 -30
- package/src/components/chat/chat-tool-toggles.tsx +12 -53
- package/src/components/chat/message-bubble.tsx +110 -42
- package/src/components/chat/tool-call-bubble.tsx +41 -3
- package/src/components/chat/tool-request-banner.tsx +1 -9
- package/src/components/connectors/connector-list.tsx +3 -8
- package/src/components/connectors/connector-sheet.tsx +24 -29
- package/src/components/input/chat-input.tsx +72 -56
- package/src/components/knowledge/knowledge-list.tsx +27 -31
- package/src/components/layout/app-layout.tsx +92 -71
- package/src/components/layout/daemon-indicator.tsx +3 -5
- package/src/components/logs/log-list.tsx +5 -9
- package/src/components/mcp-servers/mcp-server-list.tsx +24 -2
- package/src/components/memory/memory-detail.tsx +1 -1
- package/src/components/plugins/plugin-list.tsx +227 -27
- package/src/components/providers/provider-list.tsx +46 -13
- package/src/components/providers/provider-sheet.tsx +0 -45
- package/src/components/runs/run-list.tsx +6 -15
- package/src/components/schedules/schedule-card.tsx +54 -4
- package/src/components/schedules/schedule-list.tsx +6 -3
- package/src/components/schedules/schedule-sheet.tsx +0 -47
- package/src/components/secrets/secrets-list.tsx +20 -2
- package/src/components/sessions/new-session-sheet.tsx +8 -9
- package/src/components/shared/connector-platform-icon.tsx +22 -20
- package/src/components/shared/model-combobox.tsx +148 -0
- package/src/components/shared/settings/section-heartbeat.tsx +7 -39
- package/src/components/shared/settings/section-orchestrator.tsx +8 -9
- package/src/components/skills/skill-list.tsx +260 -34
- package/src/components/skills/skill-sheet.tsx +0 -45
- package/src/components/tasks/task-board.tsx +3 -6
- package/src/components/tasks/task-card.tsx +43 -1
- package/src/components/tasks/task-list.tsx +3 -5
- package/src/components/tasks/task-sheet.tsx +0 -44
- package/src/components/usage/usage-list.tsx +12 -4
- package/src/hooks/use-ws.ts +66 -0
- package/src/instrumentation.ts +2 -0
- package/src/lib/chat.ts +14 -2
- package/src/lib/providers/anthropic.ts +1 -1
- package/src/lib/providers/index.ts +2 -0
- package/src/lib/providers/ollama.ts +1 -1
- package/src/lib/providers/openai.ts +33 -12
- package/src/lib/server/chat-execution.ts +19 -4
- package/src/lib/server/connectors/manager.ts +9 -3
- package/src/lib/server/context-manager.ts +1 -1
- package/src/lib/server/daemon-state.ts +3 -0
- package/src/lib/server/data-dir.ts +1 -0
- package/src/lib/server/heartbeat-service.ts +67 -3
- package/src/lib/server/langgraph-checkpoint.ts +274 -0
- package/src/lib/server/main-agent-loop.ts +61 -2
- package/src/lib/server/orchestrator-lg.ts +394 -13
- package/src/lib/server/orchestrator.ts +25 -5
- package/src/lib/server/queue.ts +17 -3
- package/src/lib/server/session-run-manager.ts +6 -1
- package/src/lib/server/session-tools/delegate.ts +2 -2
- package/src/lib/server/session-tools/index.ts +2 -0
- package/src/lib/server/session-tools/sandbox.ts +164 -0
- package/src/lib/server/storage-mcp.test.ts +25 -2
- package/src/lib/server/storage.ts +24 -7
- package/src/lib/server/stream-agent-chat.ts +77 -22
- package/src/lib/server/task-validation.test.ts +23 -0
- package/src/lib/server/task-validation.ts +5 -3
- package/src/lib/server/ws-hub.ts +85 -0
- package/src/lib/tool-definitions.ts +42 -0
- package/src/lib/upload.ts +7 -1
- package/src/lib/ws-client.ts +124 -0
- package/src/stores/use-chat-store.ts +33 -13
- package/src/types/index.ts +8 -1
- package/src/app/api/agents/generate/route.ts +0 -42
- package/src/app/api/generate/info/route.ts +0 -12
- package/src/app/api/generate/route.ts +0 -106
- package/src/app/favicon.ico +0 -0
- package/src/components/shared/ai-gen-block.tsx +0 -77
|
@@ -6,8 +6,6 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/u
|
|
|
6
6
|
import { useAppStore } from '@/stores/use-app-store'
|
|
7
7
|
import { useMediaQuery } from '@/hooks/use-media-query'
|
|
8
8
|
import { Avatar } from '@/components/shared/avatar'
|
|
9
|
-
import { SessionList } from '@/components/sessions/session-list'
|
|
10
|
-
import { NewSessionSheet } from '@/components/sessions/new-session-sheet'
|
|
11
9
|
import { SettingsSheet } from '@/components/shared/settings-sheet'
|
|
12
10
|
import { AgentList } from '@/components/agents/agent-list'
|
|
13
11
|
import { AgentChatList } from '@/components/agents/agent-chat-list'
|
|
@@ -56,7 +54,6 @@ export function AppLayout() {
|
|
|
56
54
|
const sidebarOpen = useAppStore((s) => s.sidebarOpen)
|
|
57
55
|
const setSidebarOpen = useAppStore((s) => s.setSidebarOpen)
|
|
58
56
|
const setSettingsOpen = useAppStore((s) => s.setSettingsOpen)
|
|
59
|
-
const setNewSessionOpen = useAppStore((s) => s.setNewSessionOpen)
|
|
60
57
|
const setUser = useAppStore((s) => s.setUser)
|
|
61
58
|
const setCurrentSession = useAppStore((s) => s.setCurrentSession)
|
|
62
59
|
const activeView = useAppStore((s) => s.activeView)
|
|
@@ -72,8 +69,10 @@ export function AppLayout() {
|
|
|
72
69
|
const setMcpServerSheetOpen = useAppStore((s) => s.setMcpServerSheetOpen)
|
|
73
70
|
const setKnowledgeSheetOpen = useAppStore((s) => s.setKnowledgeSheetOpen)
|
|
74
71
|
const setPluginSheetOpen = useAppStore((s) => s.setPluginSheetOpen)
|
|
72
|
+
const tasks = useAppStore((s) => s.tasks)
|
|
75
73
|
const isDesktop = useMediaQuery('(min-width: 768px)')
|
|
76
74
|
const hasSelectedSession = !!(currentSessionId && sessions[currentSessionId])
|
|
75
|
+
const pendingApprovalCount = Object.values(tasks).filter((t) => t.pendingApproval).length
|
|
77
76
|
|
|
78
77
|
const [agentViewMode, setAgentViewMode] = useState<'chat' | 'config'>('chat')
|
|
79
78
|
const [shortcutsOpen, setShortcutsOpen] = useState(false)
|
|
@@ -118,8 +117,7 @@ export function AppLayout() {
|
|
|
118
117
|
}
|
|
119
118
|
|
|
120
119
|
const openNewSheet = () => {
|
|
121
|
-
if (activeView === '
|
|
122
|
-
else if (activeView === 'agents') setAgentSheetOpen(true)
|
|
120
|
+
if (activeView === 'agents') setAgentSheetOpen(true)
|
|
123
121
|
else if (activeView === 'schedules') setScheduleSheetOpen(true)
|
|
124
122
|
else if (activeView === 'tasks') setTaskSheetOpen(true)
|
|
125
123
|
else if (activeView === 'secrets') setSecretSheetOpen(true)
|
|
@@ -132,6 +130,18 @@ export function AppLayout() {
|
|
|
132
130
|
else if (activeView === 'plugins') setPluginSheetOpen(true)
|
|
133
131
|
}
|
|
134
132
|
|
|
133
|
+
const handleNavClick = (view: AppView) => {
|
|
134
|
+
if (FULL_WIDTH_VIEWS.has(view)) {
|
|
135
|
+
setActiveView(view)
|
|
136
|
+
setSidebarOpen(false)
|
|
137
|
+
} else if (activeView === view && sidebarOpen) {
|
|
138
|
+
setSidebarOpen(false)
|
|
139
|
+
} else {
|
|
140
|
+
setActiveView(view)
|
|
141
|
+
setSidebarOpen(true)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
135
145
|
const agents = useAppStore((s) => s.agents)
|
|
136
146
|
const currentAgentId = useAppStore((s) => s.currentAgentId)
|
|
137
147
|
const setCurrentAgent = useAppStore((s) => s.setCurrentAgent)
|
|
@@ -230,82 +240,77 @@ export function AppLayout() {
|
|
|
230
240
|
|
|
231
241
|
{/* Nav items */}
|
|
232
242
|
<div className={`flex flex-col gap-0.5 ${railExpanded ? 'px-3' : 'items-center'}`}>
|
|
233
|
-
<NavItem view="agents" label="Agents" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() =>
|
|
243
|
+
<NavItem view="agents" label="Agents" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('agents')}>
|
|
234
244
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
|
235
245
|
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" /><circle cx="12" cy="7" r="4" />
|
|
236
246
|
</svg>
|
|
237
247
|
</NavItem>
|
|
238
|
-
<NavItem view="
|
|
239
|
-
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
|
240
|
-
<rect x="3" y="3" width="7" height="7" rx="1" /><rect x="14" y="3" width="7" height="7" rx="1" /><rect x="3" y="14" width="7" height="7" rx="1" /><rect x="14" y="14" width="7" height="7" rx="1" />
|
|
241
|
-
</svg>
|
|
242
|
-
</NavItem>
|
|
243
|
-
<NavItem view="schedules" label="Schedules" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => { setActiveView('schedules'); setSidebarOpen(true) }}>
|
|
248
|
+
<NavItem view="schedules" label="Schedules" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('schedules')}>
|
|
244
249
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
|
245
250
|
<circle cx="12" cy="12" r="10" /><polyline points="12 6 12 12 16 14" />
|
|
246
251
|
</svg>
|
|
247
252
|
</NavItem>
|
|
248
|
-
<NavItem view="memory" label="Memory" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() =>
|
|
253
|
+
<NavItem view="memory" label="Memory" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('memory')}>
|
|
249
254
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
|
250
255
|
<ellipse cx="12" cy="5" rx="9" ry="3" /><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3" /><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5" />
|
|
251
256
|
</svg>
|
|
252
257
|
</NavItem>
|
|
253
|
-
<NavItem view="tasks" label="Tasks" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() =>
|
|
258
|
+
<NavItem view="tasks" label="Tasks" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('tasks')} badge={pendingApprovalCount}>
|
|
254
259
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
|
255
260
|
<path d="M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2" /><rect x="9" y="3" width="6" height="4" rx="1" /><path d="M9 14l2 2 4-4" />
|
|
256
261
|
</svg>
|
|
257
262
|
</NavItem>
|
|
258
|
-
<NavItem view="secrets" label="Secrets" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() =>
|
|
263
|
+
<NavItem view="secrets" label="Secrets" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('secrets')}>
|
|
259
264
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
|
260
265
|
<rect x="3" y="11" width="18" height="11" rx="2" ry="2" /><path d="M7 11V7a5 5 0 0 1 10 0v4" />
|
|
261
266
|
</svg>
|
|
262
267
|
</NavItem>
|
|
263
|
-
<NavItem view="providers" label="Providers" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() =>
|
|
268
|
+
<NavItem view="providers" label="Providers" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('providers')}>
|
|
264
269
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
|
265
270
|
<path d="M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z" />
|
|
266
271
|
</svg>
|
|
267
272
|
</NavItem>
|
|
268
|
-
<NavItem view="skills" label="Skills" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() =>
|
|
273
|
+
<NavItem view="skills" label="Skills" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('skills')}>
|
|
269
274
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
|
270
275
|
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z" /><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z" />
|
|
271
276
|
</svg>
|
|
272
277
|
</NavItem>
|
|
273
|
-
<NavItem view="connectors" label="Connectors" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() =>
|
|
278
|
+
<NavItem view="connectors" label="Connectors" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('connectors')}>
|
|
274
279
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
|
275
280
|
<path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3" /><line x1="8" y1="12" x2="16" y2="12" />
|
|
276
281
|
</svg>
|
|
277
282
|
</NavItem>
|
|
278
|
-
<NavItem view="webhooks" label="Webhooks" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() =>
|
|
283
|
+
<NavItem view="webhooks" label="Webhooks" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('webhooks')}>
|
|
279
284
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
|
280
285
|
<path d="M22 12h-4l-3 7L9 5l-3 7H2" />
|
|
281
286
|
</svg>
|
|
282
287
|
</NavItem>
|
|
283
|
-
<NavItem view="mcp_servers" label="MCP" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() =>
|
|
288
|
+
<NavItem view="mcp_servers" label="MCP" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('mcp_servers')}>
|
|
284
289
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
|
285
290
|
<rect x="2" y="2" width="20" height="8" rx="2" /><rect x="2" y="14" width="20" height="8" rx="2" /><line x1="6" y1="6" x2="6.01" y2="6" /><line x1="6" y1="18" x2="6.01" y2="18" />
|
|
286
291
|
</svg>
|
|
287
292
|
</NavItem>
|
|
288
|
-
<NavItem view="knowledge" label="Knowledge" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() =>
|
|
293
|
+
<NavItem view="knowledge" label="Knowledge" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('knowledge')}>
|
|
289
294
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
|
290
295
|
<circle cx="12" cy="12" r="10" /><line x1="2" y1="12" x2="22" y2="12" /><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" />
|
|
291
296
|
</svg>
|
|
292
297
|
</NavItem>
|
|
293
|
-
<NavItem view="plugins" label="Plugins" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() =>
|
|
298
|
+
<NavItem view="plugins" label="Plugins" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('plugins')}>
|
|
294
299
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
295
300
|
<path d="M12 2v4m0 12v4M2 12h4m12 0h4" /><circle cx="12" cy="12" r="4" /><path d="M8 8L5.5 5.5M16 8l2.5-2.5M8 16l-2.5 2.5M16 16l2.5 2.5" />
|
|
296
301
|
</svg>
|
|
297
302
|
</NavItem>
|
|
298
|
-
<NavItem view="usage" label="Usage" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() =>
|
|
303
|
+
<NavItem view="usage" label="Usage" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('usage')}>
|
|
299
304
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
|
300
305
|
<line x1="18" y1="20" x2="18" y2="10" /><line x1="12" y1="20" x2="12" y2="4" /><line x1="6" y1="20" x2="6" y2="14" />
|
|
301
306
|
</svg>
|
|
302
307
|
</NavItem>
|
|
303
|
-
<NavItem view="runs" label="Runs" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() =>
|
|
308
|
+
<NavItem view="runs" label="Runs" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('runs')}>
|
|
304
309
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
|
305
310
|
<polyline points="22 12 18 12 15 21 9 3 6 12 2 12" />
|
|
306
311
|
</svg>
|
|
307
312
|
</NavItem>
|
|
308
|
-
<NavItem view="logs" label="Logs" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() =>
|
|
313
|
+
<NavItem view="logs" label="Logs" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('logs')}>
|
|
309
314
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
|
310
315
|
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" /><polyline points="14 2 14 8 20 8" /><line x1="16" y1="13" x2="8" y2="13" /><line x1="16" y1="17" x2="8" y2="17" /><polyline points="10 9 9 9 8 9" />
|
|
311
316
|
</svg>
|
|
@@ -400,7 +405,7 @@ export function AppLayout() {
|
|
|
400
405
|
style={{ animation: 'panel-in 0.2s cubic-bezier(0.16, 1, 0.3, 1)' }}
|
|
401
406
|
>
|
|
402
407
|
<div className="flex items-center px-5 pt-5 pb-3 shrink-0">
|
|
403
|
-
<h2 className="font-display text-[14px] font-600 text-text-2 tracking-[-0.01em] capitalize flex-1">{activeView
|
|
408
|
+
<h2 className="font-display text-[14px] font-600 text-text-2 tracking-[-0.01em] capitalize flex-1">{activeView}</h2>
|
|
404
409
|
{activeView === 'logs' || activeView === 'usage' || activeView === 'runs' ? null : activeView === 'memory' ? (
|
|
405
410
|
<button
|
|
406
411
|
onClick={() => useAppStore.getState().setMemorySheetOpen(true)}
|
|
@@ -422,17 +427,10 @@ export function AppLayout() {
|
|
|
422
427
|
<line x1="12" y1="5" x2="12" y2="19" />
|
|
423
428
|
<line x1="5" y1="12" x2="19" y2="12" />
|
|
424
429
|
</svg>
|
|
425
|
-
{activeView === '
|
|
430
|
+
{activeView === 'agents' ? 'Agent' : activeView === 'schedules' ? 'Schedule' : activeView === 'tasks' ? 'Task' : activeView === 'secrets' ? 'Secret' : activeView === 'providers' ? 'Provider' : activeView === 'skills' ? 'Skill' : activeView === 'connectors' ? 'Connector' : activeView === 'webhooks' ? 'Webhook' : activeView === 'mcp_servers' ? 'MCP Server' : activeView === 'knowledge' ? 'Knowledge' : 'New'}
|
|
426
431
|
</button>
|
|
427
432
|
)}
|
|
428
433
|
</div>
|
|
429
|
-
{activeView === 'sessions' && (
|
|
430
|
-
<>
|
|
431
|
-
<UpdateBanner />
|
|
432
|
-
<NetworkBanner />
|
|
433
|
-
<SessionList inSidebar onSelect={() => {}} />
|
|
434
|
-
</>
|
|
435
|
-
)}
|
|
436
434
|
{activeView === 'agents' && (
|
|
437
435
|
<>
|
|
438
436
|
<div className="flex gap-1 px-4 pb-2">
|
|
@@ -500,7 +498,7 @@ export function AppLayout() {
|
|
|
500
498
|
</div>
|
|
501
499
|
{/* View selector tabs */}
|
|
502
500
|
<div className="flex px-4 py-2 gap-1 shrink-0 flex-wrap">
|
|
503
|
-
{(['agents', '
|
|
501
|
+
{(['agents', 'schedules', 'memory', 'tasks', 'secrets', 'providers', 'skills', 'connectors', 'webhooks', 'mcp_servers', 'knowledge', 'plugins', 'usage', 'runs', 'logs'] as AppView[]).map((v) => (
|
|
504
502
|
<button
|
|
505
503
|
key={v}
|
|
506
504
|
onClick={() => setActiveView(v)}
|
|
@@ -526,17 +524,10 @@ export function AppLayout() {
|
|
|
526
524
|
shadow-[0_2px_12px_rgba(99,102,241,0.15)]"
|
|
527
525
|
style={{ fontFamily: 'inherit' }}
|
|
528
526
|
>
|
|
529
|
-
+ New {activeView === '
|
|
527
|
+
+ New {activeView === 'agents' ? 'Agent' : activeView === 'schedules' ? 'Schedule' : activeView === 'tasks' ? 'Task' : activeView === 'secrets' ? 'Secret' : activeView === 'providers' ? 'Provider' : activeView === 'skills' ? 'Skill' : activeView === 'connectors' ? 'Connector' : activeView === 'webhooks' ? 'Webhook' : activeView === 'mcp_servers' ? 'MCP Server' : activeView === 'knowledge' ? 'Knowledge' : activeView === 'plugins' ? 'Plugin' : 'Entry'}
|
|
530
528
|
</button>
|
|
531
529
|
</div>
|
|
532
530
|
)}
|
|
533
|
-
{activeView === 'sessions' && (
|
|
534
|
-
<>
|
|
535
|
-
<UpdateBanner />
|
|
536
|
-
<NetworkBanner />
|
|
537
|
-
<SessionList inSidebar onSelect={() => setSidebarOpen(false)} />
|
|
538
|
-
</>
|
|
539
|
-
)}
|
|
540
531
|
{activeView === 'agents' && (
|
|
541
532
|
<>
|
|
542
533
|
<div className="flex gap-1 px-4 pb-2">
|
|
@@ -596,36 +587,48 @@ export function AppLayout() {
|
|
|
596
587
|
</div>
|
|
597
588
|
)}
|
|
598
589
|
</div>
|
|
599
|
-
) : activeView === 'sessions' && hasSelectedSession ? (
|
|
600
|
-
<ChatArea />
|
|
601
|
-
) : activeView === 'sessions' ? (
|
|
602
|
-
<div className="flex-1 flex flex-col">
|
|
603
|
-
{!isDesktop ? (
|
|
604
|
-
<SessionList />
|
|
605
|
-
) : (
|
|
606
|
-
<div className="flex-1 flex items-center justify-center px-8">
|
|
607
|
-
<div className="text-center max-w-[420px]">
|
|
608
|
-
<h2 className="font-display text-[24px] font-700 text-text mb-2 tracking-[-0.02em]">
|
|
609
|
-
No Chat Selected
|
|
610
|
-
</h2>
|
|
611
|
-
<p className="text-[14px] text-text-3">
|
|
612
|
-
Choose a session from the sidebar or switch to Agents view.
|
|
613
|
-
</p>
|
|
614
|
-
</div>
|
|
615
|
-
</div>
|
|
616
|
-
)}
|
|
617
|
-
</div>
|
|
618
590
|
) : activeView === 'tasks' && isDesktop ? (
|
|
619
591
|
<TaskBoard />
|
|
620
592
|
) : activeView === 'memory' ? (
|
|
621
593
|
<MemoryDetail />
|
|
594
|
+
) : !sidebarOpen && FULL_WIDTH_VIEWS.has(activeView) ? (
|
|
595
|
+
<div className="flex-1 flex flex-col h-full">
|
|
596
|
+
<div className="flex items-center px-6 pt-5 pb-3 shrink-0">
|
|
597
|
+
<h2 className="font-display text-[14px] font-600 text-text-2 tracking-[-0.01em] capitalize flex-1">
|
|
598
|
+
{activeView === 'mcp_servers' ? 'MCP Servers' : activeView.replace('_', ' ')}
|
|
599
|
+
</h2>
|
|
600
|
+
{activeView !== 'usage' && activeView !== 'runs' && activeView !== 'logs' && (
|
|
601
|
+
<button
|
|
602
|
+
onClick={openNewSheet}
|
|
603
|
+
className="flex items-center gap-1.5 px-2.5 py-1.5 rounded-[8px] text-[11px] font-600 text-accent-bright bg-accent-soft hover:bg-[#6366F1]/15 transition-all cursor-pointer"
|
|
604
|
+
style={{ fontFamily: 'inherit' }}
|
|
605
|
+
>
|
|
606
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round">
|
|
607
|
+
<line x1="12" y1="5" x2="12" y2="19" /><line x1="5" y1="12" x2="19" y2="12" />
|
|
608
|
+
</svg>
|
|
609
|
+
{activeView === 'schedules' ? 'Schedule' : activeView === 'secrets' ? 'Secret' : activeView === 'providers' ? 'Provider' : activeView === 'skills' ? 'Skill' : activeView === 'connectors' ? 'Connector' : activeView === 'webhooks' ? 'Webhook' : activeView === 'mcp_servers' ? 'MCP Server' : activeView === 'knowledge' ? 'Knowledge' : activeView === 'plugins' ? 'Plugin' : 'New'}
|
|
610
|
+
</button>
|
|
611
|
+
)}
|
|
612
|
+
</div>
|
|
613
|
+
{activeView === 'schedules' && <ScheduleList />}
|
|
614
|
+
{activeView === 'secrets' && <SecretsList />}
|
|
615
|
+
{activeView === 'providers' && <ProviderList />}
|
|
616
|
+
{activeView === 'skills' && <SkillList />}
|
|
617
|
+
{activeView === 'connectors' && <ConnectorList />}
|
|
618
|
+
{activeView === 'webhooks' && <WebhookList />}
|
|
619
|
+
{activeView === 'mcp_servers' && <McpServerList />}
|
|
620
|
+
{activeView === 'knowledge' && <KnowledgeList />}
|
|
621
|
+
{activeView === 'plugins' && <PluginList />}
|
|
622
|
+
{activeView === 'usage' && <UsageList />}
|
|
623
|
+
{activeView === 'runs' && <RunList />}
|
|
624
|
+
{activeView === 'logs' && <LogList />}
|
|
625
|
+
</div>
|
|
622
626
|
) : (
|
|
623
627
|
<ViewEmptyState view={activeView} />
|
|
624
628
|
)}
|
|
625
629
|
</div>
|
|
626
630
|
</ErrorBoundary>
|
|
627
631
|
|
|
628
|
-
<NewSessionSheet />
|
|
629
632
|
<SettingsSheet />
|
|
630
633
|
<AgentSheet />
|
|
631
634
|
<ScheduleSheet />
|
|
@@ -718,7 +721,6 @@ class ErrorBoundary extends Component<{ children: ReactNode }, { hasError: boole
|
|
|
718
721
|
}
|
|
719
722
|
|
|
720
723
|
const VIEW_DESCRIPTIONS: Record<AppView, string> = {
|
|
721
|
-
sessions: 'Session history & debug view',
|
|
722
724
|
agents: 'Chat with & configure your AI agents',
|
|
723
725
|
schedules: 'Automated task schedules',
|
|
724
726
|
memory: 'Long-term agent memory store',
|
|
@@ -736,7 +738,13 @@ const VIEW_DESCRIPTIONS: Record<AppView, string> = {
|
|
|
736
738
|
runs: 'Live session run monitoring & history',
|
|
737
739
|
}
|
|
738
740
|
|
|
739
|
-
const
|
|
741
|
+
const FULL_WIDTH_VIEWS = new Set<AppView>([
|
|
742
|
+
'schedules', 'secrets', 'providers', 'skills',
|
|
743
|
+
'connectors', 'webhooks', 'mcp_servers', 'knowledge', 'plugins',
|
|
744
|
+
'usage', 'runs', 'logs',
|
|
745
|
+
])
|
|
746
|
+
|
|
747
|
+
const VIEW_EMPTY_STATES: Record<Exclude<AppView, 'agents'>, { icon: string; title: string; description: string; features: string[] }> = {
|
|
740
748
|
schedules: {
|
|
741
749
|
icon: 'clock',
|
|
742
750
|
title: 'Schedules',
|
|
@@ -824,8 +832,8 @@ const VIEW_EMPTY_STATES: Record<Exclude<AppView, 'sessions' | 'agents'>, { icon:
|
|
|
824
832
|
}
|
|
825
833
|
|
|
826
834
|
function ViewEmptyState({ view }: { view: AppView }) {
|
|
827
|
-
if (view === '
|
|
828
|
-
const config = VIEW_EMPTY_STATES[view as Exclude<AppView, '
|
|
835
|
+
if (view === 'agents') return null
|
|
836
|
+
const config = VIEW_EMPTY_STATES[view as Exclude<AppView, 'agents'>]
|
|
829
837
|
if (!config) return null
|
|
830
838
|
|
|
831
839
|
return (
|
|
@@ -907,16 +915,17 @@ function ViewEmptyIcon({ type }: { type: string }) {
|
|
|
907
915
|
}
|
|
908
916
|
}
|
|
909
917
|
|
|
910
|
-
function NavItem({ view, label, expanded, active, sidebarOpen, onClick, children }: {
|
|
918
|
+
function NavItem({ view, label, expanded, active, sidebarOpen, onClick, badge, children }: {
|
|
911
919
|
view: AppView
|
|
912
920
|
label: string
|
|
913
921
|
expanded: boolean
|
|
914
922
|
active: AppView
|
|
915
923
|
sidebarOpen: boolean
|
|
916
924
|
onClick: () => void
|
|
925
|
+
badge?: number
|
|
917
926
|
children: React.ReactNode
|
|
918
927
|
}) {
|
|
919
|
-
const isActive = active === view && sidebarOpen
|
|
928
|
+
const isActive = active === view && (sidebarOpen || FULL_WIDTH_VIEWS.has(view))
|
|
920
929
|
|
|
921
930
|
if (expanded) {
|
|
922
931
|
return (
|
|
@@ -928,7 +937,14 @@ function NavItem({ view, label, expanded, active, sidebarOpen, onClick, children
|
|
|
928
937
|
: 'bg-transparent text-text-3 hover:text-text hover:bg-white/[0.04]'}`}
|
|
929
938
|
style={{ fontFamily: 'inherit' }}
|
|
930
939
|
>
|
|
931
|
-
<span className="shrink-0">
|
|
940
|
+
<span className="shrink-0 relative">
|
|
941
|
+
{children}
|
|
942
|
+
{!!badge && (
|
|
943
|
+
<span className="absolute -top-1.5 -right-1.5 min-w-[14px] h-[14px] rounded-full bg-amber-500 text-black text-[9px] font-700 flex items-center justify-center px-0.5">
|
|
944
|
+
{badge}
|
|
945
|
+
</span>
|
|
946
|
+
)}
|
|
947
|
+
</span>
|
|
932
948
|
<span className="truncate">{label}</span>
|
|
933
949
|
</button>
|
|
934
950
|
)
|
|
@@ -937,8 +953,13 @@ function NavItem({ view, label, expanded, active, sidebarOpen, onClick, children
|
|
|
937
953
|
return (
|
|
938
954
|
<Tooltip>
|
|
939
955
|
<TooltipTrigger asChild>
|
|
940
|
-
<button onClick={onClick} className={`rail-btn ${isActive ? 'active' : ''}`}>
|
|
956
|
+
<button onClick={onClick} className={`rail-btn ${isActive ? 'active' : ''} relative`}>
|
|
941
957
|
{children}
|
|
958
|
+
{!!badge && (
|
|
959
|
+
<span className="absolute -top-0.5 -right-0.5 min-w-[14px] h-[14px] rounded-full bg-amber-500 text-black text-[9px] font-700 flex items-center justify-center px-0.5">
|
|
960
|
+
{badge}
|
|
961
|
+
</span>
|
|
962
|
+
)}
|
|
942
963
|
</button>
|
|
943
964
|
</TooltipTrigger>
|
|
944
965
|
<TooltipContent side="right" sideOffset={8}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useEffect, useState } from 'react'
|
|
4
4
|
import { api } from '@/lib/api-client'
|
|
5
|
+
import { useWs } from '@/hooks/use-ws'
|
|
5
6
|
|
|
6
7
|
interface DaemonStatus {
|
|
7
8
|
running: boolean
|
|
@@ -21,11 +22,8 @@ export function DaemonIndicator() {
|
|
|
21
22
|
} catch { /* ignore */ }
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
useEffect(() => {
|
|
25
|
-
|
|
26
|
-
const interval = setInterval(fetchStatus, 30_000)
|
|
27
|
-
return () => clearInterval(interval)
|
|
28
|
-
}, [])
|
|
25
|
+
useEffect(() => { fetchStatus() }, [])
|
|
26
|
+
useWs('daemon', fetchStatus, 30_000)
|
|
29
27
|
|
|
30
28
|
const toggle = async () => {
|
|
31
29
|
try {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useEffect, useState, useRef, useCallback } from 'react'
|
|
4
4
|
import { api } from '@/lib/api-client'
|
|
5
|
+
import { useWs } from '@/hooks/use-ws'
|
|
5
6
|
import { useAppStore } from '@/stores/use-app-store'
|
|
6
7
|
import { BottomSheet } from '@/components/shared/bottom-sheet'
|
|
7
8
|
|
|
@@ -67,12 +68,7 @@ export function LogList() {
|
|
|
67
68
|
loadAgents()
|
|
68
69
|
}, [fetchLogs])
|
|
69
70
|
|
|
70
|
-
|
|
71
|
-
useEffect(() => {
|
|
72
|
-
if (!autoRefresh) return
|
|
73
|
-
const id = setInterval(fetchLogs, 3000)
|
|
74
|
-
return () => clearInterval(id)
|
|
75
|
-
}, [autoRefresh, fetchLogs])
|
|
71
|
+
useWs('logs', fetchLogs, autoRefresh ? 3000 : undefined)
|
|
76
72
|
|
|
77
73
|
const clearLogs = async () => {
|
|
78
74
|
try {
|
|
@@ -151,7 +147,7 @@ export function LogList() {
|
|
|
151
147
|
return (
|
|
152
148
|
<div className="flex-1 flex flex-col overflow-hidden">
|
|
153
149
|
{/* Controls */}
|
|
154
|
-
<div className="px-
|
|
150
|
+
<div className="px-5 py-2 space-y-2 shrink-0">
|
|
155
151
|
{/* Search */}
|
|
156
152
|
<input
|
|
157
153
|
value={search}
|
|
@@ -250,12 +246,12 @@ export function LogList() {
|
|
|
250
246
|
</div>
|
|
251
247
|
|
|
252
248
|
{/* Total count */}
|
|
253
|
-
<div className="px-
|
|
249
|
+
<div className="px-5 py-1 text-[10px] text-text-3/60">
|
|
254
250
|
{entries.length} of {total} entries
|
|
255
251
|
</div>
|
|
256
252
|
|
|
257
253
|
{/* Log entries */}
|
|
258
|
-
<div ref={scrollRef} className="flex-1 overflow-y-auto px-
|
|
254
|
+
<div ref={scrollRef} className="flex-1 overflow-y-auto px-4 pb-8">
|
|
259
255
|
{entries.length === 0 ? (
|
|
260
256
|
<div className="flex items-center justify-center h-32 text-text-3 text-[12px]">
|
|
261
257
|
No log entries
|
|
@@ -59,8 +59,19 @@ export function McpServerList({ inSidebar }: { inSidebar?: boolean }) {
|
|
|
59
59
|
await loadMcpServers()
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
const handleRetest = async (e: React.MouseEvent, id: string) => {
|
|
63
|
+
e.stopPropagation()
|
|
64
|
+
setStatuses((prev) => ({ ...prev, [id]: { ok: false, loading: true } }))
|
|
65
|
+
try {
|
|
66
|
+
const res = await api<{ ok: boolean; tools?: string[]; error?: string }>('POST', `/mcp-servers/${id}/test`)
|
|
67
|
+
setStatuses((prev) => ({ ...prev, [id]: { ok: res.ok, tools: res.tools, error: res.error, loading: false } }))
|
|
68
|
+
} catch {
|
|
69
|
+
setStatuses((prev) => ({ ...prev, [id]: { ok: false, error: 'Test failed', loading: false } }))
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
62
73
|
return (
|
|
63
|
-
<div className={`flex-1 overflow-y-auto ${inSidebar ? 'px-3 pb-4' : 'px-
|
|
74
|
+
<div className={`flex-1 overflow-y-auto ${inSidebar ? 'px-3 pb-4' : 'px-5 pb-6'}`}>
|
|
64
75
|
{serverList.length === 0 ? (
|
|
65
76
|
<div className="text-center py-12">
|
|
66
77
|
<p className="text-[13px] text-text-3/60">No MCP servers configured</p>
|
|
@@ -73,7 +84,7 @@ export function McpServerList({ inSidebar }: { inSidebar?: boolean }) {
|
|
|
73
84
|
</button>
|
|
74
85
|
</div>
|
|
75
86
|
) : (
|
|
76
|
-
<div className=
|
|
87
|
+
<div className={inSidebar ? 'space-y-2' : 'grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-3'}>
|
|
77
88
|
{serverList.map((server) => (
|
|
78
89
|
<button
|
|
79
90
|
key={server.id}
|
|
@@ -96,6 +107,17 @@ export function McpServerList({ inSidebar }: { inSidebar?: boolean }) {
|
|
|
96
107
|
<span className="font-display text-[14px] font-600 text-text truncate">{server.name}</span>
|
|
97
108
|
</div>
|
|
98
109
|
<div className="flex items-center gap-2 shrink-0 ml-2">
|
|
110
|
+
{!inSidebar && (
|
|
111
|
+
<button
|
|
112
|
+
onClick={(e) => handleRetest(e, server.id)}
|
|
113
|
+
className="text-text-3/40 hover:text-text-2 transition-colors p-0.5"
|
|
114
|
+
title="Re-test connection"
|
|
115
|
+
>
|
|
116
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
117
|
+
<path d="M21 2v6h-6M3 12a9 9 0 0 1 15-6.7L21 8M3 22v-6h6M21 12a9 9 0 0 1-15 6.7L3 16" />
|
|
118
|
+
</svg>
|
|
119
|
+
</button>
|
|
120
|
+
)}
|
|
99
121
|
<span className={`text-[10px] font-mono px-2 py-0.5 rounded-full ${transportColors[server.transport] || 'bg-white/10 text-text-3'}`}>
|
|
100
122
|
{server.transport}
|
|
101
123
|
</span>
|