@swarmclawai/swarmclaw 0.7.2 → 0.7.4
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 +116 -50
- package/bin/package-manager.js +157 -0
- package/bin/package-manager.test.js +90 -0
- package/bin/server-cmd.js +38 -7
- package/bin/swarmclaw.js +54 -4
- package/bin/update-cmd.js +48 -10
- package/bin/update-cmd.test.js +55 -0
- package/package.json +8 -3
- package/scripts/postinstall.mjs +26 -0
- package/src/app/api/agents/[id]/route.ts +43 -0
- package/src/app/api/agents/[id]/thread/route.ts +39 -8
- package/src/app/api/agents/route.ts +35 -2
- package/src/app/api/auth/route.ts +77 -8
- package/src/app/api/chatrooms/[id]/chat/route.ts +22 -6
- package/src/app/api/chatrooms/[id]/pins/route.ts +2 -1
- package/src/app/api/chatrooms/[id]/reactions/route.ts +2 -1
- package/src/app/api/chatrooms/[id]/route.ts +6 -0
- 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]/messages/route.ts +19 -13
- package/src/app/api/chats/[id]/route.ts +30 -0
- package/src/app/api/chats/[id]/stop/route.ts +6 -1
- package/src/app/api/chats/heartbeat/route.ts +2 -1
- package/src/app/api/chats/route.ts +23 -1
- 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/external-agents/[id]/heartbeat/route.ts +33 -0
- package/src/app/api/external-agents/[id]/route.ts +31 -0
- package/src/app/api/external-agents/register/route.ts +3 -0
- package/src/app/api/external-agents/route.ts +66 -0
- package/src/app/api/files/open/route.ts +16 -14
- package/src/app/api/gateways/[id]/health/route.ts +28 -0
- package/src/app/api/gateways/[id]/route.ts +79 -0
- package/src/app/api/gateways/route.ts +57 -0
- 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/gateway/route.ts +10 -7
- package/src/app/api/openclaw/skills/route.ts +12 -4
- 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/providers/[id]/discover-models/route.ts +27 -0
- package/src/app/api/schedules/[id]/route.ts +38 -9
- package/src/app/api/schedules/route.ts +51 -28
- package/src/app/api/settings/route.ts +55 -17
- package/src/app/api/setup/doctor/route.ts +6 -4
- package/src/app/api/tasks/[id]/route.ts +16 -6
- package/src/app/api/tasks/bulk/route.ts +3 -3
- package/src/app/api/tasks/route.ts +9 -4
- package/src/app/api/webhooks/[id]/route.ts +8 -1
- package/src/app/page.tsx +135 -17
- package/src/cli/binary.test.js +142 -0
- package/src/cli/index.js +38 -11
- package/src/cli/index.test.js +195 -0
- package/src/cli/index.ts +21 -12
- package/src/cli/server-cmd.test.js +59 -0
- package/src/cli/spec.js +20 -2
- 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 +456 -23
- package/src/components/agents/inspector-panel.tsx +110 -49
- package/src/components/agents/sandbox-env-panel.tsx +4 -1
- package/src/components/auth/access-key-gate.tsx +36 -97
- package/src/components/auth/setup-wizard.tsx +970 -275
- package/src/components/chat/chat-area.tsx +70 -27
- package/src/components/chat/chat-card.tsx +6 -21
- package/src/components/chat/chat-header.tsx +263 -366
- package/src/components/chat/chat-list.tsx +62 -26
- package/src/components/chat/checkpoint-timeline.tsx +1 -1
- package/src/components/chat/message-list.tsx +145 -19
- package/src/components/chatrooms/chatroom-input.tsx +96 -33
- package/src/components/chatrooms/chatroom-list.tsx +141 -72
- package/src/components/chatrooms/chatroom-message.tsx +7 -6
- package/src/components/chatrooms/chatroom-sheet.tsx +13 -1
- package/src/components/chatrooms/chatroom-tool-request-banner.tsx +5 -2
- package/src/components/chatrooms/chatroom-view.tsx +422 -209
- package/src/components/chatrooms/reaction-picker.tsx +38 -33
- package/src/components/connectors/connector-list.tsx +265 -127
- package/src/components/connectors/connector-sheet.tsx +217 -0
- package/src/components/gateways/gateway-sheet.tsx +567 -0
- package/src/components/home/home-view.tsx +128 -4
- package/src/components/input/chat-input.tsx +135 -86
- package/src/components/layout/app-layout.tsx +385 -194
- package/src/components/layout/mobile-header.tsx +26 -8
- package/src/components/memory/memory-browser.tsx +71 -6
- package/src/components/memory/memory-card.tsx +18 -0
- package/src/components/memory/memory-detail.tsx +58 -31
- package/src/components/memory/memory-sheet.tsx +32 -4
- 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 +189 -1
- package/src/components/providers/provider-list.tsx +158 -2
- package/src/components/providers/provider-sheet.tsx +81 -70
- package/src/components/shared/agent-picker-list.tsx +2 -2
- package/src/components/shared/bottom-sheet.tsx +31 -15
- package/src/components/shared/command-palette.tsx +111 -24
- package/src/components/shared/confirm-dialog.tsx +45 -30
- package/src/components/shared/model-combobox.tsx +90 -8
- 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 +88 -6
- package/src/components/shared/settings/section-orchestrator.tsx +6 -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 +248 -47
- package/src/components/tasks/approvals-panel.tsx +211 -18
- package/src/components/tasks/task-board.tsx +242 -46
- package/src/components/ui/dialog.tsx +2 -2
- package/src/components/usage/metrics-dashboard.tsx +74 -1
- package/src/components/wallets/wallet-approval-dialog.tsx +59 -54
- 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/heartbeat-defaults.ts +48 -0
- package/src/lib/memory-presentation.ts +59 -0
- package/src/lib/openclaw-agent-id.test.ts +14 -0
- package/src/lib/openclaw-agent-id.ts +31 -0
- package/src/lib/provider-model-discovery-client.ts +29 -0
- package/src/lib/providers/index.ts +12 -5
- package/src/lib/runtime-loop.ts +105 -3
- package/src/lib/safe-storage.ts +6 -1
- package/src/lib/server/agent-assignment.test.ts +112 -0
- package/src/lib/server/agent-assignment.ts +169 -0
- package/src/lib/server/agent-runtime-config.test.ts +141 -0
- package/src/lib/server/agent-runtime-config.ts +277 -0
- package/src/lib/server/approval-connector-notify.test.ts +253 -0
- package/src/lib/server/approvals-auto-approve.test.ts +264 -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 +44 -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 +219 -0
- package/src/lib/server/chat-execution.ts +402 -125
- 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 +74 -2
- package/src/lib/server/chatroom-helpers.ts +144 -11
- package/src/lib/server/chatroom-session-persistence.test.ts +87 -0
- 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 +994 -130
- 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 +189 -10
- 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 +62 -3
- 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/eval/agent-regression.test.ts +47 -0
- package/src/lib/server/eval/agent-regression.ts +1742 -0
- package/src/lib/server/eval/runner.ts +11 -1
- package/src/lib/server/eval/store.ts +2 -1
- package/src/lib/server/heartbeat-service.ts +23 -43
- 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 +31 -964
- 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 +6 -5
- package/src/lib/server/openclaw-gateway.ts +123 -36
- 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 +18 -8
- package/src/lib/server/orchestrator.ts +5 -4
- package/src/lib/server/playwright-proxy.mjs +27 -3
- package/src/lib/server/plugins.test.ts +215 -0
- package/src/lib/server/plugins.ts +832 -69
- package/src/lib/server/provider-health.ts +33 -3
- package/src/lib/server/provider-model-discovery.ts +481 -0
- package/src/lib/server/queue.ts +4 -21
- package/src/lib/server/runtime-settings.test.ts +119 -0
- package/src/lib/server/runtime-settings.ts +12 -92
- package/src/lib/server/schedule-normalization.ts +187 -0
- 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 +128 -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 +96 -34
- 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-approvals.test.ts +170 -0
- package/src/lib/server/session-tools/discovery.ts +40 -12
- 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 +98 -0
- package/src/lib/server/session-tools/file-send.test.ts +84 -1
- package/src/lib/server/session-tools/file.ts +243 -24
- package/src/lib/server/session-tools/http.ts +9 -3
- 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 +87 -2
- package/src/lib/server/session-tools/mailbox.ts +276 -0
- package/src/lib/server/session-tools/manage-schedules.test.ts +137 -0
- package/src/lib/server/session-tools/memory.ts +35 -3
- package/src/lib/server/session-tools/monitor.ts +162 -12
- package/src/lib/server/session-tools/normalize-tool-args.ts +17 -14
- package/src/lib/server/session-tools/openclaw-nodes.test.ts +111 -0
- package/src/lib/server/session-tools/openclaw-nodes.ts +86 -20
- 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 +95 -25
- 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/sandbox.ts +51 -92
- package/src/lib/server/session-tools/schedule.ts +20 -10
- package/src/lib/server/session-tools/session-info.ts +58 -4
- package/src/lib/server/session-tools/session-tools-wiring.test.ts +54 -17
- package/src/lib/server/session-tools/shell.ts +2 -2
- package/src/lib/server/session-tools/subagent.ts +195 -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 +947 -108
- package/src/lib/server/storage.ts +255 -10
- package/src/lib/server/stream-agent-chat.test.ts +61 -0
- package/src/lib/server/stream-agent-chat.ts +185 -25
- 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 -11
- package/src/lib/server/tool-aliases.ts +80 -12
- package/src/lib/server/tool-capability-policy.ts +7 -1
- 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/setup-defaults.ts +352 -11
- package/src/lib/tool-definitions.ts +3 -4
- package/src/lib/validation/schemas.test.ts +26 -0
- package/src/lib/validation/schemas.ts +62 -1
- package/src/lib/ws-client.ts +14 -12
- package/src/proxy.ts +5 -5
- package/src/stores/use-app-store.ts +43 -7
- package/src/stores/use-chat-store.ts +31 -2
- package/src/stores/use-chatroom-store.ts +153 -26
- package/src/types/index.ts +470 -44
- package/src/app/api/chats/[id]/main-loop/route.ts +0 -94
- 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
package/src/app/page.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import { useEffect, useState, useCallback } from 'react'
|
|
|
4
4
|
import { useAppStore } from '@/stores/use-app-store'
|
|
5
5
|
import { initAudioContext } from '@/lib/tts'
|
|
6
6
|
import { getStoredAccessKey, clearStoredAccessKey, api } from '@/lib/api-client'
|
|
7
|
+
import { safeStorageGet, safeStorageRemove, safeStorageSet } from '@/lib/safe-storage'
|
|
7
8
|
import { connectWs, disconnectWs } from '@/lib/ws-client'
|
|
8
9
|
import { fetchWithTimeout } from '@/lib/fetch-timeout'
|
|
9
10
|
import { useWs } from '@/hooks/use-ws'
|
|
@@ -12,10 +13,17 @@ import { UserPicker } from '@/components/auth/user-picker'
|
|
|
12
13
|
import { SetupWizard } from '@/components/auth/setup-wizard'
|
|
13
14
|
import { AppLayout } from '@/components/layout/app-layout'
|
|
14
15
|
import { useViewRouter } from '@/hooks/use-view-router'
|
|
16
|
+
import type { Agent } from '@/types'
|
|
15
17
|
|
|
16
18
|
const AUTH_CHECK_TIMEOUT_MS = 8_000
|
|
19
|
+
const POST_AUTH_BOOTSTRAP_TIMEOUT_MS = 8_000
|
|
17
20
|
|
|
18
|
-
function FullScreenLoader(
|
|
21
|
+
function FullScreenLoader(props: {
|
|
22
|
+
stage?: string | null
|
|
23
|
+
stalled?: boolean
|
|
24
|
+
onReload?: () => void
|
|
25
|
+
onReset?: () => void
|
|
26
|
+
}) {
|
|
19
27
|
return (
|
|
20
28
|
<div className="h-full flex flex-col items-center justify-center bg-bg overflow-hidden select-none">
|
|
21
29
|
{/* Animated orbital ring */}
|
|
@@ -106,6 +114,42 @@ function FullScreenLoader() {
|
|
|
106
114
|
/>
|
|
107
115
|
</div>
|
|
108
116
|
|
|
117
|
+
{props.stage ? (
|
|
118
|
+
<p
|
|
119
|
+
className="mt-4 text-[12px] text-text-3"
|
|
120
|
+
style={{ animation: 'fade-up 0.6s var(--ease-spring) 0.4s both' }}
|
|
121
|
+
>
|
|
122
|
+
{props.stage}
|
|
123
|
+
</p>
|
|
124
|
+
) : null}
|
|
125
|
+
|
|
126
|
+
{props.stalled ? (
|
|
127
|
+
<div
|
|
128
|
+
className="mt-6 max-w-[360px] px-4 text-center"
|
|
129
|
+
style={{ animation: 'fade-up 0.6s var(--ease-spring) 0.5s both' }}
|
|
130
|
+
>
|
|
131
|
+
<p className="text-[12px] text-text-2">
|
|
132
|
+
Startup is taking longer than expected. This usually means the browser kept stale local state while the dev server restarted.
|
|
133
|
+
</p>
|
|
134
|
+
<div className="mt-4 flex items-center justify-center gap-3">
|
|
135
|
+
<button
|
|
136
|
+
type="button"
|
|
137
|
+
onClick={props.onReload}
|
|
138
|
+
className="px-4 py-2 rounded-[12px] border border-white/[0.08] bg-surface text-[12px] text-text-2 transition-colors hover:bg-surface-2"
|
|
139
|
+
>
|
|
140
|
+
Reload
|
|
141
|
+
</button>
|
|
142
|
+
<button
|
|
143
|
+
type="button"
|
|
144
|
+
onClick={props.onReset}
|
|
145
|
+
className="px-4 py-2 rounded-[12px] border border-white/[0.08] bg-transparent text-[12px] text-text-3 transition-colors hover:bg-white/[0.04]"
|
|
146
|
+
>
|
|
147
|
+
Reset Local Session
|
|
148
|
+
</button>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
) : null}
|
|
152
|
+
|
|
109
153
|
{/* Loading animation keyframes */}
|
|
110
154
|
<style>{`
|
|
111
155
|
@keyframes sc-orbit {
|
|
@@ -150,16 +194,24 @@ export default function Home() {
|
|
|
150
194
|
|
|
151
195
|
const [authChecked, setAuthChecked] = useState(false)
|
|
152
196
|
const [authenticated, setAuthenticated] = useState(false)
|
|
197
|
+
const [bootTimedOut, setBootTimedOut] = useState(false)
|
|
153
198
|
const [setupDone, setSetupDone] = useState<boolean | null>(() => {
|
|
154
|
-
if (
|
|
199
|
+
if (safeStorageGet('sc_setup_done') === '1') return true
|
|
155
200
|
return null
|
|
156
201
|
})
|
|
157
202
|
|
|
158
203
|
const checkAuth = useCallback(async () => {
|
|
159
204
|
const key = getStoredAccessKey()
|
|
160
205
|
if (!key) {
|
|
161
|
-
|
|
162
|
-
|
|
206
|
+
try {
|
|
207
|
+
const res = await fetchWithTimeout('/api/auth', {}, AUTH_CHECK_TIMEOUT_MS)
|
|
208
|
+
const data = await res.json().catch(() => ({}))
|
|
209
|
+
setAuthenticated(data?.authenticated === true)
|
|
210
|
+
} catch {
|
|
211
|
+
setAuthenticated(false)
|
|
212
|
+
} finally {
|
|
213
|
+
setAuthChecked(true)
|
|
214
|
+
}
|
|
163
215
|
return
|
|
164
216
|
}
|
|
165
217
|
|
|
@@ -176,7 +228,8 @@ export default function Home() {
|
|
|
176
228
|
setAuthenticated(false)
|
|
177
229
|
}
|
|
178
230
|
} catch {
|
|
179
|
-
|
|
231
|
+
clearStoredAccessKey()
|
|
232
|
+
setAuthenticated(false)
|
|
180
233
|
} finally {
|
|
181
234
|
setAuthChecked(true)
|
|
182
235
|
}
|
|
@@ -186,7 +239,10 @@ export default function Home() {
|
|
|
186
239
|
const syncUserFromServer = useCallback(async () => {
|
|
187
240
|
if (currentUser) return // already have a name locally
|
|
188
241
|
try {
|
|
189
|
-
const settings = await api<{ userName?: string }>('GET', '/settings'
|
|
242
|
+
const settings = await api<{ userName?: string }>('GET', '/settings', undefined, {
|
|
243
|
+
timeoutMs: POST_AUTH_BOOTSTRAP_TIMEOUT_MS,
|
|
244
|
+
retries: 0,
|
|
245
|
+
})
|
|
190
246
|
if (settings.userName) {
|
|
191
247
|
setUser(settings.userName)
|
|
192
248
|
}
|
|
@@ -222,11 +278,14 @@ export default function Home() {
|
|
|
222
278
|
let cancelled = false
|
|
223
279
|
;(async () => {
|
|
224
280
|
try {
|
|
225
|
-
const
|
|
226
|
-
|
|
281
|
+
const agents = await api<Record<string, Agent>>('GET', '/agents', undefined, {
|
|
282
|
+
timeoutMs: POST_AUTH_BOOTSTRAP_TIMEOUT_MS,
|
|
283
|
+
retries: 0,
|
|
284
|
+
})
|
|
227
285
|
if (cancelled) return
|
|
286
|
+
useAppStore.setState({ agents })
|
|
228
287
|
|
|
229
|
-
const {
|
|
288
|
+
const { currentAgentId, appSettings } = useAppStore.getState()
|
|
230
289
|
// Priority: persisted agent > settings default > first agent
|
|
231
290
|
const targetId = (currentAgentId && agents[currentAgentId])
|
|
232
291
|
? currentAgentId
|
|
@@ -249,14 +308,23 @@ export default function Home() {
|
|
|
249
308
|
let cancelled = false
|
|
250
309
|
;(async () => {
|
|
251
310
|
try {
|
|
252
|
-
const [
|
|
253
|
-
api<{ setupCompleted?: boolean }>('GET', '/settings'
|
|
254
|
-
|
|
311
|
+
const [settingsResult, credsResult] = await Promise.allSettled([
|
|
312
|
+
api<{ setupCompleted?: boolean }>('GET', '/settings', undefined, {
|
|
313
|
+
timeoutMs: POST_AUTH_BOOTSTRAP_TIMEOUT_MS,
|
|
314
|
+
retries: 0,
|
|
315
|
+
}),
|
|
316
|
+
api<Record<string, unknown>>('GET', '/credentials', undefined, {
|
|
317
|
+
timeoutMs: POST_AUTH_BOOTSTRAP_TIMEOUT_MS,
|
|
318
|
+
retries: 0,
|
|
319
|
+
}),
|
|
255
320
|
])
|
|
256
321
|
if (cancelled) return
|
|
322
|
+
const settings = settingsResult.status === 'fulfilled' ? settingsResult.value : {}
|
|
323
|
+
const creds = credsResult.status === 'fulfilled' ? credsResult.value : {}
|
|
324
|
+
const bothFailed = settingsResult.status === 'rejected' && credsResult.status === 'rejected'
|
|
257
325
|
const hasCreds = Object.keys(creds).length > 0
|
|
258
|
-
const done = settings.setupCompleted === true || hasCreds
|
|
259
|
-
if (done)
|
|
326
|
+
const done = bothFailed ? true : settings.setupCompleted === true || hasCreds
|
|
327
|
+
if (done) safeStorageSet('sc_setup_done', '1')
|
|
260
328
|
setSetupDone(done)
|
|
261
329
|
} catch {
|
|
262
330
|
if (!cancelled) setSetupDone(true) // on error, skip wizard
|
|
@@ -286,10 +354,60 @@ export default function Home() {
|
|
|
286
354
|
|
|
287
355
|
useViewRouter()
|
|
288
356
|
|
|
289
|
-
|
|
357
|
+
const bootStage = !hydrated
|
|
358
|
+
? 'Restoring local session'
|
|
359
|
+
: !authChecked
|
|
360
|
+
? 'Checking access'
|
|
361
|
+
: authenticated && currentUser && setupDone === null
|
|
362
|
+
? 'Loading setup state'
|
|
363
|
+
: authenticated && currentUser && !agentReady
|
|
364
|
+
? 'Restoring agent workspace'
|
|
365
|
+
: null
|
|
366
|
+
|
|
367
|
+
useEffect(() => {
|
|
368
|
+
if (!bootStage) {
|
|
369
|
+
setBootTimedOut(false)
|
|
370
|
+
return
|
|
371
|
+
}
|
|
372
|
+
const timer = window.setTimeout(() => setBootTimedOut(true), 15_000)
|
|
373
|
+
return () => window.clearTimeout(timer)
|
|
374
|
+
}, [bootStage])
|
|
375
|
+
|
|
376
|
+
const reloadApp = useCallback(() => {
|
|
377
|
+
window.location.reload()
|
|
378
|
+
}, [])
|
|
379
|
+
|
|
380
|
+
const resetLocalSession = useCallback(() => {
|
|
381
|
+
clearStoredAccessKey()
|
|
382
|
+
disconnectWs()
|
|
383
|
+
safeStorageRemove('sc_user')
|
|
384
|
+
safeStorageRemove('sc_agent')
|
|
385
|
+
safeStorageRemove('sc_setup_done')
|
|
386
|
+
window.location.assign('/')
|
|
387
|
+
}, [])
|
|
388
|
+
|
|
389
|
+
if (!hydrated || !authChecked) {
|
|
390
|
+
return (
|
|
391
|
+
<FullScreenLoader
|
|
392
|
+
stage={bootStage}
|
|
393
|
+
stalled={bootTimedOut}
|
|
394
|
+
onReload={reloadApp}
|
|
395
|
+
onReset={resetLocalSession}
|
|
396
|
+
/>
|
|
397
|
+
)
|
|
398
|
+
}
|
|
290
399
|
if (!authenticated) return <AccessKeyGate onAuthenticated={() => setAuthenticated(true)} />
|
|
291
400
|
if (!currentUser) return <UserPicker />
|
|
292
|
-
if (setupDone === null || !agentReady)
|
|
293
|
-
|
|
401
|
+
if (setupDone === null || !agentReady) {
|
|
402
|
+
return (
|
|
403
|
+
<FullScreenLoader
|
|
404
|
+
stage={bootStage}
|
|
405
|
+
stalled={bootTimedOut}
|
|
406
|
+
onReload={reloadApp}
|
|
407
|
+
onReset={resetLocalSession}
|
|
408
|
+
/>
|
|
409
|
+
)
|
|
410
|
+
}
|
|
411
|
+
if (!setupDone) return <SetupWizard onComplete={() => { safeStorageSet('sc_setup_done', '1'); setSetupDone(true) }} />
|
|
294
412
|
return <AppLayout />
|
|
295
413
|
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-require-imports */
|
|
3
|
+
|
|
4
|
+
const test = require('node:test')
|
|
5
|
+
const assert = require('node:assert/strict')
|
|
6
|
+
const fs = require('node:fs')
|
|
7
|
+
const os = require('node:os')
|
|
8
|
+
const path = require('node:path')
|
|
9
|
+
const { spawnSync } = require('node:child_process')
|
|
10
|
+
const { buildLegacyTsCliArgs } = require('../../bin/swarmclaw.js')
|
|
11
|
+
|
|
12
|
+
const CLI_BIN = path.join(__dirname, '..', '..', 'bin', 'swarmclaw.js')
|
|
13
|
+
const PACKAGE_JSON = require('../../package.json')
|
|
14
|
+
const APP_ROOT = path.join(__dirname, '..', '..')
|
|
15
|
+
|
|
16
|
+
function runBinary(args, options = {}) {
|
|
17
|
+
return spawnSync(process.execPath, [CLI_BIN, ...args], {
|
|
18
|
+
cwd: options.cwd || APP_ROOT,
|
|
19
|
+
env: {
|
|
20
|
+
...process.env,
|
|
21
|
+
...options.env,
|
|
22
|
+
},
|
|
23
|
+
encoding: 'utf8',
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function runWithMockedFetch(args, options = {}) {
|
|
28
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'swarmclaw-binary-fetch-'))
|
|
29
|
+
const capturePath = path.join(tmpDir, 'capture.json')
|
|
30
|
+
const preloadPath = path.join(tmpDir, 'mock-fetch.cjs')
|
|
31
|
+
|
|
32
|
+
fs.writeFileSync(
|
|
33
|
+
preloadPath,
|
|
34
|
+
`
|
|
35
|
+
const fs = require('node:fs')
|
|
36
|
+
globalThis.fetch = async (url, init = {}) => {
|
|
37
|
+
const capture = {
|
|
38
|
+
url: String(url),
|
|
39
|
+
method: init.method || 'GET',
|
|
40
|
+
headers: init.headers || {},
|
|
41
|
+
body: typeof init.body === 'string'
|
|
42
|
+
? init.body
|
|
43
|
+
: (Buffer.isBuffer(init.body) ? init.body.toString('utf8') : null),
|
|
44
|
+
}
|
|
45
|
+
fs.writeFileSync(process.env.SWARMCLAW_TEST_CAPTURE, JSON.stringify(capture), 'utf8')
|
|
46
|
+
return new Response(JSON.stringify([]), {
|
|
47
|
+
status: 200,
|
|
48
|
+
headers: { 'content-type': 'application/json' },
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
`,
|
|
52
|
+
'utf8',
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
const nodeOptions = [process.env.NODE_OPTIONS, `--require=${preloadPath}`]
|
|
56
|
+
.filter(Boolean)
|
|
57
|
+
.join(' ')
|
|
58
|
+
|
|
59
|
+
const result = runBinary(args, {
|
|
60
|
+
...options,
|
|
61
|
+
env: {
|
|
62
|
+
...options.env,
|
|
63
|
+
NODE_OPTIONS: nodeOptions,
|
|
64
|
+
SWARMCLAW_TEST_CAPTURE: capturePath,
|
|
65
|
+
},
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
const capture = fs.existsSync(capturePath)
|
|
69
|
+
? JSON.parse(fs.readFileSync(capturePath, 'utf8'))
|
|
70
|
+
: null
|
|
71
|
+
|
|
72
|
+
fs.rmSync(tmpDir, { recursive: true, force: true })
|
|
73
|
+
return { result, capture }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
test('legacy-routed binary commands honor SWARMCLAW_API_KEY', () => {
|
|
77
|
+
const { result, capture } = runWithMockedFetch(
|
|
78
|
+
['runs', 'list', '--raw', '--url', 'http://localhost:3456'],
|
|
79
|
+
{
|
|
80
|
+
env: {
|
|
81
|
+
SWARMCLAW_API_KEY: 'legacy-api-key',
|
|
82
|
+
SWARMCLAW_ACCESS_KEY: '',
|
|
83
|
+
SC_ACCESS_KEY: '',
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
assert.equal(result.status, 0, result.stderr)
|
|
89
|
+
assert.equal(result.stdout.trim(), '[]')
|
|
90
|
+
assert.equal(capture.headers['X-Access-Key'], 'legacy-api-key')
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
test('legacy-routed binary commands fall back to platform-api-key.txt', () => {
|
|
94
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'swarmclaw-binary-keyfile-'))
|
|
95
|
+
fs.writeFileSync(path.join(tmpDir, 'platform-api-key.txt'), 'file-fallback-key\n', 'utf8')
|
|
96
|
+
|
|
97
|
+
const { result, capture } = runWithMockedFetch(
|
|
98
|
+
['runs', 'list', '--raw', '--url', 'http://localhost:3456'],
|
|
99
|
+
{
|
|
100
|
+
cwd: tmpDir,
|
|
101
|
+
env: {
|
|
102
|
+
SWARMCLAW_API_KEY: '',
|
|
103
|
+
SWARMCLAW_ACCESS_KEY: '',
|
|
104
|
+
SC_ACCESS_KEY: '',
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
assert.equal(result.status, 0, result.stderr)
|
|
110
|
+
assert.equal(result.stdout.trim(), '[]')
|
|
111
|
+
assert.equal(capture.headers['X-Access-Key'], 'file-fallback-key')
|
|
112
|
+
|
|
113
|
+
fs.rmSync(tmpDir, { recursive: true, force: true })
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
test('binary server help exits successfully', () => {
|
|
117
|
+
const result = runBinary(['server', '--help'])
|
|
118
|
+
assert.equal(result.status, 0, result.stderr)
|
|
119
|
+
assert.match(result.stdout, /Usage: swarmclaw server/i)
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
test('binary update help exits successfully', () => {
|
|
123
|
+
const result = runBinary(['update', '--help'])
|
|
124
|
+
assert.equal(result.status, 0, result.stderr)
|
|
125
|
+
assert.match(result.stdout, /Usage: swarmclaw update/i)
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
test('binary version output matches package version', () => {
|
|
129
|
+
const result = runBinary(['--version'])
|
|
130
|
+
assert.equal(result.status, 0, result.stderr)
|
|
131
|
+
assert.equal(result.stdout.trim(), `${PACKAGE_JSON.name} ${PACKAGE_JSON.version}`)
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
test('legacy TS launcher falls back to tsx import when strip-types is unavailable', () => {
|
|
135
|
+
const cliPath = path.join(APP_ROOT, 'src', 'cli', 'index.ts')
|
|
136
|
+
const args = buildLegacyTsCliArgs(cliPath, ['runs', 'list'], {
|
|
137
|
+
supportsStripTypes: false,
|
|
138
|
+
hasTsxRuntime: true,
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
assert.deepEqual(args, ['--no-warnings', '--import', 'tsx', cliPath, 'runs', 'list'])
|
|
142
|
+
})
|
package/src/cli/index.js
CHANGED
|
@@ -120,6 +120,9 @@ const COMMAND_GROUPS = [
|
|
|
120
120
|
defaultBody: { action: 'repair' },
|
|
121
121
|
}),
|
|
122
122
|
cmd('health', 'GET', '/connectors/:id/health', 'Get connector health status'),
|
|
123
|
+
cmd('doctor', 'GET', '/connectors/:id/doctor', 'Get connector doctor diagnostics'),
|
|
124
|
+
cmd('doctor-preview', 'POST', '/connectors/:id/doctor', 'Preview connector doctor diagnostics with temporary overrides', { expectsJsonBody: true }),
|
|
125
|
+
cmd('doctor-draft', 'POST', '/connectors/doctor', 'Preview connector doctor diagnostics before saving a connector', { expectsJsonBody: true }),
|
|
123
126
|
],
|
|
124
127
|
},
|
|
125
128
|
{
|
|
@@ -178,6 +181,17 @@ const COMMAND_GROUPS = [
|
|
|
178
181
|
cmd('suite', 'POST', '/eval/suite', 'Run a full eval suite against an agent', { expectsJsonBody: true }),
|
|
179
182
|
],
|
|
180
183
|
},
|
|
184
|
+
{
|
|
185
|
+
name: 'external-agents',
|
|
186
|
+
description: 'Manage external agent runtimes',
|
|
187
|
+
commands: [
|
|
188
|
+
cmd('list', 'GET', '/external-agents', 'List external agent runtimes'),
|
|
189
|
+
cmd('create', 'POST', '/external-agents', 'Register an external agent runtime', { expectsJsonBody: true }),
|
|
190
|
+
cmd('update', 'PUT', '/external-agents/:id', 'Update an external agent runtime', { expectsJsonBody: true }),
|
|
191
|
+
cmd('delete', 'DELETE', '/external-agents/:id', 'Delete an external agent runtime'),
|
|
192
|
+
cmd('heartbeat', 'POST', '/external-agents/:id/heartbeat', 'Record an external agent heartbeat', { expectsJsonBody: true }),
|
|
193
|
+
],
|
|
194
|
+
},
|
|
181
195
|
{
|
|
182
196
|
name: 'files',
|
|
183
197
|
description: 'Serve and manage local files',
|
|
@@ -186,6 +200,17 @@ const COMMAND_GROUPS = [
|
|
|
186
200
|
cmd('open', 'POST', '/files/open', 'Open a local file path via the host default app/browser', { expectsJsonBody: true }),
|
|
187
201
|
],
|
|
188
202
|
},
|
|
203
|
+
{
|
|
204
|
+
name: 'gateways',
|
|
205
|
+
description: 'Manage named OpenClaw gateway profiles',
|
|
206
|
+
commands: [
|
|
207
|
+
cmd('list', 'GET', '/gateways', 'List configured gateway profiles'),
|
|
208
|
+
cmd('create', 'POST', '/gateways', 'Create a gateway profile', { expectsJsonBody: true }),
|
|
209
|
+
cmd('update', 'PUT', '/gateways/:id', 'Update a gateway profile', { expectsJsonBody: true }),
|
|
210
|
+
cmd('delete', 'DELETE', '/gateways/:id', 'Delete a gateway profile'),
|
|
211
|
+
cmd('health', 'GET', '/gateways/:id/health', 'Run a gateway health check'),
|
|
212
|
+
],
|
|
213
|
+
},
|
|
189
214
|
{
|
|
190
215
|
name: 'ip',
|
|
191
216
|
description: 'Get local IP/port metadata',
|
|
@@ -344,6 +369,7 @@ const COMMAND_GROUPS = [
|
|
|
344
369
|
cmd('delete', 'DELETE', '/plugins', 'Delete an external plugin (use --query filename=plugin.js)'),
|
|
345
370
|
cmd('update', 'PATCH', '/plugins', 'Update a plugin (use --query id=plugin.js or --query all=true)'),
|
|
346
371
|
cmd('install', 'POST', '/plugins/install', 'Install plugin from URL', { expectsJsonBody: true }),
|
|
372
|
+
cmd('install-deps', 'POST', '/plugins/dependencies', 'Install or refresh plugin workspace dependencies', { expectsJsonBody: true }),
|
|
347
373
|
cmd('marketplace', 'GET', '/plugins/marketplace', 'Get marketplace catalog'),
|
|
348
374
|
cmd('settings-get', 'GET', '/plugins/settings', 'Get plugin settings (use --query pluginId=plugin_name)'),
|
|
349
375
|
cmd('settings-set', 'PUT', '/plugins/settings', 'Set plugin settings (use --query pluginId=plugin_name and --data JSON)', { expectsJsonBody: true }),
|
|
@@ -360,6 +386,7 @@ const COMMAND_GROUPS = [
|
|
|
360
386
|
cmd('update', 'PUT', '/providers/:id', 'Update provider', { expectsJsonBody: true }),
|
|
361
387
|
cmd('delete', 'DELETE', '/providers/:id', 'Delete provider'),
|
|
362
388
|
cmd('configs', 'GET', '/providers/configs', 'List saved provider configs'),
|
|
389
|
+
cmd('discover-models', 'GET', '/providers/:id/discover-models', 'Discover provider models via endpoint or credential checks'),
|
|
363
390
|
cmd('ollama', 'GET', '/providers/ollama', 'List local Ollama models (use --query endpoint=http://localhost:11434)'),
|
|
364
391
|
cmd('openclaw-health', 'GET', '/providers/openclaw/health', 'Probe OpenClaw endpoint/auth (use --query endpoint= --query credentialId= --query model=)'),
|
|
365
392
|
cmd('models', 'GET', '/providers/:id/models', 'Get provider model overrides'),
|
|
@@ -425,10 +452,6 @@ const COMMAND_GROUPS = [
|
|
|
425
452
|
cmd('messages-delete', 'DELETE', '/chats/:id/messages', 'Delete a message from a chat', { expectsJsonBody: true }),
|
|
426
453
|
cmd('fork', 'POST', '/chats/:id/fork', 'Fork chat from a specific message index', { expectsJsonBody: true }),
|
|
427
454
|
cmd('edit-resend', 'POST', '/chats/:id/edit-resend', 'Edit and resend from a specific message index', { expectsJsonBody: true }),
|
|
428
|
-
cmd('main-loop', 'GET', '/chats/:id/main-loop', 'Get main mission loop state'),
|
|
429
|
-
cmd('main-loop-action', 'POST', '/chats/:id/main-loop', 'Control main mission loop (pause/resume/set_goal/set_mode/clear_events/nudge)', {
|
|
430
|
-
expectsJsonBody: true,
|
|
431
|
-
}),
|
|
432
455
|
cmd('chat', 'POST', '/chats/:id/chat', 'Send chat message (streaming)', {
|
|
433
456
|
expectsJsonBody: true,
|
|
434
457
|
responseType: 'sse',
|
|
@@ -673,7 +696,7 @@ function normalizeBaseUrl(raw) {
|
|
|
673
696
|
|
|
674
697
|
function resolveAccessKey(opts, env, cwd) {
|
|
675
698
|
if (opts.accessKey) return String(opts.accessKey).trim()
|
|
676
|
-
const envKey = env.SWARMCLAW_API_KEY || env.SC_ACCESS_KEY || ''
|
|
699
|
+
const envKey = env.SWARMCLAW_API_KEY || env.SC_ACCESS_KEY || env.SWARMCLAW_ACCESS_KEY || ''
|
|
677
700
|
if (envKey) return String(envKey).trim()
|
|
678
701
|
|
|
679
702
|
const keyFile = path.join(cwd, 'platform-api-key.txt')
|
|
@@ -923,9 +946,11 @@ async function consumeSse(body, stdout, stderr, jsonOutput) {
|
|
|
923
946
|
const reader = body.getReader()
|
|
924
947
|
const decoder = new TextDecoder()
|
|
925
948
|
let buffer = ''
|
|
949
|
+
const eventBoundary = /\r?\n\r?\n/
|
|
926
950
|
|
|
927
951
|
function flushChunk(rawChunk) {
|
|
928
952
|
const lines = rawChunk
|
|
953
|
+
.replace(/\r\n/g, '\n')
|
|
929
954
|
.split('\n')
|
|
930
955
|
.map((line) => line.trimEnd())
|
|
931
956
|
.filter(Boolean)
|
|
@@ -968,12 +993,14 @@ async function consumeSse(body, stdout, stderr, jsonOutput) {
|
|
|
968
993
|
if (done) break
|
|
969
994
|
buffer += decoder.decode(value, { stream: true })
|
|
970
995
|
|
|
971
|
-
let
|
|
972
|
-
while (
|
|
996
|
+
let match = eventBoundary.exec(buffer)
|
|
997
|
+
while (match) {
|
|
998
|
+
const splitIndex = match.index
|
|
999
|
+
const delimiterLength = match[0].length
|
|
973
1000
|
const chunk = buffer.slice(0, splitIndex)
|
|
974
|
-
buffer = buffer.slice(splitIndex +
|
|
1001
|
+
buffer = buffer.slice(splitIndex + delimiterLength)
|
|
975
1002
|
flushChunk(chunk)
|
|
976
|
-
|
|
1003
|
+
match = eventBoundary.exec(buffer)
|
|
977
1004
|
}
|
|
978
1005
|
}
|
|
979
1006
|
|
|
@@ -1086,7 +1113,7 @@ function renderGeneralHelp() {
|
|
|
1086
1113
|
'',
|
|
1087
1114
|
'Global options:',
|
|
1088
1115
|
' --base-url <url> API base URL (default: http://localhost:3456)',
|
|
1089
|
-
' --access-key <key> Access key override (else SWARMCLAW_API_KEY or platform-api-key.txt)',
|
|
1116
|
+
' --access-key <key> Access key override (else SWARMCLAW_API_KEY/SWARMCLAW_ACCESS_KEY or platform-api-key.txt)',
|
|
1090
1117
|
' --data <json|@file|-> Request JSON body',
|
|
1091
1118
|
' --query key=value Query parameter (repeatable)',
|
|
1092
1119
|
' --header key=value Extra HTTP header (repeatable)',
|
|
@@ -1211,7 +1238,7 @@ async function runCli(argv, deps = {}) {
|
|
|
1211
1238
|
}
|
|
1212
1239
|
|
|
1213
1240
|
const accessKey = resolveAccessKey(parsed.opts, env, cwd)
|
|
1214
|
-
const baseUrl = parsed.opts.baseUrl || env.SWARMCLAW_BASE_URL || 'http://localhost:3456'
|
|
1241
|
+
const baseUrl = parsed.opts.baseUrl || env.SWARMCLAW_BASE_URL || env.SWARMCLAW_URL || 'http://localhost:3456'
|
|
1215
1242
|
|
|
1216
1243
|
const headerEntries = []
|
|
1217
1244
|
for (const raw of parsed.opts.headers) {
|