@flame0510/project-aether 1.2.0 → 1.4.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 +3 -1
- package/agent-templates/README.md +42 -22
- package/agent-templates/base-image/Dockerfile +42 -33
- package/agent-templates/base-image/entrypoint.sh +67 -12
- package/app/agents/BrowserAccessSection.tsx +510 -0
- package/app/agents/ChannelManager.tsx +19 -11
- package/app/agents/ImageDownloadBanner.tsx +53 -19
- package/app/agents/ModelSection.tsx +316 -0
- package/app/agents/PageClient.tsx +708 -167
- package/app/agents/UpdateSection.tsx +300 -0
- package/app/agents/create/PageClient.tsx +11 -49
- package/app/agents/create/page.tsx +8 -21
- package/app/api/agents/[id]/backup/route.ts +26 -69
- package/app/api/agents/[id]/channels/pairing/route.ts +3 -3
- package/app/api/agents/[id]/channels/telegram/route.ts +2 -2
- package/app/api/agents/[id]/cold-backup/route.ts +56 -0
- package/app/api/agents/[id]/devices/route.ts +126 -0
- package/app/api/agents/[id]/invite-link/route.ts +53 -0
- package/app/api/agents/[id]/lifecycle/route.ts +3 -0
- package/app/api/agents/[id]/model/route.ts +113 -0
- package/app/api/agents/[id]/open-control-ui/route.ts +58 -0
- package/app/api/agents/[id]/recreate/route.ts +33 -187
- package/app/api/agents/[id]/restart/route.ts +5 -0
- package/app/api/agents/[id]/restore/route.ts +40 -70
- package/app/api/agents/[id]/route.ts +38 -169
- package/app/api/agents/[id]/update/rollback/route.ts +30 -0
- package/app/api/agents/[id]/update/route.ts +50 -0
- package/app/api/agents/activity-summary/route.ts +67 -0
- package/app/api/agents/create/route.ts +91 -145
- package/app/api/agents/devices-summary/route.ts +37 -0
- package/app/api/agents/download-image/route.ts +16 -9
- package/app/api/agents/image-status/route.ts +31 -111
- package/app/api/agents/models-summary/route.ts +163 -0
- package/app/api/agents/route.ts +25 -49
- package/app/api/agents/token/route.ts +33 -10
- package/app/api/assistant/route.ts +37 -16
- package/app/api/gateway/agent/route.ts +37 -6
- package/app/api/gateway/provider/balance/route.ts +5 -2
- package/app/api/gateway/provider/keys.ts +13 -1
- package/app/api/gateway/provider/route.ts +43 -12
- package/app/api/gateway/sync.ts +335 -76
- package/app/api/models/route.ts +28 -34
- package/app/api/provider/auth.ts +65 -0
- package/app/api/provider/upstream.ts +9 -2
- package/app/api/provider/v1/chat/completions/route.ts +22 -16
- package/app/api/provider/v1/models/route.ts +26 -133
- package/app/api/setup/agent-image/route.ts +14 -42
- package/app/components/DashboardToolbar.tsx +1 -1
- package/app/components/PulseChat.tsx +25 -39
- package/app/components/ui/RemoveButton.tsx +46 -0
- package/app/components/ui/Select.tsx +3 -2
- package/app/components/ui/index.ts +1 -0
- package/app/credentials/PageClient.tsx +2 -2
- package/app/gateway/PageClient.tsx +253 -674
- package/app/globals.css +8 -0
- package/app/lib/models-context.tsx +43 -7
- package/app/wizard/useWizard.ts +6 -1
- package/bin/rev4a.js +116 -50
- package/daemon.js +6 -6
- package/docs/ARCHITECTURE.md +110 -12
- package/docs/FRONTEND-ARCHITECTURE.md +31 -2
- package/docs/REV4A.md +93 -33
- package/docs/dev/API-REFERENCE.md +723 -178
- package/docs/dev/DATABASE.md +96 -0
- package/docs/dev/GATEWAY.md +250 -93
- package/docs/dev/PROVIDERS.md +26 -13
- package/docs/rag/DATA-FRESHNESS.md +59 -28
- package/docs/rag/GLOSSARY.md +27 -16
- package/docs/rag/REV4A-OVERVIEW.md +37 -25
- package/docs/rag/WHAT-I-CAN-ANSWER.md +10 -8
- package/instrumentation.ts +52 -1
- package/lib/agent-busy.ts +21 -0
- package/lib/agent-devices.ts +361 -0
- package/lib/agent-edit-state.ts +108 -0
- package/lib/agent-edit.ts +157 -0
- package/lib/agent-images.ts +375 -0
- package/lib/agent-ports-server.ts +27 -0
- package/lib/agent-ports.ts +68 -0
- package/lib/agent-readiness.ts +110 -0
- package/lib/agent-recreate-state.ts +108 -0
- package/lib/agent-recreate.ts +305 -0
- package/lib/agent-restore-state.ts +107 -0
- package/lib/agent-restore.ts +135 -0
- package/lib/agent-setup.ts +66 -17
- package/lib/agent-update-state.ts +122 -0
- package/lib/agent-update.ts +448 -0
- package/lib/agent-versions.json +14 -0
- package/lib/agent-versions.ts +80 -0
- package/lib/buildAgentImage.ts +88 -290
- package/lib/channelManager.ts +153 -64
- package/lib/cold-backup.ts +354 -0
- package/lib/container-file.ts +27 -0
- package/lib/credentials/delivery.ts +3 -3
- package/lib/db-bootstrap.mjs +76 -0
- package/lib/docker-utils.ts +3 -3
- package/lib/model-catalogue.ts +140 -27
- package/lib/provider-balance.ts +33 -12
- package/lib/rev4a-paths.ts +0 -21
- package/model-pricing.json +118 -110
- package/models.config.json +27 -12
- package/package.json +1 -1
- package/app/api/gateway/route.ts +0 -191
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Rev4a API Reference
|
|
2
2
|
|
|
3
|
-
> **Last updated:** 2026-09-
|
|
3
|
+
> **Last updated:** 2026-09-15
|
|
4
4
|
|
|
5
5
|
All routes are under `/api/`. Authentication is required on every endpoint
|
|
6
6
|
unless otherwise noted.
|
|
@@ -124,15 +124,18 @@ Restart the service so a freshly written `.env` takes effect.
|
|
|
124
124
|
**Errors:** `500` if the `.env` file is missing, or the restart could not be issued.
|
|
125
125
|
|
|
126
126
|
### `GET /api/setup/agent-image`
|
|
127
|
-
Whether the
|
|
127
|
+
Whether a supported OpenClaw version of the agent image is downloaded.
|
|
128
128
|
|
|
129
129
|
**Auth:** open before a password exists, then browser cookie or bearer token (`requireAuthIfConfigured`)
|
|
130
130
|
|
|
131
|
-
**Response:** `{ "exists": true, "
|
|
132
|
-
when
|
|
131
|
+
**Response:** `{ "exists": true, "version": "2026.9.3" }` (the newest one present), or
|
|
132
|
+
`{ "exists": false, "version": null }` when none is **or Docker is unreachable** — the two
|
|
133
|
+
are not distinguished.
|
|
133
134
|
|
|
134
135
|
### `POST /api/setup/agent-image`
|
|
135
|
-
|
|
136
|
+
Download the newest supported version the registry publishes, streaming progress as SSE
|
|
137
|
+
(`status`, `progress`, `step`, `log`, `complete`, `error`). Nothing is downloaded when
|
|
138
|
+
that version is already here.
|
|
136
139
|
|
|
137
140
|
**Auth:** open before a password exists, then browser cookie or bearer token (`requireAuthIfConfigured`)
|
|
138
141
|
|
|
@@ -146,76 +149,56 @@ The Gateway API reads the model catalogue from [`models.config.json`](../../mode
|
|
|
146
149
|
(tracked in the repository). Full documentation at [GATEWAY.md](GATEWAY.md) and
|
|
147
150
|
[PROVIDERS.md](PROVIDERS.md).
|
|
148
151
|
|
|
149
|
-
### `GET /api/gateway`
|
|
150
|
-
Returns live gateway status from all agent containers.
|
|
151
|
-
|
|
152
|
-
**Auth:** any auth method
|
|
153
|
-
|
|
154
|
-
**Response:**
|
|
155
|
-
```json
|
|
156
|
-
{
|
|
157
|
-
"timestamp": "2026-08-27T22:00:00.000Z",
|
|
158
|
-
"gateway": "Rev4a Model Gateway",
|
|
159
|
-
"status": "online",
|
|
160
|
-
"models": { "total": 0, "available": 0, "byProvider": [], "list": [] },
|
|
161
|
-
"agents": {
|
|
162
|
-
"total": 1,
|
|
163
|
-
"list": [
|
|
164
|
-
{
|
|
165
|
-
"agentId": "atlas",
|
|
166
|
-
"containerName": "openclaw-atlas",
|
|
167
|
-
"state": "running",
|
|
168
|
-
"defaultModel": "rev4a/deepseek/deepseek-v4-flash",
|
|
169
|
-
"fallbacks": [],
|
|
170
|
-
"aliases": {},
|
|
171
|
-
"providers": []
|
|
172
|
-
}
|
|
173
|
-
]
|
|
174
|
-
},
|
|
175
|
-
"aliases": {},
|
|
176
|
-
"coreModel": { "defaultModel": "rev4a/deepseek/deepseek-v4-flash", "fallbacks": [] },
|
|
177
|
-
"apiKeys": { "configured": [], "all": [] }
|
|
178
|
-
}
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
**`models`, the top-level `aliases`, and `apiKeys` are always empty here.** They
|
|
182
|
-
exist only to keep the response shape stable for existing consumers. The model
|
|
183
|
-
catalogue is served by `GET /api/gateway/provider`, which reads the bundled
|
|
184
|
-
config plus user overrides from disk; provider credential state comes from the
|
|
185
|
-
same route (`configured` per provider, out of `provider-keys.json`).
|
|
186
|
-
|
|
187
|
-
Per-agent values are read from each container's `openclaw.json`, one
|
|
188
|
-
`docker exec … cat` per agent, run concurrently. An agent whose config cannot be
|
|
189
|
-
read is still listed, with an `error` field and empty values, rather than
|
|
190
|
-
failing the whole response.
|
|
191
|
-
|
|
192
152
|
### `PUT /api/gateway/provider`
|
|
193
153
|
Enable or disable a single model in the catalogue, then sync every agent.
|
|
194
154
|
|
|
195
155
|
**Auth:** browser cookie or bearer token
|
|
196
156
|
|
|
197
|
-
**Body:** `{ "modelId": "deepseek/deepseek-
|
|
157
|
+
**Body:** `{ "modelId": "deepseek/deepseek-flash", "enabled": true }` — both
|
|
198
158
|
required. Missing either returns `400`; an unknown `modelId` returns `404`.
|
|
159
|
+
If Docker cannot be reached the toggle is refused with `503` and nothing is saved,
|
|
160
|
+
so a whole-fleet outage never leaves the checkboxes out of step with the agents.
|
|
199
161
|
|
|
200
|
-
**Response:**
|
|
162
|
+
**Response:** `status` is the toggle, which is saved before the sync runs. `sync`
|
|
163
|
+
reports separately whether the agents received it.
|
|
201
164
|
```json
|
|
202
165
|
{
|
|
203
166
|
"status": "ok",
|
|
204
|
-
"modelId": "deepseek/deepseek-
|
|
167
|
+
"modelId": "deepseek/deepseek-flash",
|
|
205
168
|
"enabled": true,
|
|
206
169
|
"provider": "deepseek",
|
|
207
|
-
"sync":
|
|
170
|
+
"sync": {
|
|
171
|
+
"ok": true,
|
|
172
|
+
"activeModels": 3,
|
|
173
|
+
"total": 2,
|
|
174
|
+
"synced": ["agent_2a3c3a07", "agent_9253eee3"],
|
|
175
|
+
"stopped": [],
|
|
176
|
+
"failed": [],
|
|
177
|
+
"summary": "Synced 2 of 2 agent(s)."
|
|
178
|
+
}
|
|
208
179
|
}
|
|
209
180
|
```
|
|
210
181
|
|
|
182
|
+
`sync.ok` is true only when every agent container received the catalogue; zero
|
|
183
|
+
agents counts as ok. "Every" includes agents that are not running (stopped, paused,
|
|
184
|
+
created): they are written through `docker cp`, listed in `stopped` as well as
|
|
185
|
+
`synced`, and apply the change when started or resumed. A container whose config
|
|
186
|
+
cannot be read is listed in `failed` and left untouched — the sync never writes a
|
|
187
|
+
config it did not read. When Docker cannot be reached, `total` is `0`, `dockerError`
|
|
188
|
+
carries the cause and `ok` is false. For this toggle that case is refused up front
|
|
189
|
+
with `503`, so it appears here only if Docker drops between the check and the sync;
|
|
190
|
+
on a provider key save (`POST`) it is reported, not refused. `configError` is set,
|
|
191
|
+
and nothing is attempted, when the gateway key is missing or `models.config.json`
|
|
192
|
+
could not be read.
|
|
193
|
+
|
|
211
194
|
**Side effects:**
|
|
212
195
|
- Writes `models.providers.rev4a` to each agent container's `openclaw.json`
|
|
213
196
|
- Cleans up stale `models.json` and `auth-profiles.json` inside containers
|
|
214
|
-
- **Does NOT touch `agents.defaults.model` or `agents.list[].model`** — model references are managed per-container
|
|
197
|
+
- **Does NOT touch `agents.defaults.model` or `agents.list[].model`** — model references are managed per-container from the agent detail panel (`PUT /api/gateway/agent`)
|
|
215
198
|
- Does NOT restart the gateway (config is written live to the file)
|
|
216
199
|
|
|
217
200
|
### `PUT /api/gateway/agent`
|
|
218
|
-
Update model
|
|
201
|
+
Update the primary model and fallbacks of a single agent container.
|
|
219
202
|
|
|
220
203
|
**Auth:** any auth method
|
|
221
204
|
|
|
@@ -223,28 +206,38 @@ Update model config for a single agent container.
|
|
|
223
206
|
```json
|
|
224
207
|
{
|
|
225
208
|
"containerName": "openclaw-atlas",
|
|
226
|
-
"model": "deepseek/deepseek-
|
|
227
|
-
"fallbacks": []
|
|
209
|
+
"model": "deepseek/deepseek-flash",
|
|
210
|
+
"fallbacks": ["deepseek/deepseek-v4-pro"]
|
|
228
211
|
}
|
|
229
212
|
```
|
|
230
|
-
|
|
213
|
+
`fallbacks` omitted and `fallbacks: []` are different requests. Omitted leaves the
|
|
214
|
+
container's existing fallbacks untouched; `[]` clears them.
|
|
231
215
|
|
|
232
|
-
|
|
216
|
+
`containerName` is used verbatim as the `docker exec` target, not resolved from the
|
|
217
|
+
`AGENT_ID` label. Every container's name currently equals its label, so an agent id
|
|
218
|
+
works.
|
|
219
|
+
|
|
220
|
+
**Response:** reports what was written, with the `rev4a/` prefix applied.
|
|
233
221
|
```json
|
|
234
222
|
{
|
|
235
223
|
"status": "ok",
|
|
236
224
|
"agent": "openclaw-atlas",
|
|
237
225
|
"model": {
|
|
238
|
-
"primary": "rev4a/deepseek/deepseek-
|
|
226
|
+
"primary": "rev4a/deepseek/deepseek-flash",
|
|
227
|
+
"fallbacks": ["rev4a/deepseek/deepseek-v4-pro"]
|
|
239
228
|
},
|
|
240
229
|
"verify": { "ok": true, "bytes": 2058 }
|
|
241
230
|
}
|
|
242
231
|
```
|
|
243
232
|
|
|
244
233
|
**Behaviour:**
|
|
245
|
-
- Writes `agents.defaults.model
|
|
246
|
-
|
|
247
|
-
|
|
234
|
+
- Writes `agents.defaults.model`, and the entry in `agents.list` whose `id` is
|
|
235
|
+
`main` (skipped if there is none), both as `{ primary, fallbacks }`. The primary is
|
|
236
|
+
removed from its own fallback list.
|
|
237
|
+
- Rewrites the whole `openclaw.json` with `docker exec … cat >`. No gateway restart:
|
|
238
|
+
OpenClaw hot-applies the change.
|
|
239
|
+
- Does not check the model against the catalogue. The proxy does that when the model
|
|
240
|
+
is used, so a model saved here while disabled is refused at request time.
|
|
248
241
|
|
|
249
242
|
### `GET /api/gateway/provider`
|
|
250
243
|
Returns current provider configuration state.
|
|
@@ -267,7 +260,7 @@ Returns current provider configuration state.
|
|
|
267
260
|
"baseUrl": "https://api.deepseek.com",
|
|
268
261
|
"docsUrl": "https://platform.deepseek.com/api_keys",
|
|
269
262
|
"models": [
|
|
270
|
-
{ "id": "deepseek/deepseek-
|
|
263
|
+
{ "id": "deepseek/deepseek-flash", "name": "DeepSeek Flash", "enabled": true },
|
|
271
264
|
{ "id": "deepseek/deepseek-v4-pro", "name": "DeepSeek V4 Pro", "enabled": true }
|
|
272
265
|
]
|
|
273
266
|
}
|
|
@@ -277,7 +270,8 @@ Returns current provider configuration state.
|
|
|
277
270
|
|
|
278
271
|
### `GET /api/provider/v1/models`
|
|
279
272
|
|
|
280
|
-
Rev4a Provider Gateway — returns
|
|
273
|
+
Rev4a Provider Gateway — returns the models this deployment offers (enabled after
|
|
274
|
+
overrides, provider has a key). A missing or wrong token gets an empty list, not a 401.
|
|
281
275
|
|
|
282
276
|
**Auth:** Bearer token (must match `rev4a` key in `data/provider-keys.json`)
|
|
283
277
|
|
|
@@ -287,7 +281,7 @@ Rev4a Provider Gateway — returns available models filtered by auth token.
|
|
|
287
281
|
"object": "list",
|
|
288
282
|
"total": 9,
|
|
289
283
|
"data": [
|
|
290
|
-
{ "id": "deepseek/deepseek-
|
|
284
|
+
{ "id": "deepseek/deepseek-flash", "object": "model", "created": 1700000000, "owned_by": "deepseek", "permission": [], "root": "deepseek/deepseek-flash" }
|
|
291
285
|
]
|
|
292
286
|
}
|
|
293
287
|
```
|
|
@@ -299,6 +293,10 @@ Rev4a Provider Gateway — returns available models filtered by auth token.
|
|
|
299
293
|
|
|
300
294
|
### `POST /api/provider/v1/chat/completions`
|
|
301
295
|
|
|
296
|
+
A model the deployment does not offer is refused with `400` before anything reaches
|
|
297
|
+
upstream. Routing fields that would let the upstream choose another model — `models`,
|
|
298
|
+
`route`, `preset` — are removed from the body before forwarding.
|
|
299
|
+
|
|
302
300
|
Rev4a Provider Gateway — proxy chat completions to the correct upstream.
|
|
303
301
|
|
|
304
302
|
**Auth:** Bearer token (must match `rev4a` key in `data/provider-keys.json`)
|
|
@@ -306,7 +304,7 @@ Rev4a Provider Gateway — proxy chat completions to the correct upstream.
|
|
|
306
304
|
**Body:**
|
|
307
305
|
```json
|
|
308
306
|
{
|
|
309
|
-
"model": "rev4a/deepseek-
|
|
307
|
+
"model": "rev4a/deepseek-flash",
|
|
310
308
|
"messages": [{"role": "user", "content": "Hello"}]
|
|
311
309
|
}
|
|
312
310
|
```
|
|
@@ -319,11 +317,35 @@ Rev4a Provider Gateway — proxy chat completions to the correct upstream.
|
|
|
319
317
|
---
|
|
320
318
|
|
|
321
319
|
### `GET /api/models`
|
|
322
|
-
Models eligible to be used: the catalogue filtered down to
|
|
323
|
-
has a key configured.
|
|
320
|
+
Models eligible to be used: the catalogue filtered down to models that are enabled
|
|
321
|
+
(after overrides) and whose provider has a key configured, grouped by provider. Feeds the model pickers in the agent's
|
|
322
|
+
Model panel.
|
|
324
323
|
|
|
325
324
|
**Auth:** browser cookie or bearer token
|
|
326
325
|
|
|
326
|
+
**Response:**
|
|
327
|
+
```json
|
|
328
|
+
[
|
|
329
|
+
{
|
|
330
|
+
"provider": "DeepSeek",
|
|
331
|
+
"emoji": "🧠",
|
|
332
|
+
"models": [
|
|
333
|
+
{
|
|
334
|
+
"id": "deepseek/deepseek-flash",
|
|
335
|
+
"label": "DeepSeek Flash (V4.1)",
|
|
336
|
+
"input": 0.3,
|
|
337
|
+
"output": 1.2,
|
|
338
|
+
"deprecated": false
|
|
339
|
+
}
|
|
340
|
+
]
|
|
341
|
+
}
|
|
342
|
+
]
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
`input` and `output` are USD per 1M tokens, `null` when unpriced. `deprecated`
|
|
346
|
+
marks a name upstream has retired; it stays selectable and the pickers tag it.
|
|
347
|
+
See [GATEWAY.md](GATEWAY.md) for the deprecation lifecycle.
|
|
348
|
+
|
|
327
349
|
### `GET /api/gateway/provider/balance`
|
|
328
350
|
Remaining credit for one provider.
|
|
329
351
|
|
|
@@ -331,7 +353,7 @@ Remaining credit for one provider.
|
|
|
331
353
|
|
|
332
354
|
**Query params:** `provider` — one of `deepseek`, `openrouter`, `glm`.
|
|
333
355
|
|
|
334
|
-
**Errors:** `502` when the provider's API cannot be reached or rejects the call.
|
|
356
|
+
**Errors:** `502` when the provider's API cannot be reached or rejects the call. For `glm`, Z.AI's token-quota endpoint only serves Coding Plan subscriptions: a pay-as-you-go key gets `502` with `reason: "no-balance-api"` — Z.AI exposes no balance API for such accounts (verified 2026-09-16), and the Gateway hides the badge on this reason instead of offering a retry.
|
|
335
357
|
|
|
336
358
|
### `POST /api/gateway/provider/oauth`
|
|
337
359
|
Run the OAuth device flow for a provider that supports it.
|
|
@@ -654,8 +676,8 @@ List running agent containers (Docker containers with `AGENT_ID` label) with ful
|
|
|
654
676
|
"id": "abc123def456",
|
|
655
677
|
"agentId": "prometheus",
|
|
656
678
|
"name": "prometheus",
|
|
657
|
-
"image": "openclaw-agent-base:
|
|
658
|
-
"imageTag": "
|
|
679
|
+
"image": "openclaw-agent-base:2026.9.3",
|
|
680
|
+
"imageTag": "2026.9.3",
|
|
659
681
|
"template": "prometheus",
|
|
660
682
|
"status": "running",
|
|
661
683
|
"state": "running",
|
|
@@ -663,19 +685,96 @@ List running agent containers (Docker containers with `AGENT_ID` label) with ful
|
|
|
663
685
|
"ip": "172.19.0.5",
|
|
664
686
|
"created": "2026-07-01T16:58:42.876829416Z",
|
|
665
687
|
"env": ["AGENT_ID=prometheus", "AGENT_TEMPLATE=prometheus"],
|
|
666
|
-
"
|
|
667
|
-
"
|
|
688
|
+
"controlPort": "3033",
|
|
689
|
+
"openclawVersion": "2026.9.3",
|
|
690
|
+
"updateAvailable": false
|
|
668
691
|
}
|
|
669
692
|
]
|
|
693
|
+
```
|
|
670
694
|
|
|
671
695
|
**Notes:**
|
|
672
|
-
-
|
|
673
|
-
|
|
696
|
+
- `openclawVersion` comes from the image's `org.opencontainers.image.version` label or
|
|
697
|
+
version tag, else from `openclaw --version` inside the running container (cached per
|
|
698
|
+
image id); `null` for a stopped agent whose image carries neither.
|
|
699
|
+
- `updateAvailable` is `true` when a newer supported OpenClaw version is downloaded.
|
|
700
|
+
- `controlPort` is the host port published for the Control UI. No link or token is
|
|
701
|
+
returned: the Open buttons go through `GET /api/agents/[id]/open-control-ui`
|
|
674
702
|
- Containers are discovered via Docker API with label filter `AGENT_ID`
|
|
675
703
|
- Template is inferred from the container image name + agent ID directory match in `agent-templates/`
|
|
676
704
|
|
|
677
705
|
---
|
|
678
706
|
|
|
707
|
+
### `GET /api/agents/models-summary`
|
|
708
|
+
One row per agent container, running or not: the model it runs and whether that
|
|
709
|
+
model is still in good standing. Feeds the model chip on the agent cards. Running
|
|
710
|
+
agents are read with `docker exec`, others with `docker cp`. If Docker cannot be
|
|
711
|
+
reached the route answers `503` with an `error`, not an empty list.
|
|
712
|
+
|
|
713
|
+
**Auth:** any auth method
|
|
714
|
+
|
|
715
|
+
**Response:**
|
|
716
|
+
```json
|
|
717
|
+
{
|
|
718
|
+
"agents": [
|
|
719
|
+
{
|
|
720
|
+
"agentId": "atlas",
|
|
721
|
+
"containerName": "openclaw-atlas",
|
|
722
|
+
"primary": "rev4a/deepseek/deepseek-flash",
|
|
723
|
+
"fallbackCount": 0,
|
|
724
|
+
"firstFallback": null,
|
|
725
|
+
"health": "ok"
|
|
726
|
+
}
|
|
727
|
+
]
|
|
728
|
+
}
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
`health`, ordered by the remedy each needs:
|
|
732
|
+
|
|
733
|
+
| value | meaning | remedy |
|
|
734
|
+
|---|---|---|
|
|
735
|
+
| `missing` | the primary is not in the catalogue at all | pick another model |
|
|
736
|
+
| `disabled` | in the catalogue but not offered — unchecked, or its provider has no key; the proxy refuses it | pick another model |
|
|
737
|
+
| `out-of-sync` | offered by the gateway, absent from this container's synced copy | Sync All Agents |
|
|
738
|
+
| `deprecated` | works, on a name upstream has retired | switch when convenient |
|
|
739
|
+
| `ok` | usable | — |
|
|
740
|
+
| `unset` | no primary configured | — |
|
|
741
|
+
| `unknown` | the container did not answer | — |
|
|
742
|
+
|
|
743
|
+
### `GET /api/agents/[id]/model`
|
|
744
|
+
The model configuration of one agent, read from that container alone rather than
|
|
745
|
+
by walking the fleet. Writes still go to `PUT /api/gateway/agent`.
|
|
746
|
+
|
|
747
|
+
**Auth:** any auth method
|
|
748
|
+
|
|
749
|
+
**Response:**
|
|
750
|
+
```json
|
|
751
|
+
{
|
|
752
|
+
"agentId": "atlas",
|
|
753
|
+
"reachable": true,
|
|
754
|
+
"primary": "rev4a/deepseek/deepseek-flash",
|
|
755
|
+
"fallbacks": [],
|
|
756
|
+
"resolvable": true,
|
|
757
|
+
"catalogueSize": 41,
|
|
758
|
+
"primaryStatus": "offered"
|
|
759
|
+
}
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
`resolvable` is tri-state. `false` means the primary is absent from the catalogue
|
|
763
|
+
synced into that container, usually because the model was disabled on the Gateway
|
|
764
|
+
page while this agent still pointed at it. `true` means it is present. `null` means
|
|
765
|
+
the question does not apply: either the container did not answer, or no primary is
|
|
766
|
+
set.
|
|
767
|
+
|
|
768
|
+
When `reachable` is `false` the container did not answer, `primary` and `fallbacks`
|
|
769
|
+
come back empty rather than read, and **`catalogueSize` is omitted entirely** — it
|
|
770
|
+
is present only on the reachable branch.
|
|
771
|
+
|
|
772
|
+
`primaryStatus`, also reachable branch only, says why the primary would fail
|
|
773
|
+
regardless of this container: `offered`, `disabled` (unchecked, or its provider has
|
|
774
|
+
no key) or `missing` (no longer in the catalogue). `null` when no primary is set.
|
|
775
|
+
`resolvable` answers a different question — whether *this container's* synced copy
|
|
776
|
+
has it — so `offered` with `resolvable: false` means the container is out of sync.
|
|
777
|
+
|
|
679
778
|
### `POST /api/agents/create`
|
|
680
779
|
Create a new agent container from a template.
|
|
681
780
|
|
|
@@ -687,32 +786,34 @@ Create a new agent container from a template.
|
|
|
687
786
|
"name": "my-agent",
|
|
688
787
|
"template": "prometheus",
|
|
689
788
|
"portRange": "3700-3709",
|
|
690
|
-
"model": "deepseek/deepseek-
|
|
789
|
+
"model": "deepseek/deepseek-flash",
|
|
691
790
|
"fallbacks": []
|
|
692
791
|
}
|
|
693
792
|
```
|
|
694
793
|
|
|
695
794
|
| Field | Required | Description |
|
|
696
795
|
|---|---|---|
|
|
697
|
-
| `name` | yes |
|
|
796
|
+
| `name` | yes | Display name (`AGENT_NAME`); the container name and `AGENT_ID` are generated (`agent_<hex>`) |
|
|
698
797
|
| `template` | yes | Template name (directory in `agent-templates/`) |
|
|
699
798
|
| `portRange` | no | Optional port range (e.g. `3700-3709`) or single port (e.g. `3700`). Default: auto-assigned 10-port block |
|
|
700
|
-
| `model` | no | Primary model
|
|
799
|
+
| `model` | no | Primary model id. Must be offered by this deployment (in the catalogue, enabled, provider has a key), in any form the proxy accepts; otherwise `400`. Defaults to `rev4a/deepseek/deepseek-flash`, validated the same way |
|
|
701
800
|
| `fallbacks` | no | Array of fallback model IDs |
|
|
702
801
|
|
|
703
802
|
**Every agent is created with a host port mapping:**
|
|
704
803
|
- Port block: `-p <start>-<end>:3000-<3000+offset>` — maps a range of host ports to the same range starting at container port 3000
|
|
705
804
|
- Single port: `-p <port>:3000` — maps a single host port to container port 3000
|
|
706
|
-
- Control UI
|
|
805
|
+
- Control UI: the wizard's Open button goes through `GET /api/agents/[id]/open-control-ui`, like the Agents page
|
|
707
806
|
- Port 3740 is reserved for Rev4a
|
|
807
|
+
- Image: `openclaw-agent-base:<version>`, the newest supported OpenClaw version downloaded here; `409` when none is
|
|
708
808
|
|
|
709
809
|
**Response:**
|
|
710
810
|
```json
|
|
711
811
|
{
|
|
712
812
|
"success": true,
|
|
713
813
|
"containerId": "00f5de1eb08d...",
|
|
714
|
-
"
|
|
715
|
-
"
|
|
814
|
+
"containerName": "agent_2a3c3a07",
|
|
815
|
+
"displayName": "my-agent",
|
|
816
|
+
"image": "openclaw-agent-base:2026.9.3",
|
|
716
817
|
"network": "rev4a-network",
|
|
717
818
|
"controlToken": "asdfghjkl",
|
|
718
819
|
"port": 3700,
|
|
@@ -724,16 +825,16 @@ Create a new agent container from a template.
|
|
|
724
825
|
|
|
725
826
|
**Post-creation pipeline:**
|
|
726
827
|
1. OpenClaw gateway starts with `--allow-unconfigured`, generating its own default config
|
|
727
|
-
2. The route waits for the gateway to
|
|
728
|
-
3. Once ready,
|
|
729
|
-
|
|
828
|
+
2. The route waits for the gateway to finish starting (`waitForGatewayReady`, up to 60 s)
|
|
829
|
+
3. Once ready, rewrites `openclaw.json` in one write — `gateway.controlUi`,
|
|
830
|
+
`agents.defaults.model` (primary and fallbacks, always as `rev4a/<provider>/<model>`)
|
|
831
|
+
and `update` — then runs `openclaw gateway restart`
|
|
832
|
+
4. Applies the runtime config (hooks, shared skills) and writes `models.providers.rev4a`
|
|
833
|
+
with `openclaw config patch --stdin`. No restart for this step: `gateway.reload`
|
|
834
|
+
defaults to `hybrid`, which hot-applies changes under `agents`, `models` and
|
|
835
|
+
`routing`. If the catalogue cannot be read the provider patch is skipped and logged
|
|
730
836
|
|
|
731
|
-
|
|
732
|
-
it hot-applies changes under `agents`, `models` and `routing`, and restarts only
|
|
733
|
-
for `gateway.*`, `discovery` and `plugins.load`. Restarting here repeated work
|
|
734
|
-
the Gateway already does.
|
|
735
|
-
|
|
736
|
-
> **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.
|
|
837
|
+
Failures in steps 3 and 4 do not fail the request.
|
|
737
838
|
|
|
738
839
|
**Side effects:**
|
|
739
840
|
- Template files (`AGENTS.md`, `SOUL.md`, etc.) are copied into the container workspace via `docker cp`
|
|
@@ -765,6 +866,61 @@ docker command.
|
|
|
765
866
|
|
|
766
867
|
---
|
|
767
868
|
|
|
869
|
+
### `PATCH /api/agents/[id]`
|
|
870
|
+
Edit an agent's display name, its port range, or both. The job runs in the background
|
|
871
|
+
(`lib/agent-edit.ts`); this answers `202` once it has started. The container is rebuilt
|
|
872
|
+
on the image it already runs — **without a backup**: the persistent volume is never
|
|
873
|
+
touched (`recreateAgentContainer()` in `lib/agent-recreate.ts`), then the gateway is
|
|
874
|
+
waited for (up to 5 minutes) and the runtime config re-applied.
|
|
875
|
+
|
|
876
|
+
The job is recorded in `agent_edits`, so a page reload or a Rev4a restart never loses it:
|
|
877
|
+
`GET` below reports the running or last edit, and while it runs anything else that would
|
|
878
|
+
touch the agent answers `409`.
|
|
879
|
+
|
|
880
|
+
**Auth:** browser cookie
|
|
881
|
+
|
|
882
|
+
**Body:**
|
|
883
|
+
```json
|
|
884
|
+
{ "displayName": "Argus", "portRange": "3700-3709" }
|
|
885
|
+
```
|
|
886
|
+
|
|
887
|
+
`portRange` is `"<start>"` or `"<start>-<end>"`, mapped onto the gateway port 3000
|
|
888
|
+
(`3700-3709` → `3700-3709:3000-3009`). It is validated with the same rules and messages as
|
|
889
|
+
agent creation (`lib/agent-ports.ts`): a range that holds Rev4a's port `3740`, an invalid
|
|
890
|
+
format, or a port already published by **another** agent answers `400`
|
|
891
|
+
(`Ports already in use: 3711, 3712`) before anything is rebuilt. The agent's own published
|
|
892
|
+
ports are excluded, so keeping or shifting its block is not a conflict with itself.
|
|
893
|
+
At least one field is required; an empty `displayName` is rejected.
|
|
894
|
+
|
|
895
|
+
**Response (202):**
|
|
896
|
+
```json
|
|
897
|
+
{ "started": true, "id": 4 }
|
|
898
|
+
```
|
|
899
|
+
|
|
900
|
+
**Errors:** `400` invalid body, invalid port range or empty display name; `409` an edit,
|
|
901
|
+
recreate, restore, update or backup of this agent is already running.
|
|
902
|
+
|
|
903
|
+
---
|
|
904
|
+
|
|
905
|
+
### `GET /api/agents/[id]`
|
|
906
|
+
The running or last edit of an agent (`null` when it was never edited) — what the edit
|
|
907
|
+
banner polls to resume after a reload. `status`: `rebuilding` → `done` | `failed`;
|
|
908
|
+
`interrupted` after a Rev4a restart cut it off.
|
|
909
|
+
|
|
910
|
+
**Auth:** browser cookie or bearer token
|
|
911
|
+
|
|
912
|
+
```json
|
|
913
|
+
{
|
|
914
|
+
"edit": {
|
|
915
|
+
"id": 4, "agentId": "agent_2a3c3a07", "status": "rebuilding",
|
|
916
|
+
"displayName": "test 2 async", "portRange": null,
|
|
917
|
+
"error": null, "startedAtMs": 1790007512520, "finishedAtMs": null
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
```
|
|
921
|
+
|
|
922
|
+
---
|
|
923
|
+
|
|
768
924
|
### `POST /api/agents/[id]/restart`
|
|
769
925
|
Restart an agent container (shorter than recreate — keeps everything intact).
|
|
770
926
|
|
|
@@ -779,105 +935,265 @@ Restart an agent container (shorter than recreate — keeps everything intact).
|
|
|
779
935
|
|
|
780
936
|
---
|
|
781
937
|
|
|
782
|
-
### `
|
|
783
|
-
|
|
938
|
+
### `GET /api/agents/[id]/backup`
|
|
939
|
+
List the agent's backup archives in Docker volume `rev4a-backups`.
|
|
784
940
|
|
|
785
|
-
**Auth:** browser cookie
|
|
941
|
+
**Auth:** browser cookie or bearer token
|
|
786
942
|
|
|
787
943
|
**Response:**
|
|
788
944
|
```json
|
|
789
|
-
{ "
|
|
945
|
+
{ "backups": [ { "name": "agent-prometheus-2026-07-12_043512345.tar.gz", "size": "227.4 MB", "sizeBytes": 238442455, "date": "12/07/2026 04:35", "epoch": 1752292512 } ] }
|
|
790
946
|
```
|
|
791
947
|
|
|
792
|
-
|
|
793
|
-
package cache (`~/.npm/_cacache/`)
|
|
948
|
+
Archives are created by the cold backup (`POST /api/agents/[id]/cold-backup`) and
|
|
949
|
+
excluded the npm package cache (`~/.npm/_cacache/`). Sorted most-recent-first.
|
|
794
950
|
|
|
795
|
-
**Error (404):** `{ "error": "No persistent volume found for agent '...'" }`
|
|
796
951
|
|
|
797
952
|
---
|
|
798
953
|
|
|
799
|
-
### `
|
|
800
|
-
|
|
954
|
+
### `DELETE /api/agents/[id]/backup?file=...`
|
|
955
|
+
Remove one backup archive from `rev4a-backups`.
|
|
801
956
|
|
|
802
957
|
**Auth:** browser cookie
|
|
803
958
|
|
|
804
|
-
**Response:**
|
|
959
|
+
**Response:** `{ "success": true }`
|
|
960
|
+
|
|
961
|
+
**Error (400):** invalid or path-traversing file name.
|
|
962
|
+
|
|
963
|
+
### `POST /api/agents/[id]/cold-backup`
|
|
964
|
+
Cold backup of the agent's volume (`lib/cold-backup.ts`). Returns `202` once the job
|
|
965
|
+
has started; the work continues in a helper container, `rev4a-backup-<id>`.
|
|
966
|
+
|
|
967
|
+
1. The agent is stopped (`docker stop -t 30`, kill as a fallback) if it runs.
|
|
968
|
+
2. The helper, started from the agent's own image, measures the volume (`du`),
|
|
969
|
+
refuses when `rev4a-backups` has less free space than about 60% of it, and writes
|
|
970
|
+
`agent-<id>-cold-<ts>.tar.gz.partial` (npm cache excluded) with GNU tar checkpoints
|
|
971
|
+
for progress.
|
|
972
|
+
3. `tar -tzf` reads the whole archive back and must find `.openclaw/openclaw.json`;
|
|
973
|
+
only then is it renamed to `.tar.gz`. A failed or cancelled job leaves no archive.
|
|
974
|
+
4. The agent is started again if it was running.
|
|
975
|
+
|
|
976
|
+
The job's state is the helper container and its labels, so it survives a Rev4a
|
|
977
|
+
restart: at startup, `reconcileColdBackups()` finishes helpers that exited and keeps
|
|
978
|
+
watching the rest.
|
|
979
|
+
|
|
980
|
+
While it runs, `POST restore`, `POST recreate`, `POST lifecycle`,
|
|
981
|
+
`PATCH` and `DELETE /api/agents/[id]` answer `409`, and the provider sync skips the
|
|
982
|
+
agent, reporting it in `sync.failed`.
|
|
983
|
+
|
|
984
|
+
**Auth:** browser cookie or bearer token
|
|
985
|
+
|
|
986
|
+
**Response (202):** `{ "started": true, "file": "agent-agent_9253eee3-cold-2026-09-16_000607123.tar.gz" }`
|
|
987
|
+
|
|
988
|
+
**Errors:** `404` no container or no volume; `409` a backup of this agent is already
|
|
989
|
+
running; `500` the helper could not start (the agent is started again if it was running).
|
|
990
|
+
|
|
991
|
+
### `GET /api/agents/[id]/cold-backup`
|
|
992
|
+
The running or last job; `{ "backup": null }` when there is none since Rev4a started.
|
|
993
|
+
|
|
805
994
|
```json
|
|
806
995
|
{
|
|
807
|
-
"
|
|
808
|
-
|
|
809
|
-
|
|
996
|
+
"backup": {
|
|
997
|
+
"agentId": "agent_9253eee3", "status": "running", "kind": "manual",
|
|
998
|
+
"file": "agent-agent_9253eee3-cold-2026-09-16_000607123.tar.gz",
|
|
999
|
+
"startedAtMs": 1789524367123, "finishedAtMs": null,
|
|
1000
|
+
"totalBytes": 2023456789, "doneBytes": 812400000, "percent": 40,
|
|
1001
|
+
"archiveBytes": null, "error": null
|
|
1002
|
+
}
|
|
810
1003
|
}
|
|
811
1004
|
```
|
|
812
1005
|
|
|
813
|
-
|
|
1006
|
+
`status` is `running`, `succeeded` or `failed`; `error` is `cancelled` for a cancelled job.
|
|
1007
|
+
|
|
1008
|
+
### `DELETE /api/agents/[id]/cold-backup`
|
|
1009
|
+
Cancel the running job: the helper is removed, the partial archive deleted and the
|
|
1010
|
+
agent started again if it was running. `409` when nothing is running.
|
|
814
1011
|
|
|
815
1012
|
---
|
|
816
1013
|
|
|
817
|
-
### `
|
|
818
|
-
|
|
1014
|
+
### `POST /api/agents/[id]/restore?file=agent-prometheus-2026-07-12_043512345.tar.gz`
|
|
1015
|
+
Restore an agent's persistent volume from a backup. The job runs in the background
|
|
1016
|
+
(`lib/agent-restore.ts`); this answers `202` once it has started. No pre-restore backup is
|
|
1017
|
+
taken: restoring replaces the volume with the archive on purpose.
|
|
1018
|
+
|
|
1019
|
+
1. The container is stopped if it runs.
|
|
1020
|
+
2. The volume content is cleared and the archive extracted — up to 30 minutes, since a
|
|
1021
|
+
large workspace takes minutes to decompress. The file name reaches the container
|
|
1022
|
+
through its environment, never as shell syntax.
|
|
1023
|
+
3. The container is started again even when the extract failed, so the agent never stays
|
|
1024
|
+
down. A volume with no container (volume-only agent) is restored anyway.
|
|
1025
|
+
|
|
1026
|
+
The job is recorded in `agent_restores`, so a page reload or a Rev4a restart never loses
|
|
1027
|
+
it: while it runs, anything else that would touch the agent answers `409`
|
|
1028
|
+
(`lib/agent-busy.ts`), and after a Rev4a restart an active row becomes `interrupted` with
|
|
1029
|
+
the container started again.
|
|
819
1030
|
|
|
820
|
-
**Auth:** browser cookie
|
|
1031
|
+
**Auth:** browser cookie or bearer token
|
|
821
1032
|
|
|
822
|
-
**Response:**
|
|
1033
|
+
**Response (202):**
|
|
823
1034
|
```json
|
|
824
|
-
{ "
|
|
1035
|
+
{ "started": true, "id": 3 }
|
|
825
1036
|
```
|
|
826
1037
|
|
|
827
|
-
**
|
|
1038
|
+
**Errors:** `400` invalid file name (not `agent-<id>-<safe charset>.tar.gz`); `404` the
|
|
1039
|
+
archive does not exist; `409` a restore, recreate, update, edit or backup of this agent
|
|
1040
|
+
is already running.
|
|
828
1041
|
|
|
829
1042
|
---
|
|
830
1043
|
|
|
831
|
-
### `
|
|
832
|
-
|
|
1044
|
+
### `GET /api/agents/[id]/restore`
|
|
1045
|
+
The running or last restore of an agent (`null` when it was never restored), with the
|
|
1046
|
+
archive being applied. `status`: `restoring` → `done` | `failed`; `interrupted` after a
|
|
1047
|
+
Rev4a restart cut it off.
|
|
833
1048
|
|
|
834
|
-
|
|
835
|
-
the container is restarted. If no container exists with the given `AGENT_ID`,
|
|
836
|
-
the volume is restored anyway (useful for volume-only agents).
|
|
1049
|
+
**Auth:** browser cookie or bearer token
|
|
837
1050
|
|
|
838
|
-
|
|
1051
|
+
```json
|
|
1052
|
+
{
|
|
1053
|
+
"restore": {
|
|
1054
|
+
"id": 3, "agentId": "agent_2a3c3a07", "status": "restoring",
|
|
1055
|
+
"file": "agent-agent_2a3c3a07-cold-2026-09-21_172032317.tar.gz",
|
|
1056
|
+
"error": null, "startedAtMs": 1790004046548, "finishedAtMs": null
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
```
|
|
839
1060
|
|
|
840
|
-
|
|
1061
|
+
---
|
|
1062
|
+
|
|
1063
|
+
### `POST /api/agents/[id]/recreate`
|
|
1064
|
+
Rebuild the agent container on its **own** OpenClaw version while preserving the
|
|
1065
|
+
persistent volume (workspace files, credentials, state DB, config). A recreate never
|
|
1066
|
+
changes version: moving to a newer OpenClaw version migrates the data one way, so it
|
|
1067
|
+
is a separate update. The job runs in the background (`lib/agent-recreate.ts`); this
|
|
1068
|
+
answers `202` once it has started.
|
|
1069
|
+
|
|
1070
|
+
The image is `openclaw-agent-base:<version>` for the version the agent runs
|
|
1071
|
+
(`resolveRecreateImage()` in `lib/agent-images.ts`). When that tag is missing but the
|
|
1072
|
+
container's image is still here, the image is tagged; when it is gone, a supported
|
|
1073
|
+
version is pulled. An agent whose version cannot be read keeps its exact image id.
|
|
1074
|
+
|
|
1075
|
+
Flow (each step recorded in `agent_recreates`):
|
|
1076
|
+
1. **Cold backup** `agent-<id>-prerecreate-<ts>.tar.gz` — the agent stops for it, so
|
|
1077
|
+
the archive cannot catch its SQLite files mid-write. **If the backup fails, the
|
|
1078
|
+
recreate aborts and the agent is started again.**
|
|
1079
|
+
2. The container is rebuilt (`lib/agent-recreate.ts`): same image tag, environment
|
|
1080
|
+
variables (`AGENT_*`, `MODEL_*`, `OPENCLAW_*`), labels, port mappings and network,
|
|
1081
|
+
reattaching the same volume. The network comes from the attached networks, or from
|
|
1082
|
+
the container's `NetworkMode` when its endpoint was lost.
|
|
1083
|
+
3. The gateway is waited for (`/startupz`, up to 5 minutes), then `applyRuntimeConfig()`
|
|
1084
|
+
(`lib/agent-setup.ts`) guarantees the hooks, shared skills, provider proxy and
|
|
1085
|
+
Control UI origin policy.
|
|
1086
|
+
4. On success, older `prerecreate` archives are pruned to the newest two per agent
|
|
1087
|
+
(manual `cold` and `preupdate` archives are never touched).
|
|
1088
|
+
|
|
1089
|
+
A failure before the rebuild starts the old container again, so a failed recreate never
|
|
1090
|
+
leaves the agent down. After a Rev4a restart, a job still active becomes `interrupted`
|
|
1091
|
+
at startup and the agent is started again — but only once its backup helper has
|
|
1092
|
+
finished, never while the archive is being written.
|
|
1093
|
+
|
|
1094
|
+
**Auth:** browser cookie or bearer token
|
|
1095
|
+
|
|
1096
|
+
**Response (202):**
|
|
841
1097
|
```json
|
|
842
|
-
{ "
|
|
1098
|
+
{ "started": true, "id": 12 }
|
|
843
1099
|
```
|
|
844
1100
|
|
|
845
|
-
**
|
|
1101
|
+
**Errors:** `404` when no image can be resolved; `409` when there is no container, an
|
|
1102
|
+
update, a recreate, a restore, an edit or a backup of this agent is running.
|
|
846
1103
|
|
|
847
1104
|
---
|
|
848
1105
|
|
|
849
|
-
### `
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
config). This is how an existing agent picks up image/OpenClaw/CLI updates: the
|
|
853
|
-
software lives in the image, the data in the volume.
|
|
854
|
-
|
|
855
|
-
Flow:
|
|
856
|
-
1. **Auto-backup first.** The volume is backed up to `agent-<id>-prerecreate-<ts>.tar.gz`
|
|
857
|
-
in `rev4a-backups`. **If the backup fails, the recreate is aborted** and the
|
|
858
|
-
running container is left untouched.
|
|
859
|
-
2. The old container is removed (`docker rm -f`) and a new one is created with the
|
|
860
|
-
same image tag, environment variables (`AGENT_*`, `MODEL_*`, `OPENCLAW_*`),
|
|
861
|
-
labels, port mappings, and network — reattaching the same volume.
|
|
862
|
-
3. After startup, `applyRuntimeConfig()` (from `lib/agent-setup.ts`) guarantees:
|
|
863
|
-
- `hooks.bootstrap-extra-files` (Rev4a system rules injection)
|
|
864
|
-
- `skills.load.extraDirs` (shared skills discovery)
|
|
865
|
-
- `models.providers.rev4a` (provider proxy, host-dependent)
|
|
866
|
-
All patches use `config patch --stdin` (deep-merge) — existing user
|
|
867
|
-
customizations are never overwritten.
|
|
868
|
-
|
|
869
|
-
On the next boot, the image entrypoint runs `openclaw doctor --non-interactive`
|
|
870
|
-
**only if the OpenClaw version changed** (safe migrations, no service restart),
|
|
871
|
-
aligning the volume's state/config to the new binary.
|
|
1106
|
+
### `GET /api/agents/[id]/recreate`
|
|
1107
|
+
The running or last recreate of an agent, with live backup progress (`null` when the
|
|
1108
|
+
agent was never recreated).
|
|
872
1109
|
|
|
873
|
-
**Auth:** browser cookie
|
|
1110
|
+
**Auth:** browser cookie or bearer token
|
|
1111
|
+
|
|
1112
|
+
```json
|
|
1113
|
+
{
|
|
1114
|
+
"recreate": {
|
|
1115
|
+
"id": 12, "agentId": "agent_9253eee3", "status": "backing_up",
|
|
1116
|
+
"image": "openclaw-agent-base:2026.7.1-2",
|
|
1117
|
+
"backupFile": "agent-agent_9253eee3-prerecreate-2026-09-17_171000.tar.gz",
|
|
1118
|
+
"backupPercent": 40, "error": null,
|
|
1119
|
+
"startedAtMs": 1789656372120, "finishedAtMs": null
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
```
|
|
1123
|
+
|
|
1124
|
+
- `status`: `backing_up` → `recreating` → `done` | `failed`; `interrupted` after a
|
|
1125
|
+
Rev4a restart cut it off.
|
|
1126
|
+
- `backupPercent` is live only while `backing_up`.
|
|
1127
|
+
|
|
1128
|
+
---
|
|
1129
|
+
|
|
1130
|
+
### `GET /api/agents/[id]/update`
|
|
1131
|
+
The agent's OpenClaw version, the update it could take, and its latest update
|
|
1132
|
+
(`lib/agent-update.ts`).
|
|
1133
|
+
|
|
1134
|
+
**Auth:** browser cookie or bearer token
|
|
874
1135
|
|
|
875
|
-
**Response:**
|
|
876
1136
|
```json
|
|
877
|
-
{
|
|
1137
|
+
{
|
|
1138
|
+
"currentVersion": "2026.7.1-2",
|
|
1139
|
+
"candidate": { "fromVersion": "2026.7.1-2", "toVersion": "2026.9.3" },
|
|
1140
|
+
"update": {
|
|
1141
|
+
"id": 3, "agentId": "agent_9253eee3", "status": "backing_up",
|
|
1142
|
+
"fromVersion": "2026.7.1-2", "toVersion": "2026.9.3",
|
|
1143
|
+
"backupFile": "agent-agent_9253eee3-preupdate-2026.7.1-2-2026-09-16_003512345.tar.gz",
|
|
1144
|
+
"backupPercent": 40, "verification": null, "error": null,
|
|
1145
|
+
"startedAtMs": 1789524912000, "finishedAtMs": null
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
878
1148
|
```
|
|
879
1149
|
|
|
880
|
-
|
|
1150
|
+
- `candidate` is `null` when no newer supported version is downloaded.
|
|
1151
|
+
- `update.status`: `pending` → `backing_up` → `migrating` → `verifying` → `done` |
|
|
1152
|
+
`failed`; `interrupted` after a Rev4a restart cut it off; `rolling_back` →
|
|
1153
|
+
`rolled_back` | `rollback_failed` for a rollback.
|
|
1154
|
+
- `verification`: `{ sessions, lostEvents: [{ session, before, after }], missingCronJobs, ok }`.
|
|
1155
|
+
|
|
1156
|
+
`503` when Docker cannot be reached.
|
|
1157
|
+
|
|
1158
|
+
### `POST /api/agents/[id]/update`
|
|
1159
|
+
Start an update. **Body (optional):** `{ "version": "2026.9.3" }`; by default the newest
|
|
1160
|
+
supported version downloaded. Returns `202` with the new `update`; the steps continue in
|
|
1161
|
+
the background.
|
|
1162
|
+
|
|
1163
|
+
1. **Preflight** (refused with `409`): the agent runs, the target is downloaded,
|
|
1164
|
+
supported and newer than the agent's version, no update or backup is running.
|
|
1165
|
+
2. **Baseline**, inside the agent: transcript events per session (`.jsonl` lines on
|
|
1166
|
+
2026.7.x, `transcript_events` rows from 9.x; trajectory files not counted) and cron
|
|
1167
|
+
job names.
|
|
1168
|
+
3. **`backing_up`**: cold backup `agent-<id>-preupdate-<from>-<ts>.tar.gz`
|
|
1169
|
+
(`startColdBackup`, agent left stopped).
|
|
1170
|
+
4. **`migrating`**: the container is recreated on `openclaw-agent-base:<to>`
|
|
1171
|
+
(`lib/agent-recreate.ts`); its entrypoint runs `doctor --fix`; Rev4a waits up to 10
|
|
1172
|
+
minutes for `/startupz` to report `started` with the target version, then re-applies
|
|
1173
|
+
its config and provider block.
|
|
1174
|
+
5. **`verifying`**: every session has at least as many events as before and every cron
|
|
1175
|
+
job is still there → `done`, then unused agent images are removed (`pruneAgentImages`).
|
|
1176
|
+
|
|
1177
|
+
A failure before the recreate starts the old container again. A failure after it leaves
|
|
1178
|
+
the agent on the new version; the panel offers Rollback.
|
|
1179
|
+
|
|
1180
|
+
**Errors:** `400` unsupported version, `409` refused (reason in `error`), `500`.
|
|
1181
|
+
|
|
1182
|
+
While an update runs, `backup`, `cold-backup`, `restore`, `recreate`, `lifecycle`,
|
|
1183
|
+
`PATCH` and `DELETE /api/agents/[id]` answer `409 "An update of this agent is running"`,
|
|
1184
|
+
and the provider sync skips the agent.
|
|
1185
|
+
|
|
1186
|
+
### `POST /api/agents/[id]/update/rollback`
|
|
1187
|
+
Roll back the latest update — `done`, `failed`, `interrupted` or `rollback_failed` — while
|
|
1188
|
+
its pre-update backup exists. `202` with the `update` (`rolling_back`).
|
|
1189
|
+
|
|
1190
|
+
1. The previous version's image is made local (pulled when it is a supported version).
|
|
1191
|
+
2. The agent is stopped and its volume replaced by the pre-update backup.
|
|
1192
|
+
3. The container is recreated on `openclaw-agent-base:<from>` and must report that version.
|
|
1193
|
+
|
|
1194
|
+
Rev4a's config is not re-applied: the restored `openclaw.json` is the one that version
|
|
1195
|
+
accepted. Everything the agent did after the backup is lost. `409` when there is nothing
|
|
1196
|
+
to roll back, the backup is gone, or an update or backup is running.
|
|
881
1197
|
|
|
882
1198
|
---
|
|
883
1199
|
|
|
@@ -966,7 +1282,7 @@ Disconnect Telegram from an agent.
|
|
|
966
1282
|
**Side effects:**
|
|
967
1283
|
- Sets `channels.telegram.enabled: false`, removes `botToken` and `allowFrom`
|
|
968
1284
|
- Removes the telegram binding
|
|
969
|
-
-
|
|
1285
|
+
- Clears the channel's approved senders in OpenClaw's pairing store (`clearChannelAllowlist()`)
|
|
970
1286
|
- No restart required
|
|
971
1287
|
|
|
972
1288
|
---
|
|
@@ -974,6 +1290,12 @@ Disconnect Telegram from an agent.
|
|
|
974
1290
|
### `GET /api/agents/[id]/channels/pairing?channel=telegram`
|
|
975
1291
|
Get pending and approved pairings for a channel.
|
|
976
1292
|
|
|
1293
|
+
Pending requests come from `openclaw pairing list --channel <channel> --json`. Approved
|
|
1294
|
+
senders come from OpenClaw's pairing store — on 2026.9.x the SQLite
|
|
1295
|
+
`channel_pairing_allow_entries` rows in `~/.openclaw/state/openclaw.sqlite`, read through
|
|
1296
|
+
the store's own `readChannelAllowFromStoreSync` (the credentials JSON files older
|
|
1297
|
+
releases used are gone, and reading those returned nothing).
|
|
1298
|
+
|
|
977
1299
|
**Auth:** browser cookie
|
|
978
1300
|
|
|
979
1301
|
**Query params:**
|
|
@@ -994,7 +1316,6 @@ Get pending and approved pairings for a channel.
|
|
|
994
1316
|
```
|
|
995
1317
|
|
|
996
1318
|
- Pending codes are from `openclaw pairing list --json`
|
|
997
|
-
- Approved senders are from the credentials allowFrom file
|
|
998
1319
|
|
|
999
1320
|
---
|
|
1000
1321
|
|
|
@@ -1024,6 +1345,15 @@ Approve a pending pairing code.
|
|
|
1024
1345
|
### `DELETE /api/agents/[id]/channels/pairing?channel=telegram&senderId=123456789`
|
|
1025
1346
|
Revoke an approved sender.
|
|
1026
1347
|
|
|
1348
|
+
There is no CLI or RPC for this (`openclaw pairing` covers pending requests only,
|
|
1349
|
+
`channels.pairing.*` has no remove — verified on 2026.9.3). The pairing store's own
|
|
1350
|
+
writer is used instead: `removeChannelAllowFromStoreEntry`, located by function name in
|
|
1351
|
+
OpenClaw's `pairing-store` module and run inside the container, so the write goes
|
|
1352
|
+
through the same state transaction the CLI uses. Verifying a 2026.7.x agent is out of
|
|
1353
|
+
scope: Rev4a targets 2026.9.3. On a release that no longer exposes the writer the call
|
|
1354
|
+
fails with a message pointing at `/allowlist remove` from the chat — nothing is silently
|
|
1355
|
+
left unchanged.
|
|
1356
|
+
|
|
1027
1357
|
**Auth:** browser cookie
|
|
1028
1358
|
|
|
1029
1359
|
**Query params:**
|
|
@@ -1035,6 +1365,207 @@ Revoke an approved sender.
|
|
|
1035
1365
|
**Response (200):** `{ "success": true }`
|
|
1036
1366
|
|
|
1037
1367
|
**Response (400):** `{ "error": "senderId query param is required" }`
|
|
1368
|
+
**Response (404):** `{ "error": "..." }` when the store is unavailable
|
|
1369
|
+
(the sender is already gone when present — the desired state).
|
|
1370
|
+
|
|
1371
|
+
---
|
|
1372
|
+
|
|
1373
|
+
### `GET /api/agents/[id]/devices`
|
|
1374
|
+
Browser access to one agent's Control UI: requests waiting for approval and browsers
|
|
1375
|
+
already approved.
|
|
1376
|
+
|
|
1377
|
+
**Auth:** browser cookie
|
|
1378
|
+
|
|
1379
|
+
Runs `openclaw devices list --json` inside the container with the async `dockerExec`;
|
|
1380
|
+
see `lib/agent-devices.ts`. On OpenClaw 2026.7.x, which answers `/startupz` with HTML
|
|
1381
|
+
and whose config disables device approval, the CLI is not called and both lists are
|
|
1382
|
+
empty.
|
|
1383
|
+
|
|
1384
|
+
**Response:**
|
|
1385
|
+
```json
|
|
1386
|
+
{
|
|
1387
|
+
"running": true,
|
|
1388
|
+
"requiresApproval": true,
|
|
1389
|
+
"pending": [
|
|
1390
|
+
{
|
|
1391
|
+
"requestId": "facd76cb-0dd5-462e-8d1d-d18ed35f6129",
|
|
1392
|
+
"deviceId": "f1ad26df8bc4…",
|
|
1393
|
+
"displayName": null,
|
|
1394
|
+
"platform": "MacIntel",
|
|
1395
|
+
"clientId": "openclaw-control-ui",
|
|
1396
|
+
"clientMode": "webchat",
|
|
1397
|
+
"roles": ["operator"],
|
|
1398
|
+
"scopes": ["operator.admin", "operator.read", "operator.write"],
|
|
1399
|
+
"remoteIp": "192.168.65.1",
|
|
1400
|
+
"browserOrigin": "http://192.168.1.88:3710",
|
|
1401
|
+
"isRepair": false,
|
|
1402
|
+
"requestedAtMs": 1789490956623
|
|
1403
|
+
}
|
|
1404
|
+
],
|
|
1405
|
+
"approved": [
|
|
1406
|
+
{
|
|
1407
|
+
"deviceId": "848f99c9b0af…",
|
|
1408
|
+
"label": null,
|
|
1409
|
+
"platform": "MacIntel",
|
|
1410
|
+
"clientId": "openclaw-control-ui",
|
|
1411
|
+
"clientMode": "webchat",
|
|
1412
|
+
"roles": ["operator"],
|
|
1413
|
+
"scopes": ["operator.admin", "operator.read", "operator.write"],
|
|
1414
|
+
"remoteIp": "192.168.65.1",
|
|
1415
|
+
"approvedVia": "bootstrap",
|
|
1416
|
+
"createdAtMs": 1789492279000,
|
|
1417
|
+
"approvedAtMs": 1789492279000,
|
|
1418
|
+
"lastUsedAtMs": 1789492300000
|
|
1419
|
+
}
|
|
1420
|
+
]
|
|
1421
|
+
}
|
|
1422
|
+
```
|
|
1423
|
+
|
|
1424
|
+
- A stopped agent answers `200` with `running: false` and empty lists.
|
|
1425
|
+
- `requiresApproval` is `null` when neither `/startupz` nor the config can be read.
|
|
1426
|
+
- `approvedVia`: `silent` (local connection, approved automatically), `owner` (approved
|
|
1427
|
+
by an operator), `bootstrap` (one-time link), or another OpenClaw value.
|
|
1428
|
+
- Public keys and token values are never returned.
|
|
1429
|
+
|
|
1430
|
+
**Errors:** `404` unknown agent, `503` Docker unreachable, `502` the OpenClaw command
|
|
1431
|
+
failed or returned incomplete JSON.
|
|
1432
|
+
|
|
1433
|
+
---
|
|
1434
|
+
|
|
1435
|
+
### `POST /api/agents/[id]/devices`
|
|
1436
|
+
Approve or reject a pending request.
|
|
1437
|
+
|
|
1438
|
+
**Auth:** browser cookie
|
|
1439
|
+
|
|
1440
|
+
**Body:** `{ "action": "approve" | "reject", "requestId": "<requestId>" }`
|
|
1441
|
+
|
|
1442
|
+
**Response (200):** `{ "ok": true }`
|
|
1443
|
+
|
|
1444
|
+
**Errors:** `400` invalid action or `requestId`, `404` unknown agent, `409` agent not
|
|
1445
|
+
running, `502` command failed.
|
|
1446
|
+
|
|
1447
|
+
---
|
|
1448
|
+
|
|
1449
|
+
### `PATCH /api/agents/[id]/devices`
|
|
1450
|
+
Rename an approved browser. The label is preferred over the name the client reports.
|
|
1451
|
+
|
|
1452
|
+
**Auth:** browser cookie
|
|
1453
|
+
|
|
1454
|
+
**Body:** `{ "deviceId": "<deviceId>", "name": "Office laptop" }` — 1-64 printable characters
|
|
1455
|
+
|
|
1456
|
+
**Response (200):** `{ "ok": true }`
|
|
1457
|
+
|
|
1458
|
+
**Errors:** as for `POST`.
|
|
1459
|
+
|
|
1460
|
+
---
|
|
1461
|
+
|
|
1462
|
+
### `DELETE /api/agents/[id]/devices?deviceId=<deviceId>`
|
|
1463
|
+
Revoke an approved browser (`openclaw devices remove`). It needs approval again the next
|
|
1464
|
+
time it connects.
|
|
1465
|
+
|
|
1466
|
+
**Auth:** browser cookie
|
|
1467
|
+
|
|
1468
|
+
**Response (200):** `{ "ok": true }`
|
|
1469
|
+
|
|
1470
|
+
**Errors:** as for `POST`.
|
|
1471
|
+
|
|
1472
|
+
---
|
|
1473
|
+
|
|
1474
|
+
### `GET /api/agents/[id]/open-control-ui`
|
|
1475
|
+
Where the "Open" buttons navigate, in a new tab. Redirects (`302`) to the agent's
|
|
1476
|
+
Control UI.
|
|
1477
|
+
|
|
1478
|
+
**Auth:** browser cookie
|
|
1479
|
+
|
|
1480
|
+
The target host is the one the request reached Rev4a on (the `Host` header), with the
|
|
1481
|
+
agent's published port — never a parameter, so the link cannot be sent to another host.
|
|
1482
|
+
|
|
1483
|
+
- **OpenClaw 9.x** (it answers `/startupz` with JSON): runs `openclaw dashboard --json`
|
|
1484
|
+
in the container and redirects to its `browserUrl`, with the host, the port and the
|
|
1485
|
+
`gatewayUrl` fragment parameter rewritten to that host and the published port. The
|
|
1486
|
+
link is single-use, expires after ten minutes, and pairs the browser with no approval.
|
|
1487
|
+
- **OpenClaw 2026.7.x**, or when no one-time link can be issued: redirects to the plain
|
|
1488
|
+
link `http://<host>:<port>/#token=<token>`, with the token read inside the container
|
|
1489
|
+
(`/root/.agent-token`, then `OPENCLAW_GATEWAY_TOKEN`).
|
|
1490
|
+
|
|
1491
|
+
A navigation, not a JSON call: the button opens this URL inside the click, so no tab
|
|
1492
|
+
is left blank after an `await` and no popup can be blocked.
|
|
1493
|
+
|
|
1494
|
+
**Errors** (plain text, meant for the tab): `400` host not readable, `404` unknown agent,
|
|
1495
|
+
`409` agent not running or no published Control UI port, `503` Docker unreachable,
|
|
1496
|
+
`502` neither a one-time link nor a token available.
|
|
1497
|
+
|
|
1498
|
+
---
|
|
1499
|
+
|
|
1500
|
+
### `GET /api/agents/[id]/invite-link`
|
|
1501
|
+
A Control UI link to send to someone else — the "Invite link" button in Browser access.
|
|
1502
|
+
The browser that opens it passes gateway auth and waits in the agent's approval list;
|
|
1503
|
+
nothing opens until an operator approves it.
|
|
1504
|
+
|
|
1505
|
+
**Auth:** browser cookie
|
|
1506
|
+
|
|
1507
|
+
**Response:**
|
|
1508
|
+
```json
|
|
1509
|
+
{ "url": "http://212.0.113.7:3710/#token=…", "loopbackHost": false }
|
|
1510
|
+
```
|
|
1511
|
+
|
|
1512
|
+
- `url` is the plain token link. The host is the one the request reached Rev4a on
|
|
1513
|
+
(the `Host` header); the port is the agent's published Control UI port. The token is
|
|
1514
|
+
read inside the container with the entrypoint's precedence (`/root/.agent-token`,
|
|
1515
|
+
then `OPENCLAW_GATEWAY_TOKEN`), so it is the one the Gateway checks.
|
|
1516
|
+
- `loopbackHost` is true when that host is `localhost`, `127.x` or `[::1]`: nobody else
|
|
1517
|
+
can reach the link.
|
|
1518
|
+
- The gateway token is shared by every agent. Changing the agents token
|
|
1519
|
+
(`PUT /api/agents/token`) invalidates every link sent.
|
|
1520
|
+
- `Cache-Control: no-store`.
|
|
1521
|
+
|
|
1522
|
+
**Errors:** `400` host not readable, `404` unknown agent, `409` agent not running, no
|
|
1523
|
+
published Control UI port, or no browser approval on this agent (the link would open
|
|
1524
|
+
with no approval), `502` approval state or token not readable, `503` Docker
|
|
1525
|
+
unreachable.
|
|
1526
|
+
|
|
1527
|
+
---
|
|
1528
|
+
|
|
1529
|
+
### `GET /api/agents/devices-summary`
|
|
1530
|
+
Browsers waiting for approval, per running agent — the "browser waiting" badge on the
|
|
1531
|
+
agent list.
|
|
1532
|
+
|
|
1533
|
+
**Auth:** browser cookie
|
|
1534
|
+
|
|
1535
|
+
**Response:**
|
|
1536
|
+
```json
|
|
1537
|
+
{ "agents": [ { "agentId": "agent_2a3c3a07", "requiresApproval": true, "pending": 1 } ] }
|
|
1538
|
+
```
|
|
1539
|
+
|
|
1540
|
+
- At most three agents are read at a time; one that cannot be read reports
|
|
1541
|
+
`error: true` and `pending: 0` without failing the others.
|
|
1542
|
+
- `503` with `agents: []` when Docker cannot be reached.
|
|
1543
|
+
|
|
1544
|
+
---
|
|
1545
|
+
|
|
1546
|
+
### `GET /api/agents/activity-summary`
|
|
1547
|
+
The long operation each agent is in the middle of, if any — the chip on the agent cards:
|
|
1548
|
+
`BACKUP nn%`, `RESTORING`, `RECREATING`, `UPDATING`.
|
|
1549
|
+
|
|
1550
|
+
**Auth:** browser cookie or bearer token
|
|
1551
|
+
|
|
1552
|
+
**Response:**
|
|
1553
|
+
```json
|
|
1554
|
+
{ "agents": [
|
|
1555
|
+
{ "agentId": "agent_9253eee3", "kind": "backup", "file": "agent-agent_9253eee3-cold-2026-09-17_163103795.tar.gz", "percent": 16 },
|
|
1556
|
+
{ "agentId": "agent_2a3c3a07", "kind": "restore", "file": "agent-agent_2a3c3a07-cold-2026-09-21_172032317.tar.gz" },
|
|
1557
|
+
{ "agentId": "agent_fb540be6", "kind": "update", "version": "2026.9.3" }
|
|
1558
|
+
] }
|
|
1559
|
+
```
|
|
1560
|
+
|
|
1561
|
+
- `kind`: `backup`, `restore`, `recreate`, `update` or `edit`. A running cold backup takes
|
|
1562
|
+
precedence, because while its helper archives the volume that is the phase actually in
|
|
1563
|
+
progress (an update's or recreate's own backup phase reports `backup`).
|
|
1564
|
+
- `percent` is only on `backup` (`null` until the archive size has been measured; the
|
|
1565
|
+
client renders `BACKUP…` then). `version` is on `recreate`/`update`.
|
|
1566
|
+
- Idle agents are not listed: an idle system answers `{ "agents": [] }`.
|
|
1567
|
+
- Read from the state modules (`agent_restores`, `agent_recreates`, `agent_upgrades`,
|
|
1568
|
+
`agent_edits`) and one Docker list call for the running backup helpers.
|
|
1038
1569
|
|
|
1039
1570
|
---
|
|
1040
1571
|
|
|
@@ -1068,8 +1599,8 @@ Useful for inspecting the container config without SSH or terminal.
|
|
|
1068
1599
|
{
|
|
1069
1600
|
"name": "my-agent",
|
|
1070
1601
|
"config": {
|
|
1071
|
-
"gateway": { "controlUi": { "
|
|
1072
|
-
"agents": { "defaults": { "model": { "primary": "rev4a/deepseek/deepseek-
|
|
1602
|
+
"gateway": { "controlUi": { "dangerouslyAllowHostHeaderOriginFallback": true } },
|
|
1603
|
+
"agents": { "defaults": { "model": { "primary": "rev4a/deepseek/deepseek-flash", "fallbacks": [] } } },
|
|
1073
1604
|
"models": { "providers": { "rev4a": { "baseUrl": "...", "apiKey": "***", "models": [...] } } }
|
|
1074
1605
|
}
|
|
1075
1606
|
}
|
|
@@ -1096,6 +1627,9 @@ The token is stored in `data/agents-token.json`.
|
|
|
1096
1627
|
|
|
1097
1628
|
### `PUT /api/agents/token`
|
|
1098
1629
|
Update the shared agents gateway token and push it to all running agent containers.
|
|
1630
|
+
Agents with a long operation in flight (update, recreate, restore, edit, backup) are
|
|
1631
|
+
skipped and listed, and a container that could not be updated is reported instead of
|
|
1632
|
+
being swallowed.
|
|
1099
1633
|
|
|
1100
1634
|
**Auth:** browser cookie
|
|
1101
1635
|
|
|
@@ -1106,7 +1640,7 @@ Update the shared agents gateway token and push it to all running agent containe
|
|
|
1106
1640
|
|
|
1107
1641
|
**Response:**
|
|
1108
1642
|
```json
|
|
1109
|
-
{ "success": true, "containersUpdated": 2 }
|
|
1643
|
+
{ "success": true, "containersUpdated": 2, "skipped": ["agent_9253eee3"], "failed": [{ "container": "agent_x", "error": "…" }] }
|
|
1110
1644
|
```
|
|
1111
1645
|
|
|
1112
1646
|
**Side effects:**
|
|
@@ -1119,53 +1653,64 @@ Update the shared agents gateway token and push it to all running agent containe
|
|
|
1119
1653
|
## Agent Image Management
|
|
1120
1654
|
|
|
1121
1655
|
### `GET /api/agents/image-status`
|
|
1122
|
-
|
|
1656
|
+
Which OpenClaw versions of the agent image are downloaded, and whether the registry
|
|
1657
|
+
publishes a newer supported one.
|
|
1123
1658
|
|
|
1124
1659
|
**Auth:** browser cookie or bearer token
|
|
1125
1660
|
|
|
1126
|
-
The
|
|
1127
|
-
|
|
1661
|
+
The registry tag list is read over the registry HTTP API (anonymous token for ghcr,
|
|
1662
|
+
plain HTTP for a `localhost` registry), cached for 10 minutes and de-duplicated while
|
|
1663
|
+
in flight, so polling this endpoint does not re-query the registry every time.
|
|
1128
1664
|
|
|
1129
1665
|
**Response:**
|
|
1130
1666
|
```json
|
|
1131
1667
|
{
|
|
1668
|
+
"downloading": false,
|
|
1669
|
+
"downloadingVersion": null,
|
|
1132
1670
|
"exists": true,
|
|
1133
1671
|
"needsUpdate": false,
|
|
1134
|
-
"
|
|
1135
|
-
"
|
|
1672
|
+
"localVersions": ["2026.9.3"],
|
|
1673
|
+
"newestLocal": "2026.9.3",
|
|
1674
|
+
"available": null,
|
|
1675
|
+
"registryReachable": true
|
|
1136
1676
|
}
|
|
1137
1677
|
```
|
|
1138
1678
|
|
|
1139
1679
|
| Field | Description |
|
|
1140
1680
|
|---|---|
|
|
1141
|
-
| `exists` |
|
|
1142
|
-
| `needsUpdate` | `true`
|
|
1143
|
-
| `
|
|
1144
|
-
| `
|
|
1681
|
+
| `exists` | A supported version is downloaded as `openclaw-agent-base:<version>` |
|
|
1682
|
+
| `needsUpdate` | `true` when none is, or when `available` is set |
|
|
1683
|
+
| `localVersions` | Supported versions downloaded, newest first |
|
|
1684
|
+
| `newestLocal` | The version new agents are created on |
|
|
1685
|
+
| `available` | Newest supported version on the registry that is not downloaded and is newer than `newestLocal`, or null |
|
|
1686
|
+
| `downloading` / `downloadingVersion` | A download running, and its version |
|
|
1687
|
+
| `registryReachable` | The tag list could be read |
|
|
1145
1688
|
|
|
1146
1689
|
### `POST /api/agents/download-image`
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
The frontend polls `GET /api/agents/image-status` for progress updates.
|
|
1690
|
+
Downloads one OpenClaw version of the agent image in the background. Returns 202
|
|
1691
|
+
immediately; the frontend polls `GET /api/agents/image-status` for completion.
|
|
1150
1692
|
|
|
1151
1693
|
**Auth:** browser cookie or bearer token
|
|
1152
1694
|
|
|
1695
|
+
**Body (optional):** `{ "version": "2026.9.3" }` — a supported version. Without it, the
|
|
1696
|
+
newest supported version the registry publishes.
|
|
1697
|
+
|
|
1153
1698
|
**Response (202):**
|
|
1154
1699
|
```json
|
|
1155
|
-
{ "
|
|
1700
|
+
{ "started": true, "version": "2026.9.3" }
|
|
1156
1701
|
```
|
|
1157
1702
|
|
|
1158
|
-
**
|
|
1159
|
-
```json
|
|
1160
|
-
{ "error": "A download is already in progress" }
|
|
1161
|
-
```
|
|
1703
|
+
**Errors:** `400` unsupported version; `409` a download is already in progress.
|
|
1162
1704
|
|
|
1163
|
-
|
|
1164
|
-
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
-
|
|
1168
|
-
|
|
1705
|
+
The download is `lib/buildAgentImage.ts` → `downloadAgentImage({ version, onEvent, signal })`:
|
|
1706
|
+
- nothing to do when `openclaw-agent-base:<version>` is already here
|
|
1707
|
+
- `docker pull <registry>:<version>`, tag `openclaw-agent-base:<version>`, untag the
|
|
1708
|
+
registry reference (`pullAgentImage()` in `lib/agent-images.ts`)
|
|
1709
|
+
- when the pull fails and the repository Dockerfile's `ARG OPENCLAW_VERSION` is that
|
|
1710
|
+
version, `docker build --build-arg OPENCLAW_VERSION=<version>` instead; any other
|
|
1711
|
+
version fails
|
|
1712
|
+
- one download at a time (in-process lock, `getIsDownloading()` /
|
|
1713
|
+
`getDownloadingVersion()`); output goes to `/tmp/rev4a-download-<timestamp>.log`
|
|
1169
1714
|
|
|
1170
1715
|
### `GET /api/agents-active`
|
|
1171
1716
|
|
|
@@ -1401,7 +1946,7 @@ Run history for a specific cron job. Queries the container's gateway via `opencl
|
|
|
1401
1946
|
"error": "Channel is required ...",
|
|
1402
1947
|
"runAtMs": 1784820025581,
|
|
1403
1948
|
"durationMs": 11046,
|
|
1404
|
-
"model": "deepseek/deepseek-
|
|
1949
|
+
"model": "deepseek/deepseek-flash",
|
|
1405
1950
|
"usage": { "input_tokens": 15872, "output_tokens": 597 }
|
|
1406
1951
|
}
|
|
1407
1952
|
]
|
|
@@ -1518,7 +2063,7 @@ List all running Docker containers with resource usage.
|
|
|
1518
2063
|
"containers": [
|
|
1519
2064
|
{
|
|
1520
2065
|
"name": "openclaw-atlas",
|
|
1521
|
-
"image": "openclaw-agent-base:
|
|
2066
|
+
"image": "openclaw-agent-base:2026.9.3",
|
|
1522
2067
|
"status": "running",
|
|
1523
2068
|
"ports": ["0.0.0.0:3731->3000/tcp"],
|
|
1524
2069
|
"created": "2026-06-20T10:00:00Z",
|
|
@@ -1739,7 +2284,7 @@ Chat with the built-in AI assistant (PULSE).
|
|
|
1739
2284
|
{
|
|
1740
2285
|
"message": "What can I do on the Agents page?",
|
|
1741
2286
|
"page": "/agents",
|
|
1742
|
-
"model": "deepseek/deepseek-
|
|
2287
|
+
"model": "deepseek/deepseek-flash",
|
|
1743
2288
|
"history": []
|
|
1744
2289
|
}
|
|
1745
2290
|
```
|
|
@@ -1748,7 +2293,7 @@ Chat with the built-in AI assistant (PULSE).
|
|
|
1748
2293
|
|-----------|-------------------------|----------|--------------------------------------------------|
|
|
1749
2294
|
| `message` | string | yes | The user's question |
|
|
1750
2295
|
| `page` | string | no | Current page path (used for contextual prompts) |
|
|
1751
|
-
| `model` | string | no | AI model to use (e.g. `deepseek/deepseek-
|
|
2296
|
+
| `model` | string | no | AI model to use (e.g. `deepseek/deepseek-flash`) |
|
|
1752
2297
|
| `history` | `{role, content}[]` | no | Recent conversation history (up to 10 messages) |
|
|
1753
2298
|
|
|
1754
2299
|
**Response:** `text/event-stream` (Server-Sent Events)
|