@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/README.md
CHANGED
|
@@ -37,6 +37,8 @@ SwarmClaw was built for OpenClaw users who outgrew a single agent. Connect each
|
|
|
37
37
|
|
|
38
38
|
SwarmClaw includes the `openclaw` CLI as a bundled dependency, so there is no separate OpenClaw CLI install step.
|
|
39
39
|
|
|
40
|
+
The Providers screen now supports named OpenClaw gateway profiles with discovery, health checks, default-gateway selection, and an External Agent Runtimes view for remote workers that register/heartbeat into SwarmClaw.
|
|
41
|
+
|
|
40
42
|
The OpenClaw Control Plane in SwarmClaw adds:
|
|
41
43
|
- Reload mode switching (`hot`, `hybrid`, `full`)
|
|
42
44
|
- Config issue detection and guided repair
|
|
@@ -47,13 +49,13 @@ The Agent Inspector Panel lets you edit OpenClaw files (`SOUL.md`, `IDENTITY.md`
|
|
|
47
49
|
|
|
48
50
|
To connect an agent to an OpenClaw gateway:
|
|
49
51
|
|
|
50
|
-
1.
|
|
51
|
-
2.
|
|
52
|
-
3.
|
|
53
|
-
4.
|
|
52
|
+
1. Optional: create a named gateway profile in **Providers** and mark a default
|
|
53
|
+
2. Create or edit an agent
|
|
54
|
+
3. Toggle **OpenClaw Gateway** ON
|
|
55
|
+
4. Select a saved gateway profile or enter a direct gateway URL/token override
|
|
54
56
|
5. Click **Connect** — approve the device in your gateway's dashboard if prompted, then **Retry Connection**
|
|
55
57
|
|
|
56
|
-
Each agent can point to a **different** OpenClaw gateway — one local, several remote. This is how you manage a **swarm of OpenClaws** from a single dashboard.
|
|
58
|
+
Each agent can point to a **different** OpenClaw gateway profile or direct endpoint — one local, several remote. This is how you manage a **swarm of OpenClaws** from a single dashboard.
|
|
57
59
|
|
|
58
60
|
URLs without a protocol are auto-prefixed with `http://`. For remote gateways with TLS, use `https://` explicitly.
|
|
59
61
|
|
|
@@ -77,21 +79,37 @@ Skill source and runbook: [`swarmclaw/SKILL.md`](swarmclaw/SKILL.md).
|
|
|
77
79
|
## Requirements
|
|
78
80
|
|
|
79
81
|
- **Node.js** 22.6+
|
|
80
|
-
- **
|
|
82
|
+
- **Node.js** 22.6+
|
|
83
|
+
- One of: **npm** 10+, **pnpm**, **Yarn**, or **Bun**
|
|
81
84
|
- **Claude Code CLI** (optional, for `claude-cli` provider) — [Install](https://docs.anthropic.com/en/docs/claude-code/overview)
|
|
82
85
|
- **OpenAI Codex CLI** (optional, for `codex-cli` provider) — [Install](https://github.com/openai/codex)
|
|
83
86
|
- **OpenCode CLI** (optional, for `opencode-cli` provider) — [Install](https://github.com/opencode-ai/opencode)
|
|
84
87
|
- **Gemini CLI** (optional, for `delegate` backend `gemini`) — install and authenticate `gemini` on your host
|
|
88
|
+
- **Deno** (required for `sandbox_exec`) — auto-installed by `npm run quickstart` / `npm run setup:easy` when missing
|
|
85
89
|
|
|
86
90
|
## Quick Start
|
|
87
91
|
|
|
88
|
-
|
|
92
|
+
SwarmClaw is published to the npm registry once and can be installed with `npm`, `pnpm`, `yarn`, or `bun`. There is no separate package-manager signup for end users.
|
|
93
|
+
|
|
94
|
+
### Global install
|
|
89
95
|
|
|
90
96
|
```bash
|
|
91
97
|
npm i -g @swarmclawai/swarmclaw
|
|
98
|
+
pnpm add -g @swarmclawai/swarmclaw
|
|
99
|
+
yarn global add @swarmclawai/swarmclaw
|
|
100
|
+
bun add -g @swarmclawai/swarmclaw
|
|
92
101
|
swarmclaw
|
|
93
102
|
```
|
|
94
103
|
|
|
104
|
+
### One-off run
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
npx @swarmclawai/swarmclaw
|
|
108
|
+
pnpm dlx @swarmclawai/swarmclaw
|
|
109
|
+
yarn dlx @swarmclawai/swarmclaw
|
|
110
|
+
bunx @swarmclawai/swarmclaw
|
|
111
|
+
```
|
|
112
|
+
|
|
95
113
|
### Install script
|
|
96
114
|
|
|
97
115
|
```bash
|
|
@@ -99,7 +117,7 @@ curl -fsSL https://raw.githubusercontent.com/swarmclawai/swarmclaw/main/install.
|
|
|
99
117
|
```
|
|
100
118
|
|
|
101
119
|
The installer resolves the latest stable release tag and installs that version by default.
|
|
102
|
-
To pin a version: `SWARMCLAW_VERSION=v0.7.
|
|
120
|
+
To pin a version: `SWARMCLAW_VERSION=v0.7.4 curl ... | bash`
|
|
103
121
|
|
|
104
122
|
Or run locally from the repo (friendly for non-technical users):
|
|
105
123
|
|
|
@@ -111,10 +129,19 @@ npm run quickstart
|
|
|
111
129
|
|
|
112
130
|
`npm run quickstart` will:
|
|
113
131
|
- Check Node/npm versions
|
|
132
|
+
- Install Deno if the sandbox runtime is missing
|
|
114
133
|
- Install dependencies
|
|
115
134
|
- Prepare `.env.local` and `data/`
|
|
116
135
|
- Start the app at `http://localhost:3456`
|
|
117
136
|
|
|
137
|
+
If you prefer another package manager for local development:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
pnpm install && pnpm dev
|
|
141
|
+
yarn install && yarn dev
|
|
142
|
+
bun install && bun run dev
|
|
143
|
+
```
|
|
144
|
+
|
|
118
145
|
`postinstall` rebuilds `better-sqlite3` natively. If you install with `--ignore-scripts`, run `npm rebuild better-sqlite3` manually.
|
|
119
146
|
|
|
120
147
|
On first launch, SwarmClaw will:
|
|
@@ -143,7 +170,7 @@ Notes:
|
|
|
143
170
|
- When run with no flags in a TTY, `setup init` enters interactive mode — pick providers, enter keys, name agents, and add multiple providers in one session.
|
|
144
171
|
- Use `--no-interactive` to force flag-only mode.
|
|
145
172
|
- On a fresh instance, `setup init` can auto-discover and claim the first-run access key from `/api/auth`.
|
|
146
|
-
- For existing installs, pass `--key <ACCESS_KEY>`
|
|
173
|
+
- For existing installs, pass `--key <ACCESS_KEY>` or set `SWARMCLAW_ACCESS_KEY` / `SWARMCLAW_API_KEY`.
|
|
147
174
|
- `setup init` performs provider validation, stores credentials, creates a starter agent, and marks setup complete.
|
|
148
175
|
- Use `--skip-check` to bypass connection validation.
|
|
149
176
|
|
|
@@ -164,7 +191,7 @@ Notes:
|
|
|
164
191
|
## Features
|
|
165
192
|
|
|
166
193
|
- **15 providers out of the box** - CLI providers + major hosted APIs + OpenAI-compatible custom endpoints
|
|
167
|
-
- **OpenClaw-native control plane** -
|
|
194
|
+
- **OpenClaw-native control plane** - named gateway profiles, external runtimes, reload modes, sync, and approval flows
|
|
168
195
|
- **Agent builder + inspector** - personality/system tuning, skill management, and OpenClaw file editing
|
|
169
196
|
- **Rich toolset** - shell, files, browser, git, sandbox execution, memory, MCP, and delegation
|
|
170
197
|
- **Platform automation** - agents can manage tasks, schedules, chats, connectors, secrets, and more
|
|
@@ -249,6 +276,8 @@ src/
|
|
|
249
276
|
| xAI (Grok) | api.x.ai | Grok 3, Grok 3 Fast, Grok 3 Mini |
|
|
250
277
|
| Fireworks AI | api.fireworks.ai | DeepSeek R1, Llama 3.3 70B, Qwen 3 |
|
|
251
278
|
|
|
279
|
+
If a provider is configured, SwarmClaw can populate the model dropdown from that provider’s advertised model list. For OpenAI this means the selector can auto-fill current OpenAI models, while still allowing users to type a newer or custom model manually if it is not in the fetched list yet.
|
|
280
|
+
|
|
252
281
|
### Local & Remote
|
|
253
282
|
|
|
254
283
|
| Provider | Type | Notes |
|
|
@@ -287,6 +316,7 @@ Connector ingress now also supports optional pairing/allowlist policy:
|
|
|
287
316
|
- `dmPolicy: allowlist` blocks unknown senders until approved
|
|
288
317
|
- `/pair` flow lets approved admins generate and approve pairing codes
|
|
289
318
|
- `/think` command can set connector thread thinking level (`low`, `medium`, `high`)
|
|
319
|
+
- Session overrides also support per-thread `/reply`, `/scope`, `/thread`, `/provider`, `/model`, `/idle`, `/maxage`, and `/reset` controls
|
|
290
320
|
|
|
291
321
|
## Agent Tools
|
|
292
322
|
|
|
@@ -301,18 +331,19 @@ Agents can use the following tools when enabled:
|
|
|
301
331
|
| Edit File | Search-and-replace editing (exact match required) |
|
|
302
332
|
| Web Search | Search the web via DuckDuckGo HTML scraping |
|
|
303
333
|
| Web Fetch | Fetch and extract text content from URLs (uses cheerio) |
|
|
304
|
-
| CLI Delegation | Delegate complex tasks to Claude Code, Codex CLI, OpenCode CLI, or Gemini CLI |
|
|
305
|
-
| Spawn Subagent | Delegate a sub-task to another agent
|
|
306
|
-
| Browser | Playwright-powered web browsing via MCP
|
|
334
|
+
| CLI Delegation | Delegate complex tasks to Claude Code, Codex CLI, OpenCode CLI, or Gemini CLI, either inline or as a background job handle |
|
|
335
|
+
| Spawn Subagent | Delegate a sub-task to another agent with `status` / `list` / `wait` / `cancel` handles and inherited browser state when needed |
|
|
336
|
+
| Browser | Playwright-powered web browsing via MCP with persistent profiles, structured page reads, form helpers, verification actions, and resumable state |
|
|
307
337
|
| Canvas | Present/hide/snapshot live HTML content in a chat canvas panel |
|
|
308
338
|
| HTTP Request | Make direct API calls with method, headers, body, redirect control, and timeout |
|
|
309
339
|
| Git | Run structured git subcommands (`status`, `diff`, `log`, `add`, `commit`, `push`, etc.) with repo safety checks |
|
|
310
340
|
| Memory | Store and retrieve long-term memories with FTS5 + vector search, file references, image attachments, and linked memory graph traversal |
|
|
341
|
+
| Monitor | Inspect system state and create durable watches over files, endpoints, tasks, webhooks, and page/content changes (`monitor_tool`) |
|
|
311
342
|
| Wallet | Manage an agent-linked Solana wallet (`wallet_tool`) to check balance/address, send SOL (limits + approval), and review transaction history |
|
|
312
343
|
| Image Generation | Generate images from prompts (`generate_image`) via OpenAI, Stability, Replicate, fal.ai, Together, Fireworks, BFL, or custom endpoints; saved to uploads |
|
|
313
344
|
| Email | Send outbound email via SMTP (`email`) with `send`/`status` actions |
|
|
314
345
|
| Calendar | Manage Google/Outlook events (`calendar`) with list/create/update/delete/status actions |
|
|
315
|
-
| Sandbox | Run JS/TS
|
|
346
|
+
| Sandbox | Run JS/TS in a Deno sandbox when custom code is necessary. If Deno is unavailable it fails closed with guidance; for simple API calls, prefer HTTP Request. |
|
|
316
347
|
| MCP Servers | Connect to external Model Context Protocol servers. Tools from MCP servers are injected as first-class agent tools |
|
|
317
348
|
|
|
318
349
|
### Platform Tools
|
|
@@ -324,7 +355,7 @@ Agents with platform tools enabled can manage the SwarmClaw instance:
|
|
|
324
355
|
| Manage Agents | List, create, update, delete agents |
|
|
325
356
|
| Manage Tasks | Create and manage task board items with agent assignment |
|
|
326
357
|
| Manage Schedules | Create cron, interval, or one-time scheduled jobs |
|
|
327
|
-
| Reminders | Schedule a conversational wake event in the current chat (`schedule_wake`) |
|
|
358
|
+
| Reminders | Schedule a durable conversational wake event in the current chat (`schedule_wake`) |
|
|
328
359
|
| Manage Skills | List, create, update reusable skill definitions |
|
|
329
360
|
| Manage Documents | Upload/search/get/delete indexed docs for lightweight RAG workflows |
|
|
330
361
|
| Manage Webhooks | Register external webhook endpoints that trigger agent chats |
|
|
@@ -367,21 +398,6 @@ Daemon runtime also triggers memory consolidation (daily summary generation plus
|
|
|
367
398
|
- **API:** `GET /api/daemon` (status), `POST /api/daemon` with `{"action": "start"}` or `{"action": "stop"}`
|
|
368
399
|
- Auto-starts on first authenticated runtime traffic (`/api/auth` or `/api/daemon`) unless `SWARMCLAW_DAEMON_AUTOSTART=0`
|
|
369
400
|
|
|
370
|
-
## Main Agent Loop
|
|
371
|
-
|
|
372
|
-
For autonomous long-running missions, enable the **Main Loop** on an agent-thread or orchestrated chat. This lets an agent pursue a goal continuously with heartbeat-driven progress checks and automatic followups.
|
|
373
|
-
|
|
374
|
-
- **Heartbeat prompts:** `SWARM_MAIN_MISSION_TICK` triggers on each heartbeat, giving the agent its goal, status, and pending events
|
|
375
|
-
- **Auto-followup:** When an agent returns `[MAIN_LOOP_META] {"follow_up":true}`, the loop schedules another tick after `delay_sec`
|
|
376
|
-
- **Mission state:** Tracks `goal`, `status` (idle/progress/blocked/ok), `summary`, `nextAction`, `autonomyMode` (assist/autonomous), and pending events
|
|
377
|
-
- **Autonomy modes:**
|
|
378
|
-
- `autonomous`: Agent executes safe actions without confirmation, only asks when blocked by permissions/credentials
|
|
379
|
-
- `assist`: Agent asks before irreversible external actions (sending messages, purchases, account mutations)
|
|
380
|
-
- **API:** `POST /api/chats/[id]/main-loop` with `{"tick":true}` to trigger a mission tick
|
|
381
|
-
- **CLI:** `swarmclaw chats main-loop <id>` to inspect loop state, or `swarmclaw chats main-loop-action <id> --data '{"action":"nudge"}'` to control it
|
|
382
|
-
|
|
383
|
-
Use this for background agents that should "keep working" on a goal until blocked or complete.
|
|
384
|
-
|
|
385
401
|
## Loop Modes
|
|
386
402
|
|
|
387
403
|
Configure loop behavior in **Settings → Runtime & Loop Controls**:
|
|
@@ -440,9 +456,9 @@ Agents and chats can have **fallback credentials**. If the primary API key gets
|
|
|
440
456
|
|
|
441
457
|
## Plugin System
|
|
442
458
|
|
|
443
|
-
SwarmClaw features a
|
|
459
|
+
SwarmClaw features a modular plugin system for agent capabilities, UI extensions, provider/connectors, and post-turn automation. It supports the native SwarmClaw hook/tool format and the **OpenClaw** register/activate formats.
|
|
444
460
|
|
|
445
|
-
Plugins can be managed in **Settings → Plugins** and installed via the Marketplace, URL, or by dropping `.js` files into `data/plugins/`.
|
|
461
|
+
Plugins can be managed in **Settings → Plugins** and installed via the Marketplace, HTTPS URL, or by dropping `.js` / `.mjs` files into `data/plugins/`.
|
|
446
462
|
|
|
447
463
|
Docs:
|
|
448
464
|
- Full docs: https://swarmclaw.ai/docs
|
|
@@ -453,17 +469,30 @@ Docs:
|
|
|
453
469
|
Unlike standard tool systems, SwarmClaw plugins can modify the application itself:
|
|
454
470
|
|
|
455
471
|
- **Agent Tools**: Define custom tools that agents can autonomously discover and use.
|
|
456
|
-
- **Lifecycle Hooks**: Intercept events like `beforeAgentStart`, `afterToolExec`, and `onMessage`.
|
|
472
|
+
- **Lifecycle Hooks**: Intercept events like `beforeAgentStart`, `beforeToolExec`, `afterToolExec`, `afterChatTurn`, and `onMessage`.
|
|
457
473
|
- **UI Extensions**:
|
|
458
474
|
- `sidebarItems`: Inject new navigation links into the main sidebar.
|
|
459
475
|
- `headerWidgets`: Add status badges or indicators to the chat header (e.g., Wallet Balance).
|
|
460
476
|
- `chatInputActions`: Add custom action buttons next to the chat input (e.g., "Quick Scan").
|
|
461
477
|
- `plugin-ui` Messages: Render rich, interactive React cards in the chat stream.
|
|
462
478
|
- **Deep Chat Hooks**:
|
|
463
|
-
- `transformInboundMessage`: Modify user messages before they reach the agent.
|
|
464
|
-
- `transformOutboundMessage`: Modify agent responses before they are
|
|
479
|
+
- `transformInboundMessage`: Modify user messages before they reach the agent runtime.
|
|
480
|
+
- `transformOutboundMessage`: Modify agent responses before they are persisted or displayed.
|
|
481
|
+
- `beforeToolExec`: Can rewrite tool input before the selected tool executes.
|
|
465
482
|
- **Custom Providers**: Add new LLM backends (e.g., a specialized local model or a new API).
|
|
466
483
|
- **Custom Connectors**: Build new chat platform bridges (e.g., a proprietary internal messenger).
|
|
484
|
+
- **Per-plugin Settings**: Declare `ui.settingsFields` and read/write them via `/api/plugins/settings`. Fields marked `type: 'secret'` are encrypted at rest.
|
|
485
|
+
|
|
486
|
+
### Canonical Plugin IDs
|
|
487
|
+
|
|
488
|
+
Built-in capabilities now resolve to a single canonical plugin family ID across agent configs, policy rules, approvals, and the Plugins UI. Legacy aliases still work, but the canonical IDs are what you should document and store going forward.
|
|
489
|
+
|
|
490
|
+
- `manage_sessions` instead of `session_info`
|
|
491
|
+
- `manage_connectors` instead of `connectors`
|
|
492
|
+
- `http_request` instead of `http`
|
|
493
|
+
- `spawn_subagent` instead of `subagent`
|
|
494
|
+
- `manage_chatrooms` instead of `chatroom`
|
|
495
|
+
- `schedule_wake` instead of `schedule`
|
|
467
496
|
|
|
468
497
|
### Autonomous Capability Discovery
|
|
469
498
|
|
|
@@ -494,12 +523,53 @@ module.exports = {
|
|
|
494
523
|
};
|
|
495
524
|
```
|
|
496
525
|
|
|
526
|
+
Hook signatures of note:
|
|
527
|
+
|
|
528
|
+
- `beforeToolExec({ toolName, input })` may return a replacement input object.
|
|
529
|
+
- `afterToolExec({ session, toolName, input, output })` observes completed tool executions.
|
|
530
|
+
- `transformInboundMessage({ session, text })` and `transformOutboundMessage({ session, text })` run sequentially across enabled plugins.
|
|
531
|
+
|
|
532
|
+
### Building Plugins
|
|
533
|
+
|
|
534
|
+
The shortest reliable workflow for a new plugin:
|
|
535
|
+
|
|
536
|
+
1. Create a focused `.js` or `.mjs` file under `data/plugins/`.
|
|
537
|
+
2. Export `name`, `description`, any `hooks`, and optional `tools` / `ui.settingsFields`.
|
|
538
|
+
3. Keep tool outputs structured when the agent needs to chain them into later steps.
|
|
539
|
+
4. Use `settingsFields` for secrets or environment-specific values instead of hardcoding them.
|
|
540
|
+
5. If the plugin needs third-party npm packages, attach a `package.json` manifest so SwarmClaw can manage it in a per-plugin workspace.
|
|
541
|
+
6. Enable the plugin in **Settings → Plugins** and test both the tool path and any hook behavior.
|
|
542
|
+
7. If you host it remotely, install from a stable HTTPS URL so SwarmClaw can record source metadata and update it later.
|
|
543
|
+
|
|
544
|
+
A fuller step-by-step walkthrough lives at https://swarmclaw.ai/docs/plugin-tutorial.
|
|
545
|
+
|
|
497
546
|
### Lifecycle Management
|
|
498
547
|
|
|
499
548
|
- **Versioning**: All plugins support semantic versioning (e.g., `v1.2.3`).
|
|
500
|
-
- **Updates**:
|
|
501
|
-
- **Hot
|
|
502
|
-
- **
|
|
549
|
+
- **Updates**: External plugins installed from a recorded source URL can be updated individually or in bulk via the Plugins manager. Built-ins update with the app release.
|
|
550
|
+
- **Hot Reload**: Changes inside `data/plugins/` invalidate the plugin registry automatically, and installs/updates trigger an immediate reload.
|
|
551
|
+
- **Plugin Workspaces**: Plugins with a manifest are managed under `data/plugins/.workspaces/<plugin>/`, and dependency installs can be triggered from the plugin detail sheet or `POST /api/plugins/dependencies`.
|
|
552
|
+
- **Stability Guardrails**: Consecutive plugin failures are tracked in `data/plugin-failures.json`; failing external plugins are auto-disabled, a warning notification is emitted in-app, and users can re-enable manually from the Plugins manager.
|
|
553
|
+
- **Source Metadata**: Marketplace/URL installs record the normalized source URL and source hash in `data/plugins.json`.
|
|
554
|
+
- **Settings Safety**: Plugin settings are validated against declared `settingsFields`; unknown keys are ignored and `secret` values are stored encrypted.
|
|
555
|
+
|
|
556
|
+
### Browser, Watch, and Delegation Upgrades
|
|
557
|
+
|
|
558
|
+
- **Persistent Browser Profiles**: The built-in `browser` plugin now keeps a reusable profile per chat/session, and subagents inherit the parent profile by default. Profiles live under `~/.swarmclaw/browser-profiles` unless you override `BROWSER_PROFILES_DIR`, so cookies, storage, and authenticated state survive longer-running work without polluting the project tree. Browser state is exposed at `GET /api/chats/[id]/browser`.
|
|
559
|
+
- **Higher-Level Browser Actions**: In addition to raw Playwright-style actions, `browser` supports workflow-oriented actions such as `read_page`, `extract_links`, `extract_form_fields`, `extract_table`, `fill_form`, `submit_form`, `scroll_until`, `download_file`, `complete_web_task`, `verify_text`, `verify_element`, `verify_list`, `verify_value`, `profile`, and `reset_profile`.
|
|
560
|
+
- **Structured Browser State**: Browser sessions persist recent observations, tabs, artifacts (screenshots / PDFs / downloads), current URL, and last errors in `browser_sessions`, which makes autonomous browser tasks easier to resume, inspect, and hand off across turns.
|
|
561
|
+
- **Durable Watches**: `schedule_wake` now uses persisted watch jobs instead of an in-memory timer, and `monitor_tool` supports `create_watch`, `list_watches`, `get_watch`, and `cancel_watch` across `time`, `http`, `file`, `task`, `webhook`, and `page` conditions. The same watch system also powers the new `mailbox`, session-mailbox, and approval waits used by human-loop flows. Watches support common checks like status/status sets, regex or text matches, content changes, existence checks, inbound mailbox correlation IDs, and webhook event filters.
|
|
562
|
+
- **Long-Running Delegation Handles**: `delegate` and `spawn_subagent` support handle-based flows instead of only synchronous final text. Use `background=true` or `waitForCompletion=false` to launch long-running work, then inspect or stop it with `action=status|list|wait|cancel`.
|
|
563
|
+
- **Delegation Job Persistence**: Delegate and subagent runs are recorded in `delegation_jobs` with checkpoints, backend/session metadata, resume IDs, child session IDs, and terminal-status recovery after daemon restarts. Late completions no longer overwrite cancelled jobs.
|
|
564
|
+
|
|
565
|
+
### New Primitive Plugins
|
|
566
|
+
|
|
567
|
+
- **Mailbox / Inbox Automation**: The built-in `mailbox` plugin adds IMAP/SMTP inbox access with `status`, `list_messages`, `list_threads`, `search_messages`, `read_message`, `download_attachment`, `reply`, and `wait_for_email`. It supports durable inbound-email waits and reuses plugin settings / connector config where possible. Configure it in **Settings → Plugins** with `imapHost`, `smtpHost`, `user`, `password`, and optional reply defaults.
|
|
568
|
+
- **Human-in-the-Loop Requests**: The built-in `ask_human` plugin provides `request_input`, `request_approval`, `wait_for_reply`, `wait_for_approval`, `list_mailbox`, `ack_mailbox`, and `status`. It is backed by session mailbox envelopes plus approval records so agents can pause and resume on real human responses instead of polling ad hoc state.
|
|
569
|
+
- **Document Parsing / OCR**: The built-in `document` plugin adds `read`, `metadata`, `ocr`, `extract_tables`, `store`, `list`, `search`, `get`, and `delete`. It uses the shared document extraction helpers for PDFs, Office docs, OCR-able images, HTML, CSV/TSV/XLSX, ZIP inspection, and plain text files.
|
|
570
|
+
- **Schema-Driven Extraction**: The built-in `extract` plugin adds `extract_structured` and `summarize`, using the current session model/provider to turn raw text or local files into validated JSON objects. This is the primitive to combine with browser / document / crawl output when an agent needs structured records instead of prose.
|
|
571
|
+
- **Tabular Data Operations**: The built-in `table` plugin adds `read`, `load_csv`, `load_xlsx`, `summarize`, `filter`, `sort`, `group`, `pivot`, `dedupe`, `join`, and `write`. It operates on CSV, TSV, JSON array-of-objects, or XLSX inputs without forcing agents to drop into shell or Python for basic spreadsheet work, and transformed tables can be persisted with `outputPath` / `saveTo`.
|
|
572
|
+
- **Multi-Page Crawling**: The built-in `crawl` plugin adds `crawl_site`, `follow_pagination`, `extract_sitemap`, `dedupe_pages`, and `batch_extract`. It handles BFS-style same-origin site traversal, accepts either a fresh start URL or an explicit page list, and can hand the aggregate page set directly into structured extraction for research-heavy autonomous tasks.
|
|
503
573
|
|
|
504
574
|
## Deploy to a VPS
|
|
505
575
|
|
|
@@ -600,7 +670,7 @@ npm run update:easy # safe update helper for local installs
|
|
|
600
670
|
SwarmClaw uses tag-based releases (`vX.Y.Z`) as the stable channel.
|
|
601
671
|
|
|
602
672
|
```bash
|
|
603
|
-
# example patch release (v0.7.
|
|
673
|
+
# example patch release (v0.7.4 style)
|
|
604
674
|
npm version patch
|
|
605
675
|
git push origin main --follow-tags
|
|
606
676
|
```
|
|
@@ -610,16 +680,15 @@ On `v*` tags, GitHub Actions will:
|
|
|
610
680
|
2. Create a GitHub Release
|
|
611
681
|
3. Build and publish Docker images to `ghcr.io/swarmclawai/swarmclaw` (`:vX.Y.Z`, `:latest`, `:sha-*`)
|
|
612
682
|
|
|
613
|
-
#### v0.7.
|
|
683
|
+
#### v0.7.4 Release Readiness Notes
|
|
614
684
|
|
|
615
|
-
Before shipping `v0.7.
|
|
685
|
+
Before shipping `v0.7.4`, confirm the following user-facing changes are reflected in docs:
|
|
616
686
|
|
|
617
|
-
1.
|
|
618
|
-
2.
|
|
619
|
-
3.
|
|
620
|
-
4.
|
|
621
|
-
5.
|
|
622
|
-
6. Site release notes include a `v0.7.2` section and docs index points to the current release notes version.
|
|
687
|
+
1. Sandbox docs are updated everywhere to reflect the current Deno-only `sandbox_exec` behavior and the guidance to prefer `http_request` for simple API calls.
|
|
688
|
+
2. OpenClaw docs cover the current gateway/runtime behavior, including per-agent gateway routing, control-plane actions, and inspector-side advanced controls.
|
|
689
|
+
3. Site and README install/version strings are updated to `v0.7.4`, including install snippets, release notes index text, and sidebar/footer labels.
|
|
690
|
+
4. Release notes summarize the user-visible setup/auth/runtime changes from the current worktree, especially gateway/external-agent/setup flow improvements.
|
|
691
|
+
5. CLI and tool docs do not reference removed or non-functional surfaces such as the old `openclaw_sandbox` bridge.
|
|
623
692
|
|
|
624
693
|
## CLI
|
|
625
694
|
|
|
@@ -654,9 +723,6 @@ swarmclaw agents list
|
|
|
654
723
|
swarmclaw chats create --name "Main Ops" --agent-id <agentId>
|
|
655
724
|
swarmclaw chats list
|
|
656
725
|
|
|
657
|
-
# run a main loop action
|
|
658
|
-
swarmclaw chats main-loop-action <chatId> --data '{"action":"nudge"}'
|
|
659
|
-
|
|
660
726
|
# run setup diagnostics
|
|
661
727
|
swarmclaw setup doctor
|
|
662
728
|
```
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-require-imports */
|
|
3
|
+
|
|
4
|
+
const fs = require('node:fs')
|
|
5
|
+
const path = require('node:path')
|
|
6
|
+
|
|
7
|
+
const LOCKFILE_NAMES = [
|
|
8
|
+
'package-lock.json',
|
|
9
|
+
'pnpm-lock.yaml',
|
|
10
|
+
'yarn.lock',
|
|
11
|
+
'bun.lock',
|
|
12
|
+
'bun.lockb',
|
|
13
|
+
]
|
|
14
|
+
const INSTALL_METADATA_FILE = '.swarmclaw-install.json'
|
|
15
|
+
|
|
16
|
+
function normalizePackageManager(raw) {
|
|
17
|
+
switch (String(raw || '').trim().toLowerCase()) {
|
|
18
|
+
case 'pnpm':
|
|
19
|
+
case 'yarn':
|
|
20
|
+
case 'bun':
|
|
21
|
+
case 'npm':
|
|
22
|
+
return String(raw).trim().toLowerCase()
|
|
23
|
+
default:
|
|
24
|
+
return null
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function detectPackageManagerFromUserAgent(userAgent) {
|
|
29
|
+
const normalized = String(userAgent || '').toLowerCase()
|
|
30
|
+
if (normalized.startsWith('pnpm/')) return 'pnpm'
|
|
31
|
+
if (normalized.startsWith('yarn/')) return 'yarn'
|
|
32
|
+
if (normalized.startsWith('bun/')) return 'bun'
|
|
33
|
+
if (normalized.startsWith('npm/')) return 'npm'
|
|
34
|
+
return null
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function readInstallMetadata(rootDir) {
|
|
38
|
+
const metadataPath = path.join(rootDir, INSTALL_METADATA_FILE)
|
|
39
|
+
if (!fs.existsSync(metadataPath)) return null
|
|
40
|
+
try {
|
|
41
|
+
const raw = JSON.parse(fs.readFileSync(metadataPath, 'utf8'))
|
|
42
|
+
return raw && typeof raw === 'object' ? raw : null
|
|
43
|
+
} catch {
|
|
44
|
+
return null
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function detectPackageManager(rootDir, env = process.env) {
|
|
49
|
+
const envOverride = normalizePackageManager(env.SWARMCLAW_PACKAGE_MANAGER)
|
|
50
|
+
if (envOverride) return envOverride
|
|
51
|
+
|
|
52
|
+
const installMetadata = readInstallMetadata(rootDir)
|
|
53
|
+
const installManager = normalizePackageManager(installMetadata?.packageManager)
|
|
54
|
+
if (installManager) return installManager
|
|
55
|
+
|
|
56
|
+
if (fs.existsSync(path.join(rootDir, 'bun.lock')) || fs.existsSync(path.join(rootDir, 'bun.lockb'))) return 'bun'
|
|
57
|
+
if (fs.existsSync(path.join(rootDir, 'pnpm-lock.yaml'))) return 'pnpm'
|
|
58
|
+
if (fs.existsSync(path.join(rootDir, 'yarn.lock'))) return 'yarn'
|
|
59
|
+
if (fs.existsSync(path.join(rootDir, 'package-lock.json'))) return 'npm'
|
|
60
|
+
|
|
61
|
+
const userAgentManager = detectPackageManagerFromUserAgent(env.npm_config_user_agent)
|
|
62
|
+
if (userAgentManager) return userAgentManager
|
|
63
|
+
return 'npm'
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getInstallCommand(packageManager, omitDev = false) {
|
|
67
|
+
switch (packageManager) {
|
|
68
|
+
case 'pnpm':
|
|
69
|
+
return omitDev
|
|
70
|
+
? { command: 'pnpm', args: ['install', '--prod'] }
|
|
71
|
+
: { command: 'pnpm', args: ['install'] }
|
|
72
|
+
case 'yarn':
|
|
73
|
+
return omitDev
|
|
74
|
+
? { command: 'yarn', args: ['install', '--production=true'] }
|
|
75
|
+
: { command: 'yarn', args: ['install'] }
|
|
76
|
+
case 'bun':
|
|
77
|
+
return omitDev
|
|
78
|
+
? { command: 'bun', args: ['install', '--production'] }
|
|
79
|
+
: { command: 'bun', args: ['install'] }
|
|
80
|
+
case 'npm':
|
|
81
|
+
default:
|
|
82
|
+
return omitDev
|
|
83
|
+
? { command: 'npm', args: ['install', '--omit=dev'] }
|
|
84
|
+
: { command: 'npm', args: ['install'] }
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function getGlobalUpdateCommand(packageManager, packageName) {
|
|
89
|
+
return getGlobalUpdateSpec(packageManager, packageName).display
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function getGlobalUpdateSpec(packageManager, packageName) {
|
|
93
|
+
switch (packageManager) {
|
|
94
|
+
case 'pnpm':
|
|
95
|
+
return {
|
|
96
|
+
command: 'pnpm',
|
|
97
|
+
args: ['add', '-g', `${packageName}@latest`],
|
|
98
|
+
display: `pnpm add -g ${packageName}@latest`,
|
|
99
|
+
}
|
|
100
|
+
case 'yarn':
|
|
101
|
+
return {
|
|
102
|
+
command: 'yarn',
|
|
103
|
+
args: ['global', 'add', `${packageName}@latest`],
|
|
104
|
+
display: `yarn global add ${packageName}@latest`,
|
|
105
|
+
}
|
|
106
|
+
case 'bun':
|
|
107
|
+
return {
|
|
108
|
+
command: 'bun',
|
|
109
|
+
args: ['add', '-g', `${packageName}@latest`],
|
|
110
|
+
display: `bun add -g ${packageName}@latest`,
|
|
111
|
+
}
|
|
112
|
+
case 'npm':
|
|
113
|
+
default:
|
|
114
|
+
return {
|
|
115
|
+
command: 'npm',
|
|
116
|
+
args: ['update', '-g', packageName],
|
|
117
|
+
display: `npm update -g ${packageName}`,
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function getRunScriptCommand(packageManager, scriptName) {
|
|
123
|
+
switch (packageManager) {
|
|
124
|
+
case 'pnpm':
|
|
125
|
+
return { command: 'pnpm', args: [scriptName] }
|
|
126
|
+
case 'yarn':
|
|
127
|
+
return { command: 'yarn', args: [scriptName] }
|
|
128
|
+
case 'bun':
|
|
129
|
+
return { command: 'bun', args: ['run', scriptName] }
|
|
130
|
+
case 'npm':
|
|
131
|
+
default:
|
|
132
|
+
return { command: 'npm', args: ['run', scriptName] }
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function dependenciesChanged(diffText) {
|
|
137
|
+
if (!diffText) return false
|
|
138
|
+
return String(diffText)
|
|
139
|
+
.split('\n')
|
|
140
|
+
.map((line) => line.trim())
|
|
141
|
+
.filter(Boolean)
|
|
142
|
+
.some((file) => file === 'package.json' || LOCKFILE_NAMES.includes(file))
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
module.exports = {
|
|
146
|
+
dependenciesChanged,
|
|
147
|
+
detectPackageManager,
|
|
148
|
+
detectPackageManagerFromUserAgent,
|
|
149
|
+
getGlobalUpdateCommand,
|
|
150
|
+
getGlobalUpdateSpec,
|
|
151
|
+
getInstallCommand,
|
|
152
|
+
getRunScriptCommand,
|
|
153
|
+
INSTALL_METADATA_FILE,
|
|
154
|
+
LOCKFILE_NAMES,
|
|
155
|
+
normalizePackageManager,
|
|
156
|
+
readInstallMetadata,
|
|
157
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
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
|
+
|
|
10
|
+
const {
|
|
11
|
+
INSTALL_METADATA_FILE,
|
|
12
|
+
LOCKFILE_NAMES,
|
|
13
|
+
dependenciesChanged,
|
|
14
|
+
detectPackageManager,
|
|
15
|
+
detectPackageManagerFromUserAgent,
|
|
16
|
+
getGlobalUpdateSpec,
|
|
17
|
+
getInstallCommand,
|
|
18
|
+
getRunScriptCommand,
|
|
19
|
+
} = require('./package-manager.js')
|
|
20
|
+
|
|
21
|
+
test('detectPackageManagerFromUserAgent parses supported package managers', () => {
|
|
22
|
+
assert.equal(detectPackageManagerFromUserAgent('pnpm/10.6.1 npm/? node/v22.6.0 darwin arm64'), 'pnpm')
|
|
23
|
+
assert.equal(detectPackageManagerFromUserAgent('yarn/4.7.0 npm/? node/v22.6.0 darwin arm64'), 'yarn')
|
|
24
|
+
assert.equal(detectPackageManagerFromUserAgent('bun/1.2.10 npm/? node/v22.6.0 darwin arm64'), 'bun')
|
|
25
|
+
assert.equal(detectPackageManagerFromUserAgent('npm/10.9.2 node/v22.6.0 darwin arm64'), 'npm')
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('detectPackageManager prefers the lockfile present in the workspace', () => {
|
|
29
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'swarmclaw-pm-'))
|
|
30
|
+
|
|
31
|
+
fs.writeFileSync(path.join(tmpDir, 'pnpm-lock.yaml'), 'lock', 'utf8')
|
|
32
|
+
assert.equal(detectPackageManager(tmpDir), 'pnpm')
|
|
33
|
+
|
|
34
|
+
fs.writeFileSync(path.join(tmpDir, 'bun.lockb'), 'lock', 'utf8')
|
|
35
|
+
assert.equal(detectPackageManager(tmpDir), 'bun')
|
|
36
|
+
|
|
37
|
+
fs.rmSync(tmpDir, { recursive: true, force: true })
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
test('detectPackageManager falls back to npm when no lockfile exists', () => {
|
|
41
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'swarmclaw-pm-empty-'))
|
|
42
|
+
assert.equal(detectPackageManager(tmpDir), 'npm')
|
|
43
|
+
fs.rmSync(tmpDir, { recursive: true, force: true })
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test('detectPackageManager uses install metadata when present', () => {
|
|
47
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'swarmclaw-pm-meta-'))
|
|
48
|
+
fs.writeFileSync(
|
|
49
|
+
path.join(tmpDir, INSTALL_METADATA_FILE),
|
|
50
|
+
JSON.stringify({ packageManager: 'yarn' }),
|
|
51
|
+
'utf8',
|
|
52
|
+
)
|
|
53
|
+
assert.equal(detectPackageManager(tmpDir), 'yarn')
|
|
54
|
+
fs.rmSync(tmpDir, { recursive: true, force: true })
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
test('dependenciesChanged recognizes package.json and all supported lockfiles', () => {
|
|
58
|
+
assert.equal(dependenciesChanged('package.json\nsrc/app.ts'), true)
|
|
59
|
+
for (const lockfile of LOCKFILE_NAMES) {
|
|
60
|
+
assert.equal(dependenciesChanged(`${lockfile}\nREADME.md`), true)
|
|
61
|
+
}
|
|
62
|
+
assert.equal(dependenciesChanged('README.md\nsrc/index.ts'), false)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
test('getInstallCommand returns manager-specific install arguments', () => {
|
|
66
|
+
assert.deepEqual(getInstallCommand('npm', true), { command: 'npm', args: ['install', '--omit=dev'] })
|
|
67
|
+
assert.deepEqual(getInstallCommand('pnpm', false), { command: 'pnpm', args: ['install'] })
|
|
68
|
+
assert.deepEqual(getInstallCommand('yarn', true), { command: 'yarn', args: ['install', '--production=true'] })
|
|
69
|
+
assert.deepEqual(getInstallCommand('bun', true), { command: 'bun', args: ['install', '--production'] })
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
test('getRunScriptCommand returns manager-specific script launchers', () => {
|
|
73
|
+
assert.deepEqual(getRunScriptCommand('npm', 'build'), { command: 'npm', args: ['run', 'build'] })
|
|
74
|
+
assert.deepEqual(getRunScriptCommand('pnpm', 'start'), { command: 'pnpm', args: ['start'] })
|
|
75
|
+
assert.deepEqual(getRunScriptCommand('yarn', 'dev'), { command: 'yarn', args: ['dev'] })
|
|
76
|
+
assert.deepEqual(getRunScriptCommand('bun', 'start'), { command: 'bun', args: ['run', 'start'] })
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
test('getGlobalUpdateSpec returns manager-specific update commands', () => {
|
|
80
|
+
assert.deepEqual(getGlobalUpdateSpec('npm', '@swarmclawai/swarmclaw'), {
|
|
81
|
+
command: 'npm',
|
|
82
|
+
args: ['update', '-g', '@swarmclawai/swarmclaw'],
|
|
83
|
+
display: 'npm update -g @swarmclawai/swarmclaw',
|
|
84
|
+
})
|
|
85
|
+
assert.deepEqual(getGlobalUpdateSpec('pnpm', '@swarmclawai/swarmclaw'), {
|
|
86
|
+
command: 'pnpm',
|
|
87
|
+
args: ['add', '-g', '@swarmclawai/swarmclaw@latest'],
|
|
88
|
+
display: 'pnpm add -g @swarmclawai/swarmclaw@latest',
|
|
89
|
+
})
|
|
90
|
+
})
|