@swarmclawai/swarmclaw 0.2.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.
Files changed (319) hide show
  1. package/README.md +577 -0
  2. package/bin/server-cmd.js +359 -0
  3. package/bin/swarmclaw.js +29 -0
  4. package/bin/swarmclaw.mjs +1504 -0
  5. package/next.config.ts +33 -0
  6. package/package.json +112 -0
  7. package/postcss.config.mjs +7 -0
  8. package/public/branding/swarmclaw-org-avatar.png +0 -0
  9. package/public/branding/swarmclaw-org-avatar.svg +58 -0
  10. package/public/file.svg +1 -0
  11. package/public/globe.svg +1 -0
  12. package/public/next.svg +1 -0
  13. package/public/screenshots/agents.png +0 -0
  14. package/public/screenshots/connectors.png +0 -0
  15. package/public/screenshots/dashboard.png +0 -0
  16. package/public/screenshots/new-session-openclaw.png +0 -0
  17. package/public/screenshots/providers.png +0 -0
  18. package/public/screenshots/schedules.png +0 -0
  19. package/public/screenshots/tasks.png +0 -0
  20. package/public/vercel.svg +1 -0
  21. package/public/window.svg +1 -0
  22. package/src/app/api/agents/[id]/route.ts +30 -0
  23. package/src/app/api/agents/[id]/thread/route.ts +66 -0
  24. package/src/app/api/agents/generate/route.ts +42 -0
  25. package/src/app/api/agents/route.ts +33 -0
  26. package/src/app/api/auth/route.ts +25 -0
  27. package/src/app/api/claude-skills/route.ts +42 -0
  28. package/src/app/api/clawhub/install/route.ts +39 -0
  29. package/src/app/api/clawhub/search/route.ts +11 -0
  30. package/src/app/api/connectors/[id]/route.ts +79 -0
  31. package/src/app/api/connectors/route.ts +60 -0
  32. package/src/app/api/credentials/[id]/route.ts +14 -0
  33. package/src/app/api/credentials/route.ts +31 -0
  34. package/src/app/api/daemon/health-check/route.ts +11 -0
  35. package/src/app/api/daemon/route.ts +22 -0
  36. package/src/app/api/dirs/pick/route.ts +60 -0
  37. package/src/app/api/dirs/route.ts +29 -0
  38. package/src/app/api/documents/[id]/route.ts +47 -0
  39. package/src/app/api/documents/route.ts +93 -0
  40. package/src/app/api/files/serve/route.ts +69 -0
  41. package/src/app/api/generate/info/route.ts +12 -0
  42. package/src/app/api/generate/route.ts +106 -0
  43. package/src/app/api/ip/route.ts +6 -0
  44. package/src/app/api/knowledge/[id]/route.ts +61 -0
  45. package/src/app/api/knowledge/route.ts +48 -0
  46. package/src/app/api/knowledge/upload/route.ts +86 -0
  47. package/src/app/api/logs/route.ts +65 -0
  48. package/src/app/api/mcp-servers/[id]/route.ts +32 -0
  49. package/src/app/api/mcp-servers/[id]/test/route.ts +23 -0
  50. package/src/app/api/mcp-servers/[id]/tools/route.ts +32 -0
  51. package/src/app/api/mcp-servers/route.ts +27 -0
  52. package/src/app/api/memory/[id]/route.ts +126 -0
  53. package/src/app/api/memory/maintenance/route.ts +63 -0
  54. package/src/app/api/memory/route.ts +111 -0
  55. package/src/app/api/memory-images/[filename]/route.ts +36 -0
  56. package/src/app/api/orchestrator/run/route.ts +43 -0
  57. package/src/app/api/plugins/install/route.ts +58 -0
  58. package/src/app/api/plugins/marketplace/route.ts +33 -0
  59. package/src/app/api/plugins/route.ts +21 -0
  60. package/src/app/api/preview-server/route.ts +339 -0
  61. package/src/app/api/providers/[id]/models/route.ts +29 -0
  62. package/src/app/api/providers/[id]/route.ts +34 -0
  63. package/src/app/api/providers/configs/route.ts +7 -0
  64. package/src/app/api/providers/ollama/route.ts +30 -0
  65. package/src/app/api/providers/openclaw/health/route.ts +23 -0
  66. package/src/app/api/providers/route.ts +28 -0
  67. package/src/app/api/runs/[id]/route.ts +9 -0
  68. package/src/app/api/runs/route.ts +13 -0
  69. package/src/app/api/schedules/[id]/route.ts +28 -0
  70. package/src/app/api/schedules/[id]/run/route.ts +104 -0
  71. package/src/app/api/schedules/route.ts +78 -0
  72. package/src/app/api/secrets/[id]/route.ts +29 -0
  73. package/src/app/api/secrets/route.ts +42 -0
  74. package/src/app/api/sessions/[id]/browser/route.ts +13 -0
  75. package/src/app/api/sessions/[id]/chat/route.ts +96 -0
  76. package/src/app/api/sessions/[id]/clear/route.ts +19 -0
  77. package/src/app/api/sessions/[id]/deploy/route.ts +34 -0
  78. package/src/app/api/sessions/[id]/devserver/route.ts +69 -0
  79. package/src/app/api/sessions/[id]/mailbox/route.ts +70 -0
  80. package/src/app/api/sessions/[id]/main-loop/route.ts +94 -0
  81. package/src/app/api/sessions/[id]/messages/route.ts +9 -0
  82. package/src/app/api/sessions/[id]/retry/route.ts +28 -0
  83. package/src/app/api/sessions/[id]/route.ts +103 -0
  84. package/src/app/api/sessions/[id]/stop/route.ts +13 -0
  85. package/src/app/api/sessions/heartbeat/route.ts +26 -0
  86. package/src/app/api/sessions/route.ts +85 -0
  87. package/src/app/api/settings/route.ts +58 -0
  88. package/src/app/api/setup/check-provider/route.ts +326 -0
  89. package/src/app/api/setup/doctor/route.ts +250 -0
  90. package/src/app/api/skills/[id]/route.ts +40 -0
  91. package/src/app/api/skills/import/route.ts +69 -0
  92. package/src/app/api/skills/route.ts +28 -0
  93. package/src/app/api/tasks/[id]/route.ts +102 -0
  94. package/src/app/api/tasks/route.ts +115 -0
  95. package/src/app/api/tts/route.ts +40 -0
  96. package/src/app/api/upload/route.ts +18 -0
  97. package/src/app/api/uploads/[filename]/route.ts +59 -0
  98. package/src/app/api/usage/route.ts +35 -0
  99. package/src/app/api/version/route.ts +81 -0
  100. package/src/app/api/version/update/route.ts +95 -0
  101. package/src/app/api/webhooks/[id]/history/route.ts +13 -0
  102. package/src/app/api/webhooks/[id]/route.ts +204 -0
  103. package/src/app/api/webhooks/route.ts +37 -0
  104. package/src/app/favicon.ico +0 -0
  105. package/src/app/globals.css +370 -0
  106. package/src/app/layout.tsx +52 -0
  107. package/src/app/page.tsx +172 -0
  108. package/src/cli/index.js +1232 -0
  109. package/src/cli/index.test.js +281 -0
  110. package/src/cli/index.ts +1158 -0
  111. package/src/cli/spec.js +284 -0
  112. package/src/components/agents/agent-card.tsx +219 -0
  113. package/src/components/agents/agent-chat-list.tsx +165 -0
  114. package/src/components/agents/agent-list.tsx +110 -0
  115. package/src/components/agents/agent-sheet.tsx +1220 -0
  116. package/src/components/auth/access-key-gate.tsx +248 -0
  117. package/src/components/auth/setup-wizard.tsx +940 -0
  118. package/src/components/auth/user-picker.tsx +88 -0
  119. package/src/components/chat/chat-area.tsx +406 -0
  120. package/src/components/chat/chat-header.tsx +491 -0
  121. package/src/components/chat/chat-tool-toggles.tsx +161 -0
  122. package/src/components/chat/code-block.tsx +146 -0
  123. package/src/components/chat/dev-server-bar.tsx +39 -0
  124. package/src/components/chat/message-bubble.tsx +486 -0
  125. package/src/components/chat/message-list.tsx +299 -0
  126. package/src/components/chat/session-debug-panel.tsx +196 -0
  127. package/src/components/chat/streaming-bubble.tsx +85 -0
  128. package/src/components/chat/thinking-indicator.tsx +26 -0
  129. package/src/components/chat/tool-call-bubble.tsx +438 -0
  130. package/src/components/chat/tool-request-banner.tsx +103 -0
  131. package/src/components/connectors/connector-list.tsx +196 -0
  132. package/src/components/connectors/connector-sheet.tsx +804 -0
  133. package/src/components/input/chat-input.tsx +235 -0
  134. package/src/components/knowledge/knowledge-list.tsx +206 -0
  135. package/src/components/knowledge/knowledge-sheet.tsx +316 -0
  136. package/src/components/layout/app-layout.tsx +1016 -0
  137. package/src/components/layout/daemon-indicator.tsx +56 -0
  138. package/src/components/layout/mobile-header.tsx +31 -0
  139. package/src/components/layout/network-banner.tsx +17 -0
  140. package/src/components/layout/update-banner.tsx +130 -0
  141. package/src/components/logs/log-list.tsx +358 -0
  142. package/src/components/mcp-servers/mcp-server-list.tsx +122 -0
  143. package/src/components/mcp-servers/mcp-server-sheet.tsx +243 -0
  144. package/src/components/memory/memory-card.tsx +63 -0
  145. package/src/components/memory/memory-detail.tsx +339 -0
  146. package/src/components/memory/memory-list.tsx +198 -0
  147. package/src/components/memory/memory-sheet.tsx +70 -0
  148. package/src/components/plugins/plugin-list.tsx +60 -0
  149. package/src/components/plugins/plugin-sheet.tsx +311 -0
  150. package/src/components/providers/provider-list.tsx +96 -0
  151. package/src/components/providers/provider-sheet.tsx +542 -0
  152. package/src/components/runs/run-list.tsx +231 -0
  153. package/src/components/schedules/schedule-card.tsx +63 -0
  154. package/src/components/schedules/schedule-list.tsx +76 -0
  155. package/src/components/schedules/schedule-sheet.tsx +336 -0
  156. package/src/components/secrets/secret-sheet.tsx +180 -0
  157. package/src/components/secrets/secrets-list.tsx +91 -0
  158. package/src/components/sessions/new-session-sheet.tsx +478 -0
  159. package/src/components/sessions/session-card.tsx +144 -0
  160. package/src/components/sessions/session-list.tsx +202 -0
  161. package/src/components/shared/ai-gen-block.tsx +77 -0
  162. package/src/components/shared/avatar.tsx +48 -0
  163. package/src/components/shared/bottom-sheet.tsx +30 -0
  164. package/src/components/shared/confirm-dialog.tsx +47 -0
  165. package/src/components/shared/connector-platform-icon.tsx +113 -0
  166. package/src/components/shared/dir-browser.tsx +285 -0
  167. package/src/components/shared/dropdown.tsx +55 -0
  168. package/src/components/shared/icon-button.tsx +25 -0
  169. package/src/components/shared/settings/plugin-manager.tsx +207 -0
  170. package/src/components/shared/settings/section-capability-policy.tsx +93 -0
  171. package/src/components/shared/settings/section-embedding.tsx +99 -0
  172. package/src/components/shared/settings/section-heartbeat.tsx +168 -0
  173. package/src/components/shared/settings/section-memory.tsx +77 -0
  174. package/src/components/shared/settings/section-orchestrator.tsx +108 -0
  175. package/src/components/shared/settings/section-providers.tsx +181 -0
  176. package/src/components/shared/settings/section-runtime-loop.tsx +183 -0
  177. package/src/components/shared/settings/section-secrets.tsx +132 -0
  178. package/src/components/shared/settings/section-user-preferences.tsx +24 -0
  179. package/src/components/shared/settings/section-voice.tsx +53 -0
  180. package/src/components/shared/settings/settings-sheet.tsx +88 -0
  181. package/src/components/shared/settings/types.ts +7 -0
  182. package/src/components/shared/settings/utils.ts +13 -0
  183. package/src/components/shared/settings-sheet.tsx +1 -0
  184. package/src/components/shared/skeleton.tsx +19 -0
  185. package/src/components/shared/usage-badge.tsx +28 -0
  186. package/src/components/skills/clawhub-browser.tsx +225 -0
  187. package/src/components/skills/skill-list.tsx +70 -0
  188. package/src/components/skills/skill-sheet.tsx +254 -0
  189. package/src/components/tasks/task-board.tsx +96 -0
  190. package/src/components/tasks/task-card.tsx +179 -0
  191. package/src/components/tasks/task-column.tsx +73 -0
  192. package/src/components/tasks/task-list.tsx +118 -0
  193. package/src/components/tasks/task-sheet.tsx +415 -0
  194. package/src/components/ui/avatar.tsx +109 -0
  195. package/src/components/ui/badge.tsx +48 -0
  196. package/src/components/ui/button.tsx +64 -0
  197. package/src/components/ui/card.tsx +92 -0
  198. package/src/components/ui/dialog.tsx +158 -0
  199. package/src/components/ui/dropdown-menu.tsx +257 -0
  200. package/src/components/ui/input.tsx +21 -0
  201. package/src/components/ui/scroll-area.tsx +58 -0
  202. package/src/components/ui/select.tsx +190 -0
  203. package/src/components/ui/separator.tsx +28 -0
  204. package/src/components/ui/sheet.tsx +143 -0
  205. package/src/components/ui/sonner.tsx +22 -0
  206. package/src/components/ui/textarea.tsx +18 -0
  207. package/src/components/ui/tooltip.tsx +56 -0
  208. package/src/components/usage/usage-list.tsx +105 -0
  209. package/src/components/webhooks/webhook-list.tsx +166 -0
  210. package/src/components/webhooks/webhook-sheet.tsx +402 -0
  211. package/src/hooks/use-auto-resize.ts +20 -0
  212. package/src/hooks/use-media-query.ts +21 -0
  213. package/src/hooks/use-speech-recognition.ts +83 -0
  214. package/src/instrumentation.ts +8 -0
  215. package/src/lib/agents.ts +13 -0
  216. package/src/lib/api-client.ts +100 -0
  217. package/src/lib/chat.ts +60 -0
  218. package/src/lib/memory.ts +42 -0
  219. package/src/lib/openclaw-endpoint.test.ts +48 -0
  220. package/src/lib/openclaw-endpoint.ts +67 -0
  221. package/src/lib/provider-config.ts +13 -0
  222. package/src/lib/providers/anthropic.ts +135 -0
  223. package/src/lib/providers/claude-cli.ts +202 -0
  224. package/src/lib/providers/codex-cli.ts +260 -0
  225. package/src/lib/providers/index.ts +351 -0
  226. package/src/lib/providers/ollama.ts +131 -0
  227. package/src/lib/providers/openai.ts +164 -0
  228. package/src/lib/providers/openclaw.ts +330 -0
  229. package/src/lib/providers/opencode-cli.ts +164 -0
  230. package/src/lib/runtime-loop.ts +15 -0
  231. package/src/lib/schedule-dedupe.test.ts +84 -0
  232. package/src/lib/schedule-dedupe.ts +174 -0
  233. package/src/lib/schedule-name.ts +62 -0
  234. package/src/lib/schedules.ts +16 -0
  235. package/src/lib/server/agent-registry.ts +70 -0
  236. package/src/lib/server/api-routes.test.ts +362 -0
  237. package/src/lib/server/autonomy-contract.ts +200 -0
  238. package/src/lib/server/build-llm.ts +155 -0
  239. package/src/lib/server/capability-router.test.ts +21 -0
  240. package/src/lib/server/capability-router.ts +172 -0
  241. package/src/lib/server/chat-execution.ts +894 -0
  242. package/src/lib/server/clawhub-client.test.ts +161 -0
  243. package/src/lib/server/clawhub-client.ts +26 -0
  244. package/src/lib/server/connectors/connector-routing.test.ts +243 -0
  245. package/src/lib/server/connectors/discord.ts +116 -0
  246. package/src/lib/server/connectors/googlechat.ts +66 -0
  247. package/src/lib/server/connectors/manager.ts +559 -0
  248. package/src/lib/server/connectors/matrix.ts +78 -0
  249. package/src/lib/server/connectors/media.ts +149 -0
  250. package/src/lib/server/connectors/openclaw.test.ts +375 -0
  251. package/src/lib/server/connectors/openclaw.ts +1132 -0
  252. package/src/lib/server/connectors/signal.ts +183 -0
  253. package/src/lib/server/connectors/slack.ts +258 -0
  254. package/src/lib/server/connectors/teams.ts +94 -0
  255. package/src/lib/server/connectors/telegram.ts +221 -0
  256. package/src/lib/server/connectors/types.ts +62 -0
  257. package/src/lib/server/connectors/whatsapp.ts +349 -0
  258. package/src/lib/server/context-manager.ts +232 -0
  259. package/src/lib/server/cost.ts +31 -0
  260. package/src/lib/server/daemon-state.ts +354 -0
  261. package/src/lib/server/data-dir.ts +3 -0
  262. package/src/lib/server/embeddings.ts +111 -0
  263. package/src/lib/server/execution-log.ts +257 -0
  264. package/src/lib/server/gateway/protocol.test.ts +54 -0
  265. package/src/lib/server/gateway/protocol.ts +114 -0
  266. package/src/lib/server/heartbeat-service.ts +366 -0
  267. package/src/lib/server/knowledge-db.test.ts +441 -0
  268. package/src/lib/server/logger.ts +47 -0
  269. package/src/lib/server/main-agent-loop.ts +1017 -0
  270. package/src/lib/server/mcp-client.test.ts +342 -0
  271. package/src/lib/server/mcp-client.ts +130 -0
  272. package/src/lib/server/memory-db.ts +1078 -0
  273. package/src/lib/server/memory-graph.test.ts +153 -0
  274. package/src/lib/server/memory-graph.ts +138 -0
  275. package/src/lib/server/openclaw-health.ts +245 -0
  276. package/src/lib/server/orchestrator-lg.ts +431 -0
  277. package/src/lib/server/orchestrator.ts +364 -0
  278. package/src/lib/server/playwright-proxy.mjs +70 -0
  279. package/src/lib/server/plugins.ts +229 -0
  280. package/src/lib/server/process-manager.ts +327 -0
  281. package/src/lib/server/provider-health.ts +113 -0
  282. package/src/lib/server/queue.ts +859 -0
  283. package/src/lib/server/runtime-settings.ts +119 -0
  284. package/src/lib/server/scheduler.ts +196 -0
  285. package/src/lib/server/session-mailbox.ts +129 -0
  286. package/src/lib/server/session-run-manager.ts +512 -0
  287. package/src/lib/server/session-tools/connector.ts +124 -0
  288. package/src/lib/server/session-tools/context-mgmt.ts +103 -0
  289. package/src/lib/server/session-tools/context.ts +114 -0
  290. package/src/lib/server/session-tools/crud.ts +673 -0
  291. package/src/lib/server/session-tools/delegate.ts +708 -0
  292. package/src/lib/server/session-tools/file.ts +264 -0
  293. package/src/lib/server/session-tools/index.ts +164 -0
  294. package/src/lib/server/session-tools/memory.ts +230 -0
  295. package/src/lib/server/session-tools/session-info.ts +422 -0
  296. package/src/lib/server/session-tools/session-tools-wiring.test.ts +166 -0
  297. package/src/lib/server/session-tools/shell.ts +171 -0
  298. package/src/lib/server/session-tools/web.ts +408 -0
  299. package/src/lib/server/session-tools.ts +9 -0
  300. package/src/lib/server/skills-normalize.ts +130 -0
  301. package/src/lib/server/storage-mcp.test.ts +161 -0
  302. package/src/lib/server/storage.ts +670 -0
  303. package/src/lib/server/stream-agent-chat.ts +571 -0
  304. package/src/lib/server/task-reports.ts +122 -0
  305. package/src/lib/server/task-result.ts +161 -0
  306. package/src/lib/server/task-validation.test.ts +27 -0
  307. package/src/lib/server/task-validation.ts +90 -0
  308. package/src/lib/server/tool-capability-policy.test.ts +58 -0
  309. package/src/lib/server/tool-capability-policy.ts +262 -0
  310. package/src/lib/sessions.ts +68 -0
  311. package/src/lib/tasks.ts +20 -0
  312. package/src/lib/tts.ts +42 -0
  313. package/src/lib/upload.ts +10 -0
  314. package/src/lib/utils.ts +6 -0
  315. package/src/proxy.ts +43 -0
  316. package/src/stores/use-app-store.ts +468 -0
  317. package/src/stores/use-chat-store.ts +323 -0
  318. package/src/types/index.ts +621 -0
  319. package/tsconfig.json +34 -0
