@swarmclawai/swarmclaw 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -4
- package/bin/server-cmd.js +28 -19
- package/next.config.ts +13 -0
- package/package.json +3 -1
- package/src/app/api/agents/[id]/route.ts +39 -22
- package/src/app/api/agents/[id]/thread/route.ts +2 -2
- package/src/app/api/agents/route.ts +3 -2
- package/src/app/api/agents/trash/route.ts +44 -0
- package/src/app/api/clawhub/install/route.ts +2 -2
- package/src/app/api/connectors/[id]/route.ts +17 -7
- package/src/app/api/connectors/[id]/webhook/route.ts +103 -0
- package/src/app/api/connectors/route.ts +6 -3
- package/src/app/api/credentials/[id]/route.ts +2 -1
- package/src/app/api/credentials/route.ts +2 -2
- package/src/app/api/documents/route.ts +2 -2
- package/src/app/api/files/serve/route.ts +8 -0
- package/src/app/api/knowledge/[id]/route.ts +5 -4
- package/src/app/api/knowledge/upload/route.ts +2 -2
- package/src/app/api/mcp-servers/[id]/route.ts +11 -14
- package/src/app/api/mcp-servers/[id]/test/route.ts +2 -1
- package/src/app/api/mcp-servers/[id]/tools/route.ts +2 -1
- package/src/app/api/mcp-servers/route.ts +2 -2
- package/src/app/api/memory/[id]/route.ts +9 -8
- package/src/app/api/memory/route.ts +2 -2
- package/src/app/api/memory-images/[filename]/route.ts +2 -1
- package/src/app/api/openclaw/agent-files/route.ts +57 -0
- package/src/app/api/openclaw/approvals/route.ts +46 -0
- package/src/app/api/openclaw/config-sync/route.ts +33 -0
- package/src/app/api/openclaw/cron/route.ts +52 -0
- package/src/app/api/openclaw/directory/route.ts +27 -0
- package/src/app/api/openclaw/discover/route.ts +62 -0
- package/src/app/api/openclaw/dotenv-keys/route.ts +18 -0
- package/src/app/api/openclaw/exec-config/route.ts +41 -0
- package/src/app/api/openclaw/gateway/route.ts +72 -0
- package/src/app/api/openclaw/history/route.ts +109 -0
- package/src/app/api/openclaw/media/route.ts +53 -0
- package/src/app/api/openclaw/models/route.ts +12 -0
- package/src/app/api/openclaw/permissions/route.ts +39 -0
- package/src/app/api/openclaw/sandbox-env/route.ts +69 -0
- package/src/app/api/openclaw/skills/install/route.ts +32 -0
- package/src/app/api/openclaw/skills/remove/route.ts +24 -0
- package/src/app/api/openclaw/skills/route.ts +82 -0
- package/src/app/api/openclaw/sync/route.ts +31 -0
- package/src/app/api/orchestrator/run/route.ts +2 -2
- package/src/app/api/projects/[id]/route.ts +55 -0
- package/src/app/api/projects/route.ts +27 -0
- package/src/app/api/providers/[id]/models/route.ts +2 -1
- package/src/app/api/providers/[id]/route.ts +13 -15
- package/src/app/api/providers/route.ts +2 -2
- package/src/app/api/schedules/[id]/route.ts +16 -18
- package/src/app/api/schedules/[id]/run/route.ts +4 -3
- package/src/app/api/schedules/route.ts +2 -2
- package/src/app/api/secrets/[id]/route.ts +16 -17
- package/src/app/api/secrets/route.ts +2 -2
- package/src/app/api/sessions/[id]/clear/route.ts +2 -1
- package/src/app/api/sessions/[id]/deploy/route.ts +2 -1
- package/src/app/api/sessions/[id]/devserver/route.ts +2 -1
- package/src/app/api/sessions/[id]/edit-resend/route.ts +22 -0
- package/src/app/api/sessions/[id]/fork/route.ts +44 -0
- package/src/app/api/sessions/[id]/messages/route.ts +20 -2
- package/src/app/api/sessions/[id]/retry/route.ts +2 -1
- package/src/app/api/sessions/[id]/route.ts +14 -4
- package/src/app/api/sessions/route.ts +8 -4
- package/src/app/api/skills/[id]/route.ts +23 -21
- package/src/app/api/skills/import/route.ts +2 -2
- package/src/app/api/skills/route.ts +2 -2
- package/src/app/api/tasks/[id]/approve/route.ts +2 -1
- package/src/app/api/tasks/[id]/route.ts +6 -5
- package/src/app/api/tasks/route.ts +2 -2
- package/src/app/api/tts/stream/route.ts +48 -0
- package/src/app/api/upload/route.ts +2 -2
- package/src/app/api/uploads/[filename]/route.ts +4 -1
- package/src/app/api/webhooks/[id]/route.ts +29 -31
- package/src/app/api/webhooks/route.ts +2 -2
- package/src/app/globals.css +14 -0
- package/src/app/layout.tsx +5 -20
- package/src/app/page.tsx +3 -24
- package/src/cli/index.js +60 -0
- package/src/cli/index.ts +1 -1
- package/src/cli/spec.js +42 -0
- package/src/components/agents/agent-avatar.tsx +45 -0
- package/src/components/agents/agent-card.tsx +19 -5
- package/src/components/agents/agent-chat-list.tsx +31 -24
- package/src/components/agents/agent-files-editor.tsx +185 -0
- package/src/components/agents/agent-list.tsx +84 -3
- package/src/components/agents/agent-sheet.tsx +147 -14
- package/src/components/agents/cron-job-form.tsx +137 -0
- package/src/components/agents/exec-config-panel.tsx +147 -0
- package/src/components/agents/inspector-panel.tsx +310 -0
- package/src/components/agents/openclaw-skills-panel.tsx +230 -0
- package/src/components/agents/permission-preset-selector.tsx +79 -0
- package/src/components/agents/personality-builder.tsx +111 -0
- package/src/components/agents/sandbox-env-panel.tsx +72 -0
- package/src/components/agents/skill-install-dialog.tsx +102 -0
- package/src/components/agents/trash-list.tsx +109 -0
- package/src/components/chat/chat-area.tsx +41 -6
- package/src/components/chat/chat-header.tsx +305 -29
- package/src/components/chat/chat-preview-panel.tsx +113 -0
- package/src/components/chat/exec-approval-card.tsx +89 -0
- package/src/components/chat/message-bubble.tsx +218 -36
- package/src/components/chat/message-list.tsx +135 -31
- package/src/components/chat/streaming-bubble.tsx +59 -10
- package/src/components/chat/suggestions-bar.tsx +74 -0
- package/src/components/chat/thinking-indicator.tsx +20 -6
- package/src/components/chat/tool-call-bubble.tsx +98 -19
- package/src/components/chat/tool-request-banner.tsx +20 -2
- package/src/components/chat/trace-block.tsx +103 -0
- package/src/components/chat/voice-overlay.tsx +80 -0
- package/src/components/connectors/connector-list.tsx +6 -2
- package/src/components/connectors/connector-sheet.tsx +31 -7
- package/src/components/layout/app-layout.tsx +47 -25
- package/src/components/projects/project-list.tsx +123 -0
- package/src/components/projects/project-sheet.tsx +135 -0
- package/src/components/schedules/schedule-list.tsx +3 -1
- package/src/components/sessions/new-session-sheet.tsx +6 -6
- package/src/components/sessions/session-card.tsx +1 -1
- package/src/components/sessions/session-list.tsx +7 -7
- package/src/components/settings/gateway-connection-panel.tsx +278 -0
- package/src/components/shared/avatar.tsx +13 -2
- package/src/components/shared/connector-platform-icon.tsx +4 -0
- package/src/components/shared/settings/section-heartbeat.tsx +1 -1
- package/src/components/shared/settings/section-orchestrator.tsx +1 -2
- package/src/components/shared/settings/section-web-search.tsx +56 -0
- package/src/components/shared/settings/settings-page.tsx +74 -0
- package/src/components/skills/skill-list.tsx +2 -1
- package/src/components/tasks/task-board.tsx +1 -1
- package/src/components/tasks/task-list.tsx +5 -2
- package/src/components/tasks/task-sheet.tsx +12 -12
- package/src/hooks/use-continuous-speech.ts +181 -0
- package/src/hooks/use-openclaw-gateway.ts +63 -0
- package/src/hooks/use-view-router.ts +52 -0
- package/src/hooks/use-voice-conversation.ts +80 -0
- package/src/lib/id.ts +6 -0
- package/src/lib/notification-sounds.ts +58 -0
- package/src/lib/personality-parser.ts +97 -0
- package/src/lib/projects.ts +13 -0
- package/src/lib/provider-sets.ts +5 -0
- package/src/lib/providers/anthropic.ts +14 -1
- package/src/lib/providers/index.ts +6 -0
- package/src/lib/providers/ollama.ts +9 -1
- package/src/lib/providers/openai.ts +9 -1
- package/src/lib/providers/openclaw.ts +28 -2
- package/src/lib/runtime-loop.ts +2 -2
- package/src/lib/server/api-routes.test.ts +5 -6
- package/src/lib/server/build-llm.ts +17 -4
- package/src/lib/server/chat-execution.ts +82 -6
- package/src/lib/server/collection-helpers.ts +54 -0
- package/src/lib/server/connectors/bluebubbles.test.ts +217 -0
- package/src/lib/server/connectors/bluebubbles.ts +360 -0
- package/src/lib/server/connectors/connector-routing.test.ts +1 -1
- package/src/lib/server/connectors/googlechat.ts +51 -8
- package/src/lib/server/connectors/manager.ts +424 -13
- package/src/lib/server/connectors/media.ts +2 -2
- package/src/lib/server/connectors/openclaw.ts +65 -0
- package/src/lib/server/connectors/pairing.test.ts +99 -0
- package/src/lib/server/connectors/pairing.ts +256 -0
- package/src/lib/server/connectors/signal.ts +1 -0
- package/src/lib/server/connectors/teams.ts +5 -5
- package/src/lib/server/connectors/types.ts +10 -0
- package/src/lib/server/daemon-state.ts +11 -0
- package/src/lib/server/execution-log.ts +3 -3
- package/src/lib/server/heartbeat-service.ts +1 -1
- package/src/lib/server/knowledge-db.test.ts +2 -33
- package/src/lib/server/main-agent-loop.ts +8 -9
- package/src/lib/server/main-session.ts +21 -0
- package/src/lib/server/memory-db.ts +6 -6
- package/src/lib/server/openclaw-approvals.ts +105 -0
- package/src/lib/server/openclaw-config-sync.ts +107 -0
- package/src/lib/server/openclaw-exec-config.ts +52 -0
- package/src/lib/server/openclaw-gateway.ts +291 -0
- package/src/lib/server/openclaw-history-merge.ts +36 -0
- package/src/lib/server/openclaw-models.ts +56 -0
- package/src/lib/server/openclaw-permission-presets.ts +64 -0
- package/src/lib/server/openclaw-sync.ts +497 -0
- package/src/lib/server/orchestrator-lg.ts +30 -9
- package/src/lib/server/orchestrator.ts +4 -4
- package/src/lib/server/process-manager.ts +2 -2
- package/src/lib/server/queue.ts +24 -11
- package/src/lib/server/scheduler.ts +2 -2
- package/src/lib/server/session-mailbox.ts +2 -2
- package/src/lib/server/session-run-manager.ts +2 -2
- package/src/lib/server/session-tools/connector.ts +53 -6
- package/src/lib/server/session-tools/crud.ts +3 -3
- package/src/lib/server/session-tools/delegate.ts +22 -6
- package/src/lib/server/session-tools/file.ts +192 -19
- package/src/lib/server/session-tools/index.ts +4 -2
- package/src/lib/server/session-tools/memory.ts +2 -2
- package/src/lib/server/session-tools/openclaw-nodes.ts +112 -0
- package/src/lib/server/session-tools/sandbox.ts +33 -0
- package/src/lib/server/session-tools/search-providers.ts +277 -0
- package/src/lib/server/session-tools/session-info.ts +2 -2
- package/src/lib/server/session-tools/session-tools-wiring.test.ts +2 -2
- package/src/lib/server/session-tools/shell.ts +1 -1
- package/src/lib/server/session-tools/web.ts +53 -72
- package/src/lib/server/storage.ts +74 -11
- package/src/lib/server/stream-agent-chat.ts +53 -4
- package/src/lib/server/suggestions.ts +20 -0
- package/src/lib/server/task-result.test.ts +44 -0
- package/src/lib/server/task-result.ts +14 -0
- package/src/lib/server/ws-hub.ts +14 -0
- package/src/lib/tool-definitions.ts +5 -3
- package/src/lib/tts-stream.ts +130 -0
- package/src/lib/view-routes.ts +28 -0
- package/src/proxy.ts +3 -0
- package/src/stores/use-app-store.ts +80 -1
- package/src/stores/use-approval-store.ts +78 -0
- package/src/stores/use-chat-store.ts +162 -6
- package/src/types/index.ts +154 -3
- package/tsconfig.json +13 -4
package/README.md
CHANGED
|
@@ -25,7 +25,10 @@ Inspired by [OpenClaw](https://github.com/openclaw).
|
|
|
25
25
|
|
|
26
26
|
- **15 Built-in Providers** — Claude Code CLI, OpenAI Codex CLI, OpenCode CLI, Anthropic, OpenAI, Google Gemini, DeepSeek, Groq, Together AI, Mistral AI, xAI (Grok), Fireworks AI, Ollama, plus custom OpenAI-compatible endpoints
|
|
27
27
|
- **OpenClaw Gateway** — Per-agent toggle to connect any agent to a local or remote OpenClaw gateway. Each agent gets its own gateway URL and token — run a swarm of OpenClaws from one dashboard. The `openclaw` CLI ships as a bundled dependency (no separate install needed)
|
|
28
|
+
- **OpenClaw Control Plane** — Built-in gateway connection controls, reload mode switching (hot/hybrid/full), config issue detection/repair, remote history sync, and live execution approval handling
|
|
28
29
|
- **Agent Builder** — Create agents with custom personalities (soul), system prompts, tools, and skills. AI-powered generation from a description
|
|
30
|
+
- **Agent Inspector Panel** — Per-agent side panel for OpenClaw file editing (`SOUL.md`, `IDENTITY.md`, `USER.md`, etc.), guided personality editing, skill install/enable/remove, permission presets, sandbox env allowlist, and cron automations
|
|
31
|
+
- **Agent Fleet Management** — Avatar seeds with generated avatars, running/approval fleet filters, soft-delete agent trash with restore/permanent delete, and approval counters in agent cards
|
|
29
32
|
- **Agent Tools** — Shell, process control for long-running commands, files, edit file, send file, web search, web fetch, CLI delegation (Claude/Codex/OpenCode), Playwright browser automation, persistent memory, and sandboxed code execution (JS/TS via Deno, Python)
|
|
30
33
|
- **Platform Tools** — Agents can manage other agents, tasks, schedules, skills, connectors, sessions, and encrypted secrets via built-in platform tools
|
|
31
34
|
- **Orchestration** — Multi-agent workflows powered by LangGraph with automatic sub-agent routing, checkpointed execution, and rich delegation cards that link to sub-agent chat threads
|
|
@@ -35,8 +38,11 @@ Inspired by [OpenClaw](https://github.com/openclaw).
|
|
|
35
38
|
- **Scheduling** — Cron-based agent scheduling with human-friendly presets
|
|
36
39
|
- **Loop Runtime Controls** — Switch between bounded and ongoing loops with configurable step caps, runtime guards, heartbeat cadence, and timeout budgets
|
|
37
40
|
- **Session Run Queue** — Per-session queued runs with followup/steer/collect modes, collect coalescing for bursty inputs, and run-state APIs
|
|
41
|
+
- **Chat Iteration Workflow** — Edit-and-resend user turns, fork a new session from any message, bookmark key messages, use contextual follow-up suggestion chips, and auto-continue after tool access grants
|
|
42
|
+
- **Live Chat Telemetry** — Thinking/tool/responding stream phases, live main-loop status badges, connector activity presence, tone indicator, and optional sound notifications
|
|
43
|
+
- **Preview-Rich Chat UI** — Side preview panel for tool outputs (image/browser/html/code), inline code/PDF previews for attachments, and image lightbox support
|
|
38
44
|
- **Voice Settings** — Per-instance ElevenLabs API key + voice ID for TTS replies, plus configurable speech recognition language for chat input
|
|
39
|
-
- **Chat Connectors** — Bridge agents to Discord, Slack, Telegram, and
|
|
45
|
+
- **Chat Connectors** — Bridge agents to Discord, Slack, Telegram, WhatsApp, BlueBubbles (iMessage), Signal, Microsoft Teams, Google Chat, Matrix, and OpenClaw with media-aware inbound handling
|
|
40
46
|
- **Skills System** — Discover local skills, import skills from URL, and load OpenClaw `SKILL.md` files (frontmatter-compatible)
|
|
41
47
|
- **Execution Logging** — Structured audit trail for triggers, tool calls, file ops, commits, and errors in a dedicated `logs.db`
|
|
42
48
|
- **Context Management** — Auto-compaction of conversation history when approaching context limits, with manual `context_status` and `context_summarize` tools for agents
|
|
@@ -53,7 +59,7 @@ Inspired by [OpenClaw](https://github.com/openclaw).
|
|
|
53
59
|
|
|
54
60
|
## Requirements
|
|
55
61
|
|
|
56
|
-
- **Node.js**
|
|
62
|
+
- **Node.js** 22.6+
|
|
57
63
|
- **npm** 10+
|
|
58
64
|
- **Claude Code CLI** (optional, for `claude-cli` provider) — [Install](https://docs.anthropic.com/en/docs/claude-code/overview)
|
|
59
65
|
- **OpenAI Codex CLI** (optional, for `codex-cli` provider) — [Install](https://github.com/openai/codex)
|
|
@@ -75,7 +81,7 @@ curl -fsSL https://raw.githubusercontent.com/swarmclawai/swarmclaw/main/install.
|
|
|
75
81
|
```
|
|
76
82
|
|
|
77
83
|
The installer resolves the latest stable release tag and installs that version by default.
|
|
78
|
-
To pin a version: `SWARMCLAW_VERSION=v0.
|
|
84
|
+
To pin a version: `SWARMCLAW_VERSION=v0.5.0 curl ... | bash`
|
|
79
85
|
|
|
80
86
|
Or run locally from the repo (friendly for non-technical users):
|
|
81
87
|
|
|
@@ -229,6 +235,12 @@ Bridge any agent to a chat platform:
|
|
|
229
235
|
| Slack | @slack/bolt | Bot token + app token (Socket Mode) |
|
|
230
236
|
| Telegram | grammy | Bot token from @BotFather |
|
|
231
237
|
| WhatsApp | baileys | QR code pairing (shown in browser) |
|
|
238
|
+
| BlueBubbles | Custom webhook bridge | Server URL + password/webhook secret |
|
|
239
|
+
| Signal | signal-cli | `signal-cli` binary + linked phone |
|
|
240
|
+
| Microsoft Teams | botbuilder | Bot Framework credentials + webhook ingress |
|
|
241
|
+
| Google Chat | googleapis | Service account + webhook ingress |
|
|
242
|
+
| Matrix | matrix-bot-sdk | Homeserver URL + access token |
|
|
243
|
+
| OpenClaw | gateway protocol | OpenClaw connector credentials |
|
|
232
244
|
|
|
233
245
|
Connector sessions preserve attachment visibility in chat context:
|
|
234
246
|
- WhatsApp media is decoded and persisted to `/api/uploads/...` when possible
|
|
@@ -237,7 +249,12 @@ Connector sessions preserve attachment visibility in chat context:
|
|
|
237
249
|
|
|
238
250
|
Agents automatically suppress replies to simple acknowledgments ("ok", "thanks", thumbs-up, etc.) via a `NO_MESSAGE` response — conversations feel natural without a forced reply to every message. This is handled at the connector layer, so agents can return `NO_MESSAGE` as their response content and the platform won't deliver anything to the channel.
|
|
239
251
|
|
|
240
|
-
For proactive outreach, `connector_message_tool` supports text plus optional `imageUrl` / `fileUrl` / `mediaPath` (local file path) payloads.
|
|
252
|
+
For proactive outreach, `connector_message_tool` supports text plus optional `imageUrl` / `fileUrl` / `mediaPath` (local file path) payloads. WhatsApp, Discord, Slack, and Telegram support local file sending via `mediaPath` with auto-detected MIME types.
|
|
253
|
+
|
|
254
|
+
Connector ingress now also supports optional pairing/allowlist policy:
|
|
255
|
+
- `dmPolicy: allowlist` blocks unknown senders until approved
|
|
256
|
+
- `/pair` flow lets approved admins generate and approve pairing codes
|
|
257
|
+
- `/think` command can set connector thread thinking level (`low`, `medium`, `high`)
|
|
241
258
|
|
|
242
259
|
## Agent Tools
|
|
243
260
|
|
package/bin/server-cmd.js
CHANGED
|
@@ -17,14 +17,15 @@ const PID_FILE = path.join(SWARMCLAW_HOME, 'server.pid')
|
|
|
17
17
|
const LOG_FILE = path.join(SWARMCLAW_HOME, 'server.log')
|
|
18
18
|
const DATA_DIR = path.join(SWARMCLAW_HOME, 'data')
|
|
19
19
|
|
|
20
|
-
// Files/directories to
|
|
21
|
-
const
|
|
20
|
+
// Files/directories to copy from the npm package into SWARMCLAW_HOME
|
|
21
|
+
const BUILD_COPY_ENTRIES = [
|
|
22
22
|
'src',
|
|
23
23
|
'public',
|
|
24
24
|
'next.config.ts',
|
|
25
25
|
'tsconfig.json',
|
|
26
26
|
'postcss.config.mjs',
|
|
27
27
|
'package.json',
|
|
28
|
+
'package-lock.json',
|
|
28
29
|
]
|
|
29
30
|
|
|
30
31
|
// ---------------------------------------------------------------------------
|
|
@@ -61,16 +62,13 @@ function isProcessRunning(pid) {
|
|
|
61
62
|
}
|
|
62
63
|
}
|
|
63
64
|
|
|
64
|
-
function
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
} catch {
|
|
72
|
-
// doesn't exist
|
|
73
|
-
}
|
|
65
|
+
function copyPath(src, dest, { dereference = true } = {}) {
|
|
66
|
+
fs.rmSync(dest, { recursive: true, force: true })
|
|
67
|
+
fs.cpSync(src, dest, { recursive: true, dereference })
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function symlinkPath(src, dest) {
|
|
71
|
+
fs.rmSync(dest, { recursive: true, force: true })
|
|
74
72
|
fs.symlinkSync(src, dest)
|
|
75
73
|
}
|
|
76
74
|
|
|
@@ -89,8 +87,9 @@ function runBuild() {
|
|
|
89
87
|
ensureDir(SWARMCLAW_HOME)
|
|
90
88
|
ensureDir(DATA_DIR)
|
|
91
89
|
|
|
92
|
-
//
|
|
93
|
-
|
|
90
|
+
// Copy source/config into SWARMCLAW_HOME. Turbopack build currently rejects
|
|
91
|
+
// app source symlinks that point outside the workspace root.
|
|
92
|
+
for (const entry of BUILD_COPY_ENTRIES) {
|
|
94
93
|
const src = path.join(PKG_ROOT, entry)
|
|
95
94
|
const dest = path.join(SWARMCLAW_HOME, entry)
|
|
96
95
|
|
|
@@ -99,23 +98,33 @@ function runBuild() {
|
|
|
99
98
|
continue
|
|
100
99
|
}
|
|
101
100
|
|
|
102
|
-
|
|
101
|
+
copyPath(src, dest)
|
|
103
102
|
}
|
|
104
103
|
|
|
105
|
-
//
|
|
104
|
+
// Reuse package dependencies via symlink to avoid multi-GB duplication in
|
|
105
|
+
// SWARMCLAW_HOME. Build runs with webpack mode for symlink compatibility.
|
|
106
106
|
const nmSrc = path.join(PKG_ROOT, 'node_modules')
|
|
107
107
|
const nmDest = path.join(SWARMCLAW_HOME, 'node_modules')
|
|
108
108
|
if (fs.existsSync(nmSrc)) {
|
|
109
|
-
|
|
109
|
+
symlinkPath(nmSrc, nmDest)
|
|
110
110
|
} else {
|
|
111
111
|
// If node_modules doesn't exist at PKG_ROOT, install
|
|
112
112
|
log('Installing dependencies...')
|
|
113
|
-
execSync('npm install
|
|
113
|
+
execSync('npm install', { cwd: SWARMCLAW_HOME, stdio: 'inherit' })
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
// Run Next.js build
|
|
117
117
|
log('Building Next.js application (this may take a minute)...')
|
|
118
|
-
|
|
118
|
+
// Use webpack for production build reliability in packaged/fresh-install
|
|
119
|
+
// environments (Turbopack has intermittently failed during prerender).
|
|
120
|
+
execSync('npx next build --webpack', {
|
|
121
|
+
cwd: SWARMCLAW_HOME,
|
|
122
|
+
stdio: 'inherit',
|
|
123
|
+
env: {
|
|
124
|
+
...process.env,
|
|
125
|
+
SWARMCLAW_BUILD_MODE: '1',
|
|
126
|
+
},
|
|
127
|
+
})
|
|
119
128
|
|
|
120
129
|
// Write built marker
|
|
121
130
|
fs.writeFileSync(BUILT_MARKER, JSON.stringify({ builtAt: new Date().toISOString(), version: getVersion() }))
|
package/next.config.ts
CHANGED
|
@@ -11,6 +11,11 @@ function getGitSha(): string {
|
|
|
11
11
|
|
|
12
12
|
const nextConfig: NextConfig = {
|
|
13
13
|
output: 'standalone',
|
|
14
|
+
experimental: {
|
|
15
|
+
// Disable Turbopack persistent cache — concurrent HMR writes cause
|
|
16
|
+
// "Another write batch or compaction is already active" errors
|
|
17
|
+
turbopackFileSystemCacheForDev: false,
|
|
18
|
+
},
|
|
14
19
|
env: {
|
|
15
20
|
NEXT_PUBLIC_GIT_SHA: getGitSha(),
|
|
16
21
|
NEXT_PUBLIC_WS_PORT: String((Number(process.env.PORT) || 3456) + 1),
|
|
@@ -30,6 +35,14 @@ const nextConfig: NextConfig = {
|
|
|
30
35
|
'127.0.0.1',
|
|
31
36
|
'0.0.0.0',
|
|
32
37
|
],
|
|
38
|
+
async rewrites() {
|
|
39
|
+
return [
|
|
40
|
+
{
|
|
41
|
+
source: '/:view(agents|schedules|memory|tasks|secrets|providers|skills|connectors|webhooks|mcp-servers|knowledge|plugins|usage|runs|logs|settings|projects)',
|
|
42
|
+
destination: '/',
|
|
43
|
+
},
|
|
44
|
+
]
|
|
45
|
+
},
|
|
33
46
|
};
|
|
34
47
|
|
|
35
48
|
export default nextConfig;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swarmclawai/swarmclaw",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Self-hosted AI agent orchestration dashboard — manage LLM providers, orchestrate agent swarms, schedule tasks, and bridge agents to chat platforms.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -58,6 +58,7 @@
|
|
|
58
58
|
"@langchain/core": "^1.1.26",
|
|
59
59
|
"@langchain/langgraph": "^1.1.5",
|
|
60
60
|
"@langchain/openai": "^1.2.8",
|
|
61
|
+
"@multiavatar/multiavatar": "^1.0.7",
|
|
61
62
|
"@playwright/mcp": "^0.0.68",
|
|
62
63
|
"@slack/bolt": "^4.6.0",
|
|
63
64
|
"@whiskeysockets/baileys": "^7.0.0-rc.9",
|
|
@@ -69,6 +70,7 @@
|
|
|
69
70
|
"cron-parser": "^5.5.0",
|
|
70
71
|
"cronstrue": "^3.12.0",
|
|
71
72
|
"discord.js": "^14.25.1",
|
|
73
|
+
"exceljs": "^4.4.0",
|
|
72
74
|
"grammy": "^1.40.0",
|
|
73
75
|
"highlight.js": "^11.11.1",
|
|
74
76
|
"lucide-react": "^0.574.0",
|
|
@@ -1,33 +1,50 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
|
-
import { loadAgents, saveAgents,
|
|
2
|
+
import { loadAgents, saveAgents, loadSessions, saveSessions } from '@/lib/server/storage'
|
|
3
3
|
import { normalizeProviderEndpoint } from '@/lib/openclaw-endpoint'
|
|
4
|
-
import {
|
|
4
|
+
import { mutateItem, notFound, type CollectionOps } from '@/lib/server/collection-helpers'
|
|
5
|
+
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
7
|
+
const ops: CollectionOps<any> = { load: () => loadAgents({ includeTrashed: true }), save: saveAgents, topic: 'agents' }
|
|
5
8
|
|
|
6
9
|
export async function PUT(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
7
10
|
const { id } = await params
|
|
8
11
|
const body = await req.json()
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return NextResponse.json(agents[id])
|
|
12
|
+
const result = mutateItem(ops, id, (agent) => {
|
|
13
|
+
Object.assign(agent, body, { updatedAt: Date.now() })
|
|
14
|
+
if (body.apiEndpoint !== undefined) {
|
|
15
|
+
agent.apiEndpoint = normalizeProviderEndpoint(
|
|
16
|
+
body.provider || agent.provider,
|
|
17
|
+
body.apiEndpoint,
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
delete (agent as Record<string, unknown>).id
|
|
21
|
+
agent.id = id
|
|
22
|
+
return agent
|
|
23
|
+
})
|
|
24
|
+
if (!result) return notFound()
|
|
25
|
+
return NextResponse.json(result)
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
export async function DELETE(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
27
29
|
const { id } = await params
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
// Soft delete — set trashedAt instead of removing the record
|
|
31
|
+
const result = mutateItem(ops, id, (agent) => {
|
|
32
|
+
agent.trashedAt = Date.now()
|
|
33
|
+
return agent
|
|
34
|
+
})
|
|
35
|
+
if (!result) return notFound()
|
|
36
|
+
|
|
37
|
+
// Detach sessions from the trashed agent
|
|
38
|
+
const sessions = loadSessions()
|
|
39
|
+
let detachedSessions = 0
|
|
40
|
+
for (const session of Object.values(sessions) as Array<Record<string, unknown>>) {
|
|
41
|
+
if (!session || session.agentId !== id) continue
|
|
42
|
+
session.agentId = null
|
|
43
|
+
detachedSessions += 1
|
|
44
|
+
}
|
|
45
|
+
if (detachedSessions > 0) {
|
|
46
|
+
saveSessions(sessions)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return NextResponse.json({ ok: true, detachedSessions })
|
|
33
50
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
|
-
import
|
|
2
|
+
import { genId } from '@/lib/id'
|
|
3
3
|
import { loadAgents, saveAgents, loadSessions, saveSessions } from '@/lib/server/storage'
|
|
4
4
|
import { WORKSPACE_DIR } from '@/lib/server/data-dir'
|
|
5
5
|
|
|
@@ -32,7 +32,7 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
// Create a new thread session
|
|
35
|
-
const sessionId = `agent-thread-${agentId}-${
|
|
35
|
+
const sessionId = `agent-thread-${agentId}-${genId()}`
|
|
36
36
|
const now = Date.now()
|
|
37
37
|
const session = {
|
|
38
38
|
id: sessionId,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
|
-
import
|
|
2
|
+
import { genId } from '@/lib/id'
|
|
3
3
|
import { loadAgents, saveAgents } from '@/lib/server/storage'
|
|
4
4
|
import { normalizeProviderEndpoint } from '@/lib/openclaw-endpoint'
|
|
5
5
|
import { notify } from '@/lib/server/ws-hub'
|
|
@@ -12,7 +12,7 @@ export async function GET(_req: Request) {
|
|
|
12
12
|
|
|
13
13
|
export async function POST(req: Request) {
|
|
14
14
|
const body = await req.json()
|
|
15
|
-
const id =
|
|
15
|
+
const id = genId()
|
|
16
16
|
const now = Date.now()
|
|
17
17
|
const agents = loadAgents()
|
|
18
18
|
agents[id] = {
|
|
@@ -28,6 +28,7 @@ export async function POST(req: Request) {
|
|
|
28
28
|
subAgentIds: body.subAgentIds || [],
|
|
29
29
|
tools: body.tools || [],
|
|
30
30
|
capabilities: body.capabilities || [],
|
|
31
|
+
thinkingLevel: body.thinkingLevel || undefined,
|
|
31
32
|
createdAt: now,
|
|
32
33
|
updatedAt: now,
|
|
33
34
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { loadTrashedAgents, loadAgents, saveAgents, deleteAgent } from '@/lib/server/storage'
|
|
3
|
+
import { notify } from '@/lib/server/ws-hub'
|
|
4
|
+
import { badRequest, notFound } from '@/lib/server/collection-helpers'
|
|
5
|
+
|
|
6
|
+
/** GET — list trashed agents */
|
|
7
|
+
export async function GET() {
|
|
8
|
+
return NextResponse.json(loadTrashedAgents())
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** POST { id } — restore a trashed agent */
|
|
12
|
+
export async function POST(req: Request) {
|
|
13
|
+
const body = await req.json()
|
|
14
|
+
const id = body?.id as string | undefined
|
|
15
|
+
if (!id) return badRequest('Missing agent id')
|
|
16
|
+
|
|
17
|
+
const all = loadAgents({ includeTrashed: true })
|
|
18
|
+
const agent = all[id]
|
|
19
|
+
if (!agent) return notFound()
|
|
20
|
+
if (!agent.trashedAt) return badRequest('Agent is not trashed')
|
|
21
|
+
|
|
22
|
+
delete agent.trashedAt
|
|
23
|
+
agent.updatedAt = Date.now()
|
|
24
|
+
all[id] = agent
|
|
25
|
+
saveAgents(all)
|
|
26
|
+
notify('agents')
|
|
27
|
+
return NextResponse.json(agent)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** DELETE { id } — permanently delete a trashed agent */
|
|
31
|
+
export async function DELETE(req: Request) {
|
|
32
|
+
const body = await req.json()
|
|
33
|
+
const id = body?.id as string | undefined
|
|
34
|
+
if (!id) return badRequest('Missing agent id')
|
|
35
|
+
|
|
36
|
+
const all = loadAgents({ includeTrashed: true })
|
|
37
|
+
const agent = all[id]
|
|
38
|
+
if (!agent) return notFound()
|
|
39
|
+
if (!agent.trashedAt) return badRequest('Agent must be trashed before permanent deletion')
|
|
40
|
+
|
|
41
|
+
deleteAgent(id)
|
|
42
|
+
notify('agents')
|
|
43
|
+
return NextResponse.json({ ok: true })
|
|
44
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
|
-
import
|
|
2
|
+
import { genId } from '@/lib/id'
|
|
3
3
|
import { loadSkills, saveSkills } from '@/lib/server/storage'
|
|
4
4
|
import { fetchSkillContent } from '@/lib/server/clawhub-client'
|
|
5
5
|
|
|
@@ -20,7 +20,7 @@ export async function POST(req: Request) {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
const skills = loadSkills()
|
|
23
|
-
const id =
|
|
23
|
+
const id = genId()
|
|
24
24
|
skills[id] = {
|
|
25
25
|
id,
|
|
26
26
|
name,
|
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
2
|
import { loadConnectors, saveConnectors } from '@/lib/server/storage'
|
|
3
3
|
import { notify } from '@/lib/server/ws-hub'
|
|
4
|
+
import { notFound } from '@/lib/server/collection-helpers'
|
|
4
5
|
|
|
5
6
|
export async function GET(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
6
7
|
const { id } = await params
|
|
7
8
|
const connectors = loadConnectors()
|
|
8
9
|
const connector = connectors[id]
|
|
9
|
-
if (!connector) return
|
|
10
|
+
if (!connector) return notFound()
|
|
10
11
|
|
|
11
|
-
// Merge runtime status
|
|
12
|
+
// Merge runtime status, QR code, and presence
|
|
12
13
|
try {
|
|
13
|
-
const { getConnectorStatus, getConnectorQR, isConnectorAuthenticated, hasConnectorCredentials } = await import('@/lib/server/connectors/manager')
|
|
14
|
+
const { getConnectorStatus, getConnectorQR, isConnectorAuthenticated, hasConnectorCredentials, getConnectorPresence } = await import('@/lib/server/connectors/manager')
|
|
14
15
|
connector.status = getConnectorStatus(id)
|
|
15
16
|
const qr = getConnectorQR(id)
|
|
16
17
|
if (qr) connector.qrDataUrl = qr
|
|
17
18
|
connector.authenticated = isConnectorAuthenticated(id)
|
|
18
19
|
connector.hasCredentials = hasConnectorCredentials(id)
|
|
20
|
+
if (connector.status === 'running') {
|
|
21
|
+
connector.presence = getConnectorPresence(id)
|
|
22
|
+
}
|
|
19
23
|
} catch { /* ignore */ }
|
|
20
24
|
|
|
21
25
|
return NextResponse.json(connector)
|
|
@@ -26,7 +30,7 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
|
|
|
26
30
|
const body = await req.json()
|
|
27
31
|
const connectors = loadConnectors()
|
|
28
32
|
const connector = connectors[id]
|
|
29
|
-
if (!connector) return
|
|
33
|
+
if (!connector) return notFound()
|
|
30
34
|
|
|
31
35
|
// Handle start/stop/repair actions — these modify connector state internally,
|
|
32
36
|
// so re-read from storage after to avoid overwriting with stale data
|
|
@@ -40,10 +44,10 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
|
|
|
40
44
|
} else {
|
|
41
45
|
await manager.repairConnector(id)
|
|
42
46
|
}
|
|
43
|
-
} catch (err:
|
|
47
|
+
} catch (err: unknown) {
|
|
44
48
|
// Re-read to get the error state saved by startConnector
|
|
45
49
|
const fresh = loadConnectors()
|
|
46
|
-
return NextResponse.json(fresh[id] || { error: err.message }, { status: 500 })
|
|
50
|
+
return NextResponse.json(fresh[id] || { error: err instanceof Error ? err.message : String(err) }, { status: 500 })
|
|
47
51
|
}
|
|
48
52
|
// Re-read the connector after manager modified it
|
|
49
53
|
const fresh = loadConnectors()
|
|
@@ -68,7 +72,7 @@ export async function PUT(req: Request, { params }: { params: Promise<{ id: stri
|
|
|
68
72
|
export async function DELETE(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
69
73
|
const { id } = await params
|
|
70
74
|
const connectors = loadConnectors()
|
|
71
|
-
if (!connectors[id]) return
|
|
75
|
+
if (!connectors[id]) return notFound()
|
|
72
76
|
|
|
73
77
|
// Stop if running
|
|
74
78
|
try {
|
|
@@ -76,6 +80,12 @@ export async function DELETE(_req: Request, { params }: { params: Promise<{ id:
|
|
|
76
80
|
await stopConnector(id)
|
|
77
81
|
} catch { /* ignore */ }
|
|
78
82
|
|
|
83
|
+
// Clear persisted pairing state when connector is deleted.
|
|
84
|
+
try {
|
|
85
|
+
const { clearConnectorPairingState } = await import('@/lib/server/connectors/pairing')
|
|
86
|
+
clearConnectorPairingState(id)
|
|
87
|
+
} catch { /* ignore */ }
|
|
88
|
+
|
|
79
89
|
delete connectors[id]
|
|
80
90
|
saveConnectors(connectors)
|
|
81
91
|
notify('connectors')
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { loadConnectors } from '@/lib/server/storage'
|
|
3
|
+
|
|
4
|
+
export const dynamic = 'force-dynamic'
|
|
5
|
+
|
|
6
|
+
function readSecret(req: Request): string {
|
|
7
|
+
const url = new URL(req.url)
|
|
8
|
+
return (
|
|
9
|
+
req.headers.get('x-connector-secret')
|
|
10
|
+
|| url.searchParams.get('secret')
|
|
11
|
+
|| ''
|
|
12
|
+
).trim()
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function parseWebhookBody(rawBody: string): Record<string, unknown> {
|
|
16
|
+
const trimmed = rawBody.trim()
|
|
17
|
+
if (!trimmed) return {}
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const parsed = JSON.parse(trimmed)
|
|
21
|
+
if (Array.isArray(parsed)) return { data: parsed }
|
|
22
|
+
return parsed && typeof parsed === 'object' ? parsed as Record<string, unknown> : {}
|
|
23
|
+
} catch {
|
|
24
|
+
// Fall back to URL-encoded payloads used by some webhook providers.
|
|
25
|
+
const params = new URLSearchParams(rawBody)
|
|
26
|
+
const nested = params.get('payload') || params.get('data') || params.get('message') || ''
|
|
27
|
+
if (nested) {
|
|
28
|
+
try {
|
|
29
|
+
const parsedNested = JSON.parse(nested)
|
|
30
|
+
if (Array.isArray(parsedNested)) return { data: parsedNested }
|
|
31
|
+
return parsedNested && typeof parsedNested === 'object'
|
|
32
|
+
? parsedNested as Record<string, unknown>
|
|
33
|
+
: {}
|
|
34
|
+
} catch {
|
|
35
|
+
// Ignore malformed nested JSON and return flat map below.
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const out: Record<string, unknown> = {}
|
|
39
|
+
for (const [key, value] of params.entries()) out[key] = value
|
|
40
|
+
return out
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
45
|
+
const { id } = await params
|
|
46
|
+
const connectors = loadConnectors()
|
|
47
|
+
const connector = connectors[id]
|
|
48
|
+
if (!connector) return NextResponse.json({ error: 'Connector not found' }, { status: 404 })
|
|
49
|
+
|
|
50
|
+
const requiredSecret = String(connector.config?.webhookSecret || '').trim()
|
|
51
|
+
if (requiredSecret && readSecret(req) !== requiredSecret) {
|
|
52
|
+
return NextResponse.json({ error: 'Invalid connector webhook secret' }, { status: 401 })
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const rawBody = await req.text().catch(() => '')
|
|
56
|
+
const payload = parseWebhookBody(rawBody)
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
if (connector.platform === 'teams') {
|
|
60
|
+
const handlerKey = `__swarmclaw_teams_handler_${connector.id}__`
|
|
61
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- dynamic globalThis handler registered at runtime by connector
|
|
62
|
+
const handler = (globalThis as any)[handlerKey]
|
|
63
|
+
if (typeof handler !== 'function') {
|
|
64
|
+
return NextResponse.json({ error: 'Teams connector is not running or not ready' }, { status: 409 })
|
|
65
|
+
}
|
|
66
|
+
await handler(payload)
|
|
67
|
+
return NextResponse.json({ ok: true })
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (connector.platform === 'googlechat') {
|
|
71
|
+
const handlerKey = `__swarmclaw_googlechat_handler_${connector.id}__`
|
|
72
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- dynamic globalThis handler registered at runtime by connector
|
|
73
|
+
const handler = (globalThis as any)[handlerKey]
|
|
74
|
+
if (typeof handler !== 'function') {
|
|
75
|
+
return NextResponse.json({ error: 'Google Chat connector is not running or not ready' }, { status: 409 })
|
|
76
|
+
}
|
|
77
|
+
const result = await handler(payload)
|
|
78
|
+
if (result && typeof result === 'object' && Object.keys(result).length > 0) {
|
|
79
|
+
return NextResponse.json(result)
|
|
80
|
+
}
|
|
81
|
+
return NextResponse.json({})
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (connector.platform === 'bluebubbles') {
|
|
85
|
+
const handlerKey = `__swarmclaw_bluebubbles_handler_${connector.id}__`
|
|
86
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- dynamic globalThis handler registered at runtime by connector
|
|
87
|
+
const handler = (globalThis as any)[handlerKey]
|
|
88
|
+
if (typeof handler !== 'function') {
|
|
89
|
+
return NextResponse.json({ error: 'BlueBubbles connector is not running or not ready' }, { status: 409 })
|
|
90
|
+
}
|
|
91
|
+
const result = await handler(payload)
|
|
92
|
+
if (result && typeof result === 'object' && Object.keys(result).length > 0) {
|
|
93
|
+
return NextResponse.json(result)
|
|
94
|
+
}
|
|
95
|
+
return NextResponse.json({})
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return NextResponse.json({ error: `Platform "${connector.platform}" does not support connector webhook ingress.` }, { status: 400 })
|
|
99
|
+
} catch (err: unknown) {
|
|
100
|
+
const message = err instanceof Error ? err.message : 'Webhook processing failed'
|
|
101
|
+
return NextResponse.json({ error: message }, { status: 500 })
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
|
-
import
|
|
2
|
+
import { genId } from '@/lib/id'
|
|
3
3
|
import { loadConnectors, saveConnectors } from '@/lib/server/storage'
|
|
4
4
|
import { notify } from '@/lib/server/ws-hub'
|
|
5
5
|
import type { Connector } from '@/types'
|
|
@@ -27,7 +27,7 @@ export async function GET(_req: Request) {
|
|
|
27
27
|
export async function POST(req: Request) {
|
|
28
28
|
const body = await req.json()
|
|
29
29
|
const connectors = loadConnectors()
|
|
30
|
-
const id =
|
|
30
|
+
const id = genId()
|
|
31
31
|
|
|
32
32
|
const connector: Connector = {
|
|
33
33
|
id,
|
|
@@ -48,7 +48,10 @@ export async function POST(req: Request) {
|
|
|
48
48
|
notify('connectors')
|
|
49
49
|
|
|
50
50
|
// Auto-start if connector has credentials (or is WhatsApp which uses QR)
|
|
51
|
-
const hasCredentials = connector.platform === 'whatsapp'
|
|
51
|
+
const hasCredentials = connector.platform === 'whatsapp'
|
|
52
|
+
|| connector.platform === 'openclaw'
|
|
53
|
+
|| (connector.platform === 'bluebubbles' && (!!connector.credentialId || !!connector.config.password))
|
|
54
|
+
|| !!connector.credentialId
|
|
52
55
|
if (hasCredentials && body.autoStart !== false) {
|
|
53
56
|
try {
|
|
54
57
|
const { startConnector } = await import('@/lib/server/connectors/manager')
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
2
|
import { loadCredentials, saveCredentials } from '@/lib/server/storage'
|
|
3
|
+
import { notFound } from '@/lib/server/collection-helpers'
|
|
3
4
|
|
|
4
5
|
export async function DELETE(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
5
6
|
const { id: credId } = await params
|
|
6
7
|
const creds = loadCredentials()
|
|
7
8
|
if (!creds[credId]) {
|
|
8
|
-
return
|
|
9
|
+
return notFound()
|
|
9
10
|
}
|
|
10
11
|
delete creds[credId]
|
|
11
12
|
saveCredentials(creds)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
|
-
import
|
|
2
|
+
import { genId } from '@/lib/id'
|
|
3
3
|
import { loadCredentials, saveCredentials, encryptKey } from '@/lib/server/storage'
|
|
4
4
|
export const dynamic = 'force-dynamic'
|
|
5
5
|
|
|
@@ -18,7 +18,7 @@ export async function POST(req: Request) {
|
|
|
18
18
|
if (!provider || !apiKey) {
|
|
19
19
|
return NextResponse.json({ error: 'provider and apiKey are required' }, { status: 400 })
|
|
20
20
|
}
|
|
21
|
-
const id = 'cred_' +
|
|
21
|
+
const id = 'cred_' + genId(6)
|
|
22
22
|
const creds = loadCredentials()
|
|
23
23
|
creds[id] = {
|
|
24
24
|
id,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { genId } from '@/lib/id'
|
|
2
2
|
import { NextResponse } from 'next/server'
|
|
3
3
|
import { loadDocuments, saveDocuments } from '@/lib/server/storage'
|
|
4
4
|
|
|
@@ -69,7 +69,7 @@ export async function POST(req: Request) {
|
|
|
69
69
|
const body = await req.json().catch(() => ({}))
|
|
70
70
|
const now = Date.now()
|
|
71
71
|
const docs = loadDocuments()
|
|
72
|
-
const id = body.id ||
|
|
72
|
+
const id = body.id || genId(6)
|
|
73
73
|
const fileName = body.fileName || body.filename || ''
|
|
74
74
|
const title = body.title || fileName || 'Untitled Document'
|
|
75
75
|
const content = typeof body.content === 'string' ? body.content : ''
|
|
@@ -21,6 +21,14 @@ const MIME_MAP: Record<string, string> = {
|
|
|
21
21
|
'.jsx': 'text/plain',
|
|
22
22
|
'.py': 'text/plain',
|
|
23
23
|
'.sh': 'text/plain',
|
|
24
|
+
'.pdf': 'application/pdf',
|
|
25
|
+
'.csv': 'text/csv',
|
|
26
|
+
'.xml': 'application/xml',
|
|
27
|
+
'.zip': 'application/zip',
|
|
28
|
+
'.doc': 'application/msword',
|
|
29
|
+
'.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
30
|
+
'.xls': 'application/vnd.ms-excel',
|
|
31
|
+
'.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
24
32
|
}
|
|
25
33
|
|
|
26
34
|
const MAX_SIZE = 10 * 1024 * 1024 // 10MB
|