@flame0510/project-aether 1.1.14 → 1.2.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/app/api/agents/route.ts +16 -12
- 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/components/Skeleton.tsx +132 -0
- package/app/credentials/PageClient.tsx +460 -139
- package/app/credentials/loading.tsx +19 -5
- package/docs/ARCHITECTURE.md +78 -31
- package/docs/FRONTEND-ARCHITECTURE.md +13 -5
- package/docs/REV4A.md +23 -14
- package/docs/dev/API-REFERENCE.md +356 -184
- package/docs/dev/DATABASE.md +8 -3
- package/docs/dev/GATEWAY.md +9 -7
- package/docs/dev/PROVIDERS.md +22 -35
- package/docs/rag/GLOSSARY.md +8 -8
- package/docs/rag/REV4A-OVERVIEW.md +7 -10
- package/docs/rag/WHAT-I-CAN-ANSWER.md +6 -6
- 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/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Rev4a API Reference
|
|
2
2
|
|
|
3
|
-
> **Last updated:** 2026-
|
|
3
|
+
> **Last updated:** 2026-09-05
|
|
4
4
|
|
|
5
5
|
All routes are under `/api/`. Authentication is required on every endpoint
|
|
6
6
|
unless otherwise noted.
|
|
@@ -68,50 +68,81 @@ 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 }`
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
### `POST /api/setup/password`
|
|
108
|
+
Set the dashboard password during first-run setup.
|
|
109
|
+
|
|
110
|
+
**Auth:** none, and only usable once — returns `409` if a password already exists
|
|
111
|
+
|
|
112
|
+
**Body:** `{ "password": "..." }` — minimum 8 characters
|
|
113
|
+
|
|
114
|
+
**Errors:** `400` invalid JSON, missing password, or shorter than 8 characters;
|
|
115
|
+
`409` already configured; `500` the `.env` file could not be written.
|
|
116
|
+
|
|
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.
|
|
109
140
|
|
|
110
141
|
---
|
|
111
142
|
|
|
112
143
|
## Gateway
|
|
113
144
|
|
|
114
|
-
The Gateway API reads the model catalogue from [`models.config.json`](
|
|
145
|
+
The Gateway API reads the model catalogue from [`models.config.json`](../../models.config.json)
|
|
115
146
|
(tracked in the repository). Full documentation at [GATEWAY.md](GATEWAY.md) and
|
|
116
147
|
[PROVIDERS.md](PROVIDERS.md).
|
|
117
148
|
|
|
@@ -123,34 +154,57 @@ Returns live gateway status from all agent containers.
|
|
|
123
154
|
**Response:**
|
|
124
155
|
```json
|
|
125
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": [] },
|
|
126
161
|
"agents": {
|
|
127
162
|
"total": 1,
|
|
128
163
|
"list": [
|
|
129
164
|
{
|
|
130
|
-
"containerName": "openclaw-atlas",
|
|
131
165
|
"agentId": "atlas",
|
|
132
|
-
"
|
|
166
|
+
"containerName": "openclaw-atlas",
|
|
167
|
+
"state": "running",
|
|
133
168
|
"defaultModel": "rev4a/deepseek/deepseek-v4-flash",
|
|
134
|
-
"
|
|
169
|
+
"fallbacks": [],
|
|
170
|
+
"aliases": {},
|
|
171
|
+
"providers": []
|
|
135
172
|
}
|
|
136
173
|
]
|
|
137
|
-
}
|
|
174
|
+
},
|
|
175
|
+
"aliases": {},
|
|
176
|
+
"coreModel": { "defaultModel": "rev4a/deepseek/deepseek-v4-flash", "fallbacks": [] },
|
|
177
|
+
"apiKeys": { "configured": [], "all": [] }
|
|
138
178
|
}
|
|
139
179
|
```
|
|
140
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
|
+
|
|
141
192
|
### `PUT /api/gateway/provider`
|
|
142
|
-
|
|
193
|
+
Enable or disable a single model in the catalogue, then sync every agent.
|
|
143
194
|
|
|
144
|
-
**Auth:**
|
|
195
|
+
**Auth:** browser cookie or bearer token
|
|
145
196
|
|
|
146
|
-
**Body:**
|
|
197
|
+
**Body:** `{ "modelId": "deepseek/deepseek-chat", "enabled": true }` — both
|
|
198
|
+
required. Missing either returns `400`; an unknown `modelId` returns `404`.
|
|
147
199
|
|
|
148
200
|
**Response:**
|
|
149
201
|
```json
|
|
150
202
|
{
|
|
151
203
|
"status": "ok",
|
|
152
|
-
"
|
|
153
|
-
"
|
|
204
|
+
"modelId": "deepseek/deepseek-chat",
|
|
205
|
+
"enabled": true,
|
|
206
|
+
"provider": "deepseek",
|
|
207
|
+
"sync": ["Active models: 2 across 1 agent(s)"]
|
|
154
208
|
}
|
|
155
209
|
```
|
|
156
210
|
|
|
@@ -264,6 +318,55 @@ Rev4a Provider Gateway — proxy chat completions to the correct upstream.
|
|
|
264
318
|
|
|
265
319
|
---
|
|
266
320
|
|
|
321
|
+
### `GET /api/models`
|
|
322
|
+
Models eligible to be used: the catalogue filtered down to those whose provider
|
|
323
|
+
has a key configured.
|
|
324
|
+
|
|
325
|
+
**Auth:** browser cookie or bearer token
|
|
326
|
+
|
|
327
|
+
### `GET /api/gateway/provider/balance`
|
|
328
|
+
Remaining credit for one provider.
|
|
329
|
+
|
|
330
|
+
**Auth:** browser cookie or bearer token
|
|
331
|
+
|
|
332
|
+
**Query params:** `provider` — one of `deepseek`, `openrouter`, `glm`.
|
|
333
|
+
|
|
334
|
+
**Errors:** `502` when the provider's API cannot be reached or rejects the call.
|
|
335
|
+
|
|
336
|
+
### `POST /api/gateway/provider/oauth`
|
|
337
|
+
Run the OAuth device flow for a provider that supports it.
|
|
338
|
+
|
|
339
|
+
**Auth:** browser cookie or bearer token
|
|
340
|
+
|
|
341
|
+
**Body:** `{ "provider": "..." }`
|
|
342
|
+
|
|
343
|
+
**Errors:** `400` for an unknown provider; the response body carries `available`
|
|
344
|
+
with the providers that do support OAuth.
|
|
345
|
+
|
|
346
|
+
### `POST /api/aliases/add`
|
|
347
|
+
Add a model alias.
|
|
348
|
+
|
|
349
|
+
**Auth:** browser cookie or bearer token
|
|
350
|
+
|
|
351
|
+
**Body:** `{ "alias": "fast", "model": "deepseek/deepseek-chat" }`
|
|
352
|
+
|
|
353
|
+
**Response:** `{ "ok": true, "output": "..." }` — `output` is the CLI's stdout.
|
|
354
|
+
|
|
355
|
+
**Errors:** `400` if `alias` or `model` is missing.
|
|
356
|
+
|
|
357
|
+
### `POST /api/aliases/remove`
|
|
358
|
+
Remove a model alias.
|
|
359
|
+
|
|
360
|
+
**Auth:** browser cookie or bearer token
|
|
361
|
+
|
|
362
|
+
**Body:** `{ "alias": "fast" }`
|
|
363
|
+
|
|
364
|
+
**Response:** `{ "ok": true, "output": "..." }`
|
|
365
|
+
|
|
366
|
+
**Errors:** `400` if `alias` is missing.
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
267
370
|
## Config
|
|
268
371
|
|
|
269
372
|
### `PUT /api/config/env`
|
|
@@ -344,6 +447,17 @@ Returns a single session with its events and child sessions.
|
|
|
344
447
|
|
|
345
448
|
---
|
|
346
449
|
|
|
450
|
+
### `GET /api/sessions/[id]`
|
|
451
|
+
One session with its events and any child sessions.
|
|
452
|
+
|
|
453
|
+
**Auth:** browser cookie or bearer token
|
|
454
|
+
|
|
455
|
+
**Response:** `{ "session": {…}, "events": [...], "children": [...] }`
|
|
456
|
+
|
|
457
|
+
**Errors:** `404` if the session does not exist.
|
|
458
|
+
|
|
459
|
+
---
|
|
460
|
+
|
|
347
461
|
## Stats & Costs
|
|
348
462
|
|
|
349
463
|
### `GET /api/stats`
|
|
@@ -612,7 +726,12 @@ Create a new agent container from a template.
|
|
|
612
726
|
1. OpenClaw gateway starts with `--allow-unconfigured`, generating its own default config
|
|
613
727
|
2. The route waits for the gateway to be fully up (health check poll, up to 60 s)
|
|
614
728
|
3. Once ready, writes `gateway.controlUi.allowedOrigins` + `agents.defaults.model.primary` + fallbacks
|
|
615
|
-
4. Writes `models.providers.rev4a` + extraDirs + update config → single `openclaw.json` write
|
|
729
|
+
4. Writes `models.providers.rev4a` + extraDirs + update config → single `openclaw.json` write
|
|
730
|
+
|
|
731
|
+
No gateway restart is issued. OpenClaw's `gateway.reload` defaults to `hybrid`:
|
|
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.
|
|
616
735
|
|
|
617
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.
|
|
618
737
|
|
|
@@ -762,6 +881,21 @@ aligning the volume's state/config to the new binary.
|
|
|
762
881
|
|
|
763
882
|
---
|
|
764
883
|
|
|
884
|
+
### `POST /api/agents/[id]/lifecycle`
|
|
885
|
+
Start, stop or restart an agent container.
|
|
886
|
+
|
|
887
|
+
**Auth:** browser cookie or bearer token
|
|
888
|
+
|
|
889
|
+
**Body:** `{ "action": "start" | "stop" | "restart" }`
|
|
890
|
+
|
|
891
|
+
Redundant actions are no-ops: starting a running container, or stopping a stopped
|
|
892
|
+
one, succeeds without touching it.
|
|
893
|
+
|
|
894
|
+
**Errors:** `400` for a malformed body, an unknown action, or an invalid agent id;
|
|
895
|
+
`404` when no container carries that `AGENT_ID`.
|
|
896
|
+
|
|
897
|
+
---
|
|
898
|
+
|
|
765
899
|
## Channels
|
|
766
900
|
|
|
767
901
|
Channel APIs read/write agent channel configuration (Telegram) directly inside agent containers via `docker exec`. All endpoints require the agent container to be running.
|
|
@@ -987,7 +1121,10 @@ Update the shared agents gateway token and push it to all running agent containe
|
|
|
987
1121
|
### `GET /api/agents/image-status`
|
|
988
1122
|
Check the status of the `openclaw-agent-base` Docker image.
|
|
989
1123
|
|
|
990
|
-
**Auth:**
|
|
1124
|
+
**Auth:** browser cookie or bearer token
|
|
1125
|
+
|
|
1126
|
+
The ghcr.io digest lookup is cached for 10 minutes and de-duplicated while in
|
|
1127
|
+
flight, so polling this endpoint does not re-query the registry every time.
|
|
991
1128
|
|
|
992
1129
|
**Response:**
|
|
993
1130
|
```json
|
|
@@ -1011,7 +1148,7 @@ Starts pulling or building the `openclaw-agent-base:latest` image. Returns 202
|
|
|
1011
1148
|
Accepted immediately — the operation runs in the background and writes logs.
|
|
1012
1149
|
The frontend polls `GET /api/agents/image-status` for progress updates.
|
|
1013
1150
|
|
|
1014
|
-
**Auth:**
|
|
1151
|
+
**Auth:** browser cookie or bearer token
|
|
1015
1152
|
|
|
1016
1153
|
**Response (202):**
|
|
1017
1154
|
```json
|
|
@@ -1023,17 +1160,7 @@ The frontend polls `GET /api/agents/image-status` for progress updates.
|
|
|
1023
1160
|
{ "error": "A download is already in progress" }
|
|
1024
1161
|
```
|
|
1025
1162
|
|
|
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`:
|
|
1163
|
+
Image state is managed in `lib/buildAgentImage.ts` (library functions, not HTTP routes):
|
|
1037
1164
|
- `downloadAgentImage(opts)` — attempts `docker pull` from registry first, then falls back to `docker build`
|
|
1038
1165
|
all stdout/stderr to a log file, supports optional `onEvent` callback and `AbortSignal`
|
|
1039
1166
|
- `abortDownload()` — sends SIGKILL to the child process, releases the download lock
|
|
@@ -1361,93 +1488,23 @@ Write content to a workspace file.
|
|
|
1361
1488
|
|
|
1362
1489
|
---
|
|
1363
1490
|
|
|
1364
|
-
##
|
|
1491
|
+
## Provider keys and credentials
|
|
1365
1492
|
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
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
|
-
```
|
|
1493
|
+
Provider API keys are read with `GET /api/gateway/provider` and set with
|
|
1494
|
+
`POST /api/gateway/provider` — documented in `docs/dev/PROVIDERS.md`, which is the
|
|
1495
|
+
only file carrying that endpoint's request bodies. `PUT` toggles a model, not a
|
|
1496
|
+
key. The keys are stored in `data/provider-keys.json`.
|
|
1497
|
+
Third-party service credentials are managed under `/api/credentials`
|
|
1498
|
+
(documented below) and stored in `credentials.db`.
|
|
1384
1499
|
|
|
1385
|
-
|
|
1386
|
-
|
|
1500
|
+
There is no `/api/vault` route, and no per-credential agent scoping: any stored
|
|
1501
|
+
credential can be synced to any container.
|
|
1387
1502
|
|
|
1388
|
-
|
|
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
|
|
1437
|
-
|
|
1438
|
-
**Body:** `{ "id": "github" }`
|
|
1439
|
-
|
|
1440
|
-
**Response:** `{ "ok": true }`
|
|
1441
|
-
|
|
1442
|
-
### `PUT /api/vault/permissions`
|
|
1443
|
-
Update agent permissions for a credential.
|
|
1444
|
-
|
|
1445
|
-
**Body:** `{ "type": "provider" | "service", "id": "deepseek", "scopes": ["atlas", "argus"] }`
|
|
1446
|
-
|
|
1447
|
-
**Response:** `{ "ok": true }`
|
|
1503
|
+
Full key management documentation at [PROVIDERS.md](PROVIDERS.md).
|
|
1448
1504
|
|
|
1449
1505
|
---
|
|
1450
1506
|
|
|
1507
|
+
|
|
1451
1508
|
## Containers
|
|
1452
1509
|
|
|
1453
1510
|
### `GET /api/containers`
|
|
@@ -1472,24 +1529,13 @@ List all running Docker containers with resource usage.
|
|
|
1472
1529
|
}
|
|
1473
1530
|
```
|
|
1474
1531
|
|
|
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" }`
|
|
1532
|
+
The container list is the only container route. There is no logs endpoint and no
|
|
1533
|
+
action endpoint: logs are read through the terminal WebSocket, and lifecycle
|
|
1534
|
+
operations on agent containers go through `/api/agents/[id]/lifecycle`.
|
|
1490
1535
|
|
|
1491
1536
|
---
|
|
1492
1537
|
|
|
1538
|
+
|
|
1493
1539
|
## Tools
|
|
1494
1540
|
|
|
1495
1541
|
### `GET /api/tools-config`
|
|
@@ -1522,30 +1568,16 @@ Return the installed OpenClaw CLI version.
|
|
|
1522
1568
|
|
|
1523
1569
|
**Fallback:** `{ "version": "unknown" }` if the command fails.
|
|
1524
1570
|
|
|
1525
|
-
###
|
|
1526
|
-
Documentation placeholder for WebSocket access.
|
|
1527
|
-
|
|
1528
|
-
**Auth:** browser cookie
|
|
1571
|
+
### Terminal WebSocket
|
|
1529
1572
|
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
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
|
|
1541
|
-
|
|
1542
|
-
**Protocol notes:**
|
|
1543
|
-
- Hosted on the main HTTP port, not under `/api/`
|
|
1544
|
-
- Accepts JSON messages such as `chat.send` and `chat.history`
|
|
1545
|
-
- Intended for the dashboard client rather than generic REST consumers
|
|
1573
|
+
Not an `/api/` route and not on the dashboard port. `terminal-ws-server.js`
|
|
1574
|
+
listens on **`ws://127.0.0.1:3741`** (`TERMINAL_WS_PORT`), bound to the loopback
|
|
1575
|
+
interface only, and speaks the PTY protocol described in
|
|
1576
|
+
[CONTAINER-TERMINAL.md](../CONTAINER-TERMINAL.md).
|
|
1546
1577
|
|
|
1547
1578
|
---
|
|
1548
1579
|
|
|
1580
|
+
|
|
1549
1581
|
## Plugins
|
|
1550
1582
|
|
|
1551
1583
|
### `GET /api/plugins`
|
|
@@ -1745,11 +1777,19 @@ data: [DONE]
|
|
|
1745
1777
|
### `GET /api/credentials`
|
|
1746
1778
|
List all stored credential profiles and the provider registry.
|
|
1747
1779
|
|
|
1780
|
+
**Auth:** browser cookie or bearer token
|
|
1781
|
+
|
|
1748
1782
|
**Response:** `{ profiles: CredentialProfile[], providers: CredentialProviderDef[] }`
|
|
1749
1783
|
|
|
1784
|
+
Secrets are never returned here — each profile carries only `secretMasked`.
|
|
1785
|
+
|
|
1786
|
+
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.
|
|
1787
|
+
|
|
1750
1788
|
### `POST /api/credentials`
|
|
1751
1789
|
Create a new credential profile.
|
|
1752
1790
|
|
|
1791
|
+
**Auth:** browser cookie or bearer token
|
|
1792
|
+
|
|
1753
1793
|
**Body:** `{ providerId: string, label: string, secret: Record<string, string> }`
|
|
1754
1794
|
|
|
1755
1795
|
**Response:** `{ id: string }` (201)
|
|
@@ -1757,30 +1797,77 @@ Create a new credential profile.
|
|
|
1757
1797
|
### `GET /api/credentials/[id]`
|
|
1758
1798
|
Get a single credential profile (no secret returned).
|
|
1759
1799
|
|
|
1800
|
+
**Auth:** browser cookie or bearer token
|
|
1801
|
+
|
|
1760
1802
|
### `PATCH /api/credentials/[id]`
|
|
1761
1803
|
Update label and/or secret of a credential.
|
|
1762
1804
|
|
|
1805
|
+
**Auth:** browser cookie or bearer token
|
|
1806
|
+
|
|
1763
1807
|
**Body:** `{ label: string, secret: Record<string, string> }`
|
|
1764
1808
|
|
|
1809
|
+
Secret fields are **merged** onto the stored payload, not substituted for it:
|
|
1810
|
+
only the fields carrying a non-empty value are written, and every blank field
|
|
1811
|
+
keeps whatever is already stored. This matters for the one two-field provider,
|
|
1812
|
+
Trello, where rotating the `token` alone must leave `apiKey` intact — both are
|
|
1813
|
+
required by the REST delivery. An empty `secret` (every field blank) therefore
|
|
1814
|
+
updates the label only.
|
|
1815
|
+
|
|
1816
|
+
When the merge changes the stored payload, `last_synced_at` is reset to `NULL`:
|
|
1817
|
+
the agents still hold the previous value, so the profile reads "synced never"
|
|
1818
|
+
until it is delivered again, and `GET /api/credentials/detect` reports those
|
|
1819
|
+
agents as holding an unrecognised credential for that provider.
|
|
1820
|
+
|
|
1765
1821
|
### `DELETE /api/credentials/[id]`
|
|
1766
1822
|
Delete a credential and its secret permanently.
|
|
1767
1823
|
|
|
1824
|
+
**Auth:** browser cookie or bearer token
|
|
1825
|
+
|
|
1768
1826
|
### `POST /api/credentials/[id]/sync`
|
|
1769
|
-
Sync the credential to
|
|
1827
|
+
Sync the credential to agent containers.
|
|
1828
|
+
|
|
1829
|
+
**Auth:** browser cookie or bearer token
|
|
1770
1830
|
|
|
1771
|
-
**Body (optional):** `{ containers?: string[] }` — if omitted
|
|
1831
|
+
**Body (optional):** `{ containers?: string[] }` — if omitted, targets every agent container carrying an `AGENT_ID` label.
|
|
1772
1832
|
|
|
1773
1833
|
**Response:** `{ results: { container: string, status: 'ok'|'error', error?: string }[] }`
|
|
1774
1834
|
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1835
|
+
Each **targeted** container is first cleaned of this provider only — other
|
|
1836
|
+
providers on the same container are left alone — and then receives the new
|
|
1837
|
+
credential and its TOOLS.md row.
|
|
1838
|
+
|
|
1839
|
+
**Containers outside `containers` are not touched.** Syncing is therefore not a
|
|
1840
|
+
way to revoke: dropping an agent from the list leaves whatever it already holds
|
|
1841
|
+
in place. Use `DELETE` below to remove a credential from a container.
|
|
1842
|
+
|
|
1843
|
+
### `DELETE /api/credentials/[id]/sync`
|
|
1844
|
+
Remove this credential from a single container ("de-sync").
|
|
1845
|
+
|
|
1846
|
+
**Auth:** browser cookie or bearer token
|
|
1847
|
+
|
|
1848
|
+
**Body:** `{ container: string }` — required.
|
|
1849
|
+
|
|
1850
|
+
**Response:** `{ ok: true }`
|
|
1851
|
+
|
|
1852
|
+
Deletes the provider's CLI auth inside that container and drops its TOOLS.md
|
|
1853
|
+
row. `last_synced_at` is deliberately left untouched — it records the last
|
|
1854
|
+
delivery, and a removal is not one — and the audit entry is recorded as
|
|
1855
|
+
`desync`. The profile stays `active`, because de-sync targets one container and
|
|
1856
|
+
the credential may still be delivered to others.
|
|
1778
1857
|
|
|
1779
1858
|
### `GET /api/credentials/detect`
|
|
1780
1859
|
Live-detect which credentials are actually present inside agent containers.
|
|
1781
1860
|
|
|
1861
|
+
**Auth:** browser cookie or bearer token
|
|
1862
|
+
|
|
1782
1863
|
**Query params (optional):** `?agent=prometheus` — filter to a single container.
|
|
1783
1864
|
|
|
1865
|
+
Costly: one `docker exec` per container, measured at ~6.5s each. Almost all of
|
|
1866
|
+
that is the three CLI calls reaching the network from inside the container, so
|
|
1867
|
+
the figure is largely platform-independent — the `docker exec` itself is ~90ms,
|
|
1868
|
+
and the local file reads another ~100ms. Callers should treat it as a background
|
|
1869
|
+
refresh, not something to block a page render on.
|
|
1870
|
+
|
|
1784
1871
|
**Response:**
|
|
1785
1872
|
```json
|
|
1786
1873
|
{
|
|
@@ -1802,34 +1889,119 @@ Live-detect which credentials are actually present inside agent containers.
|
|
|
1802
1889
|
"supabaseCliInstalled": true,
|
|
1803
1890
|
"trelloLoggedIn": false,
|
|
1804
1891
|
"trelloCliInstalled": true,
|
|
1892
|
+
"notionLoggedIn": false,
|
|
1805
1893
|
"toolsMdHasMarkers": true,
|
|
1806
1894
|
"matchedProfiles": [
|
|
1807
1895
|
{ "profileId": "cred_xxx", "providerId": "github-pat", "label": "GitHub" }
|
|
1808
|
-
]
|
|
1896
|
+
],
|
|
1897
|
+
"installedProviders": ["github-pat"]
|
|
1809
1898
|
}
|
|
1810
1899
|
]
|
|
1811
1900
|
}
|
|
1812
1901
|
```
|
|
1813
1902
|
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
-
|
|
1903
|
+
An agent whose probe failed is returned in its place as
|
|
1904
|
+
`{ "container", "agentId", "error" }` — none of the fields above are present, so
|
|
1905
|
+
consumers must check `error` first.
|
|
1906
|
+
|
|
1907
|
+
Each container is read with a **single** `docker exec` running one script that
|
|
1908
|
+
emits NUL-delimited key/value pairs; containers are probed concurrently. The
|
|
1909
|
+
script reads the credential files directly and the parsing happens in
|
|
1910
|
+
JavaScript. Three CLI calls run last and only feed display fields — `githubUser`,
|
|
1911
|
+
`vercelUser`, `vercelTeam`, and the `githubLoggedIn` / `vercelLoggedIn` /
|
|
1912
|
+
`supabaseLoggedIn` flags, along with `supabaseLinked` and `supabaseProjectRef`,
|
|
1913
|
+
which are read only when `supabaseLoggedIn` is true. `supabaseLoggedIn` follows
|
|
1914
|
+
the **exit status** of `supabase projects list`, not the text it prints, so a
|
|
1915
|
+
failure whose wording is unfamiliar still reads as not logged in.
|
|
1916
|
+
`trelloLoggedIn` and `notionLoggedIn` come from the config files instead, and so
|
|
1917
|
+
survive a probe cut short. The three calls reach the network from inside the
|
|
1918
|
+
container, so a completion sentinel is emitted before them: a probe cut short
|
|
1919
|
+
during them still yields valid token matching, with those display fields empty.
|
|
1920
|
+
|
|
1921
|
+
Credential files read: `~/.config/gh/hosts.yml`,
|
|
1922
|
+
`~/.local/share/com.vercel.cli/auth.json` and `config.json`,
|
|
1923
|
+
`~/.supabase/access-token` and `/data/supabase/config.toml`,
|
|
1924
|
+
`~/.config/trello/config.json`, `~/.config/notion/config.json`, plus TOOLS.md
|
|
1925
|
+
markers at `/root/.openclaw/workspace/TOOLS.md`.
|
|
1820
1926
|
|
|
1821
1927
|
`matchedProfiles` is determined by SHA256-hashing the token installed in the container
|
|
1822
1928
|
and comparing it against the hashed token of each stored profile in the vault.
|
|
1823
1929
|
Only exact token matches produce a match — no heuristics, no broad profile-level guesses.
|
|
1824
1930
|
|
|
1931
|
+
`installedProviders` lists every provider holding a token, matched or not. A
|
|
1932
|
+
provider present there but absent from `matchedProfiles` holds a credential no
|
|
1933
|
+
stored profile accounts for — an edited-but-not-resynced credential, one
|
|
1934
|
+
installed by hand, or the remains of a deleted profile.
|
|
1935
|
+
|
|
1825
1936
|
### `POST /api/credentials/[id]/reveal`
|
|
1826
|
-
Return the raw secret payload. Every call is
|
|
1937
|
+
Return the raw secret payload. Every call is written to `credential_audit_log`, which no route or view reads.
|
|
1827
1938
|
Use sparingly.
|
|
1828
1939
|
|
|
1940
|
+
**Auth:** browser cookie or bearer token
|
|
1941
|
+
|
|
1942
|
+
Secrets are stored **unencrypted** in `credentials.db` — `credential_secrets.payload`
|
|
1943
|
+
holds the raw JSON. Anything that can read that file, or reach this endpoint with a
|
|
1944
|
+
valid session, has the secrets in the clear.
|
|
1945
|
+
|
|
1829
1946
|
**Response:** `{ secret: Record<string, string> }`
|
|
1830
1947
|
|
|
1831
1948
|
---
|
|
1832
1949
|
|
|
1950
|
+
## Diagnostics
|
|
1951
|
+
|
|
1952
|
+
### `GET /api/debug`
|
|
1953
|
+
Whether the three auth-related environment variables are set. Booleans only —
|
|
1954
|
+
no value, masked or otherwise, is returned.
|
|
1955
|
+
|
|
1956
|
+
**Auth:** browser cookie or bearer token
|
|
1957
|
+
|
|
1958
|
+
**Response:** `{ "hasPassword": true, "hasJwtSecret": true, "hasToken": false }`
|
|
1959
|
+
|
|
1960
|
+
### `GET /api/envcheck`
|
|
1961
|
+
The process environment, with `REV4A_PASSWORD`, `REV4A_TOKEN`,
|
|
1962
|
+
`REV4A_JWT_SECRET` and `PROVIDER_DEEPSEEK_API_KEY` redacted. Every other
|
|
1963
|
+
variable is returned **in the clear**, so treat the response as sensitive.
|
|
1964
|
+
|
|
1965
|
+
**Auth:** browser cookie or bearer token
|
|
1966
|
+
|
|
1967
|
+
### `GET /api/provider-usage`
|
|
1968
|
+
Usage and quota state per provider, including live OpenRouter figures.
|
|
1969
|
+
|
|
1970
|
+
**Auth:** browser cookie or bearer token
|
|
1971
|
+
|
|
1972
|
+
**Response:** `{ "providers": [...], "openrouterLive": {…}, "quotas": {…} }`
|
|
1973
|
+
|
|
1974
|
+
### `GET /api/update-check`
|
|
1975
|
+
Installed version against the latest published one.
|
|
1976
|
+
|
|
1977
|
+
**Auth:** browser cookie or bearer token
|
|
1978
|
+
|
|
1979
|
+
**Query params:** `check=1` queries the registry; without it the answer comes
|
|
1980
|
+
from local state only.
|
|
1981
|
+
|
|
1982
|
+
**Response:** `{ "installed": "1.1.15", "latest": "1.1.16", "updateAvailable": true, "package": "@flame0510/project-aether" }`
|
|
1983
|
+
|
|
1984
|
+
### `POST /api/update-check`
|
|
1985
|
+
Run `rev4a update` as a detached background process. The dashboard restarts
|
|
1986
|
+
itself as part of the update, so the connection drops.
|
|
1987
|
+
|
|
1988
|
+
**Auth:** browser cookie or bearer token
|
|
1989
|
+
|
|
1990
|
+
### `GET /api/alerts/smoke`
|
|
1991
|
+
Send a test alert, to verify the Telegram alerting path end to end.
|
|
1992
|
+
|
|
1993
|
+
**Auth:** browser cookie or bearer token
|
|
1994
|
+
|
|
1995
|
+
**Query params:** `stale=0` skips the staleness check.
|
|
1996
|
+
|
|
1997
|
+
**Response:** `{ "ok": true, "stale": true, "result": {…}, "dryRun": false }` —
|
|
1998
|
+
`dryRun` is `true` when `REV4A_TELEGRAM_BOT_TOKEN` or `REV4A_TELEGRAM_CHAT_ID`
|
|
1999
|
+
is unset, meaning nothing was actually sent.
|
|
2000
|
+
|
|
2001
|
+
**Errors:** `403` when smoke alerts are disabled.
|
|
2002
|
+
|
|
2003
|
+
---
|
|
2004
|
+
|
|
1833
2005
|
## Error Format
|
|
1834
2006
|
|
|
1835
2007
|
All errors return JSON:
|