@@ -0,0 +1,231 @@
1
+ 'use client'
2
+
3
+ import { useEffect, useState, useRef, useCallback } from 'react'
4
+ import { api } from '@/lib/api-client'
5
+ import { BottomSheet } from '@/components/shared/bottom-sheet'
6
+ import type { SessionRunRecord, SessionRunStatus } from '@/types'
7
+
8
+ const STATUS_COLORS: Record<SessionRunStatus, { bg: string; text: string }> = {
9
+ queued: { bg: 'bg-yellow-500/10', text: 'text-yellow-400' },
10
+ running: { bg: 'bg-blue-500/10', text: 'text-blue-400' },
11
+ completed: { bg: 'bg-emerald-500/10', text: 'text-emerald-400' },
12
+ failed: { bg: 'bg-red-500/10', text: 'text-red-400' },
13
+ cancelled: { bg: 'bg-white/[0.06]', text: 'text-text-3' },
14
+ }
15
+
16
+ const ALL_STATUSES: SessionRunStatus[] = ['queued', 'running', 'completed', 'failed', 'cancelled']
17
+
18
+ function relativeTime(ts: number): string {
19
+ const diff = Date.now() - ts
20
+ if (diff < 60_000) return `${Math.floor(diff / 1000)}s ago`
21
+ if (diff < 3_600_000) return `${Math.floor(diff / 60_000)}m ago`
22
+ if (diff < 86_400_000) return `${Math.floor(diff / 3_600_000)}h ago`
23
+ return `${Math.floor(diff / 86_400_000)}d ago`
24
+ }
25
+
26
+ function formatDuration(start?: number, end?: number): string {
27
+ if (!start) return '-'
28
+ const elapsed = (end || Date.now()) - start
29
+ if (elapsed < 1000) return `${elapsed}ms`
30
+ if (elapsed < 60_000) return `${(elapsed / 1000).toFixed(1)}s`
31
+ return `${Math.floor(elapsed / 60_000)}m ${Math.floor((elapsed % 60_000) / 1000)}s`
32
+ }
33
+
34
+ export function RunList() {
35
+ const [runs, setRuns] = useState<SessionRunRecord[]>([])
36
+ const [loading, setLoading] = useState(true)
37
+ const [autoRefresh, setAutoRefresh] = useState(false)
38
+ const [statusFilter, setStatusFilter] = useState<SessionRunStatus | null>(null)
39
+ const [selected, setSelected] = useState<SessionRunRecord | null>(null)
40
+ const timerRef = useRef<ReturnType<typeof setInterval> | null>(null)
41
+
42
+ const fetchRuns = useCallback(async () => {
43
+ try {
44
+ const res = await api<SessionRunRecord[]>('GET', '/runs?limit=200')
45
+ setRuns(Array.isArray(res) ? res : [])
46
+ } catch {
47
+ // ignore
48
+ } finally {
49
+ setLoading(false)
50
+ }
51
+ }, [])
52
+
53
+ useEffect(() => {
54
+ fetchRuns()
55
+ }, [fetchRuns])
56
+
57
+ useEffect(() => {
58
+ if (!autoRefresh) {
59
+ if (timerRef.current) clearInterval(timerRef.current)
60
+ return
61
+ }
62
+ timerRef.current = setInterval(fetchRuns, 3000)
63
+ return () => {
64
+ if (timerRef.current) clearInterval(timerRef.current)
65
+ }
66
+ }, [autoRefresh, fetchRuns])
67
+
68
+ const filtered = statusFilter ? runs.filter((r) => r.status === statusFilter) : runs
69
+
70
+ if (loading) {
71
+ return (
72
+ <div className="flex-1 flex items-center justify-center text-text-3 text-[13px]">
73
+ Loading runs...
74
+ </div>
75
+ )
76
+ }
77
+
78
+ return (
79
+ <div className="flex-1 flex flex-col overflow-hidden">
80
+ {/* Controls */}
81
+ <div className="px-4 py-2 space-y-2 shrink-0">
82
+ {/* Status filter + auto-refresh */}
83
+ <div className="flex items-center gap-1.5 flex-wrap">
84
+ <button
85
+ onClick={() => setStatusFilter(null)}
86
+ className={`px-2 py-1 rounded-[6px] text-[10px] font-700 uppercase tracking-wider cursor-pointer transition-all border-none ${
87
+ !statusFilter ? 'bg-accent-soft text-accent-bright' : 'bg-white/[0.02] text-text-3/70'
88
+ }`}
89
+ >
90
+ ALL
91
+ </button>
92
+ {ALL_STATUSES.map((s) => (
93
+ <button
94
+ key={s}
95
+ onClick={() => setStatusFilter(statusFilter === s ? null : s)}
96
+ className={`px-2 py-1 rounded-[6px] text-[10px] font-700 uppercase tracking-wider cursor-pointer transition-all border-none ${
97
+ statusFilter === s ? `${STATUS_COLORS[s].bg} ${STATUS_COLORS[s].text}` : 'bg-white/[0.02] text-text-3/70'
98
+ }`}
99
+ >
100
+ {s}
101
+ </button>
102
+ ))}
103
+ <div className="flex-1" />
104
+ <button
105
+ onClick={() => setAutoRefresh(!autoRefresh)}
106
+ className={`px-2 py-1 rounded-[6px] text-[10px] font-600 cursor-pointer transition-all border-none ${
107
+ autoRefresh ? 'bg-green-500/10 text-green-400' : 'bg-white/[0.04] text-text-3'
108
+ }`}
109
+ >
110
+ {autoRefresh ? 'LIVE' : 'PAUSED'}
111
+ </button>
112
+ </div>
113
+ </div>
114
+
115
+ {/* Count */}
116
+ <div className="px-4 py-1 text-[10px] text-text-3/60">
117
+ {filtered.length} run{filtered.length !== 1 ? 's' : ''}
118
+ </div>
119
+
120
+ {/* Run list */}
121
+ <div className="flex-1 overflow-y-auto px-2 pb-20">
122
+ {filtered.length === 0 ? (
123
+ <div className="flex items-center justify-center h-32 text-text-3 text-[12px]">
124
+ No runs found
125
+ </div>
126
+ ) : (
127
+ <div className="space-y-1">
128
+ {filtered.map((run) => (
129
+ <button
130
+ key={run.id}
131
+ onClick={() => setSelected(run)}
132
+ className="w-full text-left p-3 rounded-[10px] border border-white/[0.06] bg-surface hover:bg-surface-2 transition-all cursor-pointer block"
133
+ >
134
+ <div className="flex items-center gap-2 mb-1">
135
+ <span className={`text-[9px] font-700 uppercase tracking-wider px-1.5 py-0.5 rounded-[4px] ${STATUS_COLORS[run.status].bg} ${STATUS_COLORS[run.status].text}`}>
136
+ {run.status}
137
+ </span>
138
+ <span className="text-[11px] text-text-3/60 font-mono">{run.source}</span>
139
+ <span className="text-[10px] text-text-3/40 ml-auto">{relativeTime(run.queuedAt)}</span>
140
+ </div>
141
+ <div className="text-[12px] text-text-2 truncate">{run.messagePreview || run.id}</div>
142
+ {run.startedAt && (
143
+ <div className="text-[10px] text-text-3/50 mt-1">
144
+ Duration: {formatDuration(run.startedAt, run.endedAt)}
145
+ </div>
146
+ )}
147
+ </button>
148
+ ))}
149
+ </div>
150
+ )}
151
+ </div>
152
+
153
+ {/* Detail Sheet */}
154
+ <BottomSheet open={!!selected} onClose={() => setSelected(null)}>
155
+ {selected && (
156
+ <>
157
+ <div className="mb-6">
158
+ <div className="flex items-center gap-3 mb-3">
159
+ <span className={`text-[11px] font-700 uppercase tracking-wider px-2.5 py-1 rounded-[6px] ${STATUS_COLORS[selected.status].bg} ${STATUS_COLORS[selected.status].text}`}>
160
+ {selected.status}
161
+ </span>
162
+ <span className="text-[12px] font-mono text-text-3/60">{selected.source}</span>
163
+ </div>
164
+ <h2 className="font-display text-[20px] font-700 tracking-[-0.02em] mb-2 leading-snug">
165
+ Run Details
166
+ </h2>
167
+ <p className="text-[12px] text-text-3/60 font-mono">{selected.id}</p>
168
+ </div>
169
+
170
+ {/* Timing */}
171
+ <div className="mb-6 space-y-2">
172
+ <label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em]">Timing</label>
173
+ <div className="grid grid-cols-2 gap-2">
174
+ <div className="p-2.5 rounded-[10px] bg-white/[0.02] border border-white/[0.04]">
175
+ <div className="text-[10px] text-text-3/60 mb-0.5">Queued</div>
176
+ <div className="text-[12px] text-text font-mono">{new Date(selected.queuedAt).toLocaleString()}</div>
177
+ </div>
178
+ {selected.startedAt && (
179
+ <div className="p-2.5 rounded-[10px] bg-white/[0.02] border border-white/[0.04]">
180
+ <div className="text-[10px] text-text-3/60 mb-0.5">Started</div>
181
+ <div className="text-[12px] text-text font-mono">{new Date(selected.startedAt).toLocaleString()}</div>
182
+ </div>
183
+ )}
184
+ {selected.endedAt && (
185
+ <div className="p-2.5 rounded-[10px] bg-white/[0.02] border border-white/[0.04]">
186
+ <div className="text-[10px] text-text-3/60 mb-0.5">Ended</div>
187
+ <div className="text-[12px] text-text font-mono">{new Date(selected.endedAt).toLocaleString()}</div>
188
+ </div>
189
+ )}
190
+ <div className="p-2.5 rounded-[10px] bg-white/[0.02] border border-white/[0.04]">
191
+ <div className="text-[10px] text-text-3/60 mb-0.5">Duration</div>
192
+ <div className="text-[12px] text-text font-mono">{formatDuration(selected.startedAt, selected.endedAt)}</div>
193
+ </div>
194
+ </div>
195
+ </div>
196
+
197
+ {/* Message */}
198
+ {selected.messagePreview && (
199
+ <div className="mb-6">
200
+ <label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-2">Message</label>
201
+ <pre className="text-[11px] text-text-3/80 font-mono whitespace-pre-wrap break-all bg-white/[0.02] rounded-[12px] p-4 max-h-[200px] overflow-auto border border-white/[0.04]">
202
+ {selected.messagePreview}
203
+ </pre>
204
+ </div>
205
+ )}
206
+
207
+ {/* Error */}
208
+ {selected.error && (
209
+ <div className="mb-6">
210
+ <label className="block font-display text-[12px] font-600 text-red-400 uppercase tracking-[0.08em] mb-2">Error</label>
211
+ <pre className="text-[11px] text-red-300/80 font-mono whitespace-pre-wrap break-all bg-red-500/[0.05] rounded-[12px] p-4 max-h-[200px] overflow-auto border border-red-500/[0.1]">
212
+ {selected.error}
213
+ </pre>
214
+ </div>
215
+ )}
216
+
217
+ {/* Result */}
218
+ {selected.resultPreview && (
219
+ <div className="mb-6">
220
+ <label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-2">Result</label>
221
+ <pre className="text-[11px] text-text-3/80 font-mono whitespace-pre-wrap break-all bg-white/[0.02] rounded-[12px] p-4 max-h-[200px] overflow-auto border border-white/[0.04]">
222
+ {selected.resultPreview}
223
+ </pre>
224
+ </div>
225
+ )}
226
+ </>
227
+ )}
228
+ </BottomSheet>
229
+ </div>
230
+ )
231
+ }
@@ -0,0 +1,63 @@
1
+ 'use client'
2
+
3
+ import type { Schedule } from '@/types'
4
+ import { useAppStore } from '@/stores/use-app-store'
5
+
6
+ const STATUS_COLORS: Record<string, string> = {
7
+ active: 'text-emerald-400 bg-emerald-400/[0.08]',
8
+ paused: 'text-amber-400 bg-amber-400/[0.08]',
9
+ completed: 'text-text-3 bg-white/[0.03]',
10
+ failed: 'text-red-400 bg-red-400/[0.08]',
11
+ }
12
+
13
+ function formatNext(ts?: number): string {
14
+ if (!ts) return 'Not scheduled'
15
+ const d = new Date(ts)
16
+ const now = Date.now()
17
+ const diff = ts - now
18
+ if (diff < 0) return 'Overdue'
19
+ if (diff < 60000) return 'In < 1m'
20
+ if (diff < 3600000) return `In ${Math.floor(diff / 60000)}m`
21
+ if (diff < 86400000) return `In ${Math.floor(diff / 3600000)}h`
22
+ return d.toLocaleDateString()
23
+ }
24
+
25
+ interface Props {
26
+ schedule: Schedule
27
+ }
28
+
29
+ export function ScheduleCard({ schedule }: Props) {
30
+ const setEditingScheduleId = useAppStore((s) => s.setEditingScheduleId)
31
+ const setScheduleSheetOpen = useAppStore((s) => s.setScheduleSheetOpen)
32
+ const agents = useAppStore((s) => s.agents)
33
+
34
+ const handleClick = () => {
35
+ setEditingScheduleId(schedule.id)
36
+ setScheduleSheetOpen(true)
37
+ }
38
+
39
+ const agent = agents[schedule.agentId]
40
+ const statusClass = STATUS_COLORS[schedule.status] || STATUS_COLORS.paused
41
+
42
+ return (
43
+ <div
44
+ onClick={handleClick}
45
+ className="relative py-3.5 px-4 cursor-pointer rounded-[14px]
46
+ transition-all duration-200 active:scale-[0.98]
47
+ bg-transparent border border-transparent hover:bg-white/[0.02] hover:border-white/[0.03]"
48
+ >
49
+ <div className="flex items-center gap-2.5">
50
+ <span className="font-display text-[14px] font-600 truncate flex-1 tracking-[-0.01em]">{schedule.name}</span>
51
+ <span className={`shrink-0 text-[10px] font-600 uppercase tracking-wider px-2 py-0.5 rounded-[6px] ${statusClass}`}>
52
+ {schedule.status}
53
+ </span>
54
+ </div>
55
+ <div className="text-[12px] text-text-3/70 mt-1.5 truncate">
56
+ {agent?.name || 'Unknown agent'} &middot; {schedule.scheduleType}
57
+ </div>
58
+ <div className="text-[11px] text-text-3/60 mt-1">
59
+ Next: {formatNext(schedule.nextRunAt)}
60
+ </div>
61
+ </div>
62
+ )
63
+ }
@@ -0,0 +1,76 @@
1
+ 'use client'
2
+
3
+ import { useEffect, useMemo, useState } from 'react'
4
+ import { useAppStore } from '@/stores/use-app-store'
5
+ import { ScheduleCard } from './schedule-card'
6
+
7
+ interface Props {
8
+ inSidebar?: boolean
9
+ }
10
+
11
+ export function ScheduleList({ inSidebar }: Props) {
12
+ const schedules = useAppStore((s) => s.schedules)
13
+ const loadSchedules = useAppStore((s) => s.loadSchedules)
14
+ const setScheduleSheetOpen = useAppStore((s) => s.setScheduleSheetOpen)
15
+ const [search, setSearch] = useState('')
16
+
17
+ useEffect(() => { loadSchedules() }, [])
18
+
19
+ const filtered = useMemo(() => {
20
+ return Object.values(schedules)
21
+ .filter((s) => {
22
+ if (search && !s.name.toLowerCase().includes(search.toLowerCase())) return false
23
+ return true
24
+ })
25
+ .sort((a, b) => b.createdAt - a.createdAt)
26
+ }, [schedules, search])
27
+
28
+ if (!filtered.length && !search) {
29
+ return (
30
+ <div className="flex-1 flex flex-col items-center justify-center gap-4 text-text-3 p-8 text-center">
31
+ <div className="w-12 h-12 rounded-[14px] bg-accent-soft flex items-center justify-center mb-1">
32
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" className="text-accent-bright">
33
+ <circle cx="12" cy="12" r="10" />
34
+ <polyline points="12 6 12 12 16 14" />
35
+ </svg>
36
+ </div>
37
+ <p className="font-display text-[15px] font-600 text-text-2">No schedules yet</p>
38
+ <p className="text-[13px] text-text-3/50">Automate tasks with cron or intervals</p>
39
+ {!inSidebar && (
40
+ <button
41
+ onClick={() => setScheduleSheetOpen(true)}
42
+ className="mt-3 px-8 py-3 rounded-[14px] border-none bg-[#6366F1] text-white
43
+ text-[14px] font-600 cursor-pointer active:scale-95 transition-all duration-200
44
+ shadow-[0_4px_16px_rgba(99,102,241,0.2)]"
45
+ style={{ fontFamily: 'inherit' }}
46
+ >
47
+ + New Schedule
48
+ </button>
49
+ )}
50
+ </div>
51
+ )
52
+ }
53
+
54
+ return (
55
+ <div className="flex-1 overflow-y-auto">
56
+ {(filtered.length > 3 || search) && (
57
+ <div className="px-4 py-2.5">
58
+ <input
59
+ type="text"
60
+ value={search}
61
+ onChange={(e) => setSearch(e.target.value)}
62
+ placeholder="Search schedules..."
63
+ className="w-full px-4 py-2.5 rounded-[12px] border border-white/[0.04] bg-surface text-text
64
+ text-[13px] outline-none transition-all duration-200 placeholder:text-text-3/70 focus-glow"
65
+ style={{ fontFamily: 'inherit' }}
66
+ />
67
+ </div>
68
+ )}
69
+ <div className="flex flex-col gap-1 px-2 pb-4">
70
+ {filtered.map((s) => (
71
+ <ScheduleCard key={s.id} schedule={s} />
72
+ ))}
73
+ </div>
74
+ </div>
75
+ )
76
+ }