@flame0510/project-aether 1.1.15 → 1.3.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 +2 -1
- package/app/agents/ModelSection.tsx +313 -0
- package/app/agents/PageClient.tsx +83 -4
- package/app/agents/create/page.tsx +8 -21
- package/app/api/agents/[id]/model/route.ts +113 -0
- package/app/api/agents/[id]/recreate/route.ts +10 -34
- package/app/api/agents/[id]/route.ts +10 -29
- package/app/api/agents/create/route.ts +59 -57
- package/app/api/agents/models-summary/route.ts +163 -0
- package/app/api/assistant/route.ts +36 -15
- package/app/api/credentials/[id]/sync/route.ts +3 -3
- package/app/api/credentials/detect/route.ts +126 -176
- package/app/api/credentials/route.ts +3 -0
- package/app/api/gateway/agent/route.ts +23 -6
- package/app/api/gateway/provider/keys.ts +13 -1
- package/app/api/gateway/provider/route.ts +43 -12
- package/app/api/gateway/sync.ts +248 -72
- 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/components/PulseChat.tsx +25 -39
- package/app/components/Skeleton.tsx +132 -0
- 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 +461 -140
- package/app/credentials/loading.tsx +19 -5
- package/app/gateway/PageClient.tsx +257 -673
- 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 +73 -9
- package/docs/ARCHITECTURE.md +92 -33
- package/docs/FRONTEND-ARCHITECTURE.md +36 -6
- package/docs/REV4A.md +62 -30
- package/docs/dev/API-REFERENCE.md +490 -227
- package/docs/dev/DATABASE.md +8 -3
- package/docs/dev/GATEWAY.md +236 -92
- package/docs/dev/PROVIDERS.md +44 -44
- package/docs/rag/DATA-FRESHNESS.md +57 -28
- package/docs/rag/GLOSSARY.md +20 -18
- package/docs/rag/REV4A-OVERVIEW.md +28 -32
- package/docs/rag/WHAT-I-CAN-ANSWER.md +10 -12
- package/instrumentation.ts +9 -1
- package/lib/agent-readiness.ts +110 -0
- package/lib/channelManager.ts +64 -22
- package/lib/container-file.ts +27 -0
- package/lib/credentials/delivery.ts +212 -119
- package/lib/credentials/detect.ts +229 -97
- package/lib/credentials/providers.ts +38 -7
- package/lib/credentials/vault.ts +78 -13
- package/lib/docker-exec.ts +50 -14
- package/lib/model-catalogue.ts +140 -27
- 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-
|
|
3
|
+
> **Last updated:** 2026-09-14
|
|
4
4
|
|
|
5
5
|
All routes are under `/api/`. Authentication is required on every endpoint
|
|
6
6
|
unless otherwise noted.
|
|
@@ -68,100 +68,134 @@ Delete the `rev4a_token` cookie.
|
|
|
68
68
|
|
|
69
69
|
---
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
### `GET /api/auth/status`
|
|
72
|
+
Whether a password has been configured, in the environment or in the `.env` the
|
|
73
|
+
setup wizard writes. Used by the frontend to choose between the setup wizard and
|
|
74
|
+
the login form.
|
|
72
75
|
|
|
73
|
-
|
|
74
|
-
Returns per-step onboarding progress.
|
|
76
|
+
**Auth:** none — it reveals only whether setup has happened, never the password
|
|
75
77
|
|
|
76
|
-
**
|
|
78
|
+
**Response:** `{ "configured": true }`
|
|
77
79
|
|
|
78
|
-
|
|
79
|
-
```json
|
|
80
|
-
{
|
|
81
|
-
"complete": false,
|
|
82
|
-
"steps": { "welcome": true, "providers": false, "agents": false },
|
|
83
|
-
"completedAt": null
|
|
84
|
-
}
|
|
85
|
-
```
|
|
80
|
+
---
|
|
86
81
|
|
|
87
|
-
|
|
82
|
+
## Setup & wizard
|
|
88
83
|
|
|
89
|
-
### `
|
|
90
|
-
|
|
84
|
+
### `GET /api/wizard/status`
|
|
85
|
+
Whether the first-run wizard has been completed.
|
|
91
86
|
|
|
92
|
-
**Auth:** browser cookie
|
|
87
|
+
**Auth:** browser cookie or bearer token
|
|
93
88
|
|
|
94
|
-
**
|
|
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) |
|
|
89
|
+
**Response:** `{ "done": true }`
|
|
100
90
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
91
|
+
### `POST /api/wizard/complete`
|
|
92
|
+
Mark the wizard as completed.
|
|
93
|
+
|
|
94
|
+
**Auth:** browser cookie or bearer token
|
|
95
|
+
|
|
96
|
+
**Response:** `{ "done": true }`
|
|
97
|
+
|
|
98
|
+
### `POST /api/wizard/reset`
|
|
99
|
+
Reset the wizard so it runs again.
|
|
100
|
+
|
|
101
|
+
**Auth:** browser cookie or bearer token
|
|
102
|
+
|
|
103
|
+
**Response:** `{ "done": false }`
|
|
109
104
|
|
|
110
105
|
---
|
|
111
106
|
|
|
112
|
-
|
|
107
|
+
### `POST /api/setup/password`
|
|
108
|
+
Set the dashboard password during first-run setup.
|
|
113
109
|
|
|
114
|
-
|
|
115
|
-
(tracked in the repository). Full documentation at [GATEWAY.md](GATEWAY.md) and
|
|
116
|
-
[PROVIDERS.md](PROVIDERS.md).
|
|
110
|
+
**Auth:** none, and only usable once — returns `409` if a password already exists
|
|
117
111
|
|
|
118
|
-
|
|
119
|
-
Returns live gateway status from all agent containers.
|
|
112
|
+
**Body:** `{ "password": "..." }` — minimum 8 characters
|
|
120
113
|
|
|
121
|
-
**
|
|
114
|
+
**Errors:** `400` invalid JSON, missing password, or shorter than 8 characters;
|
|
115
|
+
`409` already configured; `500` the `.env` file could not be written.
|
|
122
116
|
|
|
123
|
-
**Response:**
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
117
|
+
**Response:** `{ "status": "ok" }`
|
|
118
|
+
|
|
119
|
+
### `POST /api/setup/restart`
|
|
120
|
+
Restart the service so a freshly written `.env` takes effect.
|
|
121
|
+
|
|
122
|
+
**Auth:** open before a password exists, then browser cookie or bearer token (`requireAuthIfConfigured`)
|
|
123
|
+
|
|
124
|
+
**Errors:** `500` if the `.env` file is missing, or the restart could not be issued.
|
|
125
|
+
|
|
126
|
+
### `GET /api/setup/agent-image`
|
|
127
|
+
Whether the `openclaw-agent-base` image is present.
|
|
128
|
+
|
|
129
|
+
**Auth:** open before a password exists, then browser cookie or bearer token (`requireAuthIfConfigured`)
|
|
130
|
+
|
|
131
|
+
**Response:** `{ "exists": true, "imageId": "sha256:..." }`, or `{ "exists": false }`
|
|
132
|
+
when the image is absent **or Docker is unreachable** — the two are not distinguished.
|
|
133
|
+
|
|
134
|
+
### `POST /api/setup/agent-image`
|
|
135
|
+
Pull or build the agent base image, streaming progress.
|
|
136
|
+
|
|
137
|
+
**Auth:** open before a password exists, then browser cookie or bearer token (`requireAuthIfConfigured`)
|
|
138
|
+
|
|
139
|
+
**Errors:** `409` if a download is already running.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Gateway
|
|
144
|
+
|
|
145
|
+
The Gateway API reads the model catalogue from [`models.config.json`](../../models.config.json)
|
|
146
|
+
(tracked in the repository). Full documentation at [GATEWAY.md](GATEWAY.md) and
|
|
147
|
+
[PROVIDERS.md](PROVIDERS.md).
|
|
140
148
|
|
|
141
149
|
### `PUT /api/gateway/provider`
|
|
142
|
-
|
|
150
|
+
Enable or disable a single model in the catalogue, then sync every agent.
|
|
143
151
|
|
|
144
|
-
**Auth:**
|
|
152
|
+
**Auth:** browser cookie or bearer token
|
|
145
153
|
|
|
146
|
-
**Body:**
|
|
154
|
+
**Body:** `{ "modelId": "deepseek/deepseek-flash", "enabled": true }` — both
|
|
155
|
+
required. Missing either returns `400`; an unknown `modelId` returns `404`.
|
|
156
|
+
If Docker cannot be reached the toggle is refused with `503` and nothing is saved,
|
|
157
|
+
so a whole-fleet outage never leaves the checkboxes out of step with the agents.
|
|
147
158
|
|
|
148
|
-
**Response:**
|
|
159
|
+
**Response:** `status` is the toggle, which is saved before the sync runs. `sync`
|
|
160
|
+
reports separately whether the agents received it.
|
|
149
161
|
```json
|
|
150
162
|
{
|
|
151
163
|
"status": "ok",
|
|
152
|
-
"
|
|
153
|
-
"
|
|
164
|
+
"modelId": "deepseek/deepseek-flash",
|
|
165
|
+
"enabled": true,
|
|
166
|
+
"provider": "deepseek",
|
|
167
|
+
"sync": {
|
|
168
|
+
"ok": true,
|
|
169
|
+
"activeModels": 3,
|
|
170
|
+
"total": 2,
|
|
171
|
+
"synced": ["agent_2a3c3a07", "agent_9253eee3"],
|
|
172
|
+
"stopped": [],
|
|
173
|
+
"failed": [],
|
|
174
|
+
"summary": "Synced 2 of 2 agent(s)."
|
|
175
|
+
}
|
|
154
176
|
}
|
|
155
177
|
```
|
|
156
178
|
|
|
179
|
+
`sync.ok` is true only when every agent container received the catalogue; zero
|
|
180
|
+
agents counts as ok. "Every" includes agents that are not running (stopped, paused,
|
|
181
|
+
created): they are written through `docker cp`, listed in `stopped` as well as
|
|
182
|
+
`synced`, and apply the change when started or resumed. A container whose config
|
|
183
|
+
cannot be read is listed in `failed` and left untouched — the sync never writes a
|
|
184
|
+
config it did not read. When Docker cannot be reached, `total` is `0`, `dockerError`
|
|
185
|
+
carries the cause and `ok` is false. For this toggle that case is refused up front
|
|
186
|
+
with `503`, so it appears here only if Docker drops between the check and the sync;
|
|
187
|
+
on a provider key save (`POST`) it is reported, not refused. `configError` is set,
|
|
188
|
+
and nothing is attempted, when the gateway key is missing or `models.config.json`
|
|
189
|
+
could not be read.
|
|
190
|
+
|
|
157
191
|
**Side effects:**
|
|
158
192
|
- Writes `models.providers.rev4a` to each agent container's `openclaw.json`
|
|
159
193
|
- 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
|
|
194
|
+
- **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`)
|
|
161
195
|
- Does NOT restart the gateway (config is written live to the file)
|
|
162
196
|
|
|
163
197
|
### `PUT /api/gateway/agent`
|
|
164
|
-
Update model
|
|
198
|
+
Update the primary model and fallbacks of a single agent container.
|
|
165
199
|
|
|
166
200
|
**Auth:** any auth method
|
|
167
201
|
|
|
@@ -169,28 +203,38 @@ Update model config for a single agent container.
|
|
|
169
203
|
```json
|
|
170
204
|
{
|
|
171
205
|
"containerName": "openclaw-atlas",
|
|
172
|
-
"model": "deepseek/deepseek-
|
|
173
|
-
"fallbacks": []
|
|
206
|
+
"model": "deepseek/deepseek-flash",
|
|
207
|
+
"fallbacks": ["deepseek/deepseek-v4-pro"]
|
|
174
208
|
}
|
|
175
209
|
```
|
|
176
|
-
|
|
210
|
+
`fallbacks` omitted and `fallbacks: []` are different requests. Omitted leaves the
|
|
211
|
+
container's existing fallbacks untouched; `[]` clears them.
|
|
177
212
|
|
|
178
|
-
|
|
213
|
+
`containerName` is used verbatim as the `docker exec` target, not resolved from the
|
|
214
|
+
`AGENT_ID` label. Every container's name currently equals its label, so an agent id
|
|
215
|
+
works.
|
|
216
|
+
|
|
217
|
+
**Response:** reports what was written, with the `rev4a/` prefix applied.
|
|
179
218
|
```json
|
|
180
219
|
{
|
|
181
220
|
"status": "ok",
|
|
182
221
|
"agent": "openclaw-atlas",
|
|
183
222
|
"model": {
|
|
184
|
-
"primary": "rev4a/deepseek/deepseek-
|
|
223
|
+
"primary": "rev4a/deepseek/deepseek-flash",
|
|
224
|
+
"fallbacks": ["rev4a/deepseek/deepseek-v4-pro"]
|
|
185
225
|
},
|
|
186
226
|
"verify": { "ok": true, "bytes": 2058 }
|
|
187
227
|
}
|
|
188
228
|
```
|
|
189
229
|
|
|
190
230
|
**Behaviour:**
|
|
191
|
-
- Writes `agents.defaults.model
|
|
192
|
-
|
|
193
|
-
|
|
231
|
+
- Writes `agents.defaults.model`, and the entry in `agents.list` whose `id` is
|
|
232
|
+
`main` (skipped if there is none), both as `{ primary, fallbacks }`. The primary is
|
|
233
|
+
removed from its own fallback list.
|
|
234
|
+
- Rewrites the whole `openclaw.json` with `docker exec … cat >`. No gateway restart:
|
|
235
|
+
OpenClaw hot-applies the change.
|
|
236
|
+
- Does not check the model against the catalogue. The proxy does that when the model
|
|
237
|
+
is used, so a model saved here while disabled is refused at request time.
|
|
194
238
|
|
|
195
239
|
### `GET /api/gateway/provider`
|
|
196
240
|
Returns current provider configuration state.
|
|
@@ -213,7 +257,7 @@ Returns current provider configuration state.
|
|
|
213
257
|
"baseUrl": "https://api.deepseek.com",
|
|
214
258
|
"docsUrl": "https://platform.deepseek.com/api_keys",
|
|
215
259
|
"models": [
|
|
216
|
-
{ "id": "deepseek/deepseek-
|
|
260
|
+
{ "id": "deepseek/deepseek-flash", "name": "DeepSeek Flash", "enabled": true },
|
|
217
261
|
{ "id": "deepseek/deepseek-v4-pro", "name": "DeepSeek V4 Pro", "enabled": true }
|
|
218
262
|
]
|
|
219
263
|
}
|
|
@@ -223,7 +267,8 @@ Returns current provider configuration state.
|
|
|
223
267
|
|
|
224
268
|
### `GET /api/provider/v1/models`
|
|
225
269
|
|
|
226
|
-
Rev4a Provider Gateway — returns
|
|
270
|
+
Rev4a Provider Gateway — returns the models this deployment offers (enabled after
|
|
271
|
+
overrides, provider has a key). A missing or wrong token gets an empty list, not a 401.
|
|
227
272
|
|
|
228
273
|
**Auth:** Bearer token (must match `rev4a` key in `data/provider-keys.json`)
|
|
229
274
|
|
|
@@ -233,7 +278,7 @@ Rev4a Provider Gateway — returns available models filtered by auth token.
|
|
|
233
278
|
"object": "list",
|
|
234
279
|
"total": 9,
|
|
235
280
|
"data": [
|
|
236
|
-
{ "id": "deepseek/deepseek-
|
|
281
|
+
{ "id": "deepseek/deepseek-flash", "object": "model", "created": 1700000000, "owned_by": "deepseek", "permission": [], "root": "deepseek/deepseek-flash" }
|
|
237
282
|
]
|
|
238
283
|
}
|
|
239
284
|
```
|
|
@@ -245,6 +290,10 @@ Rev4a Provider Gateway — returns available models filtered by auth token.
|
|
|
245
290
|
|
|
246
291
|
### `POST /api/provider/v1/chat/completions`
|
|
247
292
|
|
|
293
|
+
A model the deployment does not offer is refused with `400` before anything reaches
|
|
294
|
+
upstream. Routing fields that would let the upstream choose another model — `models`,
|
|
295
|
+
`route`, `preset` — are removed from the body before forwarding.
|
|
296
|
+
|
|
248
297
|
Rev4a Provider Gateway — proxy chat completions to the correct upstream.
|
|
249
298
|
|
|
250
299
|
**Auth:** Bearer token (must match `rev4a` key in `data/provider-keys.json`)
|
|
@@ -252,7 +301,7 @@ Rev4a Provider Gateway — proxy chat completions to the correct upstream.
|
|
|
252
301
|
**Body:**
|
|
253
302
|
```json
|
|
254
303
|
{
|
|
255
|
-
"model": "rev4a/deepseek-
|
|
304
|
+
"model": "rev4a/deepseek-flash",
|
|
256
305
|
"messages": [{"role": "user", "content": "Hello"}]
|
|
257
306
|
}
|
|
258
307
|
```
|
|
@@ -264,6 +313,79 @@ Rev4a Provider Gateway — proxy chat completions to the correct upstream.
|
|
|
264
313
|
|
|
265
314
|
---
|
|
266
315
|
|
|
316
|
+
### `GET /api/models`
|
|
317
|
+
Models eligible to be used: the catalogue filtered down to models that are enabled
|
|
318
|
+
(after overrides) and whose provider has a key configured, grouped by provider. Feeds the model pickers in the agent's
|
|
319
|
+
Model panel.
|
|
320
|
+
|
|
321
|
+
**Auth:** browser cookie or bearer token
|
|
322
|
+
|
|
323
|
+
**Response:**
|
|
324
|
+
```json
|
|
325
|
+
[
|
|
326
|
+
{
|
|
327
|
+
"provider": "DeepSeek",
|
|
328
|
+
"emoji": "🧠",
|
|
329
|
+
"models": [
|
|
330
|
+
{
|
|
331
|
+
"id": "deepseek/deepseek-flash",
|
|
332
|
+
"label": "DeepSeek Flash (V4.1)",
|
|
333
|
+
"input": 0.3,
|
|
334
|
+
"output": 1.2,
|
|
335
|
+
"deprecated": false
|
|
336
|
+
}
|
|
337
|
+
]
|
|
338
|
+
}
|
|
339
|
+
]
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
`input` and `output` are USD per 1M tokens, `null` when unpriced. `deprecated`
|
|
343
|
+
marks a name upstream has retired; it stays selectable and the pickers tag it.
|
|
344
|
+
See [GATEWAY.md](GATEWAY.md) for the deprecation lifecycle.
|
|
345
|
+
|
|
346
|
+
### `GET /api/gateway/provider/balance`
|
|
347
|
+
Remaining credit for one provider.
|
|
348
|
+
|
|
349
|
+
**Auth:** browser cookie or bearer token
|
|
350
|
+
|
|
351
|
+
**Query params:** `provider` — one of `deepseek`, `openrouter`, `glm`.
|
|
352
|
+
|
|
353
|
+
**Errors:** `502` when the provider's API cannot be reached or rejects the call.
|
|
354
|
+
|
|
355
|
+
### `POST /api/gateway/provider/oauth`
|
|
356
|
+
Run the OAuth device flow for a provider that supports it.
|
|
357
|
+
|
|
358
|
+
**Auth:** browser cookie or bearer token
|
|
359
|
+
|
|
360
|
+
**Body:** `{ "provider": "..." }`
|
|
361
|
+
|
|
362
|
+
**Errors:** `400` for an unknown provider; the response body carries `available`
|
|
363
|
+
with the providers that do support OAuth.
|
|
364
|
+
|
|
365
|
+
### `POST /api/aliases/add`
|
|
366
|
+
Add a model alias.
|
|
367
|
+
|
|
368
|
+
**Auth:** browser cookie or bearer token
|
|
369
|
+
|
|
370
|
+
**Body:** `{ "alias": "fast", "model": "deepseek/deepseek-chat" }`
|
|
371
|
+
|
|
372
|
+
**Response:** `{ "ok": true, "output": "..." }` — `output` is the CLI's stdout.
|
|
373
|
+
|
|
374
|
+
**Errors:** `400` if `alias` or `model` is missing.
|
|
375
|
+
|
|
376
|
+
### `POST /api/aliases/remove`
|
|
377
|
+
Remove a model alias.
|
|
378
|
+
|
|
379
|
+
**Auth:** browser cookie or bearer token
|
|
380
|
+
|
|
381
|
+
**Body:** `{ "alias": "fast" }`
|
|
382
|
+
|
|
383
|
+
**Response:** `{ "ok": true, "output": "..." }`
|
|
384
|
+
|
|
385
|
+
**Errors:** `400` if `alias` is missing.
|
|
386
|
+
|
|
387
|
+
---
|
|
388
|
+
|
|
267
389
|
## Config
|
|
268
390
|
|
|
269
391
|
### `PUT /api/config/env`
|
|
@@ -344,6 +466,17 @@ Returns a single session with its events and child sessions.
|
|
|
344
466
|
|
|
345
467
|
---
|
|
346
468
|
|
|
469
|
+
### `GET /api/sessions/[id]`
|
|
470
|
+
One session with its events and any child sessions.
|
|
471
|
+
|
|
472
|
+
**Auth:** browser cookie or bearer token
|
|
473
|
+
|
|
474
|
+
**Response:** `{ "session": {…}, "events": [...], "children": [...] }`
|
|
475
|
+
|
|
476
|
+
**Errors:** `404` if the session does not exist.
|
|
477
|
+
|
|
478
|
+
---
|
|
479
|
+
|
|
347
480
|
## Stats & Costs
|
|
348
481
|
|
|
349
482
|
### `GET /api/stats`
|
|
@@ -553,6 +686,7 @@ List running agent containers (Docker containers with `AGENT_ID` label) with ful
|
|
|
553
686
|
"controlPort": "3033"
|
|
554
687
|
}
|
|
555
688
|
]
|
|
689
|
+
```
|
|
556
690
|
|
|
557
691
|
**Notes:**
|
|
558
692
|
- The URL is always `http://<host-ip>:<port>#token=...` (every agent gets a host port mapping)
|
|
@@ -562,6 +696,77 @@ List running agent containers (Docker containers with `AGENT_ID` label) with ful
|
|
|
562
696
|
|
|
563
697
|
---
|
|
564
698
|
|
|
699
|
+
### `GET /api/agents/models-summary`
|
|
700
|
+
One row per agent container, running or not: the model it runs and whether that
|
|
701
|
+
model is still in good standing. Feeds the model chip on the agent cards. Running
|
|
702
|
+
agents are read with `docker exec`, others with `docker cp`. If Docker cannot be
|
|
703
|
+
reached the route answers `503` with an `error`, not an empty list.
|
|
704
|
+
|
|
705
|
+
**Auth:** any auth method
|
|
706
|
+
|
|
707
|
+
**Response:**
|
|
708
|
+
```json
|
|
709
|
+
{
|
|
710
|
+
"agents": [
|
|
711
|
+
{
|
|
712
|
+
"agentId": "atlas",
|
|
713
|
+
"containerName": "openclaw-atlas",
|
|
714
|
+
"primary": "rev4a/deepseek/deepseek-flash",
|
|
715
|
+
"fallbackCount": 0,
|
|
716
|
+
"firstFallback": null,
|
|
717
|
+
"health": "ok"
|
|
718
|
+
}
|
|
719
|
+
]
|
|
720
|
+
}
|
|
721
|
+
```
|
|
722
|
+
|
|
723
|
+
`health`, ordered by the remedy each needs:
|
|
724
|
+
|
|
725
|
+
| value | meaning | remedy |
|
|
726
|
+
|---|---|---|
|
|
727
|
+
| `missing` | the primary is not in the catalogue at all | pick another model |
|
|
728
|
+
| `disabled` | in the catalogue but not offered — unchecked, or its provider has no key; the proxy refuses it | pick another model |
|
|
729
|
+
| `out-of-sync` | offered by the gateway, absent from this container's synced copy | Sync All Agents |
|
|
730
|
+
| `deprecated` | works, on a name upstream has retired | switch when convenient |
|
|
731
|
+
| `ok` | usable | — |
|
|
732
|
+
| `unset` | no primary configured | — |
|
|
733
|
+
| `unknown` | the container did not answer | — |
|
|
734
|
+
|
|
735
|
+
### `GET /api/agents/[id]/model`
|
|
736
|
+
The model configuration of one agent, read from that container alone rather than
|
|
737
|
+
by walking the fleet. Writes still go to `PUT /api/gateway/agent`.
|
|
738
|
+
|
|
739
|
+
**Auth:** any auth method
|
|
740
|
+
|
|
741
|
+
**Response:**
|
|
742
|
+
```json
|
|
743
|
+
{
|
|
744
|
+
"agentId": "atlas",
|
|
745
|
+
"reachable": true,
|
|
746
|
+
"primary": "rev4a/deepseek/deepseek-flash",
|
|
747
|
+
"fallbacks": [],
|
|
748
|
+
"resolvable": true,
|
|
749
|
+
"catalogueSize": 41,
|
|
750
|
+
"primaryStatus": "offered"
|
|
751
|
+
}
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
`resolvable` is tri-state. `false` means the primary is absent from the catalogue
|
|
755
|
+
synced into that container, usually because the model was disabled on the Gateway
|
|
756
|
+
page while this agent still pointed at it. `true` means it is present. `null` means
|
|
757
|
+
the question does not apply: either the container did not answer, or no primary is
|
|
758
|
+
set.
|
|
759
|
+
|
|
760
|
+
When `reachable` is `false` the container did not answer, `primary` and `fallbacks`
|
|
761
|
+
come back empty rather than read, and **`catalogueSize` is omitted entirely** — it
|
|
762
|
+
is present only on the reachable branch.
|
|
763
|
+
|
|
764
|
+
`primaryStatus`, also reachable branch only, says why the primary would fail
|
|
765
|
+
regardless of this container: `offered`, `disabled` (unchecked, or its provider has
|
|
766
|
+
no key) or `missing` (no longer in the catalogue). `null` when no primary is set.
|
|
767
|
+
`resolvable` answers a different question — whether *this container's* synced copy
|
|
768
|
+
has it — so `offered` with `resolvable: false` means the container is out of sync.
|
|
769
|
+
|
|
565
770
|
### `POST /api/agents/create`
|
|
566
771
|
Create a new agent container from a template.
|
|
567
772
|
|
|
@@ -573,7 +778,7 @@ Create a new agent container from a template.
|
|
|
573
778
|
"name": "my-agent",
|
|
574
779
|
"template": "prometheus",
|
|
575
780
|
"portRange": "3700-3709",
|
|
576
|
-
"model": "deepseek/deepseek-
|
|
781
|
+
"model": "deepseek/deepseek-flash",
|
|
577
782
|
"fallbacks": []
|
|
578
783
|
}
|
|
579
784
|
```
|
|
@@ -583,7 +788,7 @@ Create a new agent container from a template.
|
|
|
583
788
|
| `name` | yes | Container name, also becomes `AGENT_ID` and subdomain |
|
|
584
789
|
| `template` | yes | Template name (directory in `agent-templates/`) |
|
|
585
790
|
| `portRange` | no | Optional port range (e.g. `3700-3709`) or single port (e.g. `3700`). Default: auto-assigned 10-port block |
|
|
586
|
-
| `model` | no | Primary model
|
|
791
|
+
| `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 |
|
|
587
792
|
| `fallbacks` | no | Array of fallback model IDs |
|
|
588
793
|
|
|
589
794
|
**Every agent is created with a host port mapping:**
|
|
@@ -610,11 +815,16 @@ Create a new agent container from a template.
|
|
|
610
815
|
|
|
611
816
|
**Post-creation pipeline:**
|
|
612
817
|
1. OpenClaw gateway starts with `--allow-unconfigured`, generating its own default config
|
|
613
|
-
2. The route waits for the gateway to
|
|
614
|
-
3. Once ready,
|
|
615
|
-
|
|
818
|
+
2. The route waits for the gateway to finish starting (`waitForGatewayReady`, up to 60 s)
|
|
819
|
+
3. Once ready, rewrites `openclaw.json` in one write — `gateway.controlUi`,
|
|
820
|
+
`agents.defaults.model` (primary and fallbacks, always as `rev4a/<provider>/<model>`)
|
|
821
|
+
and `update` — then runs `openclaw gateway restart`
|
|
822
|
+
4. Applies the runtime config (hooks, shared skills) and writes `models.providers.rev4a`
|
|
823
|
+
with `openclaw config patch --stdin`. No restart for this step: `gateway.reload`
|
|
824
|
+
defaults to `hybrid`, which hot-applies changes under `agents`, `models` and
|
|
825
|
+
`routing`. If the catalogue cannot be read the provider patch is skipped and logged
|
|
616
826
|
|
|
617
|
-
|
|
827
|
+
Failures in steps 3 and 4 do not fail the request.
|
|
618
828
|
|
|
619
829
|
**Side effects:**
|
|
620
830
|
- Template files (`AGENTS.md`, `SOUL.md`, etc.) are copied into the container workspace via `docker cp`
|
|
@@ -762,6 +972,21 @@ aligning the volume's state/config to the new binary.
|
|
|
762
972
|
|
|
763
973
|
---
|
|
764
974
|
|
|
975
|
+
### `POST /api/agents/[id]/lifecycle`
|
|
976
|
+
Start, stop or restart an agent container.
|
|
977
|
+
|
|
978
|
+
**Auth:** browser cookie or bearer token
|
|
979
|
+
|
|
980
|
+
**Body:** `{ "action": "start" | "stop" | "restart" }`
|
|
981
|
+
|
|
982
|
+
Redundant actions are no-ops: starting a running container, or stopping a stopped
|
|
983
|
+
one, succeeds without touching it.
|
|
984
|
+
|
|
985
|
+
**Errors:** `400` for a malformed body, an unknown action, or an invalid agent id;
|
|
986
|
+
`404` when no container carries that `AGENT_ID`.
|
|
987
|
+
|
|
988
|
+
---
|
|
989
|
+
|
|
765
990
|
## Channels
|
|
766
991
|
|
|
767
992
|
Channel APIs read/write agent channel configuration (Telegram) directly inside agent containers via `docker exec`. All endpoints require the agent container to be running.
|
|
@@ -935,7 +1160,7 @@ Useful for inspecting the container config without SSH or terminal.
|
|
|
935
1160
|
"name": "my-agent",
|
|
936
1161
|
"config": {
|
|
937
1162
|
"gateway": { "controlUi": { "allowedOrigins": ["..."], "dangerouslyDisableDeviceAuth": true } },
|
|
938
|
-
"agents": { "defaults": { "model": { "primary": "rev4a/deepseek/deepseek-
|
|
1163
|
+
"agents": { "defaults": { "model": { "primary": "rev4a/deepseek/deepseek-flash", "fallbacks": [] } } },
|
|
939
1164
|
"models": { "providers": { "rev4a": { "baseUrl": "...", "apiKey": "***", "models": [...] } } }
|
|
940
1165
|
}
|
|
941
1166
|
}
|
|
@@ -987,7 +1212,10 @@ Update the shared agents gateway token and push it to all running agent containe
|
|
|
987
1212
|
### `GET /api/agents/image-status`
|
|
988
1213
|
Check the status of the `openclaw-agent-base` Docker image.
|
|
989
1214
|
|
|
990
|
-
**Auth:**
|
|
1215
|
+
**Auth:** browser cookie or bearer token
|
|
1216
|
+
|
|
1217
|
+
The ghcr.io digest lookup is cached for 10 minutes and de-duplicated while in
|
|
1218
|
+
flight, so polling this endpoint does not re-query the registry every time.
|
|
991
1219
|
|
|
992
1220
|
**Response:**
|
|
993
1221
|
```json
|
|
@@ -1011,7 +1239,7 @@ Starts pulling or building the `openclaw-agent-base:latest` image. Returns 202
|
|
|
1011
1239
|
Accepted immediately — the operation runs in the background and writes logs.
|
|
1012
1240
|
The frontend polls `GET /api/agents/image-status` for progress updates.
|
|
1013
1241
|
|
|
1014
|
-
**Auth:**
|
|
1242
|
+
**Auth:** browser cookie or bearer token
|
|
1015
1243
|
|
|
1016
1244
|
**Response (202):**
|
|
1017
1245
|
```json
|
|
@@ -1023,17 +1251,7 @@ The frontend polls `GET /api/agents/image-status` for progress updates.
|
|
|
1023
1251
|
{ "error": "A download is already in progress" }
|
|
1024
1252
|
```
|
|
1025
1253
|
|
|
1026
|
-
|
|
1027
|
-
Kills an in-progress docker pull/build.
|
|
1028
|
-
|
|
1029
|
-
**Auth:** public (no auth required)
|
|
1030
|
-
|
|
1031
|
-
**Response:**
|
|
1032
|
-
```json
|
|
1033
|
-
{ "aborted": true }
|
|
1034
|
-
```
|
|
1035
|
-
|
|
1036
|
-
Image state is managed in `lib/buildAgentImage.ts`:
|
|
1254
|
+
Image state is managed in `lib/buildAgentImage.ts` (library functions, not HTTP routes):
|
|
1037
1255
|
- `downloadAgentImage(opts)` — attempts `docker pull` from registry first, then falls back to `docker build`
|
|
1038
1256
|
all stdout/stderr to a log file, supports optional `onEvent` callback and `AbortSignal`
|
|
1039
1257
|
- `abortDownload()` — sends SIGKILL to the child process, releases the download lock
|
|
@@ -1274,7 +1492,7 @@ Run history for a specific cron job. Queries the container's gateway via `opencl
|
|
|
1274
1492
|
"error": "Channel is required ...",
|
|
1275
1493
|
"runAtMs": 1784820025581,
|
|
1276
1494
|
"durationMs": 11046,
|
|
1277
|
-
"model": "deepseek/deepseek-
|
|
1495
|
+
"model": "deepseek/deepseek-flash",
|
|
1278
1496
|
"usage": { "input_tokens": 15872, "output_tokens": 597 }
|
|
1279
1497
|
}
|
|
1280
1498
|
]
|
|
@@ -1361,93 +1579,23 @@ Write content to a workspace file.
|
|
|
1361
1579
|
|
|
1362
1580
|
---
|
|
1363
1581
|
|
|
1364
|
-
##
|
|
1365
|
-
|
|
1366
|
-
Full key management documentation at [PROVIDERS.md](PROVIDERS.md).
|
|
1367
|
-
|
|
1368
|
-
### `GET /api/vault`
|
|
1369
|
-
List all stored credentials (keys masked).
|
|
1370
|
-
|
|
1371
|
-
**Auth:** browser cookie
|
|
1372
|
-
|
|
1373
|
-
**Response:**
|
|
1374
|
-
```json
|
|
1375
|
-
{
|
|
1376
|
-
"providers": {
|
|
1377
|
-
"deepseek": { "keyStatus": "present", "scoped": ["atlas"] }
|
|
1378
|
-
},
|
|
1379
|
-
"services": {
|
|
1380
|
-
"github": { "tokenStatus": "present", "user": "Flame0510", "scoped": ["argus", "atlas"] }
|
|
1381
|
-
}
|
|
1382
|
-
}
|
|
1383
|
-
```
|
|
1384
|
-
|
|
1385
|
-
### `GET /api/vault/provider/key`
|
|
1386
|
-
Return the full API key for a provider.
|
|
1387
|
-
|
|
1388
|
-
**Auth:** browser cookie
|
|
1389
|
-
|
|
1390
|
-
**Query params:** `provider` (required), `agent` (optional)
|
|
1391
|
-
|
|
1392
|
-
**Response (200):**
|
|
1393
|
-
```json
|
|
1394
|
-
{
|
|
1395
|
-
"provider": "deepseek",
|
|
1396
|
-
"apiKey": "sk-...full-key...",
|
|
1397
|
-
"masked": "sk-...be03",
|
|
1398
|
-
"source": "local"
|
|
1399
|
-
}
|
|
1400
|
-
```
|
|
1401
|
-
|
|
1402
|
-
**Error:** `400` if `provider` missing; `404` if key not found.
|
|
1403
|
-
|
|
1404
|
-
See [PROVIDERS.md](PROVIDERS.md#get-apivaultproviderkey) for full detail.
|
|
1405
|
-
|
|
1406
|
-
### `POST /api/vault/provider`
|
|
1407
|
-
Add or update a provider API key.
|
|
1408
|
-
|
|
1409
|
-
**Auth:** browser cookie
|
|
1410
|
-
|
|
1411
|
-
**Body:** `{ "provider": "deepseek", "apiKey": "sk-...", "baseUrl": "https://api.deepseek.com" }`
|
|
1412
|
-
|
|
1413
|
-
**Response:** `{ "status": "ok", "provider": "deepseek", "masked": "sk-...be03", "updatedAt": 1749200000000 }`
|
|
1414
|
-
|
|
1415
|
-
### `DELETE /api/vault/provider`
|
|
1416
|
-
Remove a provider and all its keys.
|
|
1417
|
-
|
|
1418
|
-
**Auth:** browser cookie
|
|
1419
|
-
|
|
1420
|
-
**Body:** `{ "provider": "deepseek" }`
|
|
1421
|
-
|
|
1422
|
-
**Response:** `{ "status": "removed" }`
|
|
1423
|
-
|
|
1424
|
-
### `PUT /api/vault/service`
|
|
1425
|
-
Add or update a service token.
|
|
1426
|
-
|
|
1427
|
-
**Auth:** browser cookie
|
|
1428
|
-
|
|
1429
|
-
**Body:** `{ "id": "github", "token": "***", "user": "Flame0510", "scopes": ["atlas"] }`
|
|
1430
|
-
|
|
1431
|
-
**Response:** `{ "ok": true }`
|
|
1432
|
-
|
|
1433
|
-
### `DELETE /api/vault/service`
|
|
1434
|
-
Remove a service.
|
|
1435
|
-
|
|
1436
|
-
**Auth:** browser cookie
|
|
1582
|
+
## Provider keys and credentials
|
|
1437
1583
|
|
|
1438
|
-
|
|
1584
|
+
Provider API keys are read with `GET /api/gateway/provider` and set with
|
|
1585
|
+
`POST /api/gateway/provider` — documented in `docs/dev/PROVIDERS.md`, which is the
|
|
1586
|
+
only file carrying that endpoint's request bodies. `PUT` toggles a model, not a
|
|
1587
|
+
key. The keys are stored in `data/provider-keys.json`.
|
|
1588
|
+
Third-party service credentials are managed under `/api/credentials`
|
|
1589
|
+
(documented below) and stored in `credentials.db`.
|
|
1439
1590
|
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
### `PUT /api/vault/permissions`
|
|
1443
|
-
Update agent permissions for a credential.
|
|
1591
|
+
There is no `/api/vault` route, and no per-credential agent scoping: any stored
|
|
1592
|
+
credential can be synced to any container.
|
|
1444
1593
|
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
**Response:** `{ "ok": true }`
|
|
1594
|
+
Full key management documentation at [PROVIDERS.md](PROVIDERS.md).
|
|
1448
1595
|
|
|
1449
1596
|
---
|
|
1450
1597
|
|
|
1598
|
+
|
|
1451
1599
|
## Containers
|
|
1452
1600
|
|
|
1453
1601
|
### `GET /api/containers`
|
|
@@ -1472,24 +1620,13 @@ List all running Docker containers with resource usage.
|
|
|
1472
1620
|
}
|
|
1473
1621
|
```
|
|
1474
1622
|
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
**Auth:** browser cookie
|
|
1479
|
-
|
|
1480
|
-
**Query params:** `name` (required), `tail` (default 50)
|
|
1481
|
-
|
|
1482
|
-
**Response:** `{ "name": "openclaw-atlas", "logs": "[log lines...]" }`
|
|
1483
|
-
|
|
1484
|
-
### `POST /api/containers/action`
|
|
1485
|
-
Execute action on a container.
|
|
1486
|
-
|
|
1487
|
-
**Body:** `{ "action": "restart" | "stop" | "start", "name": "openclaw-atlas" }`
|
|
1488
|
-
|
|
1489
|
-
**Response:** `{ "ok": true, "message": "Container openclaw-atlas restarted" }`
|
|
1623
|
+
The container list is the only container route. There is no logs endpoint and no
|
|
1624
|
+
action endpoint: logs are read through the terminal WebSocket, and lifecycle
|
|
1625
|
+
operations on agent containers go through `/api/agents/[id]/lifecycle`.
|
|
1490
1626
|
|
|
1491
1627
|
---
|
|
1492
1628
|
|
|
1629
|
+
|
|
1493
1630
|
## Tools
|
|
1494
1631
|
|
|
1495
1632
|
### `GET /api/tools-config`
|
|
@@ -1522,30 +1659,16 @@ Return the installed OpenClaw CLI version.
|
|
|
1522
1659
|
|
|
1523
1660
|
**Fallback:** `{ "version": "unknown" }` if the command fails.
|
|
1524
1661
|
|
|
1525
|
-
###
|
|
1526
|
-
Documentation placeholder for WebSocket access.
|
|
1527
|
-
|
|
1528
|
-
**Auth:** browser cookie
|
|
1529
|
-
|
|
1530
|
-
**Response:** plain text with HTTP `426 Upgrade Required`
|
|
1531
|
-
|
|
1532
|
-
Example body:
|
|
1533
|
-
```text
|
|
1534
|
-
WebSocket endpoint available at ws://HOST/ws. This route is a documentation placeholder and does not upgrade connections.
|
|
1535
|
-
```
|
|
1536
|
-
|
|
1537
|
-
### `GET /ws`
|
|
1538
|
-
Live WebSocket endpoint exposed by the custom server.
|
|
1539
|
-
|
|
1540
|
-
**Auth:** same browser session as the dashboard
|
|
1662
|
+
### Terminal WebSocket
|
|
1541
1663
|
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
-
|
|
1664
|
+
Not an `/api/` route and not on the dashboard port. `terminal-ws-server.js`
|
|
1665
|
+
listens on **`ws://127.0.0.1:3741`** (`TERMINAL_WS_PORT`), bound to the loopback
|
|
1666
|
+
interface only, and speaks the PTY protocol described in
|
|
1667
|
+
[CONTAINER-TERMINAL.md](../CONTAINER-TERMINAL.md).
|
|
1546
1668
|
|
|
1547
1669
|
---
|
|
1548
1670
|
|
|
1671
|
+
|
|
1549
1672
|
## Plugins
|
|
1550
1673
|
|
|
1551
1674
|
### `GET /api/plugins`
|
|
@@ -1707,7 +1830,7 @@ Chat with the built-in AI assistant (PULSE).
|
|
|
1707
1830
|
{
|
|
1708
1831
|
"message": "What can I do on the Agents page?",
|
|
1709
1832
|
"page": "/agents",
|
|
1710
|
-
"model": "deepseek/deepseek-
|
|
1833
|
+
"model": "deepseek/deepseek-flash",
|
|
1711
1834
|
"history": []
|
|
1712
1835
|
}
|
|
1713
1836
|
```
|
|
@@ -1716,7 +1839,7 @@ Chat with the built-in AI assistant (PULSE).
|
|
|
1716
1839
|
|-----------|-------------------------|----------|--------------------------------------------------|
|
|
1717
1840
|
| `message` | string | yes | The user's question |
|
|
1718
1841
|
| `page` | string | no | Current page path (used for contextual prompts) |
|
|
1719
|
-
| `model` | string | no | AI model to use (e.g. `deepseek/deepseek-
|
|
1842
|
+
| `model` | string | no | AI model to use (e.g. `deepseek/deepseek-flash`) |
|
|
1720
1843
|
| `history` | `{role, content}[]` | no | Recent conversation history (up to 10 messages) |
|
|
1721
1844
|
|
|
1722
1845
|
**Response:** `text/event-stream` (Server-Sent Events)
|
|
@@ -1745,11 +1868,19 @@ data: [DONE]
|
|
|
1745
1868
|
### `GET /api/credentials`
|
|
1746
1869
|
List all stored credential profiles and the provider registry.
|
|
1747
1870
|
|
|
1871
|
+
**Auth:** browser cookie or bearer token
|
|
1872
|
+
|
|
1748
1873
|
**Response:** `{ profiles: CredentialProfile[], providers: CredentialProviderDef[] }`
|
|
1749
1874
|
|
|
1875
|
+
Secrets are never returned here — each profile carries only `secretMasked`.
|
|
1876
|
+
|
|
1877
|
+
Each entry in a provider's `fields` may carry `docsUrl` and `docsLabel`, pointing at the page where that specific value is created. The provider-level `docsUrl` is the general one shown on the card; the field-level pair is what the add/edit form renders next to the input it is asking you to fill.
|
|
1878
|
+
|
|
1750
1879
|
### `POST /api/credentials`
|
|
1751
1880
|
Create a new credential profile.
|
|
1752
1881
|
|
|
1882
|
+
**Auth:** browser cookie or bearer token
|
|
1883
|
+
|
|
1753
1884
|
**Body:** `{ providerId: string, label: string, secret: Record<string, string> }`
|
|
1754
1885
|
|
|
1755
1886
|
**Response:** `{ id: string }` (201)
|
|
@@ -1757,30 +1888,77 @@ Create a new credential profile.
|
|
|
1757
1888
|
### `GET /api/credentials/[id]`
|
|
1758
1889
|
Get a single credential profile (no secret returned).
|
|
1759
1890
|
|
|
1891
|
+
**Auth:** browser cookie or bearer token
|
|
1892
|
+
|
|
1760
1893
|
### `PATCH /api/credentials/[id]`
|
|
1761
1894
|
Update label and/or secret of a credential.
|
|
1762
1895
|
|
|
1896
|
+
**Auth:** browser cookie or bearer token
|
|
1897
|
+
|
|
1763
1898
|
**Body:** `{ label: string, secret: Record<string, string> }`
|
|
1764
1899
|
|
|
1900
|
+
Secret fields are **merged** onto the stored payload, not substituted for it:
|
|
1901
|
+
only the fields carrying a non-empty value are written, and every blank field
|
|
1902
|
+
keeps whatever is already stored. This matters for the one two-field provider,
|
|
1903
|
+
Trello, where rotating the `token` alone must leave `apiKey` intact — both are
|
|
1904
|
+
required by the REST delivery. An empty `secret` (every field blank) therefore
|
|
1905
|
+
updates the label only.
|
|
1906
|
+
|
|
1907
|
+
When the merge changes the stored payload, `last_synced_at` is reset to `NULL`:
|
|
1908
|
+
the agents still hold the previous value, so the profile reads "synced never"
|
|
1909
|
+
until it is delivered again, and `GET /api/credentials/detect` reports those
|
|
1910
|
+
agents as holding an unrecognised credential for that provider.
|
|
1911
|
+
|
|
1765
1912
|
### `DELETE /api/credentials/[id]`
|
|
1766
1913
|
Delete a credential and its secret permanently.
|
|
1767
1914
|
|
|
1915
|
+
**Auth:** browser cookie or bearer token
|
|
1916
|
+
|
|
1768
1917
|
### `POST /api/credentials/[id]/sync`
|
|
1769
|
-
Sync the credential to
|
|
1918
|
+
Sync the credential to agent containers.
|
|
1770
1919
|
|
|
1771
|
-
**
|
|
1920
|
+
**Auth:** browser cookie or bearer token
|
|
1921
|
+
|
|
1922
|
+
**Body (optional):** `{ containers?: string[] }` — if omitted, targets every agent container carrying an `AGENT_ID` label.
|
|
1772
1923
|
|
|
1773
1924
|
**Response:** `{ results: { container: string, status: 'ok'|'error', error?: string }[] }`
|
|
1774
1925
|
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1926
|
+
Each **targeted** container is first cleaned of this provider only — other
|
|
1927
|
+
providers on the same container are left alone — and then receives the new
|
|
1928
|
+
credential and its TOOLS.md row.
|
|
1929
|
+
|
|
1930
|
+
**Containers outside `containers` are not touched.** Syncing is therefore not a
|
|
1931
|
+
way to revoke: dropping an agent from the list leaves whatever it already holds
|
|
1932
|
+
in place. Use `DELETE` below to remove a credential from a container.
|
|
1933
|
+
|
|
1934
|
+
### `DELETE /api/credentials/[id]/sync`
|
|
1935
|
+
Remove this credential from a single container ("de-sync").
|
|
1936
|
+
|
|
1937
|
+
**Auth:** browser cookie or bearer token
|
|
1938
|
+
|
|
1939
|
+
**Body:** `{ container: string }` — required.
|
|
1940
|
+
|
|
1941
|
+
**Response:** `{ ok: true }`
|
|
1942
|
+
|
|
1943
|
+
Deletes the provider's CLI auth inside that container and drops its TOOLS.md
|
|
1944
|
+
row. `last_synced_at` is deliberately left untouched — it records the last
|
|
1945
|
+
delivery, and a removal is not one — and the audit entry is recorded as
|
|
1946
|
+
`desync`. The profile stays `active`, because de-sync targets one container and
|
|
1947
|
+
the credential may still be delivered to others.
|
|
1778
1948
|
|
|
1779
1949
|
### `GET /api/credentials/detect`
|
|
1780
1950
|
Live-detect which credentials are actually present inside agent containers.
|
|
1781
1951
|
|
|
1952
|
+
**Auth:** browser cookie or bearer token
|
|
1953
|
+
|
|
1782
1954
|
**Query params (optional):** `?agent=prometheus` — filter to a single container.
|
|
1783
1955
|
|
|
1956
|
+
Costly: one `docker exec` per container, measured at ~6.5s each. Almost all of
|
|
1957
|
+
that is the three CLI calls reaching the network from inside the container, so
|
|
1958
|
+
the figure is largely platform-independent — the `docker exec` itself is ~90ms,
|
|
1959
|
+
and the local file reads another ~100ms. Callers should treat it as a background
|
|
1960
|
+
refresh, not something to block a page render on.
|
|
1961
|
+
|
|
1784
1962
|
**Response:**
|
|
1785
1963
|
```json
|
|
1786
1964
|
{
|
|
@@ -1802,34 +1980,119 @@ Live-detect which credentials are actually present inside agent containers.
|
|
|
1802
1980
|
"supabaseCliInstalled": true,
|
|
1803
1981
|
"trelloLoggedIn": false,
|
|
1804
1982
|
"trelloCliInstalled": true,
|
|
1983
|
+
"notionLoggedIn": false,
|
|
1805
1984
|
"toolsMdHasMarkers": true,
|
|
1806
1985
|
"matchedProfiles": [
|
|
1807
1986
|
{ "profileId": "cred_xxx", "providerId": "github-pat", "label": "GitHub" }
|
|
1808
|
-
]
|
|
1987
|
+
],
|
|
1988
|
+
"installedProviders": ["github-pat"]
|
|
1809
1989
|
}
|
|
1810
1990
|
]
|
|
1811
1991
|
}
|
|
1812
1992
|
```
|
|
1813
1993
|
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
-
|
|
1994
|
+
An agent whose probe failed is returned in its place as
|
|
1995
|
+
`{ "container", "agentId", "error" }` — none of the fields above are present, so
|
|
1996
|
+
consumers must check `error` first.
|
|
1997
|
+
|
|
1998
|
+
Each container is read with a **single** `docker exec` running one script that
|
|
1999
|
+
emits NUL-delimited key/value pairs; containers are probed concurrently. The
|
|
2000
|
+
script reads the credential files directly and the parsing happens in
|
|
2001
|
+
JavaScript. Three CLI calls run last and only feed display fields — `githubUser`,
|
|
2002
|
+
`vercelUser`, `vercelTeam`, and the `githubLoggedIn` / `vercelLoggedIn` /
|
|
2003
|
+
`supabaseLoggedIn` flags, along with `supabaseLinked` and `supabaseProjectRef`,
|
|
2004
|
+
which are read only when `supabaseLoggedIn` is true. `supabaseLoggedIn` follows
|
|
2005
|
+
the **exit status** of `supabase projects list`, not the text it prints, so a
|
|
2006
|
+
failure whose wording is unfamiliar still reads as not logged in.
|
|
2007
|
+
`trelloLoggedIn` and `notionLoggedIn` come from the config files instead, and so
|
|
2008
|
+
survive a probe cut short. The three calls reach the network from inside the
|
|
2009
|
+
container, so a completion sentinel is emitted before them: a probe cut short
|
|
2010
|
+
during them still yields valid token matching, with those display fields empty.
|
|
2011
|
+
|
|
2012
|
+
Credential files read: `~/.config/gh/hosts.yml`,
|
|
2013
|
+
`~/.local/share/com.vercel.cli/auth.json` and `config.json`,
|
|
2014
|
+
`~/.supabase/access-token` and `/data/supabase/config.toml`,
|
|
2015
|
+
`~/.config/trello/config.json`, `~/.config/notion/config.json`, plus TOOLS.md
|
|
2016
|
+
markers at `/root/.openclaw/workspace/TOOLS.md`.
|
|
1820
2017
|
|
|
1821
2018
|
`matchedProfiles` is determined by SHA256-hashing the token installed in the container
|
|
1822
2019
|
and comparing it against the hashed token of each stored profile in the vault.
|
|
1823
2020
|
Only exact token matches produce a match — no heuristics, no broad profile-level guesses.
|
|
1824
2021
|
|
|
2022
|
+
`installedProviders` lists every provider holding a token, matched or not. A
|
|
2023
|
+
provider present there but absent from `matchedProfiles` holds a credential no
|
|
2024
|
+
stored profile accounts for — an edited-but-not-resynced credential, one
|
|
2025
|
+
installed by hand, or the remains of a deleted profile.
|
|
2026
|
+
|
|
1825
2027
|
### `POST /api/credentials/[id]/reveal`
|
|
1826
|
-
Return the raw secret payload. Every call is
|
|
2028
|
+
Return the raw secret payload. Every call is written to `credential_audit_log`, which no route or view reads.
|
|
1827
2029
|
Use sparingly.
|
|
1828
2030
|
|
|
2031
|
+
**Auth:** browser cookie or bearer token
|
|
2032
|
+
|
|
2033
|
+
Secrets are stored **unencrypted** in `credentials.db` — `credential_secrets.payload`
|
|
2034
|
+
holds the raw JSON. Anything that can read that file, or reach this endpoint with a
|
|
2035
|
+
valid session, has the secrets in the clear.
|
|
2036
|
+
|
|
1829
2037
|
**Response:** `{ secret: Record<string, string> }`
|
|
1830
2038
|
|
|
1831
2039
|
---
|
|
1832
2040
|
|
|
2041
|
+
## Diagnostics
|
|
2042
|
+
|
|
2043
|
+
### `GET /api/debug`
|
|
2044
|
+
Whether the three auth-related environment variables are set. Booleans only —
|
|
2045
|
+
no value, masked or otherwise, is returned.
|
|
2046
|
+
|
|
2047
|
+
**Auth:** browser cookie or bearer token
|
|
2048
|
+
|
|
2049
|
+
**Response:** `{ "hasPassword": true, "hasJwtSecret": true, "hasToken": false }`
|
|
2050
|
+
|
|
2051
|
+
### `GET /api/envcheck`
|
|
2052
|
+
The process environment, with `REV4A_PASSWORD`, `REV4A_TOKEN`,
|
|
2053
|
+
`REV4A_JWT_SECRET` and `PROVIDER_DEEPSEEK_API_KEY` redacted. Every other
|
|
2054
|
+
variable is returned **in the clear**, so treat the response as sensitive.
|
|
2055
|
+
|
|
2056
|
+
**Auth:** browser cookie or bearer token
|
|
2057
|
+
|
|
2058
|
+
### `GET /api/provider-usage`
|
|
2059
|
+
Usage and quota state per provider, including live OpenRouter figures.
|
|
2060
|
+
|
|
2061
|
+
**Auth:** browser cookie or bearer token
|
|
2062
|
+
|
|
2063
|
+
**Response:** `{ "providers": [...], "openrouterLive": {…}, "quotas": {…} }`
|
|
2064
|
+
|
|
2065
|
+
### `GET /api/update-check`
|
|
2066
|
+
Installed version against the latest published one.
|
|
2067
|
+
|
|
2068
|
+
**Auth:** browser cookie or bearer token
|
|
2069
|
+
|
|
2070
|
+
**Query params:** `check=1` queries the registry; without it the answer comes
|
|
2071
|
+
from local state only.
|
|
2072
|
+
|
|
2073
|
+
**Response:** `{ "installed": "1.1.15", "latest": "1.1.16", "updateAvailable": true, "package": "@flame0510/project-aether" }`
|
|
2074
|
+
|
|
2075
|
+
### `POST /api/update-check`
|
|
2076
|
+
Run `rev4a update` as a detached background process. The dashboard restarts
|
|
2077
|
+
itself as part of the update, so the connection drops.
|
|
2078
|
+
|
|
2079
|
+
**Auth:** browser cookie or bearer token
|
|
2080
|
+
|
|
2081
|
+
### `GET /api/alerts/smoke`
|
|
2082
|
+
Send a test alert, to verify the Telegram alerting path end to end.
|
|
2083
|
+
|
|
2084
|
+
**Auth:** browser cookie or bearer token
|
|
2085
|
+
|
|
2086
|
+
**Query params:** `stale=0` skips the staleness check.
|
|
2087
|
+
|
|
2088
|
+
**Response:** `{ "ok": true, "stale": true, "result": {…}, "dryRun": false }` —
|
|
2089
|
+
`dryRun` is `true` when `REV4A_TELEGRAM_BOT_TOKEN` or `REV4A_TELEGRAM_CHAT_ID`
|
|
2090
|
+
is unset, meaning nothing was actually sent.
|
|
2091
|
+
|
|
2092
|
+
**Errors:** `403` when smoke alerts are disabled.
|
|
2093
|
+
|
|
2094
|
+
---
|
|
2095
|
+
|
|
1833
2096
|
## Error Format
|
|
1834
2097
|
|
|
1835
2098
|
All errors return JSON:
|