@swarmclawai/swarmclaw 1.0.5 → 1.0.7
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 +43 -13
- package/package.json +10 -10
- package/src/app/activity/page.tsx +2 -0
- package/src/app/api/agents/[id]/clone/route.ts +1 -1
- package/src/app/api/agents/[id]/route.ts +25 -11
- package/src/app/api/agents/route.ts +15 -14
- package/src/app/api/autonomy/incidents/route.ts +19 -0
- package/src/app/api/autonomy/reflections/route.ts +19 -0
- package/src/app/api/canvas/[sessionId]/route.ts +2 -2
- package/src/app/api/chatrooms/[id]/chat/route.ts +1 -1
- package/src/app/api/chats/[id]/route.ts +13 -3
- package/src/app/api/chats/route.ts +13 -3
- package/src/app/api/clawhub/install/route.test.ts +97 -0
- package/src/app/api/clawhub/install/route.ts +78 -4
- package/src/app/api/clawhub/preview/route.ts +59 -0
- package/src/app/api/clawhub/search/route.ts +2 -1
- package/src/app/api/connectors/[id]/access/route.ts +254 -0
- package/src/app/api/{plugins → extensions}/dependencies/route.ts +1 -4
- package/src/app/api/{plugins → extensions}/install/route.ts +2 -6
- package/src/app/api/{plugins → extensions}/marketplace/route.ts +6 -9
- package/src/app/api/{plugins → extensions}/route.ts +17 -13
- package/src/app/api/{plugins → extensions}/settings/route.ts +13 -6
- package/src/app/api/{plugins → extensions}/ui/route.ts +9 -9
- package/src/app/api/projects/[id]/route.ts +5 -1
- package/src/app/api/schedules/[id]/route.ts +71 -8
- package/src/app/api/schedules/[id]/run/route.ts +14 -2
- package/src/app/api/schedules/route.ts +20 -3
- package/src/app/api/search/route.ts +2 -2
- package/src/app/api/settings/route.ts +3 -0
- package/src/app/api/tasks/[id]/route.ts +8 -1
- package/src/app/api/tasks/route.ts +2 -1
- package/src/app/api/webhooks/[id]/helpers.ts +5 -5
- package/src/app/{plugins → extensions}/layout.tsx +3 -3
- package/src/app/{plugins → extensions}/page.tsx +3 -3
- package/src/app/home/page.tsx +7 -2
- package/src/app/inbox/page.tsx +7 -0
- package/src/app/layout.tsx +1 -1
- package/src/app/schedules/page.tsx +2 -2
- package/src/app/settings/page.tsx +10 -10
- package/src/app/skills/page.tsx +2 -22
- package/src/app/tasks/page.tsx +8 -2
- package/src/cli/index.js +23 -23
- package/src/cli/index.ts +13 -6
- package/src/cli/spec.js +25 -17
- package/src/components/agents/agent-card.tsx +3 -2
- package/src/components/agents/agent-chat-list.tsx +6 -5
- package/src/components/agents/agent-list.tsx +5 -4
- package/src/components/agents/agent-sheet.tsx +591 -735
- package/src/components/agents/inspector-panel.tsx +6 -5
- package/src/components/auth/setup-wizard/index.tsx +13 -7
- package/src/components/auth/setup-wizard/step-agents.tsx +2 -2
- package/src/components/auth/setup-wizard/types.ts +3 -1
- package/src/components/auth/setup-wizard/utils.ts +3 -1
- package/src/components/chat/chat-area.tsx +6 -5
- package/src/components/chat/chat-card.tsx +2 -1
- package/src/components/chat/chat-header.tsx +7 -6
- package/src/components/chat/chat-tool-toggles.tsx +9 -4
- package/src/components/chat/swarm-panel.test.ts +1 -1
- package/src/components/chat/swarm-panel.tsx +1 -1
- package/src/components/chat/tool-request-banner.tsx +6 -5
- package/src/components/chatrooms/agent-hover-card.tsx +4 -3
- package/src/components/chatrooms/chatroom-tool-request-banner.tsx +3 -2
- package/src/components/connectors/connector-access-panel.tsx +416 -0
- package/src/components/connectors/connector-inbox.tsx +880 -0
- package/src/components/connectors/connector-sheet.tsx +368 -120
- package/src/components/input/chat-input.tsx +9 -7
- package/src/components/layout/dashboard-shell.tsx +14 -13
- package/src/components/layout/mobile-drawer.tsx +5 -5
- package/src/components/layout/sidebar-rail.tsx +9 -1
- package/src/components/mcp-servers/mcp-server-sheet.tsx +1 -1
- package/src/components/plugins/plugin-list.tsx +25 -45
- package/src/components/plugins/plugin-sheet.tsx +41 -42
- package/src/components/schedules/schedule-console.tsx +596 -0
- package/src/components/schedules/schedule-list.tsx +4 -1
- package/src/components/schedules/schedule-sheet.tsx +6 -6
- package/src/components/shared/advanced-settings-section.tsx +59 -0
- package/src/components/shared/agent-picker-list.tsx +3 -3
- package/src/components/skills/skill-list.tsx +172 -512
- package/src/components/skills/skills-workspace.tsx +1715 -0
- package/src/components/tasks/task-card.tsx +9 -3
- package/src/components/tasks/task-column.tsx +1 -0
- package/src/components/tasks/task-list.tsx +3 -2
- package/src/instrumentation.ts +3 -2
- package/src/lib/agent-default-tools.ts +24 -14
- package/src/lib/app/navigation.ts +2 -1
- package/src/lib/app/view-constants.ts +17 -9
- package/src/lib/autonomy/supervisor-settings.ts +80 -0
- package/src/lib/capability-selection.test.ts +44 -0
- package/src/lib/capability-selection.ts +91 -0
- package/src/lib/chat/chats.ts +3 -2
- package/src/lib/chat/queued-message-queue.test.ts +41 -0
- package/src/lib/chat/queued-message-queue.ts +49 -0
- package/src/lib/connectors/sender-id.test.ts +32 -0
- package/src/lib/connectors/sender-id.ts +31 -0
- package/src/lib/observability/local-observability.test.ts +2 -0
- package/src/lib/observability/local-observability.ts +1 -1
- package/src/lib/plugin-install-cors.ts +1 -1
- package/src/lib/provider-sets.ts +1 -1
- package/src/lib/providers/claude-cli.ts +2 -1
- package/src/lib/runtime/runtime-loop.ts +0 -20
- package/src/lib/schedules/schedules.ts +31 -3
- package/src/lib/server/agents/agent-registry.ts +2 -2
- package/src/lib/server/agents/agent-thread-session.ts +8 -3
- package/src/lib/server/agents/delegation-jobs-advanced.test.ts +4 -4
- package/src/lib/server/agents/main-agent-loop-advanced.test.ts +35 -0
- package/src/lib/server/agents/main-agent-loop.ts +45 -8
- package/src/lib/server/agents/subagent-runtime.ts +23 -29
- package/src/lib/server/agents/subagent-swarm.ts +1 -1
- package/src/lib/server/agents/task-session.ts +55 -0
- package/src/lib/server/autonomy/supervisor-reflection.test.ts +281 -0
- package/src/lib/server/autonomy/supervisor-reflection.ts +815 -0
- package/src/lib/server/build-llm.test.ts +130 -102
- package/src/lib/server/build-llm.ts +123 -71
- package/src/lib/server/chat-execution/chat-execution-advanced.test.ts +50 -0
- package/src/lib/server/chat-execution/chat-execution-connector-delivery.ts +22 -3
- package/src/lib/server/chat-execution/chat-execution-session-sync.test.ts +94 -0
- package/src/lib/server/chat-execution/chat-execution-tool-events.test.ts +10 -0
- package/src/lib/server/chat-execution/chat-execution-utils.ts +45 -3
- package/src/lib/server/chat-execution/chat-execution.ts +118 -126
- package/src/lib/server/chat-execution/chat-streaming-utils.ts +57 -1
- package/src/lib/server/chat-execution/chat-turn-tool-routing.ts +8 -2
- package/src/lib/server/chat-execution/stream-agent-chat.test.ts +49 -1
- package/src/lib/server/chat-execution/stream-agent-chat.ts +68 -42
- package/src/lib/server/chatrooms/chatroom-helpers.ts +12 -9
- package/src/lib/server/chatrooms/session-mailbox.test.ts +61 -0
- package/src/lib/server/chatrooms/session-mailbox.ts +128 -3
- package/src/lib/server/connectors/access.test.ts +146 -0
- package/src/lib/server/connectors/access.ts +231 -29
- package/src/lib/server/connectors/commands.ts +2 -1
- package/src/lib/server/connectors/manager-roundtrip.test.ts +1 -2
- package/src/lib/server/connectors/manager.test.ts +1082 -161
- package/src/lib/server/connectors/manager.ts +84 -86
- package/src/lib/server/connectors/outbox.ts +5 -1
- package/src/lib/server/connectors/pairing.test.ts +29 -0
- package/src/lib/server/connectors/pairing.ts +162 -35
- package/src/lib/server/connectors/policy.ts +16 -1
- package/src/lib/server/connectors/session-consolidation.ts +42 -3
- package/src/lib/server/connectors/session.test.ts +134 -0
- package/src/lib/server/connectors/session.ts +36 -2
- package/src/lib/server/connectors/types.ts +2 -0
- package/src/lib/server/connectors/whatsapp.test.ts +22 -2
- package/src/lib/server/connectors/whatsapp.ts +70 -1
- package/src/lib/server/cost.ts +1 -1
- package/src/lib/server/embeddings.ts +1 -1
- package/src/lib/server/eval/agent-regression-advanced.test.ts +2 -2
- package/src/lib/server/eval/agent-regression.test.ts +2 -2
- package/src/lib/server/eval/agent-regression.ts +56 -44
- package/src/lib/server/eval/runner.ts +1 -1
- package/src/lib/server/knowledge-db.test.ts +1 -1
- package/src/lib/server/memory/memory-consolidation.ts +2 -2
- package/src/lib/server/memory/session-memory-scope.test.ts +65 -0
- package/src/lib/server/memory/session-memory-scope.ts +47 -0
- package/src/lib/server/memory/temporal-decay.ts +6 -0
- package/src/lib/server/native-capabilities.test.ts +25 -0
- package/src/lib/server/native-capabilities.ts +681 -0
- package/src/lib/server/plugins.ts +3 -3
- package/src/lib/server/project-utils.ts +2 -2
- package/src/lib/server/runtime/daemon-state.ts +14 -13
- package/src/lib/server/runtime/heartbeat-service.ts +1 -1
- package/src/lib/server/runtime/heartbeat-wake.test.ts +26 -0
- package/src/lib/server/runtime/heartbeat-wake.ts +24 -3
- package/src/lib/server/runtime/queue.ts +193 -42
- package/src/lib/server/runtime/runtime-settings.test.ts +0 -8
- package/src/lib/server/runtime/runtime-settings.ts +0 -12
- package/src/lib/server/runtime/runtime-storage-write-paths.test.ts +1 -11
- package/src/lib/server/runtime/scheduler.test.ts +74 -0
- package/src/lib/server/runtime/scheduler.ts +32 -15
- package/src/lib/server/runtime/session-run-manager.ts +54 -7
- package/src/lib/server/runtime/watch-jobs.test.ts +24 -0
- package/src/lib/server/runtime/watch-jobs.ts +35 -2
- package/src/lib/server/schedules/schedule-lifecycle.test.ts +180 -0
- package/src/lib/server/schedules/schedule-lifecycle.ts +393 -0
- package/src/lib/server/schedules/schedule-normalization.test.ts +35 -1
- package/src/lib/server/schedules/schedule-normalization.ts +60 -8
- package/src/lib/server/schedules/schedule-service.ts +2 -1
- package/src/lib/server/session-tools/canvas.ts +5 -5
- package/src/lib/server/session-tools/chatroom.ts +2 -2
- package/src/lib/server/session-tools/connector.test.ts +85 -10
- package/src/lib/server/session-tools/connector.ts +269 -50
- package/src/lib/server/session-tools/context-mgmt.ts +2 -2
- package/src/lib/server/session-tools/context.ts +3 -1
- package/src/lib/server/session-tools/crawl.ts +2 -1
- package/src/lib/server/session-tools/crud.test.ts +5 -3
- package/src/lib/server/session-tools/crud.ts +40 -17
- package/src/lib/server/session-tools/delegate-fallback.test.ts +8 -8
- package/src/lib/server/session-tools/delegate.ts +21 -7
- package/src/lib/server/session-tools/discovery-approvals.test.ts +34 -15
- package/src/lib/server/session-tools/discovery.ts +55 -26
- package/src/lib/server/session-tools/extract.ts +2 -1
- package/src/lib/server/session-tools/human-loop.ts +31 -7
- package/src/lib/server/session-tools/index.ts +34 -9
- package/src/lib/server/session-tools/manage-connectors.test.ts +2 -2
- package/src/lib/server/session-tools/manage-schedules-advanced.test.ts +72 -3
- package/src/lib/server/session-tools/manage-schedules.test.ts +152 -8
- package/src/lib/server/session-tools/manage-skills.test.ts +4 -2
- package/src/lib/server/session-tools/manage-tasks-advanced.test.ts +3 -3
- package/src/lib/server/session-tools/manage-tasks.test.ts +1 -1
- package/src/lib/server/session-tools/memory.ts +20 -11
- package/src/lib/server/session-tools/monitor.ts +2 -2
- package/src/lib/server/session-tools/openclaw-workspace.ts +2 -2
- package/src/lib/server/session-tools/platform.ts +4 -5
- package/src/lib/server/session-tools/plugin-creator.ts +4 -3
- package/src/lib/server/session-tools/primitive-tools.test.ts +15 -2
- package/src/lib/server/session-tools/schedule.ts +2 -2
- package/src/lib/server/session-tools/session-info.ts +8 -4
- package/src/lib/server/session-tools/skill-runtime.test.ts +6 -2
- package/src/lib/server/session-tools/skills.ts +11 -1
- package/src/lib/server/session-tools/subagent.test.ts +50 -0
- package/src/lib/server/session-tools/subagent.ts +26 -4
- package/src/lib/server/session-tools/wallet.ts +2 -2
- package/src/lib/server/skills/clawhub-client.test.ts +109 -19
- package/src/lib/server/skills/clawhub-client.ts +115 -23
- package/src/lib/server/skills/skill-discovery.ts +1 -1
- package/src/lib/server/skills/skill-suggestions.ts +23 -5
- package/src/lib/server/storage.ts +338 -55
- package/src/lib/server/tasks/task-followups.test.ts +114 -0
- package/src/lib/server/tasks/task-followups.ts +78 -31
- package/src/lib/server/tasks/task-service.ts +1 -0
- package/src/lib/server/tool-planning.ts +7 -1
- package/src/lib/setup-defaults.ts +5 -5
- package/src/lib/validation/schemas.test.ts +21 -7
- package/src/lib/validation/schemas.ts +6 -6
- package/src/stores/slices/data-slice.ts +13 -13
- package/src/stores/use-chat-store.ts +56 -27
- package/src/types/index.ts +142 -22
- package/src/views/settings/plugin-manager.tsx +27 -27
- package/src/views/settings/section-capability-policy.tsx +3 -3
- package/src/views/settings/section-embedding.tsx +25 -12
- package/src/views/settings/section-runtime-loop.tsx +1 -33
- package/src/views/settings/section-supervisor-reflection.tsx +148 -0
- package/src/app/api/orchestrator/graph/route.ts +0 -25
- package/src/app/api/orchestrator/run/route.ts +0 -34
- package/src/lib/server/agents/orchestrator-lg-structure.test.ts +0 -17
- package/src/lib/server/agents/orchestrator-lg.ts +0 -636
- package/src/lib/server/agents/orchestrator.ts +0 -409
- package/src/views/settings/section-orchestrator.tsx +0 -141
- /package/src/lib/server/chatrooms/{chatroom-orchestration.ts → chatroom-agent-signals.ts} +0 -0
package/README.md
CHANGED
|
@@ -8,22 +8,32 @@
|
|
|
8
8
|
<img src="https://raw.githubusercontent.com/swarmclawai/swarmclaw/main/public/branding/swarmclaw-org-avatar.png" alt="SwarmClaw lobster logo" width="120" />
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
|
-
SwarmClaw is a self-hosted AI
|
|
11
|
+
SwarmClaw is a self-hosted AI runtime for OpenClaw and multi-agent work. It helps you run autonomous agents with heartbeats, schedules, delegation, memory, runtime skills, and reviewed conversation-to-skill learning across OpenClaw gateways and other providers.
|
|
12
12
|
|
|
13
13
|
GitHub: https://github.com/swarmclawai/swarmclaw
|
|
14
14
|
Docs: https://swarmclaw.ai/docs
|
|
15
15
|
Website: https://swarmclaw.ai
|
|
16
|
-
|
|
16
|
+
Extension tutorial: https://swarmclaw.ai/docs/extension-tutorial
|
|
17
|
+
|
|
18
|
+
## Release Notes
|
|
19
|
+
|
|
20
|
+
### v1.0.7 Highlights
|
|
21
|
+
|
|
22
|
+
- **Connector inbox + isolation**: external connector conversations now live in a dedicated Inbox with owner routing, allow/deny controls, pairing management, sender avatars, and strict sender-scoped memory.
|
|
23
|
+
- **Schedules as an operations surface**: schedules now use explicit session routing, archive/cancel cascades, `cancelled` task outcomes, and a proper `Live / Archived / Runs` console instead of a simple list.
|
|
24
|
+
- **Delegation, not orchestration**: orchestration is no longer a special product/runtime concept. Agents either delegate or they do not, and background AI work now uses the current agent/session model config rather than a separate orchestration engine.
|
|
25
|
+
- **Tools + extensions cutover**: built-in capabilities are native tools, external add-ons are extensions, and persisted agent/session data now stores canonical `tools` + `extensions` fields only.
|
|
26
|
+
- Breaking change: agent and session records now persist `tools` and `extensions` only. The legacy `plugins` field is no longer part of the runtime data model.
|
|
17
27
|
|
|
18
28
|
## What SwarmClaw Focuses On
|
|
19
29
|
|
|
20
|
-
- **
|
|
21
|
-
- **Autonomy and memory**: heartbeats, schedules, long-running execution, durable memory,
|
|
30
|
+
- **Delegation and background execution**: delegated work, subagents, durable jobs, checkpointing, and background task execution.
|
|
31
|
+
- **Autonomy and memory**: heartbeats, schedules, long-running execution, durable memory, reflection memory, human-context learning, document recall, and project-aware context.
|
|
22
32
|
- **OpenClaw integration**: named gateway profiles, external runtimes, deploy helpers, config sync, approval handling, and OpenClaw agent file editing.
|
|
23
33
|
- **Runtime skills**: pinned skills, OpenClaw-compatible `SKILL.md` import, on-demand skill execution, and configurable keyword or embedding-based recommendation.
|
|
24
34
|
- **Conversation-to-skill drafts**: draft a reusable skill from a real chat, review it, then approve it into the skill library.
|
|
25
35
|
- **Crypto wallets**: agent-linked Solana and Ethereum wallets for balances, approvals, signing, simulation, and execution.
|
|
26
|
-
- **Operator tooling**: connectors,
|
|
36
|
+
- **Operator tooling**: connectors, extensions, browser automation, shell/files/git tooling, and runtime guardrails.
|
|
27
37
|
|
|
28
38
|
## OpenClaw
|
|
29
39
|
|
|
@@ -45,14 +55,23 @@ npm i -g @swarmclawai/swarmclaw
|
|
|
45
55
|
swarmclaw
|
|
46
56
|
```
|
|
47
57
|
|
|
48
|
-
|
|
58
|
+
```bash
|
|
59
|
+
yarn global add @swarmclawai/swarmclaw
|
|
60
|
+
swarmclaw
|
|
61
|
+
```
|
|
49
62
|
|
|
50
|
-
|
|
63
|
+
```bash
|
|
64
|
+
pnpm add -g @swarmclawai/swarmclaw
|
|
65
|
+
swarmclaw
|
|
66
|
+
```
|
|
51
67
|
|
|
52
68
|
```bash
|
|
53
|
-
|
|
69
|
+
bun add -g @swarmclawai/swarmclaw
|
|
70
|
+
swarmclaw
|
|
54
71
|
```
|
|
55
72
|
|
|
73
|
+
Running `swarmclaw` starts the server on `http://localhost:3456`.
|
|
74
|
+
|
|
56
75
|
### From the repo
|
|
57
76
|
|
|
58
77
|
```bash
|
|
@@ -63,6 +82,18 @@ npm run quickstart
|
|
|
63
82
|
|
|
64
83
|
`npm run quickstart` installs dependencies, prepares local config and runtime state, and starts SwarmClaw.
|
|
65
84
|
|
|
85
|
+
### Docker
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
git clone https://github.com/swarmclawai/swarmclaw.git
|
|
89
|
+
cd swarmclaw
|
|
90
|
+
mkdir -p data
|
|
91
|
+
touch .env.local
|
|
92
|
+
docker compose up -d --build
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Then open `http://localhost:3456`.
|
|
96
|
+
|
|
66
97
|
## Skill Drafts From Conversations
|
|
67
98
|
|
|
68
99
|
- From any active chat, use **Draft Skill** in the chat header.
|
|
@@ -77,11 +108,11 @@ npm run quickstart
|
|
|
77
108
|
|
|
78
109
|
- **Providers**: OpenClaw, OpenAI, Anthropic, Ollama, Google, DeepSeek, Groq, Together, Mistral, xAI, Fireworks, plus compatible custom endpoints.
|
|
79
110
|
- **Delegation**: built-in delegation to Claude Code, Codex CLI, OpenCode CLI, Gemini CLI, and native SwarmClaw subagents.
|
|
80
|
-
- **Autonomy**: heartbeat loops, schedules, background jobs, task execution, and agent wakeups.
|
|
81
|
-
- **Memory**: hybrid recall, graph traversal, journaling, durable documents,
|
|
111
|
+
- **Autonomy**: heartbeat loops, schedules, background jobs, task execution, supervisor recovery, and agent wakeups.
|
|
112
|
+
- **Memory**: hybrid recall, graph traversal, journaling, durable documents, project-scoped context, automatic reflection memory, communication preferences, profile and boundary memory, significant events, and open follow-up loops.
|
|
82
113
|
- **Wallets**: balances, transfers, signatures, EVM call/quote/swap flows, and approval-gated execution.
|
|
83
114
|
- **Connectors**: Discord, Slack, Telegram, WhatsApp, Teams, Matrix, OpenClaw, and more.
|
|
84
|
-
- **
|
|
115
|
+
- **Extensions**: external tool extensions, UI modules, hooks, and install/update flows.
|
|
85
116
|
|
|
86
117
|
## Requirements
|
|
87
118
|
|
|
@@ -102,7 +133,6 @@ npm run quickstart
|
|
|
102
133
|
- Getting started: https://swarmclaw.ai/docs/getting-started
|
|
103
134
|
- OpenClaw setup: https://swarmclaw.ai/docs/openclaw-setup
|
|
104
135
|
- Agents: https://swarmclaw.ai/docs/agents
|
|
105
|
-
- Orchestration: https://swarmclaw.ai/docs/orchestration
|
|
106
136
|
- Connectors: https://swarmclaw.ai/docs/connectors
|
|
107
|
-
-
|
|
137
|
+
- Extensions: https://swarmclaw.ai/docs/extensions
|
|
108
138
|
- CLI reference: https://swarmclaw.ai/docs/cli
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swarmclawai/swarmclaw",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Self-hosted AI
|
|
3
|
+
"version": "1.0.7",
|
|
4
|
+
"description": "Self-hosted AI runtime for OpenClaw, delegation, autonomy, runtime skills, crypto wallets, and chat platform connectors.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public",
|
|
@@ -12,15 +12,15 @@
|
|
|
12
12
|
"url": "git+https://github.com/swarmclawai/swarmclaw.git"
|
|
13
13
|
},
|
|
14
14
|
"keywords": [
|
|
15
|
-
"ai",
|
|
16
|
-
"agents",
|
|
17
|
-
"llm",
|
|
18
|
-
"orchestration",
|
|
19
|
-
"swarm",
|
|
20
15
|
"openclaw",
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
16
|
+
"crustacean",
|
|
17
|
+
"clawd",
|
|
18
|
+
"clawdbot",
|
|
19
|
+
"moltbot",
|
|
20
|
+
"openclaw-skill",
|
|
21
|
+
"openclaw-dashboard",
|
|
22
|
+
"openclaw-gateway",
|
|
23
|
+
"clawdbot-dashboard"
|
|
24
24
|
],
|
|
25
25
|
"engines": {
|
|
26
26
|
"node": ">=22.6.0"
|
|
@@ -21,6 +21,8 @@ const ACTION_COLORS: Record<string, string> = {
|
|
|
21
21
|
queued: 'bg-amber-500/15 text-amber-400',
|
|
22
22
|
completed: 'bg-emerald-500/15 text-emerald-400',
|
|
23
23
|
failed: 'bg-red-500/15 text-red-400',
|
|
24
|
+
archived: 'bg-white/[0.06] text-text-3',
|
|
25
|
+
restored: 'bg-sky-500/15 text-sky-400',
|
|
24
26
|
approved: 'bg-green-500/15 text-green-400',
|
|
25
27
|
rejected: 'bg-red-500/15 text-red-400',
|
|
26
28
|
}
|
|
@@ -14,7 +14,7 @@ export async function POST(_req: Request, { params }: { params: Promise<{ id: st
|
|
|
14
14
|
const now = Date.now()
|
|
15
15
|
|
|
16
16
|
// Deep-copy the source agent, then override clone-specific fields
|
|
17
|
-
const cloned = JSON.parse(JSON.stringify(source)) as
|
|
17
|
+
const cloned = JSON.parse(JSON.stringify(source)) as typeof source
|
|
18
18
|
cloned.id = newId
|
|
19
19
|
cloned.name = `${source.name} (Copy)`
|
|
20
20
|
cloned.createdAt = now
|
|
@@ -6,6 +6,7 @@ import { ensureAgentThreadSession } from '@/lib/server/agents/agent-thread-sessi
|
|
|
6
6
|
import { suspendAgentReferences } from '@/lib/server/agents/agent-cascade'
|
|
7
7
|
import { notify } from '@/lib/server/ws-hub'
|
|
8
8
|
import { normalizeAgentSandboxConfig } from '@/lib/agent-sandbox-defaults'
|
|
9
|
+
import { normalizeCapabilitySelection } from '@/lib/capability-selection'
|
|
9
10
|
|
|
10
11
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
11
12
|
const ops: CollectionOps<any> = { load: () => loadAgents({ includeTrashed: true }), save: saveAgents, topic: 'agents', table: 'agents' }
|
|
@@ -15,15 +16,27 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
|
|
|
15
16
|
const body = await req.json()
|
|
16
17
|
const result = mutateItem(ops, id, (agent) => {
|
|
17
18
|
Object.assign(agent, body, { updatedAt: Date.now() })
|
|
18
|
-
if (
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
if (body.tools !== undefined || body.extensions !== undefined) {
|
|
20
|
+
const nextSelection = normalizeCapabilitySelection({
|
|
21
|
+
tools: Array.isArray(body.tools) ? body.tools : agent.tools,
|
|
22
|
+
extensions: Array.isArray(body.extensions) ? body.extensions : agent.extensions,
|
|
23
|
+
})
|
|
24
|
+
agent.tools = nextSelection.tools
|
|
25
|
+
agent.extensions = nextSelection.extensions
|
|
21
26
|
}
|
|
22
|
-
if (body.
|
|
23
|
-
agent.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
agent.
|
|
27
|
+
if (body.delegationEnabled !== undefined) {
|
|
28
|
+
agent.delegationEnabled = body.delegationEnabled === true
|
|
29
|
+
}
|
|
30
|
+
if (body.delegationTargetMode === 'all' || body.delegationTargetMode === 'selected') {
|
|
31
|
+
agent.delegationTargetMode = body.delegationTargetMode
|
|
32
|
+
}
|
|
33
|
+
if (body.delegationTargetAgentIds !== undefined) {
|
|
34
|
+
agent.delegationTargetAgentIds = Array.isArray(body.delegationTargetAgentIds)
|
|
35
|
+
? body.delegationTargetAgentIds.filter((entry: unknown): entry is string => typeof entry === 'string' && entry.trim().length > 0)
|
|
36
|
+
: []
|
|
37
|
+
}
|
|
38
|
+
if (agent.delegationTargetMode !== 'selected') {
|
|
39
|
+
agent.delegationTargetAgentIds = []
|
|
27
40
|
}
|
|
28
41
|
if (body.apiEndpoint !== undefined) {
|
|
29
42
|
agent.apiEndpoint = normalizeProviderEndpoint(
|
|
@@ -67,8 +80,9 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
|
|
|
67
80
|
priority: typeof target.priority === 'number' ? target.priority : index + 1,
|
|
68
81
|
}))
|
|
69
82
|
}
|
|
83
|
+
delete (agent as Record<string, unknown>).platformAssignScope
|
|
84
|
+
delete (agent as Record<string, unknown>).subAgentIds
|
|
70
85
|
delete (agent as Record<string, unknown>).isOrchestrator
|
|
71
|
-
agent.isOrchestrator = agent.platformAssignScope === 'all'
|
|
72
86
|
delete (agent as Record<string, unknown>).id
|
|
73
87
|
agent.id = id
|
|
74
88
|
return agent
|
|
@@ -113,11 +127,11 @@ export async function DELETE(_req: Request, { params }: { params: Promise<{ id:
|
|
|
113
127
|
// Detach sessions from the trashed agent
|
|
114
128
|
const sessions = loadSessions()
|
|
115
129
|
const detached: Array<[string, unknown]> = []
|
|
116
|
-
for (const session of Object.values(sessions)
|
|
130
|
+
for (const session of Object.values(sessions)) {
|
|
117
131
|
if (!session || session.agentId !== id) continue
|
|
118
132
|
session.agentId = null
|
|
119
133
|
session.heartbeatEnabled = false
|
|
120
|
-
detached.push([session.id
|
|
134
|
+
detached.push([session.id, session])
|
|
121
135
|
}
|
|
122
136
|
if (detached.length > 0) {
|
|
123
137
|
upsertStoredItems('sessions', detached)
|
|
@@ -5,7 +5,7 @@ import { loadAgents, loadSessions, loadUsage, logActivity, upsertStoredItem } fr
|
|
|
5
5
|
import { normalizeProviderEndpoint } from '@/lib/openclaw/openclaw-endpoint'
|
|
6
6
|
import { notify } from '@/lib/server/ws-hub'
|
|
7
7
|
import { getAgentSpendWindows } from '@/lib/server/cost'
|
|
8
|
-
import {
|
|
8
|
+
import { resolveAgentToolSelection } from '@/lib/agent-default-tools'
|
|
9
9
|
import { normalizeAgentSandboxConfig } from '@/lib/agent-sandbox-defaults'
|
|
10
10
|
import { AgentCreateSchema, formatZodError } from '@/lib/validation/schemas'
|
|
11
11
|
import { z } from 'zod'
|
|
@@ -22,9 +22,6 @@ export async function GET(req: Request) {
|
|
|
22
22
|
const agents = loadAgents()
|
|
23
23
|
const sessions = loadSessions()
|
|
24
24
|
const usage = loadUsage()
|
|
25
|
-
for (const agent of Object.values(agents)) {
|
|
26
|
-
agent.isOrchestrator = agent.platformAssignScope === 'all'
|
|
27
|
-
}
|
|
28
25
|
// Enrich agents that have spend limits with current spend windows
|
|
29
26
|
for (const agent of Object.values(agents)) {
|
|
30
27
|
if (
|
|
@@ -32,7 +29,10 @@ export async function GET(req: Request) {
|
|
|
32
29
|
|| (typeof agent.dailyBudget === 'number' && agent.dailyBudget > 0)
|
|
33
30
|
|| (typeof agent.hourlyBudget === 'number' && agent.hourlyBudget > 0)
|
|
34
31
|
) {
|
|
35
|
-
const spend = getAgentSpendWindows(agent.id, Date.now(), {
|
|
32
|
+
const spend = getAgentSpendWindows(agent.id, Date.now(), {
|
|
33
|
+
sessions: sessions as unknown as Record<string, Record<string, unknown>>,
|
|
34
|
+
usage,
|
|
35
|
+
})
|
|
36
36
|
if (typeof agent.monthlyBudget === 'number' && agent.monthlyBudget > 0) agent.monthlySpend = spend.monthly
|
|
37
37
|
if (typeof agent.dailyBudget === 'number' && agent.dailyBudget > 0) agent.dailySpend = spend.daily
|
|
38
38
|
if (typeof agent.hourlyBudget === 'number' && agent.hourlyBudget > 0) agent.hourlySpend = spend.hourly
|
|
@@ -63,15 +63,14 @@ export async function POST(req: Request) {
|
|
|
63
63
|
return NextResponse.json(formatZodError(parsed.error as z.ZodError), { status: 400 })
|
|
64
64
|
}
|
|
65
65
|
const body = parsed.data
|
|
66
|
-
const
|
|
67
|
-
hasExplicitPlugins: Boolean(rawRecord && Object.prototype.hasOwnProperty.call(rawRecord, 'plugins')),
|
|
66
|
+
const capabilitySelection = resolveAgentToolSelection({
|
|
68
67
|
hasExplicitTools: Boolean(rawRecord && Object.prototype.hasOwnProperty.call(rawRecord, 'tools')),
|
|
69
|
-
|
|
68
|
+
hasExplicitExtensions: Boolean(rawRecord && Object.prototype.hasOwnProperty.call(rawRecord, 'extensions')),
|
|
70
69
|
tools: body.tools,
|
|
70
|
+
extensions: body.extensions,
|
|
71
71
|
})
|
|
72
72
|
const id = genId()
|
|
73
73
|
const now = Date.now()
|
|
74
|
-
const platformAssignScope = body.platformAssignScope
|
|
75
74
|
const agent = {
|
|
76
75
|
id,
|
|
77
76
|
name: body.name,
|
|
@@ -91,10 +90,11 @@ export async function POST(req: Request) {
|
|
|
91
90
|
...target,
|
|
92
91
|
apiEndpoint: normalizeProviderEndpoint(target.provider, target.apiEndpoint || null),
|
|
93
92
|
})),
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
93
|
+
delegationEnabled: body.delegationEnabled ?? false,
|
|
94
|
+
delegationTargetMode: body.delegationTargetMode ?? 'all',
|
|
95
|
+
delegationTargetAgentIds: (body.delegationTargetMode === 'selected' ? body.delegationTargetAgentIds : []).filter(Boolean),
|
|
96
|
+
tools: capabilitySelection.tools,
|
|
97
|
+
extensions: capabilitySelection.extensions,
|
|
98
98
|
skills: body.skills,
|
|
99
99
|
skillIds: body.skillIds,
|
|
100
100
|
mcpServerIds: body.mcpServerIds,
|
|
@@ -103,7 +103,7 @@ export async function POST(req: Request) {
|
|
|
103
103
|
thinkingLevel: body.thinkingLevel || undefined,
|
|
104
104
|
autoRecovery: body.autoRecovery || false,
|
|
105
105
|
disabled: body.disabled || false,
|
|
106
|
-
heartbeatEnabled: body.heartbeatEnabled
|
|
106
|
+
heartbeatEnabled: body.heartbeatEnabled ?? true,
|
|
107
107
|
heartbeatInterval: body.heartbeatInterval,
|
|
108
108
|
heartbeatIntervalSec: body.heartbeatIntervalSec,
|
|
109
109
|
heartbeatModel: body.heartbeatModel,
|
|
@@ -116,6 +116,7 @@ export async function POST(req: Request) {
|
|
|
116
116
|
identityState: body.identityState ?? null,
|
|
117
117
|
memoryScopeMode: body.memoryScopeMode,
|
|
118
118
|
memoryTierPreference: body.memoryTierPreference,
|
|
119
|
+
proactiveMemory: body.proactiveMemory ?? true,
|
|
119
120
|
autoDraftSkillSuggestions: body.autoDraftSkillSuggestions,
|
|
120
121
|
projectId: body.projectId,
|
|
121
122
|
avatarSeed: body.avatarSeed,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
|
|
3
|
+
import { listSupervisorIncidents } from '@/lib/server/autonomy/supervisor-reflection'
|
|
4
|
+
|
|
5
|
+
export const dynamic = 'force-dynamic'
|
|
6
|
+
|
|
7
|
+
function parseLimit(value: string | null): number | undefined {
|
|
8
|
+
if (!value) return undefined
|
|
9
|
+
const parsed = Number.parseInt(value, 10)
|
|
10
|
+
return Number.isFinite(parsed) ? parsed : undefined
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function GET(req: Request) {
|
|
14
|
+
const url = new URL(req.url)
|
|
15
|
+
const sessionId = url.searchParams.get('sessionId') || undefined
|
|
16
|
+
const taskId = url.searchParams.get('taskId') || undefined
|
|
17
|
+
const limit = parseLimit(url.searchParams.get('limit'))
|
|
18
|
+
return NextResponse.json(listSupervisorIncidents({ sessionId, taskId, limit }))
|
|
19
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
|
|
3
|
+
import { listRunReflections } from '@/lib/server/autonomy/supervisor-reflection'
|
|
4
|
+
|
|
5
|
+
export const dynamic = 'force-dynamic'
|
|
6
|
+
|
|
7
|
+
function parseLimit(value: string | null): number | undefined {
|
|
8
|
+
if (!value) return undefined
|
|
9
|
+
const parsed = Number.parseInt(value, 10)
|
|
10
|
+
return Number.isFinite(parsed) ? parsed : undefined
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function GET(req: Request) {
|
|
14
|
+
const url = new URL(req.url)
|
|
15
|
+
const sessionId = url.searchParams.get('sessionId') || undefined
|
|
16
|
+
const taskId = url.searchParams.get('taskId') || undefined
|
|
17
|
+
const limit = parseLimit(url.searchParams.get('limit'))
|
|
18
|
+
return NextResponse.json(listRunReflections({ sessionId, taskId, limit }))
|
|
19
|
+
}
|
|
@@ -11,7 +11,7 @@ export async function GET(_req: Request, { params }: { params: Promise<{ session
|
|
|
11
11
|
|
|
12
12
|
return NextResponse.json({
|
|
13
13
|
sessionId,
|
|
14
|
-
content: (session as Record<string, unknown>).canvasContent || null,
|
|
14
|
+
content: (session as unknown as Record<string, unknown>).canvasContent || null,
|
|
15
15
|
})
|
|
16
16
|
}
|
|
17
17
|
|
|
@@ -23,7 +23,7 @@ export async function POST(req: Request, { params }: { params: Promise<{ session
|
|
|
23
23
|
if (!session) return NextResponse.json({ error: 'Session not found' }, { status: 404 })
|
|
24
24
|
|
|
25
25
|
const nextContent = normalizeCanvasContent(body.document ?? body.content)
|
|
26
|
-
;(session as Record<string, unknown>).canvasContent = nextContent
|
|
26
|
+
;(session as unknown as Record<string, unknown>).canvasContent = nextContent
|
|
27
27
|
session.lastActiveAt = Date.now()
|
|
28
28
|
sessions[sessionId] = session
|
|
29
29
|
saveSessions(sessions)
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
import { filterHealthyChatroomAgents } from '@/lib/server/chatrooms/chatroom-health'
|
|
22
22
|
import { evaluateRoutingRules } from '@/lib/server/chatrooms/chatroom-routing'
|
|
23
23
|
import { markProviderFailure, markProviderSuccess } from '@/lib/server/provider-health'
|
|
24
|
-
import { applyAgentReactionsFromText } from '@/lib/server/chatrooms/chatroom-
|
|
24
|
+
import { applyAgentReactionsFromText } from '@/lib/server/chatrooms/chatroom-agent-signals'
|
|
25
25
|
import { resolvePrimaryAgentRoute } from '@/lib/server/agents/agent-runtime-config'
|
|
26
26
|
import { shouldSuppressHiddenControlText, stripHiddenControlTokens } from '@/lib/server/agents/assistant-control'
|
|
27
27
|
import type { Chatroom, ChatroomMessage, Agent } from '@/types'
|
|
@@ -5,7 +5,7 @@ import { normalizeProviderEndpoint } from '@/lib/openclaw/openclaw-endpoint'
|
|
|
5
5
|
import { resolvePrimaryAgentRoute } from '@/lib/server/agents/agent-runtime-config'
|
|
6
6
|
import { clearMainLoopStateForSession } from '@/lib/server/agents/main-agent-loop'
|
|
7
7
|
import { getSessionRunState } from '@/lib/server/runtime/session-run-manager'
|
|
8
|
-
import
|
|
8
|
+
import { normalizeCapabilitySelection } from '@/lib/capability-selection'
|
|
9
9
|
|
|
10
10
|
export async function GET(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
11
11
|
const { id } = await params
|
|
@@ -79,8 +79,18 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
|
|
|
79
79
|
session.routePreferredGatewayUseCase = routePreferredGatewayUseCase
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
if (updates.
|
|
83
|
-
|
|
82
|
+
if (updates.tools !== undefined || updates.extensions !== undefined || (agentIdUpdateProvided && linkedAgent)) {
|
|
83
|
+
const nextSelection = normalizeCapabilitySelection({
|
|
84
|
+
tools: Array.isArray(updates.tools)
|
|
85
|
+
? updates.tools
|
|
86
|
+
: (agentIdUpdateProvided && linkedAgent ? linkedAgent.tools : session.tools as string[] | undefined),
|
|
87
|
+
extensions: Array.isArray(updates.extensions)
|
|
88
|
+
? updates.extensions
|
|
89
|
+
: (agentIdUpdateProvided && linkedAgent ? linkedAgent.extensions : session.extensions as string[] | undefined),
|
|
90
|
+
})
|
|
91
|
+
session.tools = nextSelection.tools
|
|
92
|
+
session.extensions = nextSelection.extensions
|
|
93
|
+
}
|
|
84
94
|
|
|
85
95
|
if (updates.apiEndpoint !== undefined) {
|
|
86
96
|
session.apiEndpoint = normalizeProviderEndpoint(
|
|
@@ -12,6 +12,7 @@ import { applyResolvedRoute, resolvePrimaryAgentRoute } from '@/lib/server/agent
|
|
|
12
12
|
import { buildAgentDisabledMessage, isAgentDisabled } from '@/lib/server/agents/agent-availability'
|
|
13
13
|
import { materializeStreamingAssistantArtifacts } from '@/lib/chat/chat-streaming-state'
|
|
14
14
|
import { buildSessionListSummary } from '@/lib/chat/session-summary'
|
|
15
|
+
import { normalizeCapabilitySelection } from '@/lib/capability-selection'
|
|
15
16
|
export const dynamic = 'force-dynamic'
|
|
16
17
|
|
|
17
18
|
async function ensureDaemonIfNeeded(source: string) {
|
|
@@ -22,6 +23,12 @@ async function ensureDaemonIfNeeded(source: string) {
|
|
|
22
23
|
|
|
23
24
|
export async function GET(req: Request) {
|
|
24
25
|
const endPerf = perf.start('api', 'GET /api/chats')
|
|
26
|
+
try {
|
|
27
|
+
const { pruneThreadConnectorMirrors } = await import('@/lib/server/connectors/session-consolidation')
|
|
28
|
+
pruneThreadConnectorMirrors()
|
|
29
|
+
} catch (err) {
|
|
30
|
+
console.error('[api/chats] pruneThreadConnectorMirrors failed:', err)
|
|
31
|
+
}
|
|
25
32
|
const sessions = loadSessions()
|
|
26
33
|
const changedSessionIds: string[] = []
|
|
27
34
|
for (const id of Object.keys(sessions)) {
|
|
@@ -105,8 +112,10 @@ export async function POST(req: Request) {
|
|
|
105
112
|
preferredGatewayTags: routePreferredGatewayTags,
|
|
106
113
|
preferredGatewayUseCase: routePreferredGatewayUseCase,
|
|
107
114
|
}) : null
|
|
108
|
-
const
|
|
109
|
-
|
|
115
|
+
const resolvedCapabilities = normalizeCapabilitySelection({
|
|
116
|
+
tools: Array.isArray(body.tools) ? body.tools : agent?.tools,
|
|
117
|
+
extensions: Array.isArray(body.extensions) ? body.extensions : agent?.extensions,
|
|
118
|
+
})
|
|
110
119
|
|
|
111
120
|
// If session with this ID already exists, return it as-is
|
|
112
121
|
if (body.id && sessions[id]) {
|
|
@@ -142,7 +151,8 @@ export async function POST(req: Request) {
|
|
|
142
151
|
sessionType: body.sessionType || 'human',
|
|
143
152
|
agentId: body.agentId || null,
|
|
144
153
|
parentSessionId: body.parentSessionId || null,
|
|
145
|
-
|
|
154
|
+
tools: resolvedCapabilities.tools,
|
|
155
|
+
extensions: resolvedCapabilities.extensions,
|
|
146
156
|
heartbeatEnabled: body.heartbeatEnabled ?? null,
|
|
147
157
|
heartbeatIntervalSec: body.heartbeatIntervalSec ?? null,
|
|
148
158
|
sessionResetMode: body.sessionResetMode ?? agent?.sessionResetMode ?? null,
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
import os from 'node:os'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
import { spawnSync } from 'node:child_process'
|
|
6
|
+
import test from 'node:test'
|
|
7
|
+
|
|
8
|
+
const repoRoot = path.resolve(path.dirname(new URL(import.meta.url).pathname), '../../../../..')
|
|
9
|
+
|
|
10
|
+
function runWithTempDataDir(script: string) {
|
|
11
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'swarmclaw-clawhub-install-route-'))
|
|
12
|
+
try {
|
|
13
|
+
const result = spawnSync(process.execPath, ['--import', 'tsx', '--input-type=module', '--eval', script], {
|
|
14
|
+
cwd: repoRoot,
|
|
15
|
+
env: {
|
|
16
|
+
...process.env,
|
|
17
|
+
DATA_DIR: path.join(tempDir, 'data'),
|
|
18
|
+
WORKSPACE_DIR: path.join(tempDir, 'workspace'),
|
|
19
|
+
SWARMCLAW_HOME: path.join(tempDir, 'swarmclaw-home'),
|
|
20
|
+
},
|
|
21
|
+
encoding: 'utf-8',
|
|
22
|
+
})
|
|
23
|
+
assert.equal(result.status, 0, result.stderr || result.stdout || 'subprocess failed')
|
|
24
|
+
const lines = (result.stdout || '')
|
|
25
|
+
.trim()
|
|
26
|
+
.split('\n')
|
|
27
|
+
.map((line) => line.trim())
|
|
28
|
+
.filter(Boolean)
|
|
29
|
+
const jsonLine = [...lines].reverse().find((line) => line.startsWith('{'))
|
|
30
|
+
return JSON.parse(jsonLine || '{}')
|
|
31
|
+
} finally {
|
|
32
|
+
fs.rmSync(tempDir, { recursive: true, force: true })
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
test('POST /api/clawhub/install materializes bundle files into the workspace skills directory', () => {
|
|
37
|
+
const output = runWithTempDataDir(`
|
|
38
|
+
const fs = await import('node:fs')
|
|
39
|
+
const path = await import('node:path')
|
|
40
|
+
const JSZip = (await import('jszip')).default
|
|
41
|
+
const storageMod = await import('./src/lib/server/storage')
|
|
42
|
+
const routeMod = await import('./src/app/api/clawhub/install/route')
|
|
43
|
+
const storage = storageMod.default || storageMod
|
|
44
|
+
const route = routeMod.default || routeMod
|
|
45
|
+
|
|
46
|
+
const archive = new JSZip()
|
|
47
|
+
archive.file('SKILL.md', \`---
|
|
48
|
+
name: test-hub-skill
|
|
49
|
+
description: A ClawHub test skill.
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
# Test Hub Skill
|
|
53
|
+
|
|
54
|
+
Use this skill when the user asks for a ClawHub installation test.
|
|
55
|
+
\`)
|
|
56
|
+
archive.file('scripts/run.sh', '#!/bin/sh\\necho hi\\n')
|
|
57
|
+
archive.file('references/notes.md', '# Notes\\n')
|
|
58
|
+
const zipBuffer = await archive.generateAsync({ type: 'nodebuffer' })
|
|
59
|
+
|
|
60
|
+
globalThis.fetch = async () => new Response(zipBuffer, {
|
|
61
|
+
status: 200,
|
|
62
|
+
headers: { 'content-type': 'application/zip' },
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const response = await route.POST(new Request('http://local/api/clawhub/install', {
|
|
66
|
+
method: 'POST',
|
|
67
|
+
headers: { 'content-type': 'application/json' },
|
|
68
|
+
body: JSON.stringify({
|
|
69
|
+
name: 'test-hub-skill',
|
|
70
|
+
description: 'A ClawHub test skill.',
|
|
71
|
+
url: 'https://clawhub.ai/skills/test-hub-skill',
|
|
72
|
+
author: 'ClawHub',
|
|
73
|
+
tags: ['test'],
|
|
74
|
+
}),
|
|
75
|
+
}))
|
|
76
|
+
const payload = await response.json()
|
|
77
|
+
const skillsDir = path.join(process.env.SWARMCLAW_HOME, 'skills', 'test-hub-skill')
|
|
78
|
+
const storedSkills = storage.loadSkills()
|
|
79
|
+
const stored = Object.values(storedSkills).find((skill) => skill.name === 'test-hub-skill')
|
|
80
|
+
|
|
81
|
+
console.log(JSON.stringify({
|
|
82
|
+
status: response.status,
|
|
83
|
+
installedName: payload?.name || null,
|
|
84
|
+
storedSkillId: stored?.id || null,
|
|
85
|
+
hasWorkspaceSkill: fs.existsSync(path.join(skillsDir, 'SKILL.md')),
|
|
86
|
+
hasWorkspaceScript: fs.existsSync(path.join(skillsDir, 'scripts', 'run.sh')),
|
|
87
|
+
hasWorkspaceReference: fs.existsSync(path.join(skillsDir, 'references', 'notes.md')),
|
|
88
|
+
}))
|
|
89
|
+
`)
|
|
90
|
+
|
|
91
|
+
assert.equal(output.status, 200)
|
|
92
|
+
assert.equal(output.installedName, 'test-hub-skill')
|
|
93
|
+
assert.notEqual(output.storedSkillId, null)
|
|
94
|
+
assert.equal(output.hasWorkspaceSkill, true)
|
|
95
|
+
assert.equal(output.hasWorkspaceScript, true)
|
|
96
|
+
assert.equal(output.hasWorkspaceReference, true)
|
|
97
|
+
})
|