@flame0510/project-aether 1.1.9
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 +150 -0
- package/agent-templates/README.md +37 -0
- package/agent-templates/argus/AGENTS.md +52 -0
- package/agent-templates/argus/IDENTITY.md +7 -0
- package/agent-templates/argus/MEMORY.md +9 -0
- package/agent-templates/argus/SOUL.md +24 -0
- package/agent-templates/argus/TOOLS.md +8 -0
- package/agent-templates/atlas/AGENTS.md +47 -0
- package/agent-templates/atlas/HEARTBEAT.md +10 -0
- package/agent-templates/atlas/IDENTITY.md +6 -0
- package/agent-templates/atlas/MEMORY.md +9 -0
- package/agent-templates/atlas/SOUL.md +21 -0
- package/agent-templates/atlas/TOOLS.md +4 -0
- package/agent-templates/base-image/.trigger +0 -0
- package/agent-templates/base-image/Dockerfile +90 -0
- package/agent-templates/base-image/entrypoint.sh +37 -0
- package/agent-templates/custom/AGENTS.md +29 -0
- package/agent-templates/custom/BOOTSTRAP.md +56 -0
- package/agent-templates/custom/MEMORY.md +8 -0
- package/agent-templates/prometheus/AGENTS.md +37 -0
- package/agent-templates/prometheus/IDENTITY.md +6 -0
- package/agent-templates/prometheus/MEMORY.md +9 -0
- package/agent-templates/prometheus/SOUL.md +21 -0
- package/agent-templates/prometheus/TOOLS.md +4 -0
- package/app/agents/ChannelManager.tsx +465 -0
- package/app/agents/ImageDownloadBanner.tsx +159 -0
- package/app/agents/PageClient.tsx +1285 -0
- package/app/agents/create/PageClient.tsx +437 -0
- package/app/agents/create/page.tsx +126 -0
- package/app/agents/loading.tsx +14 -0
- package/app/agents/page.tsx +5 -0
- package/app/api/agents/[id]/backup/route.ts +169 -0
- package/app/api/agents/[id]/channels/pairing/route.ts +69 -0
- package/app/api/agents/[id]/channels/route.ts +14 -0
- package/app/api/agents/[id]/channels/telegram/route.ts +50 -0
- package/app/api/agents/[id]/lifecycle/route.ts +94 -0
- package/app/api/agents/[id]/recreate/route.ts +199 -0
- package/app/api/agents/[id]/restart/route.ts +33 -0
- package/app/api/agents/[id]/restore/route.ts +81 -0
- package/app/api/agents/[id]/route.ts +280 -0
- package/app/api/agents/channels-summary/route.ts +39 -0
- package/app/api/agents/config-json/route.ts +58 -0
- package/app/api/agents/create/route.ts +488 -0
- package/app/api/agents/download-image/route.ts +32 -0
- package/app/api/agents/image-status/route.ts +126 -0
- package/app/api/agents/route.ts +213 -0
- package/app/api/agents/token/route.ts +94 -0
- package/app/api/agents-active/route.ts +190 -0
- package/app/api/agents-config/route.ts +629 -0
- package/app/api/alerts/smoke/route.ts +26 -0
- package/app/api/aliases/add/route.ts +28 -0
- package/app/api/aliases/remove/route.ts +27 -0
- package/app/api/assistant/route.ts +204 -0
- package/app/api/auth/check/route.ts +38 -0
- package/app/api/auth/login/route.ts +85 -0
- package/app/api/auth/logout/route.ts +7 -0
- package/app/api/auth/status/route.ts +33 -0
- package/app/api/config/env/route.ts +132 -0
- package/app/api/config/restart/route.ts +26 -0
- package/app/api/containers/route.ts +75 -0
- package/app/api/cost-override/route.ts +46 -0
- package/app/api/costs/route.ts +84 -0
- package/app/api/credentials/[id]/reveal/route.ts +21 -0
- package/app/api/credentials/[id]/route.ts +52 -0
- package/app/api/credentials/[id]/sync/route.ts +58 -0
- package/app/api/credentials/detect/route.ts +246 -0
- package/app/api/credentials/route.ts +33 -0
- package/app/api/crons/[id]/route.ts +69 -0
- package/app/api/crons/route.ts +12 -0
- package/app/api/crons/runs/route.ts +31 -0
- package/app/api/debug/route.ts +11 -0
- package/app/api/envcheck/route.ts +18 -0
- package/app/api/events/route.ts +24 -0
- package/app/api/gateway/agent/route.ts +154 -0
- package/app/api/gateway/provider/balance/route.ts +38 -0
- package/app/api/gateway/provider/keys.ts +38 -0
- package/app/api/gateway/provider/oauth/route.ts +304 -0
- package/app/api/gateway/provider/route.ts +209 -0
- package/app/api/gateway/route.ts +223 -0
- package/app/api/gateway/sync.ts +189 -0
- package/app/api/lineage/route.ts +54 -0
- package/app/api/memory-context/route.ts +15 -0
- package/app/api/metrics/route.ts +35 -0
- package/app/api/models/route.ts +77 -0
- package/app/api/plugins/route.js +37 -0
- package/app/api/provider/upstream.ts +142 -0
- package/app/api/provider/v1/chat/completions/route.ts +146 -0
- package/app/api/provider/v1/models/route.ts +167 -0
- package/app/api/provider-usage/route.ts +629 -0
- package/app/api/session/route.ts +39 -0
- package/app/api/sessions/[id]/route.ts +38 -0
- package/app/api/sessions/route.ts +23 -0
- package/app/api/setup/agent-image/route.ts +102 -0
- package/app/api/setup/password/route.ts +111 -0
- package/app/api/setup/restart/route.ts +76 -0
- package/app/api/skills/delete/route.js +105 -0
- package/app/api/skills/promote/route.js +129 -0
- package/app/api/skills/route.js +203 -0
- package/app/api/skills/save/route.js +142 -0
- package/app/api/stats/route.ts +34 -0
- package/app/api/stats-since/route.ts +20 -0
- package/app/api/stream/route.ts +70 -0
- package/app/api/system-health/route.ts +233 -0
- package/app/api/tool-calls/route.ts +24 -0
- package/app/api/tools-config/route.ts +163 -0
- package/app/api/update-check/route.ts +127 -0
- package/app/api/version/route.ts +25 -0
- package/app/api/wizard/complete/route.ts +32 -0
- package/app/api/wizard/reset/route.ts +28 -0
- package/app/api/wizard/status/route.ts +28 -0
- package/app/api/workspace/route.ts +392 -0
- package/app/api/workspace/stream/route.ts +136 -0
- package/app/components/AppShell.tsx +33 -0
- package/app/components/AuthGuard.tsx +91 -0
- package/app/components/CostBreakdown.tsx +37 -0
- package/app/components/DashboardHeader.tsx +73 -0
- package/app/components/DashboardLayout.tsx +76 -0
- package/app/components/DashboardToolbar.tsx +61 -0
- package/app/components/FloatingActions.tsx +33 -0
- package/app/components/Icons.tsx +78 -0
- package/app/components/LineageGraphPage.tsx +142 -0
- package/app/components/LiveFeed.tsx +48 -0
- package/app/components/MobileBottomNav.tsx +84 -0
- package/app/components/ModelPickerModal.tsx +94 -0
- package/app/components/PasswordInput.tsx +86 -0
- package/app/components/PulseChat.tsx +362 -0
- package/app/components/Rev4aLoader.tsx +48 -0
- package/app/components/SessionDrawer.tsx +263 -0
- package/app/components/SessionTopology.tsx +289 -0
- package/app/components/Sidebar.tsx +294 -0
- package/app/components/Skeleton.tsx +133 -0
- package/app/components/SystemCockpit.tsx +169 -0
- package/app/components/VersionBanner.tsx +105 -0
- package/app/components/WizardButton.tsx +30 -0
- package/app/components/ui/Badge.tsx +30 -0
- package/app/components/ui/Button.tsx +59 -0
- package/app/components/ui/ConfirmModal.tsx +46 -0
- package/app/components/ui/FilterBar.tsx +33 -0
- package/app/components/ui/Input.tsx +25 -0
- package/app/components/ui/ItemList.tsx +83 -0
- package/app/components/ui/LoadingSpinner.tsx +30 -0
- package/app/components/ui/Metric.tsx +22 -0
- package/app/components/ui/Modal.tsx +42 -0
- package/app/components/ui/Page.tsx +18 -0
- package/app/components/ui/Pill.tsx +6 -0
- package/app/components/ui/PropertyList.tsx +24 -0
- package/app/components/ui/Select.tsx +26 -0
- package/app/components/ui/StatusCard.tsx +32 -0
- package/app/components/ui/Surface.tsx +14 -0
- package/app/components/ui/Tabs.tsx +55 -0
- package/app/components/ui/TemplateOption.tsx +59 -0
- package/app/components/ui/Toast.tsx +44 -0
- package/app/components/ui/index.ts +19 -0
- package/app/components/ui/tokens.ts +18 -0
- package/app/config/PageClient.tsx +293 -0
- package/app/config/loading.tsx +10 -0
- package/app/config/page.tsx +7 -0
- package/app/containers/ContainersClient.tsx +129 -0
- package/app/containers/loading.tsx +21 -0
- package/app/containers/page.tsx +5 -0
- package/app/containers/terminal/[id]/TerminalClient.tsx +313 -0
- package/app/containers/terminal/[id]/page.tsx +6 -0
- package/app/credentials/PageClient.tsx +598 -0
- package/app/credentials/loading.tsx +10 -0
- package/app/credentials/page.tsx +7 -0
- package/app/crons/PageClient.tsx +538 -0
- package/app/crons/loading.tsx +10 -0
- package/app/crons/page.tsx +5 -0
- package/app/design-system.ts +57 -0
- package/app/gateway/PageClient.tsx +1048 -0
- package/app/gateway/loading.tsx +24 -0
- package/app/gateway/page.tsx +5 -0
- package/app/globals.css +1474 -0
- package/app/layout.tsx +45 -0
- package/app/lib/models-context.tsx +41 -0
- package/app/lineage/loading.tsx +10 -0
- package/app/lineage/page.tsx +13 -0
- package/app/loading.tsx +16 -0
- package/app/login/page.tsx +160 -0
- package/app/memory/MemoryContextPageClient.tsx +196 -0
- package/app/memory/loading.tsx +10 -0
- package/app/memory/page.tsx +8 -0
- package/app/page.tsx +31 -0
- package/app/plugins/PageClient.tsx +296 -0
- package/app/plugins/loading.tsx +10 -0
- package/app/plugins/page.tsx +5 -0
- package/app/setup/PageClient.tsx +227 -0
- package/app/setup/loading.tsx +21 -0
- package/app/setup/page.tsx +7 -0
- package/app/skills/PageClient.tsx +499 -0
- package/app/skills/loading.tsx +10 -0
- package/app/skills/page.tsx +5 -0
- package/app/tools/ToolsPageClient.tsx +308 -0
- package/app/tools/loading.tsx +10 -0
- package/app/tools/page.tsx +62 -0
- package/app/tools/tools-catalog.ts +141 -0
- package/app/wizard/PageClient.tsx +485 -0
- package/app/wizard/icons.tsx +89 -0
- package/app/wizard/loading.tsx +24 -0
- package/app/wizard/page.tsx +7 -0
- package/app/wizard/storage.ts +33 -0
- package/app/wizard/useWizard.ts +101 -0
- package/app/workspace/WorkspaceClient.tsx +633 -0
- package/app/workspace/loading.tsx +36 -0
- package/app/workspace/page.tsx +8 -0
- package/bin/docker-entrypoint.sh +21 -0
- package/bin/postinstall.js +20 -0
- package/bin/rev4a.js +696 -0
- package/daemon.js +783 -0
- package/docs/ARCHITECTURE.md +557 -0
- package/docs/CONTAINER-TERMINAL.md +280 -0
- package/docs/DESIGN-SYSTEM.md +99 -0
- package/docs/FRONTEND-ARCHITECTURE.md +119 -0
- package/docs/REV4A.md +504 -0
- package/docs/dev/API-REFERENCE.md +1835 -0
- package/docs/dev/DATABASE.md +224 -0
- package/docs/dev/GATEWAY.md +300 -0
- package/docs/dev/PROVIDERS.md +229 -0
- package/docs/dev/SESSION-MAINTENANCE-PLAN.md +607 -0
- package/docs/dev/WORKSPACE.md +199 -0
- package/docs/rag/DATA-FRESHNESS.md +109 -0
- package/docs/rag/GLOSSARY.md +125 -0
- package/docs/rag/REV4A-OVERVIEW.md +135 -0
- package/docs/rag/WHAT-I-CAN-ANSWER.md +124 -0
- package/lib/agent-setup.ts +130 -0
- package/lib/alerts.ts +115 -0
- package/lib/apiFetch.ts +7 -0
- package/lib/auth.ts +28 -0
- package/lib/billing.ts +100 -0
- package/lib/buildAgentImage.ts +346 -0
- package/lib/channelManager.ts +386 -0
- package/lib/container.ts +60 -0
- package/lib/credentials/db.ts +86 -0
- package/lib/credentials/delivery.ts +448 -0
- package/lib/credentials/detect.ts +149 -0
- package/lib/credentials/hash.ts +9 -0
- package/lib/credentials/providers.ts +110 -0
- package/lib/credentials/vault.ts +195 -0
- package/lib/db-bootstrap.d.ts +3 -0
- package/lib/db-bootstrap.mjs +144 -0
- package/lib/db.ts +25 -0
- package/lib/docker-utils.ts +60 -0
- package/lib/hooks/useDashboard.ts +264 -0
- package/lib/hooks/useRev4aTimezone.ts +28 -0
- package/lib/hooks/useScreenshot.ts +30 -0
- package/lib/memory-context.ts +385 -0
- package/lib/model-catalogue.ts +131 -0
- package/lib/model-pricing.ts +63 -0
- package/lib/openclaw-cron.ts +350 -0
- package/lib/patterns/ApiAdapter.ts +73 -0
- package/lib/patterns/ApiClient.ts +65 -0
- package/lib/patterns/EventBus.ts +113 -0
- package/lib/patterns/FilterStrategy.ts +79 -0
- package/lib/patterns/SessionFactory.ts +117 -0
- package/lib/patterns/sessionPresentation.ts +117 -0
- package/lib/provider-balance.ts +163 -0
- package/lib/requireAuth.tsx +38 -0
- package/lib/rev4a-auth.d.ts +3 -0
- package/lib/rev4a-auth.js +36 -0
- package/lib/rev4a-paths.ts +145 -0
- package/lib/timezone.ts +46 -0
- package/lib/types/index.ts +96 -0
- package/lib/utils/format.ts +82 -0
- package/lineage.js +56 -0
- package/model-pricing.json +482 -0
- package/models.config.json +844 -0
- package/next-env.d.ts +6 -0
- package/next.config.mjs +7 -0
- package/package.json +82 -0
- package/public/apple-touch-icon.png +0 -0
- package/public/favicon-64.png +0 -0
- package/public/favicon.svg +58 -0
- package/public/icon-192-maskable.png +0 -0
- package/public/icon-192.png +0 -0
- package/public/icon-512-maskable.png +0 -0
- package/public/icon-512.png +0 -0
- package/public/manifest.json +38 -0
- package/public/rev4a-logo.png +0 -0
- package/public/rev4a-logo.svg +58 -0
- package/rev4a-rules/AGENTS.md +37 -0
- package/scripts/add-auth.cjs +185 -0
- package/scripts/backup.sh +66 -0
- package/scripts/check-providers.mjs +143 -0
- package/scripts/docker/traefik/docker-compose.yml +32 -0
- package/scripts/docker/traefik/dynamic/.gitkeep +18 -0
- package/scripts/docker/traefik/traefik.yml +37 -0
- package/scripts/remove-auth-profile.js +56 -0
- package/scripts/restore.sh +82 -0
- package/terminal-ws-server.js +139 -0
- package/tsconfig.json +42 -0
|
@@ -0,0 +1,1835 @@
|
|
|
1
|
+
# Rev4a API Reference
|
|
2
|
+
|
|
3
|
+
> **Last updated:** 2026-07-19
|
|
4
|
+
|
|
5
|
+
All routes are under `/api/`. Authentication is required on every endpoint
|
|
6
|
+
unless otherwise noted.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Authentication
|
|
11
|
+
|
|
12
|
+
### Bearer token (programmatic)
|
|
13
|
+
```
|
|
14
|
+
Authorization: Bearer <REV4A_TOKEN>
|
|
15
|
+
```
|
|
16
|
+
Set `REV4A_TOKEN` env var. No default — auth is disabled when unset.
|
|
17
|
+
|
|
18
|
+
### Browser cookie (UI)
|
|
19
|
+
After a successful `POST /api/auth/login`, the server sets an `rev4a_token`
|
|
20
|
+
cookie (signed JWT, 7-day expiry). All subsequent UI requests use this cookie
|
|
21
|
+
automatically.
|
|
22
|
+
|
|
23
|
+
### API-only fallbacks (in order of precedence)
|
|
24
|
+
1. `Authorization: Bearer <token>`
|
|
25
|
+
2. `?token=<token>` query param
|
|
26
|
+
3. `x-agent-token` header
|
|
27
|
+
|
|
28
|
+
For Control UI deep links, prefer `#token=<token>` instead of `?token=<token>`. The hash fragment is consumed by the browser client before the WebSocket connects.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Auth
|
|
33
|
+
|
|
34
|
+
### `POST /api/auth/login`
|
|
35
|
+
Login — exchange the static password for a signed JWT cookie.
|
|
36
|
+
|
|
37
|
+
**Body:** `{ "password": "***" }`
|
|
38
|
+
|
|
39
|
+
**Response:**
|
|
40
|
+
```json
|
|
41
|
+
{ "ok": true }
|
|
42
|
+
```
|
|
43
|
+
Sets `Set-Cookie: rev4a_token=<jwt>; HttpOnly; SameSite=Lax`.
|
|
44
|
+
|
|
45
|
+
**Error:** `401` if password is wrong.
|
|
46
|
+
|
|
47
|
+
### `GET /api/auth/check`
|
|
48
|
+
Validate the current browser cookie and report whether the user is authenticated.
|
|
49
|
+
|
|
50
|
+
**Auth:** browser cookie
|
|
51
|
+
|
|
52
|
+
**Response:**
|
|
53
|
+
```json
|
|
54
|
+
{ "authenticated": true }
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Error:** `401` with `{ "authenticated": false }` if the cookie is missing or invalid.
|
|
58
|
+
|
|
59
|
+
### `POST /api/auth/logout`
|
|
60
|
+
Delete the `rev4a_token` cookie.
|
|
61
|
+
|
|
62
|
+
**Auth:** browser cookie
|
|
63
|
+
|
|
64
|
+
**Response:**
|
|
65
|
+
```json
|
|
66
|
+
{ "ok": true }
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Onboarding
|
|
72
|
+
|
|
73
|
+
### `GET /api/onboarding/status`
|
|
74
|
+
Returns per-step onboarding progress.
|
|
75
|
+
|
|
76
|
+
**Auth:** browser cookie
|
|
77
|
+
|
|
78
|
+
**Response:**
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"complete": false,
|
|
82
|
+
"steps": { "welcome": true, "providers": false, "agents": false },
|
|
83
|
+
"completedAt": null
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
If no progress file exists, returns all steps as `false`.
|
|
88
|
+
|
|
89
|
+
### `POST /api/onboarding/complete`
|
|
90
|
+
Mark a step as completed, complete all steps, or reset the wizard.
|
|
91
|
+
|
|
92
|
+
**Auth:** browser cookie
|
|
93
|
+
|
|
94
|
+
**Body:**
|
|
95
|
+
| Shape | Effect |
|
|
96
|
+
|---|---|
|
|
97
|
+
| `{ "step": "providers" }` | Mark a single step as done |
|
|
98
|
+
| `{}` | Mark all steps as done (complete onboarding) |
|
|
99
|
+
| `{ "reset": true }` | Reset all steps to `false` (restart wizard) |
|
|
100
|
+
|
|
101
|
+
**Response:**
|
|
102
|
+
```json
|
|
103
|
+
{
|
|
104
|
+
"status": "ok",
|
|
105
|
+
"steps": { "welcome": true, "providers": true, "agents": false },
|
|
106
|
+
"complete": false
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Gateway
|
|
113
|
+
|
|
114
|
+
The Gateway API reads the model catalogue from [`models.config.json`](../models.config.json)
|
|
115
|
+
(tracked in the repository). Full documentation at [GATEWAY.md](GATEWAY.md) and
|
|
116
|
+
[PROVIDERS.md](PROVIDERS.md).
|
|
117
|
+
|
|
118
|
+
### `GET /api/gateway`
|
|
119
|
+
Returns live gateway status from all agent containers.
|
|
120
|
+
|
|
121
|
+
**Auth:** any auth method
|
|
122
|
+
|
|
123
|
+
**Response:**
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"agents": {
|
|
127
|
+
"total": 1,
|
|
128
|
+
"list": [
|
|
129
|
+
{
|
|
130
|
+
"containerName": "openclaw-atlas",
|
|
131
|
+
"agentId": "atlas",
|
|
132
|
+
"agentName": "Atlas",
|
|
133
|
+
"defaultModel": "rev4a/deepseek/deepseek-v4-flash",
|
|
134
|
+
"configured": true
|
|
135
|
+
}
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### `PUT /api/gateway/provider`
|
|
142
|
+
Trigger a full provider model sync to all agent containers.
|
|
143
|
+
|
|
144
|
+
**Auth:** any auth method
|
|
145
|
+
|
|
146
|
+
**Body:** none (reads current provider state from `models.config.json` and `data/provider-keys.json`)
|
|
147
|
+
|
|
148
|
+
**Response:**
|
|
149
|
+
```json
|
|
150
|
+
{
|
|
151
|
+
"status": "ok",
|
|
152
|
+
"activeModels": 2,
|
|
153
|
+
"results": ["Active models: 2 across 1 agent(s)"]
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Side effects:**
|
|
158
|
+
- Writes `models.providers.rev4a` to each agent container's `openclaw.json`
|
|
159
|
+
- Cleans up stale `models.json` and `auth-profiles.json` inside containers
|
|
160
|
+
- **Does NOT touch `agents.defaults.model` or `agents.list[].model`** — model references are managed per-container via the Agents Tab (`PUT /api/gateway/agent`)
|
|
161
|
+
- Does NOT restart the gateway (config is written live to the file)
|
|
162
|
+
|
|
163
|
+
### `PUT /api/gateway/agent`
|
|
164
|
+
Update model config for a single agent container.
|
|
165
|
+
|
|
166
|
+
**Auth:** any auth method
|
|
167
|
+
|
|
168
|
+
**Body:**
|
|
169
|
+
```json
|
|
170
|
+
{
|
|
171
|
+
"containerName": "openclaw-atlas",
|
|
172
|
+
"model": "deepseek/deepseek-v4-flash",
|
|
173
|
+
"fallbacks": []
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
If `fallbacks` is omitted or empty, no fallback models are set.
|
|
177
|
+
|
|
178
|
+
**Response:**
|
|
179
|
+
```json
|
|
180
|
+
{
|
|
181
|
+
"status": "ok",
|
|
182
|
+
"agent": "openclaw-atlas",
|
|
183
|
+
"model": {
|
|
184
|
+
"primary": "rev4a/deepseek/deepseek-v4-flash"
|
|
185
|
+
},
|
|
186
|
+
"verify": { "ok": true, "bytes": 2058 }
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**Behaviour:**
|
|
191
|
+
- Writes `agents.defaults.model.primary` and `agents.list[0].model.primary` with format `rev4a/<provider>/<model>`
|
|
192
|
+
- No restart — writes are live via `docker exec node -e`
|
|
193
|
+
- Does NOT set fallbacks unless explicitly provided
|
|
194
|
+
|
|
195
|
+
### `GET /api/gateway/provider`
|
|
196
|
+
Returns current provider configuration state.
|
|
197
|
+
|
|
198
|
+
**Auth:** any auth method
|
|
199
|
+
|
|
200
|
+
**Response:**
|
|
201
|
+
```json
|
|
202
|
+
{
|
|
203
|
+
"providers": [
|
|
204
|
+
{
|
|
205
|
+
"provider": "deepseek",
|
|
206
|
+
"label": "DeepSeek",
|
|
207
|
+
"configured": true,
|
|
208
|
+
"baseUrl": "https://api.deepseek.com",
|
|
209
|
+
"docsUrl": "https://platform.deepseek.com/api_keys",
|
|
210
|
+
"models": [
|
|
211
|
+
{ "id": "deepseek/deepseek-v4-flash", "name": "DeepSeek V4 Flash", "enabled": true },
|
|
212
|
+
{ "id": "deepseek/deepseek-v4-pro", "name": "DeepSeek V4 Pro", "enabled": true }
|
|
213
|
+
]
|
|
214
|
+
}
|
|
215
|
+
]
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### `GET /api/provider/v1/models`
|
|
220
|
+
|
|
221
|
+
Rev4a Provider Gateway — returns available models filtered by auth token.
|
|
222
|
+
|
|
223
|
+
**Auth:** Bearer token (must match `rev4a` key in `data/provider-keys.json`)
|
|
224
|
+
|
|
225
|
+
**Response (authenticated):**
|
|
226
|
+
```json
|
|
227
|
+
{
|
|
228
|
+
"object": "list",
|
|
229
|
+
"total": 9,
|
|
230
|
+
"data": [
|
|
231
|
+
{ "id": "deepseek/deepseek-v4-flash", "object": "model", "created": 1700000000, "owned_by": "deepseek", "permission": [], "root": "deepseek/deepseek-v4-flash" }
|
|
232
|
+
]
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**Response (unauthenticated):**
|
|
237
|
+
```json
|
|
238
|
+
{ "object": "list", "total": 0, "data": [] }
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### `POST /api/provider/v1/chat/completions`
|
|
242
|
+
|
|
243
|
+
Rev4a Provider Gateway — proxy chat completions to the correct upstream.
|
|
244
|
+
|
|
245
|
+
**Auth:** Bearer token (must match `rev4a` key in `data/provider-keys.json`)
|
|
246
|
+
|
|
247
|
+
**Body:**
|
|
248
|
+
```json
|
|
249
|
+
{
|
|
250
|
+
"model": "rev4a/deepseek-v4-flash",
|
|
251
|
+
"messages": [{"role": "user", "content": "Hello"}]
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
**Error (401):**
|
|
256
|
+
```json
|
|
257
|
+
{ "error": { "message": "Invalid API key...", "type": "authentication_error" } }
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## Config
|
|
263
|
+
|
|
264
|
+
### `PUT /api/config/env`
|
|
265
|
+
Update environment variables on the VPS host.
|
|
266
|
+
|
|
267
|
+
**Auth:** browser cookie
|
|
268
|
+
|
|
269
|
+
**Body:** `{ "REV4A_PASSWORD": "newpass", ... }`
|
|
270
|
+
|
|
271
|
+
**Side effects:**
|
|
272
|
+
- Writes to `.env` in the project root
|
|
273
|
+
- Automatically syncs to `/etc/systemd/system/rev4a.service` (removes all `Environment=REV4A_*` and `Environment=PROVIDER_*` lines, appends current vars)
|
|
274
|
+
- Runs `systemctl daemon-reload` so the next restart picks up the new environment
|
|
275
|
+
- Only keys prefixed with `REV4A_` or `PROVIDER_` are accepted
|
|
276
|
+
|
|
277
|
+
**Response:** `{ "status": "ok", "updated": ["REV4A_PASSWORD", ...] }`
|
|
278
|
+
|
|
279
|
+
**Error (400):** `{ "error": "Keys not allowed: ..." }`
|
|
280
|
+
|
|
281
|
+
### `POST /api/config/restart`
|
|
282
|
+
Restart the Rev4a server (via systemd).
|
|
283
|
+
|
|
284
|
+
**Auth:** browser cookie
|
|
285
|
+
|
|
286
|
+
**Response:** `{ "status": "ok", "message": "Service restarted" }`
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## Sessions
|
|
291
|
+
|
|
292
|
+
### `GET /api/sessions`
|
|
293
|
+
Returns all sessions (up to 2000), newest first, joined with lineage labels.
|
|
294
|
+
|
|
295
|
+
**Auth:** browser cookie
|
|
296
|
+
|
|
297
|
+
**Response:**
|
|
298
|
+
```json
|
|
299
|
+
[
|
|
300
|
+
{
|
|
301
|
+
"session_id": "agent:ops:main",
|
|
302
|
+
"parent_id": null,
|
|
303
|
+
"label": "Argus",
|
|
304
|
+
"model": "openai-codex/gpt-5.4",
|
|
305
|
+
"tokens_in": 12500,
|
|
306
|
+
"tokens_out": 3200,
|
|
307
|
+
"cost_usd": 0.085,
|
|
308
|
+
"status": "idle",
|
|
309
|
+
"task_preview": "Run hygiene audit...",
|
|
310
|
+
"started_at": 1749200000,
|
|
311
|
+
"ended_at": null,
|
|
312
|
+
"updated_at": 1749201000,
|
|
313
|
+
"trello_card_url": null,
|
|
314
|
+
"lineage_label": null,
|
|
315
|
+
"lineage_agent_name": null
|
|
316
|
+
}
|
|
317
|
+
]
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
### `GET /api/session?id=<session_id>`
|
|
323
|
+
Returns a single session with its events and child sessions.
|
|
324
|
+
|
|
325
|
+
**Auth:** browser cookie
|
|
326
|
+
|
|
327
|
+
**Query params:** `id` (required)
|
|
328
|
+
|
|
329
|
+
**Response:**
|
|
330
|
+
```json
|
|
331
|
+
{
|
|
332
|
+
"session": { ...session row... },
|
|
333
|
+
"events": [ ...event rows... ],
|
|
334
|
+
"children": [ ...session rows... ]
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
**Error:** `400` if `id` missing; `404` if not found.
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## Stats & Costs
|
|
343
|
+
|
|
344
|
+
### `GET /api/stats`
|
|
345
|
+
Current-month aggregate stats (tokens + cost) grouped by model.
|
|
346
|
+
|
|
347
|
+
**Auth:** Bearer or browser cookie
|
|
348
|
+
|
|
349
|
+
**Response:**
|
|
350
|
+
```json
|
|
351
|
+
{
|
|
352
|
+
"total": {
|
|
353
|
+
"total": 1.24,
|
|
354
|
+
"total_in": 850000,
|
|
355
|
+
"total_out": 210000,
|
|
356
|
+
"sessions": 47
|
|
357
|
+
},
|
|
358
|
+
"byModel": [
|
|
359
|
+
{ "model": "openai-codex/gpt-5.4", "cost": 0.95, "tokens_in": 600000, "tokens_out": 150000, "sessions": 30 }
|
|
360
|
+
]
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
### `GET /api/stats-since?ts=<unix_ms>`
|
|
367
|
+
Total cost (USD) since a given timestamp.
|
|
368
|
+
|
|
369
|
+
**Auth:** Bearer or browser cookie
|
|
370
|
+
|
|
371
|
+
**Query params:** `ts` — Unix timestamp in milliseconds
|
|
372
|
+
|
|
373
|
+
**Response:**
|
|
374
|
+
```json
|
|
375
|
+
{ "total": 0.42 }
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
### `GET /api/costs`
|
|
381
|
+
Full cost breakdown: today, 7d, 30d, all-time, per-model, plus cost-override for the current month.
|
|
382
|
+
|
|
383
|
+
**Auth:** browser cookie
|
|
384
|
+
|
|
385
|
+
**Response:**
|
|
386
|
+
```json
|
|
387
|
+
{
|
|
388
|
+
"today": 0.12,
|
|
389
|
+
"week": 1.85,
|
|
390
|
+
"month": 6.40,
|
|
391
|
+
"allTime": 42.10,
|
|
392
|
+
"override": null,
|
|
393
|
+
"byModel": [ ... ]
|
|
394
|
+
}
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
|
|
399
|
+
### `GET /api/cost-override?month=YYYY-MM`
|
|
400
|
+
Read manual cost override for a month.
|
|
401
|
+
|
|
402
|
+
**Auth:** Bearer or browser cookie
|
|
403
|
+
|
|
404
|
+
**Response:** `{ "month": "2026-06", "amount": 124.10, "note": "GitHub billing" }` or `{ "month": "2026-06", "amount": null, "note": null }` if not set.
|
|
405
|
+
|
|
406
|
+
---
|
|
407
|
+
|
|
408
|
+
### `POST /api/cost-override`
|
|
409
|
+
Set or update a manual cost override.
|
|
410
|
+
|
|
411
|
+
**Auth:** Bearer or browser cookie
|
|
412
|
+
|
|
413
|
+
**Body:** `{ "month": "2026-06", "amount": 124.10, "note": "GitHub billing" }`
|
|
414
|
+
|
|
415
|
+
**Response:** `{ "ok": true }`
|
|
416
|
+
|
|
417
|
+
**Error:** `400` if `amount` missing.
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
## Events
|
|
422
|
+
|
|
423
|
+
### `GET /api/events?limit=50&offset=0`
|
|
424
|
+
Paginated event log.
|
|
425
|
+
|
|
426
|
+
**Auth:** browser cookie
|
|
427
|
+
|
|
428
|
+
**Query params:** `limit` (default 50), `offset` (default 0)
|
|
429
|
+
|
|
430
|
+
**Response:**
|
|
431
|
+
```json
|
|
432
|
+
[
|
|
433
|
+
{
|
|
434
|
+
"id": 1042,
|
|
435
|
+
"ts": 1749201500000,
|
|
436
|
+
"session_id": "agent:ops:subagent:uuid",
|
|
437
|
+
"type": "spawn",
|
|
438
|
+
"data": "{\"task\":\"run hygiene...\"}"
|
|
439
|
+
}
|
|
440
|
+
]
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
---
|
|
444
|
+
|
|
445
|
+
### `GET /api/stream`
|
|
446
|
+
Server-Sent Events stream. Pushes a combined payload every ~3 s.
|
|
447
|
+
|
|
448
|
+
**Auth:** browser cookie
|
|
449
|
+
|
|
450
|
+
**Event format:**
|
|
451
|
+
```
|
|
452
|
+
data: {"events":[...],"sessions":[...],"costs":{"today":0.12},"lineage":[...]}
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
Use `EventSource` in the browser or `curl -N` for testing.
|
|
456
|
+
|
|
457
|
+
### `GET /api/workspace/stream`
|
|
458
|
+
Workspace filesystem change stream for the editor.
|
|
459
|
+
|
|
460
|
+
**Auth:** browser cookie
|
|
461
|
+
|
|
462
|
+
**Response:** `text/event-stream`
|
|
463
|
+
|
|
464
|
+
**Event types:**
|
|
465
|
+
- `workspace_ready` — initial snapshot metadata
|
|
466
|
+
- `workspace_changed` — added, modified, or removed files/directories
|
|
467
|
+
- `heartbeat` — emitted when no changes are detected
|
|
468
|
+
- `workspace_error` — emitted if a scan cycle fails
|
|
469
|
+
|
|
470
|
+
**Initial payload example:**
|
|
471
|
+
```json
|
|
472
|
+
{
|
|
473
|
+
"type": "workspace_ready",
|
|
474
|
+
"ts": 1751270000000,
|
|
475
|
+
"root": "/path/to/workspace",
|
|
476
|
+
"count": 42
|
|
477
|
+
}
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
**Change payload example:**
|
|
481
|
+
```json
|
|
482
|
+
{
|
|
483
|
+
"type": "workspace_changed",
|
|
484
|
+
"ts": 1751270003000,
|
|
485
|
+
"changed": [
|
|
486
|
+
{
|
|
487
|
+
"path": "/path/to/workspace/rev4a/README.md",
|
|
488
|
+
"rel_path": "rev4a/README.md",
|
|
489
|
+
"type": "file",
|
|
490
|
+
"size": 2048,
|
|
491
|
+
"mtimeMs": 1751270002999,
|
|
492
|
+
"change": "modified"
|
|
493
|
+
}
|
|
494
|
+
],
|
|
495
|
+
"truncated": false
|
|
496
|
+
}
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
**Notes:**
|
|
500
|
+
- The stream polls every 3 seconds.
|
|
501
|
+
- Hidden paths and directories such as `.trash` and `node_modules` are ignored.
|
|
502
|
+
- Only a fixed allowlist of file extensions is included.
|
|
503
|
+
|
|
504
|
+
---
|
|
505
|
+
|
|
506
|
+
## Tool Calls
|
|
507
|
+
|
|
508
|
+
### `GET /api/tool-calls?session_id=<id>`
|
|
509
|
+
Tool call events for a session.
|
|
510
|
+
|
|
511
|
+
**Auth:** Bearer or browser cookie
|
|
512
|
+
|
|
513
|
+
**Query params:** `session_id` (required)
|
|
514
|
+
|
|
515
|
+
**Response:**
|
|
516
|
+
```json
|
|
517
|
+
[
|
|
518
|
+
{ "id": 12, "ts": 1749200100000, "session_id": "...", "type": "tool_call", "data": "{...}" }
|
|
519
|
+
]
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
---
|
|
523
|
+
|
|
524
|
+
## Agents
|
|
525
|
+
|
|
526
|
+
### `GET /api/agents`
|
|
527
|
+
List running agent containers (Docker containers with `AGENT_ID` label) with full metadata.
|
|
528
|
+
|
|
529
|
+
**Auth:** browser cookie
|
|
530
|
+
|
|
531
|
+
**Response:**
|
|
532
|
+
```json
|
|
533
|
+
[
|
|
534
|
+
{
|
|
535
|
+
"id": "abc123def456",
|
|
536
|
+
"agentId": "prometheus",
|
|
537
|
+
"name": "prometheus",
|
|
538
|
+
"image": "openclaw-agent-base:latest",
|
|
539
|
+
"imageTag": "latest",
|
|
540
|
+
"template": "prometheus",
|
|
541
|
+
"status": "running",
|
|
542
|
+
"state": "running",
|
|
543
|
+
"ports": "",
|
|
544
|
+
"ip": "172.19.0.5",
|
|
545
|
+
"created": "2026-07-01T16:58:42.876829416Z",
|
|
546
|
+
"env": ["AGENT_ID=prometheus", "AGENT_HOSTNAME=<agent-name>.<your-domain>.com"],
|
|
547
|
+
"authToken": "***",
|
|
548
|
+
"traefikUrl": "http://187.77.156.41:3033#token=asdfghjkl"
|
|
549
|
+
}
|
|
550
|
+
]
|
|
551
|
+
|
|
552
|
+
**Notes:**
|
|
553
|
+
- The URL is always `http://<host-ip>:<port>#token=...` (every agent gets a host port mapping)
|
|
554
|
+
- The `authToken` uses the shared gateway token from `data/agents-token.json`
|
|
555
|
+
- Containers are discovered via Docker API with label filter `AGENT_ID`
|
|
556
|
+
- Template is inferred from the container image name + agent ID directory match in `agent-templates/`
|
|
557
|
+
|
|
558
|
+
---
|
|
559
|
+
|
|
560
|
+
### `POST /api/agents/create`
|
|
561
|
+
Create a new agent container from a template.
|
|
562
|
+
|
|
563
|
+
**Auth:** browser cookie
|
|
564
|
+
|
|
565
|
+
**Body:**
|
|
566
|
+
```json
|
|
567
|
+
{
|
|
568
|
+
"name": "my-agent",
|
|
569
|
+
"template": "prometheus",
|
|
570
|
+
"portRange": "3700-3709",
|
|
571
|
+
"model": "deepseek/deepseek-v4-flash",
|
|
572
|
+
"fallbacks": []
|
|
573
|
+
}
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
| Field | Required | Description |
|
|
577
|
+
|---|---|---|
|
|
578
|
+
| `name` | yes | Container name, also becomes `AGENT_ID` and subdomain |
|
|
579
|
+
| `template` | yes | Template name (directory in `agent-templates/`) |
|
|
580
|
+
| `portRange` | no | Optional port range (e.g. `3700-3709`) or single port (e.g. `3700`). Default: auto-assigned 10-port block |
|
|
581
|
+
| `model` | no | Primary model ID from `models.config.json` |
|
|
582
|
+
| `fallbacks` | no | Array of fallback model IDs |
|
|
583
|
+
|
|
584
|
+
**Every agent is created with a host port mapping:**
|
|
585
|
+
- Port block: `-p <start>-<end>:3000-<3000+offset>` — maps a range of host ports to the same range starting at container port 3000
|
|
586
|
+
- Single port: `-p <port>:3000` — maps a single host port to container port 3000
|
|
587
|
+
- URL format: `http://<host-ip>:<portStart>#token=...`
|
|
588
|
+
- Port 3740 is reserved for Rev4a
|
|
589
|
+
|
|
590
|
+
**Response:**
|
|
591
|
+
```json
|
|
592
|
+
{
|
|
593
|
+
"success": true,
|
|
594
|
+
"containerId": "00f5de1eb08d...",
|
|
595
|
+
"name": "my-agent",
|
|
596
|
+
"image": "openclaw-agent-base:latest",
|
|
597
|
+
"network": "rev4a-network",
|
|
598
|
+
"traefikUrl": "http://187.77.156.41:3700#token=asdfghjkl",
|
|
599
|
+
"port": 3700,
|
|
600
|
+
"portEnd": 3709,
|
|
601
|
+
"isBlock": true,
|
|
602
|
+
"portDisplay": "3700-3709"
|
|
603
|
+
}
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
**Post-creation pipeline:**
|
|
607
|
+
1. OpenClaw gateway starts with `--allow-unconfigured`, generating its own default config
|
|
608
|
+
2. The route waits for the gateway to be fully up (health check poll, up to 60 s)
|
|
609
|
+
3. Once ready, writes `gateway.controlUi.allowedOrigins` + `agents.defaults.model.primary` + fallbacks
|
|
610
|
+
4. Writes `models.providers.rev4a` + extraDirs + update config → single `openclaw.json` write → single `gateway restart`
|
|
611
|
+
|
|
612
|
+
> **Note:** The entrypoint no longer generates `openclaw.json`. All post-creation config is written by the create route after gateway readiness, eliminating the race condition where the entrypoint would overwrite synced provider config.
|
|
613
|
+
|
|
614
|
+
**Side effects:**
|
|
615
|
+
- Template files (`AGENTS.md`, `SOUL.md`, etc.) are copied into the container workspace via `docker cp`
|
|
616
|
+
- The agent URL is included in the response
|
|
617
|
+
- An event is logged to `data/events.db` if available
|
|
618
|
+
|
|
619
|
+
**Error:** `400` for validation errors, `409` if name/port already in use
|
|
620
|
+
|
|
621
|
+
---
|
|
622
|
+
|
|
623
|
+
### `DELETE /api/agents/[id]`
|
|
624
|
+
**Destructive.** Permanently deletes the agent: the container, its persistent
|
|
625
|
+
volume (`agent-<id>-data`), and **all** its backups (`agent-<id>-*.tar.gz` in the
|
|
626
|
+
`rev4a-backups` volume). This cannot be undone. Volume and backup removal are
|
|
627
|
+
best-effort — a missing volume or absent backups do not fail the delete.
|
|
628
|
+
|
|
629
|
+
The `id` is validated (`isValidAgentId`) before being interpolated into any
|
|
630
|
+
docker command.
|
|
631
|
+
|
|
632
|
+
**Auth:** browser cookie
|
|
633
|
+
|
|
634
|
+
**Response:**
|
|
635
|
+
```json
|
|
636
|
+
{ "success": true }
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
**Error (400):** `{ "success": false, "error": "Invalid agent id" }`
|
|
640
|
+
**Error (404):** `{ "error": "No container found with AGENT_ID '...'" }`
|
|
641
|
+
|
|
642
|
+
---
|
|
643
|
+
|
|
644
|
+
### `POST /api/agents/[id]/restart`
|
|
645
|
+
Restart an agent container (shorter than recreate — keeps everything intact).
|
|
646
|
+
|
|
647
|
+
**Auth:** browser cookie
|
|
648
|
+
|
|
649
|
+
**Response:**
|
|
650
|
+
```json
|
|
651
|
+
{ "success": true }
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
**Error (404):** `{ "error": "No container found with AGENT_ID '...'" }`
|
|
655
|
+
|
|
656
|
+
---
|
|
657
|
+
|
|
658
|
+
### `POST /api/agents/[id]/backup`
|
|
659
|
+
Create a backup of the agent's persistent volume.
|
|
660
|
+
|
|
661
|
+
**Auth:** browser cookie
|
|
662
|
+
|
|
663
|
+
**Response:**
|
|
664
|
+
```json
|
|
665
|
+
{ "success": true, "file": "agent-prometheus-2026-07-12_043512345.tar.gz" }
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
The backup file lives in Docker volume `rev4a-backups`. Backups exclude the npm
|
|
669
|
+
package cache (`~/.npm/_cacache/`) to keep them small (~1.5 MB instead of 227 MB).
|
|
670
|
+
|
|
671
|
+
**Error (404):** `{ "error": "No persistent volume found for agent '...'" }`
|
|
672
|
+
|
|
673
|
+
---
|
|
674
|
+
|
|
675
|
+
### `GET /api/agents/[id]/backup`
|
|
676
|
+
List available backups for a specific agent.
|
|
677
|
+
|
|
678
|
+
**Auth:** browser cookie
|
|
679
|
+
|
|
680
|
+
**Response:**
|
|
681
|
+
```json
|
|
682
|
+
{
|
|
683
|
+
"backups": [
|
|
684
|
+
{ "name": "agent-prometheus-2026-07-12_043512345.tar.gz", "size": "1.6 MB", "date": "12/07/2026 04:35" }
|
|
685
|
+
]
|
|
686
|
+
}
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
Backups are sorted most-recent-first.
|
|
690
|
+
|
|
691
|
+
---
|
|
692
|
+
|
|
693
|
+
### `DELETE /api/agents/[id]/backup?file=agent-prometheus-2026-07-12_043512345.tar.gz`
|
|
694
|
+
Remove a specific backup file.
|
|
695
|
+
|
|
696
|
+
**Auth:** browser cookie
|
|
697
|
+
|
|
698
|
+
**Response:**
|
|
699
|
+
```json
|
|
700
|
+
{ "success": true }
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
**Error (400):** if filename is invalid or contains path traversal
|
|
704
|
+
|
|
705
|
+
---
|
|
706
|
+
|
|
707
|
+
### `POST /api/agents/[id]/restore?file=agent-prometheus-2026-07-12_043512345.tar.gz`
|
|
708
|
+
Restore an agent's persistent volume from a backup.
|
|
709
|
+
|
|
710
|
+
The container is stopped, the volume content is replaced with the backup, then
|
|
711
|
+
the container is restarted. If no container exists with the given `AGENT_ID`,
|
|
712
|
+
the volume is restored anyway (useful for volume-only agents).
|
|
713
|
+
|
|
714
|
+
**Auth:** browser cookie
|
|
715
|
+
|
|
716
|
+
**Response:**
|
|
717
|
+
```json
|
|
718
|
+
{ "success": true, "container": "prometheus" }
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
**Error (404):** if the backup file is not found
|
|
722
|
+
|
|
723
|
+
---
|
|
724
|
+
|
|
725
|
+
### `POST /api/agents/[id]/recreate`
|
|
726
|
+
Rebuild the agent container from the **latest** `openclaw-agent-base:latest` image
|
|
727
|
+
while preserving the persistent volume (workspace files, credentials, state DB,
|
|
728
|
+
config). This is how an existing agent picks up image/OpenClaw/CLI updates: the
|
|
729
|
+
software lives in the image, the data in the volume.
|
|
730
|
+
|
|
731
|
+
Flow:
|
|
732
|
+
1. **Auto-backup first.** The volume is backed up to `agent-<id>-prerecreate-<ts>.tar.gz`
|
|
733
|
+
in `rev4a-backups`. **If the backup fails, the recreate is aborted** and the
|
|
734
|
+
running container is left untouched.
|
|
735
|
+
2. The old container is removed (`docker rm -f`) and a new one is created with the
|
|
736
|
+
same image tag, environment variables (`AGENT_*`, `MODEL_*`, `OPENCLAW_*`),
|
|
737
|
+
labels, port mappings, and network — reattaching the same volume.
|
|
738
|
+
3. After startup, `applyRuntimeConfig()` (from `lib/agent-setup.ts`) guarantees:
|
|
739
|
+
- `hooks.bootstrap-extra-files` (Rev4a system rules injection)
|
|
740
|
+
- `skills.load.extraDirs` (shared skills discovery)
|
|
741
|
+
- `models.providers.rev4a` (provider proxy, host-dependent)
|
|
742
|
+
All patches use `config patch --stdin` (deep-merge) — existing user
|
|
743
|
+
customizations are never overwritten.
|
|
744
|
+
|
|
745
|
+
On the next boot, the image entrypoint runs `openclaw doctor --non-interactive`
|
|
746
|
+
**only if the OpenClaw version changed** (safe migrations, no service restart),
|
|
747
|
+
aligning the volume's state/config to the new binary.
|
|
748
|
+
|
|
749
|
+
**Auth:** browser cookie
|
|
750
|
+
|
|
751
|
+
**Response:**
|
|
752
|
+
```json
|
|
753
|
+
{ "success": true, "name": "prometheus", "image": "openclaw-agent-base:latest", "network": "openclaw-core_default", "backup": "agent-prometheus-prerecreate-2026-07-12_195512345.tar.gz" }
|
|
754
|
+
```
|
|
755
|
+
|
|
756
|
+
**Error (500):** `{ "error": "Pre-recreate backup failed, aborting: ..." }`
|
|
757
|
+
|
|
758
|
+
---
|
|
759
|
+
|
|
760
|
+
## Channels
|
|
761
|
+
|
|
762
|
+
Channel APIs read/write agent channel configuration (Telegram) directly inside agent containers via `docker exec`. All endpoints require the agent container to be running.
|
|
763
|
+
|
|
764
|
+
### `GET /api/agents/[id]/channels`
|
|
765
|
+
Get channel configuration for an agent.
|
|
766
|
+
|
|
767
|
+
**Auth:** browser cookie
|
|
768
|
+
|
|
769
|
+
**Response:**
|
|
770
|
+
```json
|
|
771
|
+
{
|
|
772
|
+
"agentId": "atlas",
|
|
773
|
+
"telegram": {
|
|
774
|
+
"connected": true,
|
|
775
|
+
"botToken": "1234...5678",
|
|
776
|
+
"dmPolicy": "pairing",
|
|
777
|
+
"allowFrom": ["123456789"]
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
- `telegram` is `null` if not configured
|
|
783
|
+
- `botToken` is masked (first 4 + last 4 chars)
|
|
784
|
+
- `allowFrom` lists approved sender IDs
|
|
785
|
+
|
|
786
|
+
---
|
|
787
|
+
|
|
788
|
+
### `PUT /api/agents/[id]/channels/telegram`
|
|
789
|
+
Connect Telegram to an agent by saving a bot token.
|
|
790
|
+
|
|
791
|
+
**Auth:** browser cookie
|
|
792
|
+
|
|
793
|
+
**Body:**
|
|
794
|
+
```json
|
|
795
|
+
{
|
|
796
|
+
"botToken": "1234567890:ABCdefGHIjklMNOpqrsTUVwxyz"
|
|
797
|
+
}
|
|
798
|
+
```
|
|
799
|
+
|
|
800
|
+
| Field | Required | Description |
|
|
801
|
+
|---|---|---|
|
|
802
|
+
| `botToken` | yes | Bot token from @BotFather |
|
|
803
|
+
|
|
804
|
+
**Response (200):** `{ "success": true }`
|
|
805
|
+
|
|
806
|
+
**Response (400):** `{ "error": "botToken is required" }`
|
|
807
|
+
|
|
808
|
+
**Response (404):** `{ "error": "Agent container not found: ..." }`
|
|
809
|
+
|
|
810
|
+
**Side effects:**
|
|
811
|
+
- Writes `channels.telegram.botToken` and `channels.telegram.dmPolicy: "pairing"` to `/root/.openclaw/openclaw.json`
|
|
812
|
+
- Creates a binding for this agent → telegram channel
|
|
813
|
+
- If the token changed, wipes previous pairing data
|
|
814
|
+
- The agent must be restarted for the gateway to pick up the new config
|
|
815
|
+
|
|
816
|
+
---
|
|
817
|
+
|
|
818
|
+
### `DELETE /api/agents/[id]/channels/telegram`
|
|
819
|
+
Disconnect Telegram from an agent.
|
|
820
|
+
|
|
821
|
+
**Auth:** browser cookie
|
|
822
|
+
|
|
823
|
+
**Response (200):** `{ "success": true }`
|
|
824
|
+
|
|
825
|
+
**Response (404):** `{ "error": "Agent container not found: ..." }`
|
|
826
|
+
|
|
827
|
+
**Side effects:**
|
|
828
|
+
- Sets `channels.telegram.enabled: false`, removes `botToken` and `allowFrom`
|
|
829
|
+
- Removes the telegram binding
|
|
830
|
+
- Deletes pairing files (`telegram-pairing.json`, `telegram-default-allowFrom.json`)
|
|
831
|
+
- No restart required
|
|
832
|
+
|
|
833
|
+
---
|
|
834
|
+
|
|
835
|
+
### `GET /api/agents/[id]/channels/pairing?channel=telegram`
|
|
836
|
+
Get pending and approved pairings for a channel.
|
|
837
|
+
|
|
838
|
+
**Auth:** browser cookie
|
|
839
|
+
|
|
840
|
+
**Query params:**
|
|
841
|
+
| Param | Required | Description |
|
|
842
|
+
|---|---|---|
|
|
843
|
+
| `channel` | no | Channel name (default: `telegram`) |
|
|
844
|
+
|
|
845
|
+
**Response:**
|
|
846
|
+
```json
|
|
847
|
+
{
|
|
848
|
+
"pending": [
|
|
849
|
+
{ "code": "A1B2", "senderId": null }
|
|
850
|
+
],
|
|
851
|
+
"approved": [
|
|
852
|
+
{ "senderId": "123456789" }
|
|
853
|
+
]
|
|
854
|
+
}
|
|
855
|
+
```
|
|
856
|
+
|
|
857
|
+
- Pending codes are from `openclaw pairing list --json`
|
|
858
|
+
- Approved senders are from the credentials allowFrom file
|
|
859
|
+
|
|
860
|
+
---
|
|
861
|
+
|
|
862
|
+
### `POST /api/agents/[id]/channels/pairing?channel=telegram`
|
|
863
|
+
Approve a pending pairing code.
|
|
864
|
+
|
|
865
|
+
**Auth:** browser cookie
|
|
866
|
+
|
|
867
|
+
**Query params:**
|
|
868
|
+
| Param | Required | Description |
|
|
869
|
+
|---|---|---|
|
|
870
|
+
| `channel` | no | Channel name (default: `telegram`) |
|
|
871
|
+
|
|
872
|
+
**Body:**
|
|
873
|
+
```json
|
|
874
|
+
{ "code": "A1B2" }
|
|
875
|
+
```
|
|
876
|
+
|
|
877
|
+
**Response (200):** `{ "success": true }`
|
|
878
|
+
|
|
879
|
+
**Response (400):** `{ "error": "Pairing code is required" }`
|
|
880
|
+
|
|
881
|
+
**Response (404):** `{ "error": "..." }`
|
|
882
|
+
|
|
883
|
+
---
|
|
884
|
+
|
|
885
|
+
### `DELETE /api/agents/[id]/channels/pairing?channel=telegram&senderId=123456789`
|
|
886
|
+
Revoke an approved sender.
|
|
887
|
+
|
|
888
|
+
**Auth:** browser cookie
|
|
889
|
+
|
|
890
|
+
**Query params:**
|
|
891
|
+
| Param | Required | Description |
|
|
892
|
+
|---|---|---|
|
|
893
|
+
| `channel` | no | Channel name (default: `telegram`) |
|
|
894
|
+
| `senderId` | yes | Sender ID to revoke |
|
|
895
|
+
|
|
896
|
+
**Response (200):** `{ "success": true }`
|
|
897
|
+
|
|
898
|
+
**Response (400):** `{ "error": "senderId query param is required" }`
|
|
899
|
+
|
|
900
|
+
---
|
|
901
|
+
|
|
902
|
+
### `GET /api/agents/channels-summary`
|
|
903
|
+
Lightweight channel status for all running agents (used for TG chips on the agent list).
|
|
904
|
+
|
|
905
|
+
**Auth:** browser cookie
|
|
906
|
+
|
|
907
|
+
**Response:**
|
|
908
|
+
```json
|
|
909
|
+
{
|
|
910
|
+
"atlas": { "telegram": true },
|
|
911
|
+
"prometheus": { "telegram": false }
|
|
912
|
+
}
|
|
913
|
+
```
|
|
914
|
+
|
|
915
|
+
- Only includes agents with `AGENT_ID` Docker label
|
|
916
|
+
- `telegram: true` means the channel is connected and configured
|
|
917
|
+
- Cached for 15 seconds (maxDuration)
|
|
918
|
+
|
|
919
|
+
---
|
|
920
|
+
|
|
921
|
+
### `GET /api/agents/config-json`
|
|
922
|
+
Read the full `openclaw.json` from a running agent container (token masked).
|
|
923
|
+
Useful for inspecting the container config without SSH or terminal.
|
|
924
|
+
|
|
925
|
+
**Query params:** `name` (required) — Docker container name
|
|
926
|
+
|
|
927
|
+
**Response:**
|
|
928
|
+
```json
|
|
929
|
+
{
|
|
930
|
+
"name": "my-agent",
|
|
931
|
+
"config": {
|
|
932
|
+
"gateway": { "controlUi": { "allowedOrigins": ["..."], "dangerouslyDisableDeviceAuth": true } },
|
|
933
|
+
"agents": { "defaults": { "model": { "primary": "rev4a/deepseek/deepseek-v4-flash", "fallbacks": [] } } },
|
|
934
|
+
"models": { "providers": { "rev4a": { "baseUrl": "...", "apiKey": "***", "models": [...] } } }
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
```
|
|
938
|
+
|
|
939
|
+
**Error (404):** `{ "error": "Config file not found or empty in container" }`
|
|
940
|
+
**Error (400):** `{ "error": "name query param is required" }`
|
|
941
|
+
|
|
942
|
+
---
|
|
943
|
+
|
|
944
|
+
### `GET /api/agents/token`
|
|
945
|
+
Read the shared agents gateway token.
|
|
946
|
+
|
|
947
|
+
**Auth:** browser cookie
|
|
948
|
+
|
|
949
|
+
**Response:**
|
|
950
|
+
```json
|
|
951
|
+
{ "token": "asdfghjkl", "updated_at": 1751270000000 }
|
|
952
|
+
```
|
|
953
|
+
|
|
954
|
+
The token is stored in `data/agents-token.json`.
|
|
955
|
+
|
|
956
|
+
---
|
|
957
|
+
|
|
958
|
+
### `PUT /api/agents/token`
|
|
959
|
+
Update the shared agents gateway token and push it to all running agent containers.
|
|
960
|
+
|
|
961
|
+
**Auth:** browser cookie
|
|
962
|
+
|
|
963
|
+
**Body:**
|
|
964
|
+
```json
|
|
965
|
+
{ "token": "new-token-value" }
|
|
966
|
+
```
|
|
967
|
+
|
|
968
|
+
**Response:**
|
|
969
|
+
```json
|
|
970
|
+
{ "success": true, "containersUpdated": 2 }
|
|
971
|
+
```
|
|
972
|
+
|
|
973
|
+
**Side effects:**
|
|
974
|
+
- Writes the new token to `data/agents-token.json`
|
|
975
|
+
- Writes the token file (`/root/.agent-token`) into every running container with `AGENT_ID` label
|
|
976
|
+
- Restarts each container so the entrypoint picks up the new token via `OPENCLAW_GATEWAY_TOKEN` env
|
|
977
|
+
|
|
978
|
+
---
|
|
979
|
+
|
|
980
|
+
## Agent Image Management
|
|
981
|
+
|
|
982
|
+
### `GET /api/agents/image-status`
|
|
983
|
+
Check the status of the `openclaw-agent-base` Docker image.
|
|
984
|
+
|
|
985
|
+
**Auth:** public (no auth required)
|
|
986
|
+
|
|
987
|
+
**Response:**
|
|
988
|
+
```json
|
|
989
|
+
{
|
|
990
|
+
"exists": true,
|
|
991
|
+
"needsUpdate": false,
|
|
992
|
+
"downloading": false,
|
|
993
|
+
"localId": "sha256:a1b2c3d4e5f6..."
|
|
994
|
+
}
|
|
995
|
+
```
|
|
996
|
+
|
|
997
|
+
| Field | Description |
|
|
998
|
+
|---|---|
|
|
999
|
+
| `exists` | Whether `openclaw-agent-base:latest` exists locally |
|
|
1000
|
+
| `needsUpdate` | `true` if local manifest digest differs from remote registry |
|
|
1001
|
+
| `downloading` | Whether a download is currently running |
|
|
1002
|
+
| `localId` | SHA256 ID of the local image, or null |
|
|
1003
|
+
|
|
1004
|
+
### `POST /api/agents/download-image`
|
|
1005
|
+
Starts pulling or building the `openclaw-agent-base:latest` image. Returns 202
|
|
1006
|
+
Accepted immediately — the operation runs in the background and writes logs.
|
|
1007
|
+
The frontend polls `GET /api/agents/image-status` for progress updates.
|
|
1008
|
+
|
|
1009
|
+
**Auth:** public (no auth required)
|
|
1010
|
+
|
|
1011
|
+
**Response (202):**
|
|
1012
|
+
```json
|
|
1013
|
+
{ "status": "downloading" }
|
|
1014
|
+
```
|
|
1015
|
+
|
|
1016
|
+
**Error (409):**
|
|
1017
|
+
```json
|
|
1018
|
+
{ "error": "A download is already in progress" }
|
|
1019
|
+
```
|
|
1020
|
+
|
|
1021
|
+
### `DELETE /api/agents/abort-download`
|
|
1022
|
+
Kills an in-progress docker pull/build.
|
|
1023
|
+
|
|
1024
|
+
**Auth:** public (no auth required)
|
|
1025
|
+
|
|
1026
|
+
**Response:**
|
|
1027
|
+
```json
|
|
1028
|
+
{ "aborted": true }
|
|
1029
|
+
```
|
|
1030
|
+
|
|
1031
|
+
Image state is managed in `lib/buildAgentImage.ts`:
|
|
1032
|
+
- `downloadAgentImage(opts)` — attempts `docker pull` from registry first, then falls back to `docker build`
|
|
1033
|
+
all stdout/stderr to a log file, supports optional `onEvent` callback and `AbortSignal`
|
|
1034
|
+
- `abortDownload()` — sends SIGKILL to the child process, releases the download lock
|
|
1035
|
+
- `getDownloadLogPath()` — returns the path to the current/last download log file
|
|
1036
|
+
- `getIsDownloading()` — checks the in-memory lock flag
|
|
1037
|
+
|
|
1038
|
+
### `GET /api/agents-active`
|
|
1039
|
+
|
|
1040
|
+
### `GET /api/agents-active`
|
|
1041
|
+
Configured agents (from `openclaw.json`) enriched with recent session activity
|
|
1042
|
+
and workspace files.
|
|
1043
|
+
|
|
1044
|
+
**Auth:** browser cookie
|
|
1045
|
+
|
|
1046
|
+
**Response:**
|
|
1047
|
+
```json
|
|
1048
|
+
[
|
|
1049
|
+
{
|
|
1050
|
+
"agent_id": "ops",
|
|
1051
|
+
"label": "Argus",
|
|
1052
|
+
"displayName": "Argus",
|
|
1053
|
+
"config_model": "openai-codex/gpt-5.4",
|
|
1054
|
+
"workspace_path": "/data/.openclaw/workspace-ops/",
|
|
1055
|
+
"files": [ { "name": "MEMORY.md", "path": "...", "rel_path": "MEMORY.md", "type": "markdown" } ],
|
|
1056
|
+
"sessions": [ ...last 5 session rows... ],
|
|
1057
|
+
"status": "idle",
|
|
1058
|
+
"config": { ...raw agent config... }
|
|
1059
|
+
}
|
|
1060
|
+
]
|
|
1061
|
+
```
|
|
1062
|
+
|
|
1063
|
+
---
|
|
1064
|
+
|
|
1065
|
+
### `GET /api/agents-config`
|
|
1066
|
+
Read agents, Telegram accounts, and bindings from `openclaw.json` (sanitized — no raw tokens).
|
|
1067
|
+
|
|
1068
|
+
**Auth:** browser cookie
|
|
1069
|
+
|
|
1070
|
+
**Response:**
|
|
1071
|
+
```json
|
|
1072
|
+
{
|
|
1073
|
+
"agents": [ { "id": "ops", "label": "Argus", "model": "..." } ],
|
|
1074
|
+
"telegramAccounts": [ { "accountId": "argus", "tokenStatus": "masked", "enabled": true } ],
|
|
1075
|
+
"bindings": [ { "bindingKey": "0", "type": "telegram", "agentId": "ops" } ]
|
|
1076
|
+
}
|
|
1077
|
+
```
|
|
1078
|
+
|
|
1079
|
+
---
|
|
1080
|
+
|
|
1081
|
+
### `POST /api/agents-config`
|
|
1082
|
+
Update agents, Telegram accounts, or bindings. Writes to `openclaw.json` with atomic rename + backup.
|
|
1083
|
+
|
|
1084
|
+
**Auth:** browser cookie
|
|
1085
|
+
|
|
1086
|
+
**Body:**
|
|
1087
|
+
```json
|
|
1088
|
+
{
|
|
1089
|
+
"agents": [ { "currentId": "ops", "id": "ops", "name": "Argus" } ],
|
|
1090
|
+
"telegramAccounts": [],
|
|
1091
|
+
"bindings": []
|
|
1092
|
+
}
|
|
1093
|
+
```
|
|
1094
|
+
|
|
1095
|
+
**Response:** `{ "success": true, "data": { ...updated config payload... } }`
|
|
1096
|
+
|
|
1097
|
+
---
|
|
1098
|
+
|
|
1099
|
+
## Lineage
|
|
1100
|
+
|
|
1101
|
+
### `POST /api/lineage`
|
|
1102
|
+
Register a parent→child relationship between sessions.
|
|
1103
|
+
|
|
1104
|
+
**Auth:** browser cookie or Bearer
|
|
1105
|
+
|
|
1106
|
+
**Body:**
|
|
1107
|
+
```json
|
|
1108
|
+
{ "childId": "agent:ops:subagent:uuid", "parentId": "agent:ops:main", "label": "Hygiene Agent" }
|
|
1109
|
+
```
|
|
1110
|
+
|
|
1111
|
+
**Response:** `{ "ok": true, "childId": "...", "parentId": "...", "label": "Hygiene Agent" }`
|
|
1112
|
+
|
|
1113
|
+
**Error:** `400` if `childId` or `parentId` missing.
|
|
1114
|
+
|
|
1115
|
+
---
|
|
1116
|
+
|
|
1117
|
+
## Metrics
|
|
1118
|
+
|
|
1119
|
+
### `GET /api/metrics`
|
|
1120
|
+
System metrics: latest snapshot + 24 h history + 24 h aggregates.
|
|
1121
|
+
|
|
1122
|
+
**Auth:** Bearer or browser cookie
|
|
1123
|
+
|
|
1124
|
+
**Response:**
|
|
1125
|
+
```json
|
|
1126
|
+
{
|
|
1127
|
+
"latest": { "ts": 1749201000000, "cpu_percent": 12.5, "ram_used_mb": 1820, "ram_total_mb": 4096, "disk_used_gb": 38.2, "disk_total_gb": 100.0, "load_avg_1m": 0.42 },
|
|
1128
|
+
"history": [ ...up to 288 rows (24h at 5min intervals)... ],
|
|
1129
|
+
"stats_24h": { "avg_cpu": 14.2, "max_cpu": 68.0, "avg_ram_mb": 1750 }
|
|
1130
|
+
}
|
|
1131
|
+
```
|
|
1132
|
+
|
|
1133
|
+
---
|
|
1134
|
+
|
|
1135
|
+
## System Health
|
|
1136
|
+
|
|
1137
|
+
### `GET /api/system-health`
|
|
1138
|
+
Aggregated health checks with recommendations.
|
|
1139
|
+
|
|
1140
|
+
**Auth:** browser cookie
|
|
1141
|
+
|
|
1142
|
+
**Response:**
|
|
1143
|
+
```json
|
|
1144
|
+
{
|
|
1145
|
+
"health": "ok",
|
|
1146
|
+
"checks": [
|
|
1147
|
+
{ "name": "daemon", "status": "ok", "detail": "last poll 18s ago" },
|
|
1148
|
+
{ "name": "disk", "status": "warn", "detail": "82% used" }
|
|
1149
|
+
],
|
|
1150
|
+
"recommendations": [ "Consider archiving old sessions to reduce disk usage." ],
|
|
1151
|
+
"generatedAt": 1749201500000
|
|
1152
|
+
}
|
|
1153
|
+
```
|
|
1154
|
+
|
|
1155
|
+
`health` values: `"ok"` / `"warn"` / `"error"`
|
|
1156
|
+
|
|
1157
|
+
---
|
|
1158
|
+
|
|
1159
|
+
## Crons
|
|
1160
|
+
|
|
1161
|
+
### `GET /api/crons`
|
|
1162
|
+
List of scheduled cron jobs from the host OpenClaw gateway (via `openclaw cron list --json`) and every running Docker container with the `AGENT_ID` label (via `docker exec openclaw cron list --json`).
|
|
1163
|
+
|
|
1164
|
+
Jobs from agent containers include `containerName` and `source: "container"` fields.
|
|
1165
|
+
Jobs from the host gateway have `source: "host"`.
|
|
1166
|
+
|
|
1167
|
+
**Auth:** Bearer or browser cookie
|
|
1168
|
+
|
|
1169
|
+
**Response:**
|
|
1170
|
+
```json
|
|
1171
|
+
[
|
|
1172
|
+
{
|
|
1173
|
+
"id": "eef708de-...",
|
|
1174
|
+
"name": "openclaw-hygiene-nightly",
|
|
1175
|
+
"description": "Nightly workspace cleanup",
|
|
1176
|
+
"schedule": "15 3 * * *",
|
|
1177
|
+
"scheduleExpr": "15 3 * * *",
|
|
1178
|
+
"model": "openai-codex/gpt-5.4-mini",
|
|
1179
|
+
"enabled": true,
|
|
1180
|
+
"source": "host",
|
|
1181
|
+
"state": {
|
|
1182
|
+
"lastStatus": "ok",
|
|
1183
|
+
"lastRunAtMs": 1749100000000,
|
|
1184
|
+
"nextRunAtMs": 1749186000000
|
|
1185
|
+
},
|
|
1186
|
+
"computedNextRunAtMs": 1749186000000,
|
|
1187
|
+
"createdAtMs": 1748000000000
|
|
1188
|
+
},
|
|
1189
|
+
{
|
|
1190
|
+
"id": "c1b3e47f-...",
|
|
1191
|
+
"name": "atlas-weekly-report",
|
|
1192
|
+
"schedule": "0 9 * * 1",
|
|
1193
|
+
"enabled": true,
|
|
1194
|
+
"source": "container",
|
|
1195
|
+
"agentId": "atlas",
|
|
1196
|
+
"containerName": "openclaw-atlas",
|
|
1197
|
+
"computedNextRunAtMs": 1749600000000
|
|
1198
|
+
}
|
|
1199
|
+
]
|
|
1200
|
+
```
|
|
1201
|
+
|
|
1202
|
+
### `PATCH /api/crons/[id]`
|
|
1203
|
+
Update a cron job. Only the `enabled` field can be patched.
|
|
1204
|
+
|
|
1205
|
+
For jobs inside a container, pass `containerName` in the body to identify which container to write to.
|
|
1206
|
+
|
|
1207
|
+
**Auth:** Bearer or browser cookie
|
|
1208
|
+
|
|
1209
|
+
**Body:**
|
|
1210
|
+
```json
|
|
1211
|
+
{
|
|
1212
|
+
"enabled": false
|
|
1213
|
+
}
|
|
1214
|
+
```
|
|
1215
|
+
|
|
1216
|
+
**Body (container cron):**
|
|
1217
|
+
```json
|
|
1218
|
+
{
|
|
1219
|
+
"enabled": false,
|
|
1220
|
+
"containerName": "openclaw-atlas"
|
|
1221
|
+
}
|
|
1222
|
+
```
|
|
1223
|
+
|
|
1224
|
+
**Response:**
|
|
1225
|
+
```json
|
|
1226
|
+
{
|
|
1227
|
+
"id": "eef708de-...",
|
|
1228
|
+
"name": "openclaw-hygiene-nightly",
|
|
1229
|
+
"enabled": false,
|
|
1230
|
+
"scheduleExpr": "15 3 * * *"
|
|
1231
|
+
}
|
|
1232
|
+
```
|
|
1233
|
+
|
|
1234
|
+
### `DELETE /api/crons/[id]`
|
|
1235
|
+
Remove a cron job permanently. For jobs inside a container, pass `containerName` as a query parameter.
|
|
1236
|
+
|
|
1237
|
+
**Auth:** Bearer or browser cookie
|
|
1238
|
+
|
|
1239
|
+
**Query params:**
|
|
1240
|
+
- `containerName` (optional) — Docker container name for container-scoped jobs
|
|
1241
|
+
|
|
1242
|
+
**Response:**
|
|
1243
|
+
```json
|
|
1244
|
+
{ "ok": true }
|
|
1245
|
+
```
|
|
1246
|
+
|
|
1247
|
+
**Error (404):** `{ "error": "Job ... not found" }`
|
|
1248
|
+
**Error (502):** `{ "error": "Failed to remove cron job in container ..." }`
|
|
1249
|
+
|
|
1250
|
+
### `GET /api/crons/runs`
|
|
1251
|
+
Run history for a specific cron job. Queries the container's gateway via `openclaw cron runs --id <jobId> --limit <limit>`.
|
|
1252
|
+
|
|
1253
|
+
**Auth:** Bearer or browser cookie
|
|
1254
|
+
|
|
1255
|
+
**Query params:**
|
|
1256
|
+
- `containerName` (required) — Docker container name
|
|
1257
|
+
- `jobId` (required) — cron job UUID
|
|
1258
|
+
- `limit` (optional, default 20, max 100) — number of recent runs to return
|
|
1259
|
+
|
|
1260
|
+
**Response:**
|
|
1261
|
+
```json
|
|
1262
|
+
{
|
|
1263
|
+
"entries": [
|
|
1264
|
+
{
|
|
1265
|
+
"ts": 1784820036635,
|
|
1266
|
+
"jobId": "7a607208-...",
|
|
1267
|
+
"action": "finished",
|
|
1268
|
+
"status": "error",
|
|
1269
|
+
"error": "Channel is required ...",
|
|
1270
|
+
"runAtMs": 1784820025581,
|
|
1271
|
+
"durationMs": 11046,
|
|
1272
|
+
"model": "deepseek/deepseek-v4-flash",
|
|
1273
|
+
"usage": { "input_tokens": 15872, "output_tokens": 597 }
|
|
1274
|
+
}
|
|
1275
|
+
]
|
|
1276
|
+
}
|
|
1277
|
+
```
|
|
1278
|
+
|
|
1279
|
+
On gateway-connection failure:
|
|
1280
|
+
```json
|
|
1281
|
+
{ "error": "Cannot connect to container gateway for runs", "entries": [] }
|
|
1282
|
+
```
|
|
1283
|
+
(HTTP 502)
|
|
1284
|
+
|
|
1285
|
+
---
|
|
1286
|
+
|
|
1287
|
+
## Memory & Context
|
|
1288
|
+
|
|
1289
|
+
### `GET /api/memory-context`
|
|
1290
|
+
Memory context snapshot for the current agent workspace.
|
|
1291
|
+
|
|
1292
|
+
**Auth:** browser cookie
|
|
1293
|
+
|
|
1294
|
+
**Response:**
|
|
1295
|
+
```json
|
|
1296
|
+
{
|
|
1297
|
+
"files": [ { "path": "MEMORY.md", "sizeBytes": 13800, "lastModified": 1749200000000 } ],
|
|
1298
|
+
"totalBytes": 29800,
|
|
1299
|
+
"budgetBytes": 25600,
|
|
1300
|
+
"overBudget": true
|
|
1301
|
+
}
|
|
1302
|
+
```
|
|
1303
|
+
|
|
1304
|
+
---
|
|
1305
|
+
|
|
1306
|
+
## Workspace
|
|
1307
|
+
|
|
1308
|
+
Full design, decisions, and usage guide at [WORKSPACE.md](WORKSPACE.md).
|
|
1309
|
+
|
|
1310
|
+
### `GET /api/workspace?action=list`
|
|
1311
|
+
List available workspaces (VPS host + Docker containers with `AGENT_ID` label).
|
|
1312
|
+
|
|
1313
|
+
**Auth:** Bearer, query param, x-agent-token, or browser cookie
|
|
1314
|
+
|
|
1315
|
+
**Response:**
|
|
1316
|
+
```json
|
|
1317
|
+
{
|
|
1318
|
+
"workspaces": [
|
|
1319
|
+
{ "id": "vps", "label": "VPS Host (Nexus)", "type": "host" },
|
|
1320
|
+
{ "id": "container-agent_8ba21f83", "label": "Giuanmuzzziins", "type": "container" }
|
|
1321
|
+
]
|
|
1322
|
+
}
|
|
1323
|
+
```
|
|
1324
|
+
|
|
1325
|
+
### `GET /api/workspace?workspace=<id>`
|
|
1326
|
+
List files in a workspace, read a file, or return a recursive tree payload.
|
|
1327
|
+
|
|
1328
|
+
**Auth:** any auth method
|
|
1329
|
+
|
|
1330
|
+
**Query params:** `workspace` (optional, defaults to `vps`), `path` (optional), `tree=1` (optional)
|
|
1331
|
+
|
|
1332
|
+
**Response (directory):**
|
|
1333
|
+
```json
|
|
1334
|
+
{
|
|
1335
|
+
"workspace": "vps",
|
|
1336
|
+
"label": "VPS Host (Nexus)",
|
|
1337
|
+
"path": "/path/to/workspace",
|
|
1338
|
+
"type": "host",
|
|
1339
|
+
"files": [
|
|
1340
|
+
{ "name": "config", "isDirectory": true, "isFile": false, "path": "..." },
|
|
1341
|
+
{ "name": "SOUL.md", "isDirectory": false, "isFile": true, "path": "..." }
|
|
1342
|
+
]
|
|
1343
|
+
}
|
|
1344
|
+
```
|
|
1345
|
+
|
|
1346
|
+
**Response (file):** Same structure with `content` field instead of `files`.
|
|
1347
|
+
|
|
1348
|
+
### `PUT /api/workspace`
|
|
1349
|
+
Write content to a workspace file.
|
|
1350
|
+
|
|
1351
|
+
**Auth:** any auth method
|
|
1352
|
+
|
|
1353
|
+
**Body:** `{ "workspace": "vps", "path": "/path/to/workspace/test.md", "content": "hello" }`
|
|
1354
|
+
|
|
1355
|
+
**Response:** `{ "ok": true }`
|
|
1356
|
+
|
|
1357
|
+
---
|
|
1358
|
+
|
|
1359
|
+
## Vault
|
|
1360
|
+
|
|
1361
|
+
Full key management documentation at [PROVIDERS.md](PROVIDERS.md).
|
|
1362
|
+
|
|
1363
|
+
### `GET /api/vault`
|
|
1364
|
+
List all stored credentials (keys masked).
|
|
1365
|
+
|
|
1366
|
+
**Auth:** browser cookie
|
|
1367
|
+
|
|
1368
|
+
**Response:**
|
|
1369
|
+
```json
|
|
1370
|
+
{
|
|
1371
|
+
"providers": {
|
|
1372
|
+
"deepseek": { "keyStatus": "present", "scoped": ["atlas"] }
|
|
1373
|
+
},
|
|
1374
|
+
"services": {
|
|
1375
|
+
"github": { "tokenStatus": "present", "user": "Flame0510", "scoped": ["argus", "atlas"] }
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
```
|
|
1379
|
+
|
|
1380
|
+
### `GET /api/vault/provider/key`
|
|
1381
|
+
Return the full API key for a provider.
|
|
1382
|
+
|
|
1383
|
+
**Auth:** browser cookie
|
|
1384
|
+
|
|
1385
|
+
**Query params:** `provider` (required), `agent` (optional)
|
|
1386
|
+
|
|
1387
|
+
**Response (200):**
|
|
1388
|
+
```json
|
|
1389
|
+
{
|
|
1390
|
+
"provider": "deepseek",
|
|
1391
|
+
"apiKey": "sk-...full-key...",
|
|
1392
|
+
"masked": "sk-...be03",
|
|
1393
|
+
"source": "local"
|
|
1394
|
+
}
|
|
1395
|
+
```
|
|
1396
|
+
|
|
1397
|
+
**Error:** `400` if `provider` missing; `404` if key not found.
|
|
1398
|
+
|
|
1399
|
+
See [PROVIDERS.md](PROVIDERS.md#get-apivaultproviderkey) for full detail.
|
|
1400
|
+
|
|
1401
|
+
### `POST /api/vault/provider`
|
|
1402
|
+
Add or update a provider API key.
|
|
1403
|
+
|
|
1404
|
+
**Auth:** browser cookie
|
|
1405
|
+
|
|
1406
|
+
**Body:** `{ "provider": "deepseek", "apiKey": "sk-...", "baseUrl": "https://api.deepseek.com" }`
|
|
1407
|
+
|
|
1408
|
+
**Response:** `{ "status": "ok", "provider": "deepseek", "masked": "sk-...be03", "updatedAt": 1749200000000 }`
|
|
1409
|
+
|
|
1410
|
+
### `DELETE /api/vault/provider`
|
|
1411
|
+
Remove a provider and all its keys.
|
|
1412
|
+
|
|
1413
|
+
**Auth:** browser cookie
|
|
1414
|
+
|
|
1415
|
+
**Body:** `{ "provider": "deepseek" }`
|
|
1416
|
+
|
|
1417
|
+
**Response:** `{ "status": "removed" }`
|
|
1418
|
+
|
|
1419
|
+
### `PUT /api/vault/service`
|
|
1420
|
+
Add or update a service token.
|
|
1421
|
+
|
|
1422
|
+
**Auth:** browser cookie
|
|
1423
|
+
|
|
1424
|
+
**Body:** `{ "id": "github", "token": "***", "user": "Flame0510", "scopes": ["atlas"] }`
|
|
1425
|
+
|
|
1426
|
+
**Response:** `{ "ok": true }`
|
|
1427
|
+
|
|
1428
|
+
### `DELETE /api/vault/service`
|
|
1429
|
+
Remove a service.
|
|
1430
|
+
|
|
1431
|
+
**Auth:** browser cookie
|
|
1432
|
+
|
|
1433
|
+
**Body:** `{ "id": "github" }`
|
|
1434
|
+
|
|
1435
|
+
**Response:** `{ "ok": true }`
|
|
1436
|
+
|
|
1437
|
+
### `PUT /api/vault/permissions`
|
|
1438
|
+
Update agent permissions for a credential.
|
|
1439
|
+
|
|
1440
|
+
**Body:** `{ "type": "provider" | "service", "id": "deepseek", "scopes": ["atlas", "argus"] }`
|
|
1441
|
+
|
|
1442
|
+
**Response:** `{ "ok": true }`
|
|
1443
|
+
|
|
1444
|
+
---
|
|
1445
|
+
|
|
1446
|
+
## Containers
|
|
1447
|
+
|
|
1448
|
+
### `GET /api/containers`
|
|
1449
|
+
List all running Docker containers with resource usage.
|
|
1450
|
+
|
|
1451
|
+
**Auth:** browser cookie
|
|
1452
|
+
|
|
1453
|
+
**Response:**
|
|
1454
|
+
```json
|
|
1455
|
+
{
|
|
1456
|
+
"containers": [
|
|
1457
|
+
{
|
|
1458
|
+
"name": "openclaw-atlas",
|
|
1459
|
+
"image": "openclaw-agent-base:latest",
|
|
1460
|
+
"status": "running",
|
|
1461
|
+
"ports": ["0.0.0.0:3731->3000/tcp"],
|
|
1462
|
+
"created": "2026-06-20T10:00:00Z",
|
|
1463
|
+
"cpu_percent": 2.1,
|
|
1464
|
+
"mem_usage_mb": 340
|
|
1465
|
+
}
|
|
1466
|
+
]
|
|
1467
|
+
}
|
|
1468
|
+
```
|
|
1469
|
+
|
|
1470
|
+
### `GET /api/containers/logs?name=<name>&tail=<lines>`
|
|
1471
|
+
Get container logs.
|
|
1472
|
+
|
|
1473
|
+
**Auth:** browser cookie
|
|
1474
|
+
|
|
1475
|
+
**Query params:** `name` (required), `tail` (default 50)
|
|
1476
|
+
|
|
1477
|
+
**Response:** `{ "name": "openclaw-atlas", "logs": "[log lines...]" }`
|
|
1478
|
+
|
|
1479
|
+
### `POST /api/containers/action`
|
|
1480
|
+
Execute action on a container.
|
|
1481
|
+
|
|
1482
|
+
**Body:** `{ "action": "restart" | "stop" | "start", "name": "openclaw-atlas" }`
|
|
1483
|
+
|
|
1484
|
+
**Response:** `{ "ok": true, "message": "Container openclaw-atlas restarted" }`
|
|
1485
|
+
|
|
1486
|
+
---
|
|
1487
|
+
|
|
1488
|
+
## Tools
|
|
1489
|
+
|
|
1490
|
+
### `GET /api/tools-config`
|
|
1491
|
+
Read audio/TTS and timezone tool configuration.
|
|
1492
|
+
|
|
1493
|
+
**Auth:** browser cookie
|
|
1494
|
+
|
|
1495
|
+
**Response:** `{ "audio": { ... }, "timezone": "Europe/Rome" }`
|
|
1496
|
+
|
|
1497
|
+
### `POST /api/tools-config`
|
|
1498
|
+
Update audio or timezone config in `openclaw.json`.
|
|
1499
|
+
|
|
1500
|
+
**Body:** `{ "timezone": "America/New_York" }` or `{ "audio": { ... } }`
|
|
1501
|
+
|
|
1502
|
+
**Response:** `{ "ok": true, "timezone": "America/New_York", "audio": { ... } }`
|
|
1503
|
+
|
|
1504
|
+
---
|
|
1505
|
+
|
|
1506
|
+
## Version & WebSocket
|
|
1507
|
+
|
|
1508
|
+
### `GET /api/version`
|
|
1509
|
+
Return the installed OpenClaw CLI version.
|
|
1510
|
+
|
|
1511
|
+
**Auth:** browser cookie
|
|
1512
|
+
|
|
1513
|
+
**Response:**
|
|
1514
|
+
```json
|
|
1515
|
+
{ "version": "openclaw x.y.z" }
|
|
1516
|
+
```
|
|
1517
|
+
|
|
1518
|
+
**Fallback:** `{ "version": "unknown" }` if the command fails.
|
|
1519
|
+
|
|
1520
|
+
### `GET /api/ws`
|
|
1521
|
+
Documentation placeholder for WebSocket access.
|
|
1522
|
+
|
|
1523
|
+
**Auth:** browser cookie
|
|
1524
|
+
|
|
1525
|
+
**Response:** plain text with HTTP `426 Upgrade Required`
|
|
1526
|
+
|
|
1527
|
+
Example body:
|
|
1528
|
+
```text
|
|
1529
|
+
WebSocket endpoint available at ws://HOST/ws. This route is a documentation placeholder and does not upgrade connections.
|
|
1530
|
+
```
|
|
1531
|
+
|
|
1532
|
+
### `GET /ws`
|
|
1533
|
+
Live WebSocket endpoint exposed by the custom server.
|
|
1534
|
+
|
|
1535
|
+
**Auth:** same browser session as the dashboard
|
|
1536
|
+
|
|
1537
|
+
**Protocol notes:**
|
|
1538
|
+
- Hosted on the main HTTP port, not under `/api/`
|
|
1539
|
+
- Accepts JSON messages such as `chat.send` and `chat.history`
|
|
1540
|
+
- Intended for the dashboard client rather than generic REST consumers
|
|
1541
|
+
|
|
1542
|
+
---
|
|
1543
|
+
|
|
1544
|
+
## Plugins
|
|
1545
|
+
|
|
1546
|
+
### `GET /api/plugins`
|
|
1547
|
+
List installed OpenClaw plugins.
|
|
1548
|
+
|
|
1549
|
+
**Auth:** browser cookie
|
|
1550
|
+
|
|
1551
|
+
**Response:** `{ "plugins": [ { "id": "file-transfer", "enabled": true, ... } ] }`
|
|
1552
|
+
|
|
1553
|
+
### `POST /api/plugins`
|
|
1554
|
+
Enable or disable a plugin.
|
|
1555
|
+
|
|
1556
|
+
**Body:** `{ "action": "enable" | "disable", "pluginId": "file-transfer" }`
|
|
1557
|
+
|
|
1558
|
+
**Response:** `{ "ok": true }`
|
|
1559
|
+
|
|
1560
|
+
---
|
|
1561
|
+
|
|
1562
|
+
## Skills
|
|
1563
|
+
|
|
1564
|
+
Skill discovery across three sources:
|
|
1565
|
+
- **Shared:** skills in `/docker/shared-skills` on the VPS host (global, available to all agents)
|
|
1566
|
+
- **Workspace:** skills in `<workspace>/skills` inside each agent container (discovered via `docker exec`)
|
|
1567
|
+
- **Bundled:** skills shipped with OpenClaw (read-only)
|
|
1568
|
+
|
|
1569
|
+
### `GET /api/skills`
|
|
1570
|
+
List all available skills from all sources.
|
|
1571
|
+
|
|
1572
|
+
**Auth:** browser cookie
|
|
1573
|
+
|
|
1574
|
+
**Response:**
|
|
1575
|
+
```json
|
|
1576
|
+
{
|
|
1577
|
+
"skills": [
|
|
1578
|
+
{
|
|
1579
|
+
"name": "code-review",
|
|
1580
|
+
"type": "shared",
|
|
1581
|
+
"path": "/docker/shared-skills/code-review",
|
|
1582
|
+
"skillMdPath": "/docker/shared-skills/code-review/SKILL.md",
|
|
1583
|
+
"hasSkillMd": true,
|
|
1584
|
+
"description": "Systematic code review patterns...",
|
|
1585
|
+
"version": "1.0"
|
|
1586
|
+
},
|
|
1587
|
+
{
|
|
1588
|
+
"name": "my-custom-skill",
|
|
1589
|
+
"type": "workspace",
|
|
1590
|
+
"agentId": "atlas",
|
|
1591
|
+
"containerName": "openclaw-atlas",
|
|
1592
|
+
"path": "/root/.openclaw/workspace/skills/my-custom-skill",
|
|
1593
|
+
"skillMdPath": "/root/.openclaw/workspace/skills/my-custom-skill/SKILL.md",
|
|
1594
|
+
"hasSkillMd": true,
|
|
1595
|
+
"description": "Custom agent skill"
|
|
1596
|
+
}
|
|
1597
|
+
]
|
|
1598
|
+
}
|
|
1599
|
+
```
|
|
1600
|
+
|
|
1601
|
+
### `GET /api/skills/save?path=<path>[&containerName=<name>]`
|
|
1602
|
+
Read a SKILL.md file. For container skills, pass `containerName`.
|
|
1603
|
+
|
|
1604
|
+
**Auth:** browser cookie
|
|
1605
|
+
|
|
1606
|
+
**Response:** `{ "content": "---\nname: my-skill\n..." }`
|
|
1607
|
+
|
|
1608
|
+
### `POST /api/skills/save`
|
|
1609
|
+
Write a SKILL.md file. Supports shared directory and container workspaces.
|
|
1610
|
+
|
|
1611
|
+
**Auth:** browser cookie
|
|
1612
|
+
|
|
1613
|
+
**Body:**
|
|
1614
|
+
```json
|
|
1615
|
+
{
|
|
1616
|
+
"skillPath": "/root/.openclaw/workspace/skills/my-skill/SKILL.md",
|
|
1617
|
+
"content": "---\nname: my-skill\n...",
|
|
1618
|
+
"containerName": "openclaw-atlas"
|
|
1619
|
+
}
|
|
1620
|
+
```
|
|
1621
|
+
- `containerName` is required for container skills, omitted for shared/host skills
|
|
1622
|
+
- Bundled skills are read-only
|
|
1623
|
+
|
|
1624
|
+
**Response:** `{ "ok": true, "saved": "..." }`
|
|
1625
|
+
|
|
1626
|
+
### `POST /api/skills/promote`
|
|
1627
|
+
Copy a skill from an agent's workspace to the shared skills directory, making it available to all agents.
|
|
1628
|
+
|
|
1629
|
+
**Auth:** browser cookie
|
|
1630
|
+
|
|
1631
|
+
**Body:**
|
|
1632
|
+
```json
|
|
1633
|
+
{
|
|
1634
|
+
"containerName": "openclaw-atlas",
|
|
1635
|
+
"skillName": "my-custom-skill"
|
|
1636
|
+
}
|
|
1637
|
+
```
|
|
1638
|
+
|
|
1639
|
+
**Response:**
|
|
1640
|
+
```json
|
|
1641
|
+
{
|
|
1642
|
+
"ok": true,
|
|
1643
|
+
"skillName": "my-custom-skill",
|
|
1644
|
+
"agentId": "atlas",
|
|
1645
|
+
"containerName": "openclaw-atlas",
|
|
1646
|
+
"destPath": "/docker/shared-skills/my-custom-skill",
|
|
1647
|
+
"overwritten": false,
|
|
1648
|
+
"extraCopied": 0
|
|
1649
|
+
}
|
|
1650
|
+
```
|
|
1651
|
+
|
|
1652
|
+
- `overwritten`: `true` if a shared skill with the same name already existed
|
|
1653
|
+
- `extraCopied`: number of additional files (beyond SKILL.md) copied from the skill directory
|
|
1654
|
+
- The skill name is validated against `^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\/[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$`
|
|
1655
|
+
- Container must have an `AGENT_ID` Docker label
|
|
1656
|
+
|
|
1657
|
+
### `DELETE /api/skills/delete`
|
|
1658
|
+
Delete a skill from shared skills directory or from an agent's workspace.
|
|
1659
|
+
|
|
1660
|
+
**Auth:** browser cookie
|
|
1661
|
+
|
|
1662
|
+
**Body (shared skill):**
|
|
1663
|
+
```json
|
|
1664
|
+
{
|
|
1665
|
+
"skillName": "my-custom-skill"
|
|
1666
|
+
}
|
|
1667
|
+
```
|
|
1668
|
+
|
|
1669
|
+
**Body (workspace/container skill):**
|
|
1670
|
+
```json
|
|
1671
|
+
{
|
|
1672
|
+
"skillName": "my-custom-skill",
|
|
1673
|
+
"containerName": "openclaw-atlas"
|
|
1674
|
+
}
|
|
1675
|
+
```
|
|
1676
|
+
|
|
1677
|
+
**Response:**
|
|
1678
|
+
```json
|
|
1679
|
+
{
|
|
1680
|
+
"ok": true,
|
|
1681
|
+
"skillName": "my-custom-skill",
|
|
1682
|
+
"deleted": true,
|
|
1683
|
+
"from": "shared"
|
|
1684
|
+
}
|
|
1685
|
+
```
|
|
1686
|
+
|
|
1687
|
+
- `from` is either `"shared"` or `"workspace"`
|
|
1688
|
+
- Bundled skills cannot be deleted (read-only)
|
|
1689
|
+
- The skill name is validated against the same regex as promote
|
|
1690
|
+
|
|
1691
|
+
---
|
|
1692
|
+
|
|
1693
|
+
## Assistant (PULSE)
|
|
1694
|
+
|
|
1695
|
+
### `POST /api/assistant`
|
|
1696
|
+
Chat with the built-in AI assistant (PULSE).
|
|
1697
|
+
|
|
1698
|
+
**Auth:** browser cookie
|
|
1699
|
+
|
|
1700
|
+
**Body:**
|
|
1701
|
+
```json
|
|
1702
|
+
{
|
|
1703
|
+
"message": "What can I do on the Agents page?",
|
|
1704
|
+
"page": "/agents",
|
|
1705
|
+
"model": "deepseek/deepseek-v4-flash",
|
|
1706
|
+
"history": []
|
|
1707
|
+
}
|
|
1708
|
+
```
|
|
1709
|
+
|
|
1710
|
+
| Field | Type | Required | Description |
|
|
1711
|
+
|-----------|-------------------------|----------|--------------------------------------------------|
|
|
1712
|
+
| `message` | string | yes | The user's question |
|
|
1713
|
+
| `page` | string | no | Current page path (used for contextual prompts) |
|
|
1714
|
+
| `model` | string | no | AI model to use (e.g. `deepseek/deepseek-v4-flash`) |
|
|
1715
|
+
| `history` | `{role, content}[]` | no | Recent conversation history (up to 10 messages) |
|
|
1716
|
+
|
|
1717
|
+
**Response:** `text/event-stream` (Server-Sent Events)
|
|
1718
|
+
|
|
1719
|
+
PULSE streams the assistant response as SSE chunks. Each chunk follows the
|
|
1720
|
+
[OpenAI streaming format](https://platform.openai.com/docs/api-reference/chat/streaming):
|
|
1721
|
+
```text
|
|
1722
|
+
data: {"choices":[{"delta":{"content":"Hello"},"index":0}]}
|
|
1723
|
+
|
|
1724
|
+
data: {"choices":[{"delta":{"content":"! How can"},"index":0}]}
|
|
1725
|
+
|
|
1726
|
+
data: [DONE]
|
|
1727
|
+
```
|
|
1728
|
+
|
|
1729
|
+
**Errors:** JSON `{ "error": "..." }` with the appropriate HTTP status code.
|
|
1730
|
+
|
|
1731
|
+
**Notes:**
|
|
1732
|
+
- PULSE routes the request **directly to the upstream provider** (not via the local Provider Gateway), using the API key from `data/provider-keys.json`.
|
|
1733
|
+
- The system prompt is built dynamically based on `PAGE_CONTEXT` (hardcoded in the route handler) — no database access.
|
|
1734
|
+
- PULSE does **not** have access to live data; she only knows page descriptions and navigation links.
|
|
1735
|
+
|
|
1736
|
+
---
|
|
1737
|
+
|
|
1738
|
+
## Credentials
|
|
1739
|
+
|
|
1740
|
+
### `GET /api/credentials`
|
|
1741
|
+
List all stored credential profiles and the provider registry.
|
|
1742
|
+
|
|
1743
|
+
**Response:** `{ profiles: CredentialProfile[], providers: CredentialProviderDef[] }`
|
|
1744
|
+
|
|
1745
|
+
### `POST /api/credentials`
|
|
1746
|
+
Create a new credential profile.
|
|
1747
|
+
|
|
1748
|
+
**Body:** `{ providerId: string, label: string, secret: Record<string, string> }`
|
|
1749
|
+
|
|
1750
|
+
**Response:** `{ id: string }` (201)
|
|
1751
|
+
|
|
1752
|
+
### `GET /api/credentials/[id]`
|
|
1753
|
+
Get a single credential profile (no secret returned).
|
|
1754
|
+
|
|
1755
|
+
### `PATCH /api/credentials/[id]`
|
|
1756
|
+
Update label and/or secret of a credential.
|
|
1757
|
+
|
|
1758
|
+
**Body:** `{ label: string, secret: Record<string, string> }`
|
|
1759
|
+
|
|
1760
|
+
### `DELETE /api/credentials/[id]`
|
|
1761
|
+
Delete a credential and its secret permanently.
|
|
1762
|
+
|
|
1763
|
+
### `POST /api/credentials/[id]/sync`
|
|
1764
|
+
Sync the credential to all agent containers (or a specified subset).
|
|
1765
|
+
|
|
1766
|
+
**Body (optional):** `{ containers?: string[] }` — if omitted or empty, syncs to all agent containers with `AGENT_ID` labels.
|
|
1767
|
+
|
|
1768
|
+
**Response:** `{ results: { container: string, status: 'ok'|'error', error?: string }[] }`
|
|
1769
|
+
|
|
1770
|
+
Before syncing, **all agent containers are cleaned first** (env files, CLI auth, TOOLS.md integration markers removed).
|
|
1771
|
+
Only the selected containers receive the new credential and TOOLS.md section.
|
|
1772
|
+
This ensures that deselecting an agent or deleting a profile removes the credential from all agents.
|
|
1773
|
+
|
|
1774
|
+
### `GET /api/credentials/detect`
|
|
1775
|
+
Live-detect which credentials are actually present inside agent containers.
|
|
1776
|
+
|
|
1777
|
+
**Query params (optional):** `?agent=prometheus` — filter to a single container.
|
|
1778
|
+
|
|
1779
|
+
**Response:**
|
|
1780
|
+
```json
|
|
1781
|
+
{
|
|
1782
|
+
"agents": [
|
|
1783
|
+
{
|
|
1784
|
+
"container": "prometheus",
|
|
1785
|
+
"agentId": "prometheus",
|
|
1786
|
+
"displayName": "Prometheus",
|
|
1787
|
+
"githubLoggedIn": true,
|
|
1788
|
+
"githubUser": "Flame0510",
|
|
1789
|
+
"githubCliInstalled": true,
|
|
1790
|
+
"vercelLoggedIn": false,
|
|
1791
|
+
"vercelUser": "",
|
|
1792
|
+
"vercelTeam": "",
|
|
1793
|
+
"vercelCliInstalled": true,
|
|
1794
|
+
"supabaseLoggedIn": true,
|
|
1795
|
+
"supabaseLinked": false,
|
|
1796
|
+
"supabaseProjectRef": "",
|
|
1797
|
+
"supabaseCliInstalled": true,
|
|
1798
|
+
"trelloLoggedIn": false,
|
|
1799
|
+
"trelloCliInstalled": true,
|
|
1800
|
+
"toolsMdHasMarkers": true,
|
|
1801
|
+
"matchedProfiles": [
|
|
1802
|
+
{ "profileId": "cred_xxx", "providerId": "github-pat", "label": "GitHub" }
|
|
1803
|
+
]
|
|
1804
|
+
}
|
|
1805
|
+
]
|
|
1806
|
+
}
|
|
1807
|
+
```
|
|
1808
|
+
|
|
1809
|
+
Detection runs `docker exec` inside each container:
|
|
1810
|
+
- `gh auth status` for GitHub (token read from `~/.config/gh/hosts.yml`)
|
|
1811
|
+
- `vercel whoami` for Vercel (token read from `~/.local/share/com.vercel.cli/auth.json`)
|
|
1812
|
+
- `supabase projects list --output json` for Supabase (token read from `~/.supabase/access-token`)
|
|
1813
|
+
- `test -f ~/.trello-cli/default/config.json` for Trello
|
|
1814
|
+
- TOOLS.md markers at `/root/.openclaw/workspace/TOOLS.md`
|
|
1815
|
+
|
|
1816
|
+
`matchedProfiles` is determined by SHA256-hashing the token installed in the container
|
|
1817
|
+
and comparing it against the hashed token of each stored profile in the vault.
|
|
1818
|
+
Only exact token matches produce a match — no heuristics, no broad profile-level guesses.
|
|
1819
|
+
|
|
1820
|
+
### `POST /api/credentials/[id]/reveal`
|
|
1821
|
+
Return the raw secret payload. Every call is logged in the audit trail.
|
|
1822
|
+
Use sparingly.
|
|
1823
|
+
|
|
1824
|
+
**Response:** `{ secret: Record<string, string> }`
|
|
1825
|
+
|
|
1826
|
+
---
|
|
1827
|
+
|
|
1828
|
+
## Error Format
|
|
1829
|
+
|
|
1830
|
+
All errors return JSON:
|
|
1831
|
+
```json
|
|
1832
|
+
{ "error": "description of what went wrong" }
|
|
1833
|
+
```
|
|
1834
|
+
|
|
1835
|
+
Common status codes: `400` bad request, `401` unauthorized, `404` not found, `500` server error.
|