@flame0510/project-aether 1.3.0 → 1.4.1
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 +1 -0
- 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 +4 -1
- package/app/agents/PageClient.tsx +629 -167
- package/app/agents/UpdateSection.tsx +300 -0
- package/app/agents/create/PageClient.tsx +11 -49
- package/app/agents/create/page.tsx +14 -28
- 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]/open-control-ui/route.ts +58 -0
- package/app/api/agents/[id]/recreate/route.ts +33 -163
- 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 -150
- 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 +38 -92
- 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/route.ts +25 -49
- package/app/api/agents/token/route.ts +33 -10
- package/app/api/assistant/route.ts +2 -2
- package/app/api/gateway/agent/route.ts +14 -0
- package/app/api/gateway/provider/balance/route.ts +5 -2
- package/app/api/gateway/sync.ts +97 -14
- package/app/api/setup/agent-image/route.ts +14 -42
- package/app/api/version/route.ts +2 -1
- package/app/components/DashboardToolbar.tsx +1 -1
- package/app/gateway/PageClient.tsx +27 -32
- package/bin/rev4a.js +43 -41
- package/daemon.js +6 -6
- package/docs/ARCHITECTURE.md +107 -9
- package/docs/FRONTEND-ARCHITECTURE.md +8 -1
- package/docs/REV4A.md +54 -17
- package/docs/dev/API-REFERENCE.md +573 -105
- package/docs/dev/DATABASE.md +96 -0
- package/docs/dev/GATEWAY.md +21 -6
- package/docs/rag/DATA-FRESHNESS.md +6 -4
- package/docs/rag/GLOSSARY.md +12 -3
- package/docs/rag/REV4A-OVERVIEW.md +18 -5
- package/docs/rag/WHAT-I-CAN-ANSWER.md +6 -2
- package/instrumentation.ts +43 -0
- 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 +149 -0
- package/lib/agent-images.ts +375 -0
- package/lib/agent-ports-server.ts +78 -0
- package/lib/agent-ports.ts +91 -0
- package/lib/agent-recreate-state.ts +108 -0
- package/lib/agent-recreate.ts +359 -0
- package/lib/agent-restore-state.ts +107 -0
- package/lib/agent-restore.ts +141 -0
- package/lib/agent-setup.ts +66 -17
- package/lib/agent-update-state.ts +122 -0
- package/lib/agent-update.ts +456 -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 +149 -102
- package/lib/cold-backup.ts +354 -0
- package/lib/credentials/delivery.ts +3 -3
- package/lib/db-bootstrap.mjs +76 -0
- package/lib/docker-utils.ts +3 -3
- package/lib/provider-balance.ts +33 -12
- package/package.json +1 -1
|
@@ -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
|
|
|
@@ -350,7 +353,7 @@ Remaining credit for one provider.
|
|
|
350
353
|
|
|
351
354
|
**Query params:** `provider` — one of `deepseek`, `openrouter`, `glm`.
|
|
352
355
|
|
|
353
|
-
**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.
|
|
354
357
|
|
|
355
358
|
### `POST /api/gateway/provider/oauth`
|
|
356
359
|
Run the OAuth device flow for a provider that supports it.
|
|
@@ -673,8 +676,8 @@ List running agent containers (Docker containers with `AGENT_ID` label) with ful
|
|
|
673
676
|
"id": "abc123def456",
|
|
674
677
|
"agentId": "prometheus",
|
|
675
678
|
"name": "prometheus",
|
|
676
|
-
"image": "openclaw-agent-base:
|
|
677
|
-
"imageTag": "
|
|
679
|
+
"image": "openclaw-agent-base:2026.9.3",
|
|
680
|
+
"imageTag": "2026.9.3",
|
|
678
681
|
"template": "prometheus",
|
|
679
682
|
"status": "running",
|
|
680
683
|
"state": "running",
|
|
@@ -682,15 +685,20 @@ List running agent containers (Docker containers with `AGENT_ID` label) with ful
|
|
|
682
685
|
"ip": "172.19.0.5",
|
|
683
686
|
"created": "2026-07-01T16:58:42.876829416Z",
|
|
684
687
|
"env": ["AGENT_ID=prometheus", "AGENT_TEMPLATE=prometheus"],
|
|
685
|
-
"
|
|
686
|
-
"
|
|
688
|
+
"controlPort": "3033",
|
|
689
|
+
"openclawVersion": "2026.9.3",
|
|
690
|
+
"updateAvailable": false
|
|
687
691
|
}
|
|
688
692
|
]
|
|
689
693
|
```
|
|
690
694
|
|
|
691
695
|
**Notes:**
|
|
692
|
-
-
|
|
693
|
-
|
|
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`
|
|
694
702
|
- Containers are discovered via Docker API with label filter `AGENT_ID`
|
|
695
703
|
- Template is inferred from the container image name + agent ID directory match in `agent-templates/`
|
|
696
704
|
|
|
@@ -785,25 +793,30 @@ Create a new agent container from a template.
|
|
|
785
793
|
|
|
786
794
|
| Field | Required | Description |
|
|
787
795
|
|---|---|---|
|
|
788
|
-
| `name` | yes |
|
|
796
|
+
| `name` | yes | Display name (`AGENT_NAME`); the container name and `AGENT_ID` are generated (`agent_<hex>`) |
|
|
789
797
|
| `template` | yes | Template name (directory in `agent-templates/`) |
|
|
790
|
-
| `portRange` | no | Optional port range (e.g. `3700-3709`) or single port (e.g. `3700`). Default: auto-assigned 10-port block |
|
|
798
|
+
| `portRange` | no | Optional port range (e.g. `3700-3709`) or single port (e.g. `3700`). Default: auto-assigned 10-port block, chosen by `findAvailablePortBlock()` |
|
|
791
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 |
|
|
792
800
|
| `fallbacks` | no | Array of fallback model IDs |
|
|
793
801
|
|
|
794
802
|
**Every agent is created with a host port mapping:**
|
|
795
803
|
- Port block: `-p <start>-<end>:3000-<3000+offset>` — maps a range of host ports to the same range starting at container port 3000
|
|
796
804
|
- Single port: `-p <port>:3000` — maps a single host port to container port 3000
|
|
797
|
-
- Control UI
|
|
805
|
+
- Control UI: the wizard's Open button goes through `GET /api/agents/[id]/open-control-ui`, like the Agents page
|
|
798
806
|
- Port 3740 is reserved for Rev4a
|
|
807
|
+
- A port held by any container, running or stopped, is refused (`400`): the shared lookup
|
|
808
|
+
reads every container's bindings (`lib/agent-ports-server.ts`), because a stopped
|
|
809
|
+
container keeps its allocation while `docker ps -a` and the API's list report none
|
|
810
|
+
- Image: `openclaw-agent-base:<version>`, the newest supported OpenClaw version downloaded here; `409` when none is
|
|
799
811
|
|
|
800
812
|
**Response:**
|
|
801
813
|
```json
|
|
802
814
|
{
|
|
803
815
|
"success": true,
|
|
804
816
|
"containerId": "00f5de1eb08d...",
|
|
805
|
-
"
|
|
806
|
-
"
|
|
817
|
+
"containerName": "agent_2a3c3a07",
|
|
818
|
+
"displayName": "my-agent",
|
|
819
|
+
"image": "openclaw-agent-base:2026.9.3",
|
|
807
820
|
"network": "rev4a-network",
|
|
808
821
|
"controlToken": "asdfghjkl",
|
|
809
822
|
"port": 3700,
|
|
@@ -856,6 +869,72 @@ docker command.
|
|
|
856
869
|
|
|
857
870
|
---
|
|
858
871
|
|
|
872
|
+
### `PATCH /api/agents/[id]`
|
|
873
|
+
Edit an agent's display name, its port range, or both. The job runs in the background
|
|
874
|
+
(`lib/agent-edit.ts`); this answers `202` once it has started. The container is rebuilt
|
|
875
|
+
on the image it already runs — **without a backup**: the persistent volume is never
|
|
876
|
+
touched (`recreateAgentContainer()` in `lib/agent-recreate.ts`), then the gateway is
|
|
877
|
+
waited for (up to 5 minutes) and the runtime config re-applied.
|
|
878
|
+
|
|
879
|
+
The job is recorded in `agent_edits`, so a page reload or a Rev4a restart never loses it:
|
|
880
|
+
`GET` below reports the running or last edit, and while it runs anything else that would
|
|
881
|
+
touch the agent answers `409`.
|
|
882
|
+
|
|
883
|
+
**Auth:** browser cookie
|
|
884
|
+
|
|
885
|
+
**Body:**
|
|
886
|
+
```json
|
|
887
|
+
{ "displayName": "Argus", "portRange": "3700-3709" }
|
|
888
|
+
```
|
|
889
|
+
|
|
890
|
+
`portRange` is `"<start>"` or `"<start>-<end>"`, mapped onto the gateway port 3000
|
|
891
|
+
(`3700-3709` → `3700-3709:3000-3009`). It is validated with the same rules and messages as
|
|
892
|
+
agent creation (`lib/agent-ports.ts`) against the shared lookup
|
|
893
|
+
(`getUsedHostPorts()`, `lib/agent-ports-server.ts`): a range that holds Rev4a's port
|
|
894
|
+
`3740`, an invalid format, or a port already held by **another** container answers `400`
|
|
895
|
+
(`Ports already in use: 3711, 3712`) before anything is rebuilt. The agent's own published
|
|
896
|
+
ports are excluded, so keeping or shifting its block is not a conflict with itself.
|
|
897
|
+
A port held by a **stopped** container counts as in use: the lookup reads every
|
|
898
|
+
container's `HostConfig.PortBindings`, because both `docker ps -a --format '{{.Ports}}'`
|
|
899
|
+
and the API's container list report nothing for a container that is not running.
|
|
900
|
+
At least one field is required; an empty `displayName` is rejected.
|
|
901
|
+
|
|
902
|
+
The rebuild itself (`recreateAgentContainer()`) re-checks the ports it is about to
|
|
903
|
+
publish against every other container and refuses **before removing anything** when one
|
|
904
|
+
is taken, naming the holder: `Port 3700 is already published by agent_24a68ac9 (Drill
|
|
905
|
+
Wake). Change this agent's port range in the Edit panel, then retry: nothing was
|
|
906
|
+
changed.` The row lands in `failed` with that message and the agent keeps running. The
|
|
907
|
+
same guard protects Update and Rollback, whose rows record the same text.
|
|
908
|
+
|
|
909
|
+
**Response (202):**
|
|
910
|
+
```json
|
|
911
|
+
{ "started": true, "id": 4 }
|
|
912
|
+
```
|
|
913
|
+
|
|
914
|
+
**Errors:** `400` invalid body, invalid port range or empty display name; `409` an edit,
|
|
915
|
+
recreate, restore, update or backup of this agent is already running.
|
|
916
|
+
|
|
917
|
+
---
|
|
918
|
+
|
|
919
|
+
### `GET /api/agents/[id]`
|
|
920
|
+
The running or last edit of an agent (`null` when it was never edited) — what the edit
|
|
921
|
+
banner polls to resume after a reload. `status`: `rebuilding` → `done` | `failed`;
|
|
922
|
+
`interrupted` after a Rev4a restart cut it off.
|
|
923
|
+
|
|
924
|
+
**Auth:** browser cookie or bearer token
|
|
925
|
+
|
|
926
|
+
```json
|
|
927
|
+
{
|
|
928
|
+
"edit": {
|
|
929
|
+
"id": 4, "agentId": "agent_2a3c3a07", "status": "rebuilding",
|
|
930
|
+
"displayName": "test 2 async", "portRange": null,
|
|
931
|
+
"error": null, "startedAtMs": 1790007512520, "finishedAtMs": null
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
```
|
|
935
|
+
|
|
936
|
+
---
|
|
937
|
+
|
|
859
938
|
### `POST /api/agents/[id]/restart`
|
|
860
939
|
Restart an agent container (shorter than recreate — keeps everything intact).
|
|
861
940
|
|
|
@@ -870,105 +949,265 @@ Restart an agent container (shorter than recreate — keeps everything intact).
|
|
|
870
949
|
|
|
871
950
|
---
|
|
872
951
|
|
|
873
|
-
### `
|
|
874
|
-
|
|
952
|
+
### `GET /api/agents/[id]/backup`
|
|
953
|
+
List the agent's backup archives in Docker volume `rev4a-backups`.
|
|
875
954
|
|
|
876
|
-
**Auth:** browser cookie
|
|
955
|
+
**Auth:** browser cookie or bearer token
|
|
877
956
|
|
|
878
957
|
**Response:**
|
|
879
958
|
```json
|
|
880
|
-
{ "
|
|
959
|
+
{ "backups": [ { "name": "agent-prometheus-2026-07-12_043512345.tar.gz", "size": "227.4 MB", "sizeBytes": 238442455, "date": "12/07/2026 04:35", "epoch": 1752292512 } ] }
|
|
881
960
|
```
|
|
882
961
|
|
|
883
|
-
|
|
884
|
-
package cache (`~/.npm/_cacache/`)
|
|
962
|
+
Archives are created by the cold backup (`POST /api/agents/[id]/cold-backup`) and
|
|
963
|
+
excluded the npm package cache (`~/.npm/_cacache/`). Sorted most-recent-first.
|
|
885
964
|
|
|
886
|
-
**Error (404):** `{ "error": "No persistent volume found for agent '...'" }`
|
|
887
965
|
|
|
888
966
|
---
|
|
889
967
|
|
|
890
|
-
### `
|
|
891
|
-
|
|
968
|
+
### `DELETE /api/agents/[id]/backup?file=...`
|
|
969
|
+
Remove one backup archive from `rev4a-backups`.
|
|
892
970
|
|
|
893
971
|
**Auth:** browser cookie
|
|
894
972
|
|
|
895
|
-
**Response:**
|
|
973
|
+
**Response:** `{ "success": true }`
|
|
974
|
+
|
|
975
|
+
**Error (400):** invalid or path-traversing file name.
|
|
976
|
+
|
|
977
|
+
### `POST /api/agents/[id]/cold-backup`
|
|
978
|
+
Cold backup of the agent's volume (`lib/cold-backup.ts`). Returns `202` once the job
|
|
979
|
+
has started; the work continues in a helper container, `rev4a-backup-<id>`.
|
|
980
|
+
|
|
981
|
+
1. The agent is stopped (`docker stop -t 30`, kill as a fallback) if it runs.
|
|
982
|
+
2. The helper, started from the agent's own image, measures the volume (`du`),
|
|
983
|
+
refuses when `rev4a-backups` has less free space than about 60% of it, and writes
|
|
984
|
+
`agent-<id>-cold-<ts>.tar.gz.partial` (npm cache excluded) with GNU tar checkpoints
|
|
985
|
+
for progress.
|
|
986
|
+
3. `tar -tzf` reads the whole archive back and must find `.openclaw/openclaw.json`;
|
|
987
|
+
only then is it renamed to `.tar.gz`. A failed or cancelled job leaves no archive.
|
|
988
|
+
4. The agent is started again if it was running.
|
|
989
|
+
|
|
990
|
+
The job's state is the helper container and its labels, so it survives a Rev4a
|
|
991
|
+
restart: at startup, `reconcileColdBackups()` finishes helpers that exited and keeps
|
|
992
|
+
watching the rest.
|
|
993
|
+
|
|
994
|
+
While it runs, `POST restore`, `POST recreate`, `POST lifecycle`,
|
|
995
|
+
`PATCH` and `DELETE /api/agents/[id]` answer `409`, and the provider sync skips the
|
|
996
|
+
agent, reporting it in `sync.failed`.
|
|
997
|
+
|
|
998
|
+
**Auth:** browser cookie or bearer token
|
|
999
|
+
|
|
1000
|
+
**Response (202):** `{ "started": true, "file": "agent-agent_9253eee3-cold-2026-09-16_000607123.tar.gz" }`
|
|
1001
|
+
|
|
1002
|
+
**Errors:** `404` no container or no volume; `409` a backup of this agent is already
|
|
1003
|
+
running; `500` the helper could not start (the agent is started again if it was running).
|
|
1004
|
+
|
|
1005
|
+
### `GET /api/agents/[id]/cold-backup`
|
|
1006
|
+
The running or last job; `{ "backup": null }` when there is none since Rev4a started.
|
|
1007
|
+
|
|
896
1008
|
```json
|
|
897
1009
|
{
|
|
898
|
-
"
|
|
899
|
-
|
|
900
|
-
|
|
1010
|
+
"backup": {
|
|
1011
|
+
"agentId": "agent_9253eee3", "status": "running", "kind": "manual",
|
|
1012
|
+
"file": "agent-agent_9253eee3-cold-2026-09-16_000607123.tar.gz",
|
|
1013
|
+
"startedAtMs": 1789524367123, "finishedAtMs": null,
|
|
1014
|
+
"totalBytes": 2023456789, "doneBytes": 812400000, "percent": 40,
|
|
1015
|
+
"archiveBytes": null, "error": null
|
|
1016
|
+
}
|
|
901
1017
|
}
|
|
902
1018
|
```
|
|
903
1019
|
|
|
904
|
-
|
|
1020
|
+
`status` is `running`, `succeeded` or `failed`; `error` is `cancelled` for a cancelled job.
|
|
1021
|
+
|
|
1022
|
+
### `DELETE /api/agents/[id]/cold-backup`
|
|
1023
|
+
Cancel the running job: the helper is removed, the partial archive deleted and the
|
|
1024
|
+
agent started again if it was running. `409` when nothing is running.
|
|
905
1025
|
|
|
906
1026
|
---
|
|
907
1027
|
|
|
908
|
-
### `
|
|
909
|
-
|
|
1028
|
+
### `POST /api/agents/[id]/restore?file=agent-prometheus-2026-07-12_043512345.tar.gz`
|
|
1029
|
+
Restore an agent's persistent volume from a backup. The job runs in the background
|
|
1030
|
+
(`lib/agent-restore.ts`); this answers `202` once it has started. No pre-restore backup is
|
|
1031
|
+
taken: restoring replaces the volume with the archive on purpose.
|
|
1032
|
+
|
|
1033
|
+
1. The container is stopped if it runs.
|
|
1034
|
+
2. The volume content is cleared and the archive extracted — up to 30 minutes, since a
|
|
1035
|
+
large workspace takes minutes to decompress. The file name reaches the container
|
|
1036
|
+
through its environment, never as shell syntax.
|
|
1037
|
+
3. The container is started again even when the extract failed, so the agent never stays
|
|
1038
|
+
down. A volume with no container (volume-only agent) is restored anyway.
|
|
1039
|
+
|
|
1040
|
+
The job is recorded in `agent_restores`, so a page reload or a Rev4a restart never loses
|
|
1041
|
+
it: while it runs, anything else that would touch the agent answers `409`
|
|
1042
|
+
(`lib/agent-busy.ts`), and after a Rev4a restart an active row becomes `interrupted` with
|
|
1043
|
+
the container started again.
|
|
910
1044
|
|
|
911
|
-
**Auth:** browser cookie
|
|
1045
|
+
**Auth:** browser cookie or bearer token
|
|
912
1046
|
|
|
913
|
-
**Response:**
|
|
1047
|
+
**Response (202):**
|
|
914
1048
|
```json
|
|
915
|
-
{ "
|
|
1049
|
+
{ "started": true, "id": 3 }
|
|
916
1050
|
```
|
|
917
1051
|
|
|
918
|
-
**
|
|
1052
|
+
**Errors:** `400` invalid file name (not `agent-<id>-<safe charset>.tar.gz`); `404` the
|
|
1053
|
+
archive does not exist; `409` a restore, recreate, update, edit or backup of this agent
|
|
1054
|
+
is already running.
|
|
919
1055
|
|
|
920
1056
|
---
|
|
921
1057
|
|
|
922
|
-
### `
|
|
923
|
-
|
|
1058
|
+
### `GET /api/agents/[id]/restore`
|
|
1059
|
+
The running or last restore of an agent (`null` when it was never restored), with the
|
|
1060
|
+
archive being applied. `status`: `restoring` → `done` | `failed`; `interrupted` after a
|
|
1061
|
+
Rev4a restart cut it off.
|
|
924
1062
|
|
|
925
|
-
|
|
926
|
-
the container is restarted. If no container exists with the given `AGENT_ID`,
|
|
927
|
-
the volume is restored anyway (useful for volume-only agents).
|
|
1063
|
+
**Auth:** browser cookie or bearer token
|
|
928
1064
|
|
|
929
|
-
|
|
1065
|
+
```json
|
|
1066
|
+
{
|
|
1067
|
+
"restore": {
|
|
1068
|
+
"id": 3, "agentId": "agent_2a3c3a07", "status": "restoring",
|
|
1069
|
+
"file": "agent-agent_2a3c3a07-cold-2026-09-21_172032317.tar.gz",
|
|
1070
|
+
"error": null, "startedAtMs": 1790004046548, "finishedAtMs": null
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
```
|
|
930
1074
|
|
|
931
|
-
|
|
1075
|
+
---
|
|
1076
|
+
|
|
1077
|
+
### `POST /api/agents/[id]/recreate`
|
|
1078
|
+
Rebuild the agent container on its **own** OpenClaw version while preserving the
|
|
1079
|
+
persistent volume (workspace files, credentials, state DB, config). A recreate never
|
|
1080
|
+
changes version: moving to a newer OpenClaw version migrates the data one way, so it
|
|
1081
|
+
is a separate update. The job runs in the background (`lib/agent-recreate.ts`); this
|
|
1082
|
+
answers `202` once it has started.
|
|
1083
|
+
|
|
1084
|
+
The image is `openclaw-agent-base:<version>` for the version the agent runs
|
|
1085
|
+
(`resolveRecreateImage()` in `lib/agent-images.ts`). When that tag is missing but the
|
|
1086
|
+
container's image is still here, the image is tagged; when it is gone, a supported
|
|
1087
|
+
version is pulled. An agent whose version cannot be read keeps its exact image id.
|
|
1088
|
+
|
|
1089
|
+
Flow (each step recorded in `agent_recreates`):
|
|
1090
|
+
1. **Cold backup** `agent-<id>-prerecreate-<ts>.tar.gz` — the agent stops for it, so
|
|
1091
|
+
the archive cannot catch its SQLite files mid-write. **If the backup fails, the
|
|
1092
|
+
recreate aborts and the agent is started again.**
|
|
1093
|
+
2. The container is rebuilt (`lib/agent-recreate.ts`): same image tag, environment
|
|
1094
|
+
variables (`AGENT_*`, `MODEL_*`, `OPENCLAW_*`), labels, port mappings and network,
|
|
1095
|
+
reattaching the same volume. The network comes from the attached networks, or from
|
|
1096
|
+
the container's `NetworkMode` when its endpoint was lost.
|
|
1097
|
+
3. The gateway is waited for (`/startupz`, up to 5 minutes), then `applyRuntimeConfig()`
|
|
1098
|
+
(`lib/agent-setup.ts`) guarantees the hooks, shared skills, provider proxy and
|
|
1099
|
+
Control UI origin policy.
|
|
1100
|
+
4. On success, older `prerecreate` archives are pruned to the newest two per agent
|
|
1101
|
+
(manual `cold` and `preupdate` archives are never touched).
|
|
1102
|
+
|
|
1103
|
+
A failure before the rebuild starts the old container again, so a failed recreate never
|
|
1104
|
+
leaves the agent down. After a Rev4a restart, a job still active becomes `interrupted`
|
|
1105
|
+
at startup and the agent is started again — but only once its backup helper has
|
|
1106
|
+
finished, never while the archive is being written.
|
|
1107
|
+
|
|
1108
|
+
**Auth:** browser cookie or bearer token
|
|
1109
|
+
|
|
1110
|
+
**Response (202):**
|
|
932
1111
|
```json
|
|
933
|
-
{ "
|
|
1112
|
+
{ "started": true, "id": 12 }
|
|
934
1113
|
```
|
|
935
1114
|
|
|
936
|
-
**
|
|
1115
|
+
**Errors:** `404` when no image can be resolved; `409` when there is no container, an
|
|
1116
|
+
update, a recreate, a restore, an edit or a backup of this agent is running.
|
|
937
1117
|
|
|
938
1118
|
---
|
|
939
1119
|
|
|
940
|
-
### `
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
config). This is how an existing agent picks up image/OpenClaw/CLI updates: the
|
|
944
|
-
software lives in the image, the data in the volume.
|
|
945
|
-
|
|
946
|
-
Flow:
|
|
947
|
-
1. **Auto-backup first.** The volume is backed up to `agent-<id>-prerecreate-<ts>.tar.gz`
|
|
948
|
-
in `rev4a-backups`. **If the backup fails, the recreate is aborted** and the
|
|
949
|
-
running container is left untouched.
|
|
950
|
-
2. The old container is removed (`docker rm -f`) and a new one is created with the
|
|
951
|
-
same image tag, environment variables (`AGENT_*`, `MODEL_*`, `OPENCLAW_*`),
|
|
952
|
-
labels, port mappings, and network — reattaching the same volume.
|
|
953
|
-
3. After startup, `applyRuntimeConfig()` (from `lib/agent-setup.ts`) guarantees:
|
|
954
|
-
- `hooks.bootstrap-extra-files` (Rev4a system rules injection)
|
|
955
|
-
- `skills.load.extraDirs` (shared skills discovery)
|
|
956
|
-
- `models.providers.rev4a` (provider proxy, host-dependent)
|
|
957
|
-
All patches use `config patch --stdin` (deep-merge) — existing user
|
|
958
|
-
customizations are never overwritten.
|
|
959
|
-
|
|
960
|
-
On the next boot, the image entrypoint runs `openclaw doctor --non-interactive`
|
|
961
|
-
**only if the OpenClaw version changed** (safe migrations, no service restart),
|
|
962
|
-
aligning the volume's state/config to the new binary.
|
|
1120
|
+
### `GET /api/agents/[id]/recreate`
|
|
1121
|
+
The running or last recreate of an agent, with live backup progress (`null` when the
|
|
1122
|
+
agent was never recreated).
|
|
963
1123
|
|
|
964
|
-
**Auth:** browser cookie
|
|
1124
|
+
**Auth:** browser cookie or bearer token
|
|
965
1125
|
|
|
966
|
-
**Response:**
|
|
967
1126
|
```json
|
|
968
|
-
{
|
|
1127
|
+
{
|
|
1128
|
+
"recreate": {
|
|
1129
|
+
"id": 12, "agentId": "agent_9253eee3", "status": "backing_up",
|
|
1130
|
+
"image": "openclaw-agent-base:2026.7.1-2",
|
|
1131
|
+
"backupFile": "agent-agent_9253eee3-prerecreate-2026-09-17_171000.tar.gz",
|
|
1132
|
+
"backupPercent": 40, "error": null,
|
|
1133
|
+
"startedAtMs": 1789656372120, "finishedAtMs": null
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
969
1136
|
```
|
|
970
1137
|
|
|
971
|
-
|
|
1138
|
+
- `status`: `backing_up` → `recreating` → `done` | `failed`; `interrupted` after a
|
|
1139
|
+
Rev4a restart cut it off.
|
|
1140
|
+
- `backupPercent` is live only while `backing_up`.
|
|
1141
|
+
|
|
1142
|
+
---
|
|
1143
|
+
|
|
1144
|
+
### `GET /api/agents/[id]/update`
|
|
1145
|
+
The agent's OpenClaw version, the update it could take, and its latest update
|
|
1146
|
+
(`lib/agent-update.ts`).
|
|
1147
|
+
|
|
1148
|
+
**Auth:** browser cookie or bearer token
|
|
1149
|
+
|
|
1150
|
+
```json
|
|
1151
|
+
{
|
|
1152
|
+
"currentVersion": "2026.7.1-2",
|
|
1153
|
+
"candidate": { "fromVersion": "2026.7.1-2", "toVersion": "2026.9.3" },
|
|
1154
|
+
"update": {
|
|
1155
|
+
"id": 3, "agentId": "agent_9253eee3", "status": "backing_up",
|
|
1156
|
+
"fromVersion": "2026.7.1-2", "toVersion": "2026.9.3",
|
|
1157
|
+
"backupFile": "agent-agent_9253eee3-preupdate-2026.7.1-2-2026-09-16_003512345.tar.gz",
|
|
1158
|
+
"backupPercent": 40, "verification": null, "error": null,
|
|
1159
|
+
"startedAtMs": 1789524912000, "finishedAtMs": null
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
```
|
|
1163
|
+
|
|
1164
|
+
- `candidate` is `null` when no newer supported version is downloaded.
|
|
1165
|
+
- `update.status`: `pending` → `backing_up` → `migrating` → `verifying` → `done` |
|
|
1166
|
+
`failed`; `interrupted` after a Rev4a restart cut it off; `rolling_back` →
|
|
1167
|
+
`rolled_back` | `rollback_failed` for a rollback.
|
|
1168
|
+
- `verification`: `{ sessions, lostEvents: [{ session, before, after }], missingCronJobs, ok }`.
|
|
1169
|
+
|
|
1170
|
+
`503` when Docker cannot be reached.
|
|
1171
|
+
|
|
1172
|
+
### `POST /api/agents/[id]/update`
|
|
1173
|
+
Start an update. **Body (optional):** `{ "version": "2026.9.3" }`; by default the newest
|
|
1174
|
+
supported version downloaded. Returns `202` with the new `update`; the steps continue in
|
|
1175
|
+
the background.
|
|
1176
|
+
|
|
1177
|
+
1. **Preflight** (refused with `409`): the agent runs, the target is downloaded,
|
|
1178
|
+
supported and newer than the agent's version, no update or backup is running.
|
|
1179
|
+
2. **Baseline**, inside the agent: transcript events per session (`.jsonl` lines on
|
|
1180
|
+
2026.7.x, `transcript_events` rows from 9.x; trajectory files not counted) and cron
|
|
1181
|
+
job names.
|
|
1182
|
+
3. **`backing_up`**: cold backup `agent-<id>-preupdate-<from>-<ts>.tar.gz`
|
|
1183
|
+
(`startColdBackup`, agent left stopped).
|
|
1184
|
+
4. **`migrating`**: the container is recreated on `openclaw-agent-base:<to>`
|
|
1185
|
+
(`lib/agent-recreate.ts`); its entrypoint runs `doctor --fix`; Rev4a waits up to 10
|
|
1186
|
+
minutes for `/startupz` to report `started` with the target version, then re-applies
|
|
1187
|
+
its config and provider block.
|
|
1188
|
+
5. **`verifying`**: every session has at least as many events as before and every cron
|
|
1189
|
+
job is still there → `done`, then unused agent images are removed (`pruneAgentImages`).
|
|
1190
|
+
|
|
1191
|
+
A failure before the recreate starts the old container again. A failure after it leaves
|
|
1192
|
+
the agent on the new version; the panel offers Rollback.
|
|
1193
|
+
|
|
1194
|
+
**Errors:** `400` unsupported version, `409` refused (reason in `error`), `500`.
|
|
1195
|
+
|
|
1196
|
+
While an update runs, `backup`, `cold-backup`, `restore`, `recreate`, `lifecycle`,
|
|
1197
|
+
`PATCH` and `DELETE /api/agents/[id]` answer `409 "An update of this agent is running"`,
|
|
1198
|
+
and the provider sync skips the agent.
|
|
1199
|
+
|
|
1200
|
+
### `POST /api/agents/[id]/update/rollback`
|
|
1201
|
+
Roll back the latest update — `done`, `failed`, `interrupted` or `rollback_failed` — while
|
|
1202
|
+
its pre-update backup exists. `202` with the `update` (`rolling_back`).
|
|
1203
|
+
|
|
1204
|
+
1. The previous version's image is made local (pulled when it is a supported version).
|
|
1205
|
+
2. The agent is stopped and its volume replaced by the pre-update backup.
|
|
1206
|
+
3. The container is recreated on `openclaw-agent-base:<from>` and must report that version.
|
|
1207
|
+
|
|
1208
|
+
Rev4a's config is not re-applied: the restored `openclaw.json` is the one that version
|
|
1209
|
+
accepted. Everything the agent did after the backup is lost. `409` when there is nothing
|
|
1210
|
+
to roll back, the backup is gone, or an update or backup is running.
|
|
972
1211
|
|
|
973
1212
|
---
|
|
974
1213
|
|
|
@@ -1057,7 +1296,7 @@ Disconnect Telegram from an agent.
|
|
|
1057
1296
|
**Side effects:**
|
|
1058
1297
|
- Sets `channels.telegram.enabled: false`, removes `botToken` and `allowFrom`
|
|
1059
1298
|
- Removes the telegram binding
|
|
1060
|
-
-
|
|
1299
|
+
- Clears the channel's approved senders in OpenClaw's pairing store (`clearChannelAllowlist()`)
|
|
1061
1300
|
- No restart required
|
|
1062
1301
|
|
|
1063
1302
|
---
|
|
@@ -1065,6 +1304,12 @@ Disconnect Telegram from an agent.
|
|
|
1065
1304
|
### `GET /api/agents/[id]/channels/pairing?channel=telegram`
|
|
1066
1305
|
Get pending and approved pairings for a channel.
|
|
1067
1306
|
|
|
1307
|
+
Pending requests come from `openclaw pairing list --channel <channel> --json`. Approved
|
|
1308
|
+
senders come from OpenClaw's pairing store — on 2026.9.x the SQLite
|
|
1309
|
+
`channel_pairing_allow_entries` rows in `~/.openclaw/state/openclaw.sqlite`, read through
|
|
1310
|
+
the store's own `readChannelAllowFromStoreSync` (the credentials JSON files older
|
|
1311
|
+
releases used are gone, and reading those returned nothing).
|
|
1312
|
+
|
|
1068
1313
|
**Auth:** browser cookie
|
|
1069
1314
|
|
|
1070
1315
|
**Query params:**
|
|
@@ -1085,7 +1330,6 @@ Get pending and approved pairings for a channel.
|
|
|
1085
1330
|
```
|
|
1086
1331
|
|
|
1087
1332
|
- Pending codes are from `openclaw pairing list --json`
|
|
1088
|
-
- Approved senders are from the credentials allowFrom file
|
|
1089
1333
|
|
|
1090
1334
|
---
|
|
1091
1335
|
|
|
@@ -1115,6 +1359,15 @@ Approve a pending pairing code.
|
|
|
1115
1359
|
### `DELETE /api/agents/[id]/channels/pairing?channel=telegram&senderId=123456789`
|
|
1116
1360
|
Revoke an approved sender.
|
|
1117
1361
|
|
|
1362
|
+
There is no CLI or RPC for this (`openclaw pairing` covers pending requests only,
|
|
1363
|
+
`channels.pairing.*` has no remove — verified on 2026.9.3). The pairing store's own
|
|
1364
|
+
writer is used instead: `removeChannelAllowFromStoreEntry`, located by function name in
|
|
1365
|
+
OpenClaw's `pairing-store` module and run inside the container, so the write goes
|
|
1366
|
+
through the same state transaction the CLI uses. Verifying a 2026.7.x agent is out of
|
|
1367
|
+
scope: Rev4a targets 2026.9.3. On a release that no longer exposes the writer the call
|
|
1368
|
+
fails with a message pointing at `/allowlist remove` from the chat — nothing is silently
|
|
1369
|
+
left unchanged.
|
|
1370
|
+
|
|
1118
1371
|
**Auth:** browser cookie
|
|
1119
1372
|
|
|
1120
1373
|
**Query params:**
|
|
@@ -1126,6 +1379,207 @@ Revoke an approved sender.
|
|
|
1126
1379
|
**Response (200):** `{ "success": true }`
|
|
1127
1380
|
|
|
1128
1381
|
**Response (400):** `{ "error": "senderId query param is required" }`
|
|
1382
|
+
**Response (404):** `{ "error": "..." }` when the store is unavailable
|
|
1383
|
+
(the sender is already gone when present — the desired state).
|
|
1384
|
+
|
|
1385
|
+
---
|
|
1386
|
+
|
|
1387
|
+
### `GET /api/agents/[id]/devices`
|
|
1388
|
+
Browser access to one agent's Control UI: requests waiting for approval and browsers
|
|
1389
|
+
already approved.
|
|
1390
|
+
|
|
1391
|
+
**Auth:** browser cookie
|
|
1392
|
+
|
|
1393
|
+
Runs `openclaw devices list --json` inside the container with the async `dockerExec`;
|
|
1394
|
+
see `lib/agent-devices.ts`. On OpenClaw 2026.7.x, which answers `/startupz` with HTML
|
|
1395
|
+
and whose config disables device approval, the CLI is not called and both lists are
|
|
1396
|
+
empty.
|
|
1397
|
+
|
|
1398
|
+
**Response:**
|
|
1399
|
+
```json
|
|
1400
|
+
{
|
|
1401
|
+
"running": true,
|
|
1402
|
+
"requiresApproval": true,
|
|
1403
|
+
"pending": [
|
|
1404
|
+
{
|
|
1405
|
+
"requestId": "facd76cb-0dd5-462e-8d1d-d18ed35f6129",
|
|
1406
|
+
"deviceId": "f1ad26df8bc4…",
|
|
1407
|
+
"displayName": null,
|
|
1408
|
+
"platform": "MacIntel",
|
|
1409
|
+
"clientId": "openclaw-control-ui",
|
|
1410
|
+
"clientMode": "webchat",
|
|
1411
|
+
"roles": ["operator"],
|
|
1412
|
+
"scopes": ["operator.admin", "operator.read", "operator.write"],
|
|
1413
|
+
"remoteIp": "192.168.65.1",
|
|
1414
|
+
"browserOrigin": "http://192.168.1.88:3710",
|
|
1415
|
+
"isRepair": false,
|
|
1416
|
+
"requestedAtMs": 1789490956623
|
|
1417
|
+
}
|
|
1418
|
+
],
|
|
1419
|
+
"approved": [
|
|
1420
|
+
{
|
|
1421
|
+
"deviceId": "848f99c9b0af…",
|
|
1422
|
+
"label": null,
|
|
1423
|
+
"platform": "MacIntel",
|
|
1424
|
+
"clientId": "openclaw-control-ui",
|
|
1425
|
+
"clientMode": "webchat",
|
|
1426
|
+
"roles": ["operator"],
|
|
1427
|
+
"scopes": ["operator.admin", "operator.read", "operator.write"],
|
|
1428
|
+
"remoteIp": "192.168.65.1",
|
|
1429
|
+
"approvedVia": "bootstrap",
|
|
1430
|
+
"createdAtMs": 1789492279000,
|
|
1431
|
+
"approvedAtMs": 1789492279000,
|
|
1432
|
+
"lastUsedAtMs": 1789492300000
|
|
1433
|
+
}
|
|
1434
|
+
]
|
|
1435
|
+
}
|
|
1436
|
+
```
|
|
1437
|
+
|
|
1438
|
+
- A stopped agent answers `200` with `running: false` and empty lists.
|
|
1439
|
+
- `requiresApproval` is `null` when neither `/startupz` nor the config can be read.
|
|
1440
|
+
- `approvedVia`: `silent` (local connection, approved automatically), `owner` (approved
|
|
1441
|
+
by an operator), `bootstrap` (one-time link), or another OpenClaw value.
|
|
1442
|
+
- Public keys and token values are never returned.
|
|
1443
|
+
|
|
1444
|
+
**Errors:** `404` unknown agent, `503` Docker unreachable, `502` the OpenClaw command
|
|
1445
|
+
failed or returned incomplete JSON.
|
|
1446
|
+
|
|
1447
|
+
---
|
|
1448
|
+
|
|
1449
|
+
### `POST /api/agents/[id]/devices`
|
|
1450
|
+
Approve or reject a pending request.
|
|
1451
|
+
|
|
1452
|
+
**Auth:** browser cookie
|
|
1453
|
+
|
|
1454
|
+
**Body:** `{ "action": "approve" | "reject", "requestId": "<requestId>" }`
|
|
1455
|
+
|
|
1456
|
+
**Response (200):** `{ "ok": true }`
|
|
1457
|
+
|
|
1458
|
+
**Errors:** `400` invalid action or `requestId`, `404` unknown agent, `409` agent not
|
|
1459
|
+
running, `502` command failed.
|
|
1460
|
+
|
|
1461
|
+
---
|
|
1462
|
+
|
|
1463
|
+
### `PATCH /api/agents/[id]/devices`
|
|
1464
|
+
Rename an approved browser. The label is preferred over the name the client reports.
|
|
1465
|
+
|
|
1466
|
+
**Auth:** browser cookie
|
|
1467
|
+
|
|
1468
|
+
**Body:** `{ "deviceId": "<deviceId>", "name": "Office laptop" }` — 1-64 printable characters
|
|
1469
|
+
|
|
1470
|
+
**Response (200):** `{ "ok": true }`
|
|
1471
|
+
|
|
1472
|
+
**Errors:** as for `POST`.
|
|
1473
|
+
|
|
1474
|
+
---
|
|
1475
|
+
|
|
1476
|
+
### `DELETE /api/agents/[id]/devices?deviceId=<deviceId>`
|
|
1477
|
+
Revoke an approved browser (`openclaw devices remove`). It needs approval again the next
|
|
1478
|
+
time it connects.
|
|
1479
|
+
|
|
1480
|
+
**Auth:** browser cookie
|
|
1481
|
+
|
|
1482
|
+
**Response (200):** `{ "ok": true }`
|
|
1483
|
+
|
|
1484
|
+
**Errors:** as for `POST`.
|
|
1485
|
+
|
|
1486
|
+
---
|
|
1487
|
+
|
|
1488
|
+
### `GET /api/agents/[id]/open-control-ui`
|
|
1489
|
+
Where the "Open" buttons navigate, in a new tab. Redirects (`302`) to the agent's
|
|
1490
|
+
Control UI.
|
|
1491
|
+
|
|
1492
|
+
**Auth:** browser cookie
|
|
1493
|
+
|
|
1494
|
+
The target host is the one the request reached Rev4a on (the `Host` header), with the
|
|
1495
|
+
agent's published port — never a parameter, so the link cannot be sent to another host.
|
|
1496
|
+
|
|
1497
|
+
- **OpenClaw 9.x** (it answers `/startupz` with JSON): runs `openclaw dashboard --json`
|
|
1498
|
+
in the container and redirects to its `browserUrl`, with the host, the port and the
|
|
1499
|
+
`gatewayUrl` fragment parameter rewritten to that host and the published port. The
|
|
1500
|
+
link is single-use, expires after ten minutes, and pairs the browser with no approval.
|
|
1501
|
+
- **OpenClaw 2026.7.x**, or when no one-time link can be issued: redirects to the plain
|
|
1502
|
+
link `http://<host>:<port>/#token=<token>`, with the token read inside the container
|
|
1503
|
+
(`/root/.agent-token`, then `OPENCLAW_GATEWAY_TOKEN`).
|
|
1504
|
+
|
|
1505
|
+
A navigation, not a JSON call: the button opens this URL inside the click, so no tab
|
|
1506
|
+
is left blank after an `await` and no popup can be blocked.
|
|
1507
|
+
|
|
1508
|
+
**Errors** (plain text, meant for the tab): `400` host not readable, `404` unknown agent,
|
|
1509
|
+
`409` agent not running or no published Control UI port, `503` Docker unreachable,
|
|
1510
|
+
`502` neither a one-time link nor a token available.
|
|
1511
|
+
|
|
1512
|
+
---
|
|
1513
|
+
|
|
1514
|
+
### `GET /api/agents/[id]/invite-link`
|
|
1515
|
+
A Control UI link to send to someone else — the "Invite link" button in Browser access.
|
|
1516
|
+
The browser that opens it passes gateway auth and waits in the agent's approval list;
|
|
1517
|
+
nothing opens until an operator approves it.
|
|
1518
|
+
|
|
1519
|
+
**Auth:** browser cookie
|
|
1520
|
+
|
|
1521
|
+
**Response:**
|
|
1522
|
+
```json
|
|
1523
|
+
{ "url": "http://212.0.113.7:3710/#token=…", "loopbackHost": false }
|
|
1524
|
+
```
|
|
1525
|
+
|
|
1526
|
+
- `url` is the plain token link. The host is the one the request reached Rev4a on
|
|
1527
|
+
(the `Host` header); the port is the agent's published Control UI port. The token is
|
|
1528
|
+
read inside the container with the entrypoint's precedence (`/root/.agent-token`,
|
|
1529
|
+
then `OPENCLAW_GATEWAY_TOKEN`), so it is the one the Gateway checks.
|
|
1530
|
+
- `loopbackHost` is true when that host is `localhost`, `127.x` or `[::1]`: nobody else
|
|
1531
|
+
can reach the link.
|
|
1532
|
+
- The gateway token is shared by every agent. Changing the agents token
|
|
1533
|
+
(`PUT /api/agents/token`) invalidates every link sent.
|
|
1534
|
+
- `Cache-Control: no-store`.
|
|
1535
|
+
|
|
1536
|
+
**Errors:** `400` host not readable, `404` unknown agent, `409` agent not running, no
|
|
1537
|
+
published Control UI port, or no browser approval on this agent (the link would open
|
|
1538
|
+
with no approval), `502` approval state or token not readable, `503` Docker
|
|
1539
|
+
unreachable.
|
|
1540
|
+
|
|
1541
|
+
---
|
|
1542
|
+
|
|
1543
|
+
### `GET /api/agents/devices-summary`
|
|
1544
|
+
Browsers waiting for approval, per running agent — the "browser waiting" badge on the
|
|
1545
|
+
agent list.
|
|
1546
|
+
|
|
1547
|
+
**Auth:** browser cookie
|
|
1548
|
+
|
|
1549
|
+
**Response:**
|
|
1550
|
+
```json
|
|
1551
|
+
{ "agents": [ { "agentId": "agent_2a3c3a07", "requiresApproval": true, "pending": 1 } ] }
|
|
1552
|
+
```
|
|
1553
|
+
|
|
1554
|
+
- At most three agents are read at a time; one that cannot be read reports
|
|
1555
|
+
`error: true` and `pending: 0` without failing the others.
|
|
1556
|
+
- `503` with `agents: []` when Docker cannot be reached.
|
|
1557
|
+
|
|
1558
|
+
---
|
|
1559
|
+
|
|
1560
|
+
### `GET /api/agents/activity-summary`
|
|
1561
|
+
The long operation each agent is in the middle of, if any — the chip on the agent cards:
|
|
1562
|
+
`BACKUP nn%`, `RESTORING`, `RECREATING`, `UPDATING`.
|
|
1563
|
+
|
|
1564
|
+
**Auth:** browser cookie or bearer token
|
|
1565
|
+
|
|
1566
|
+
**Response:**
|
|
1567
|
+
```json
|
|
1568
|
+
{ "agents": [
|
|
1569
|
+
{ "agentId": "agent_9253eee3", "kind": "backup", "file": "agent-agent_9253eee3-cold-2026-09-17_163103795.tar.gz", "percent": 16 },
|
|
1570
|
+
{ "agentId": "agent_2a3c3a07", "kind": "restore", "file": "agent-agent_2a3c3a07-cold-2026-09-21_172032317.tar.gz" },
|
|
1571
|
+
{ "agentId": "agent_fb540be6", "kind": "update", "version": "2026.9.3" }
|
|
1572
|
+
] }
|
|
1573
|
+
```
|
|
1574
|
+
|
|
1575
|
+
- `kind`: `backup`, `restore`, `recreate`, `update` or `edit`. A running cold backup takes
|
|
1576
|
+
precedence, because while its helper archives the volume that is the phase actually in
|
|
1577
|
+
progress (an update's or recreate's own backup phase reports `backup`).
|
|
1578
|
+
- `percent` is only on `backup` (`null` until the archive size has been measured; the
|
|
1579
|
+
client renders `BACKUP…` then). `version` is on `recreate`/`update`.
|
|
1580
|
+
- Idle agents are not listed: an idle system answers `{ "agents": [] }`.
|
|
1581
|
+
- Read from the state modules (`agent_restores`, `agent_recreates`, `agent_upgrades`,
|
|
1582
|
+
`agent_edits`) and one Docker list call for the running backup helpers.
|
|
1129
1583
|
|
|
1130
1584
|
---
|
|
1131
1585
|
|
|
@@ -1159,7 +1613,7 @@ Useful for inspecting the container config without SSH or terminal.
|
|
|
1159
1613
|
{
|
|
1160
1614
|
"name": "my-agent",
|
|
1161
1615
|
"config": {
|
|
1162
|
-
"gateway": { "controlUi": { "
|
|
1616
|
+
"gateway": { "controlUi": { "dangerouslyAllowHostHeaderOriginFallback": true } },
|
|
1163
1617
|
"agents": { "defaults": { "model": { "primary": "rev4a/deepseek/deepseek-flash", "fallbacks": [] } } },
|
|
1164
1618
|
"models": { "providers": { "rev4a": { "baseUrl": "...", "apiKey": "***", "models": [...] } } }
|
|
1165
1619
|
}
|
|
@@ -1187,6 +1641,9 @@ The token is stored in `data/agents-token.json`.
|
|
|
1187
1641
|
|
|
1188
1642
|
### `PUT /api/agents/token`
|
|
1189
1643
|
Update the shared agents gateway token and push it to all running agent containers.
|
|
1644
|
+
Agents with a long operation in flight (update, recreate, restore, edit, backup) are
|
|
1645
|
+
skipped and listed, and a container that could not be updated is reported instead of
|
|
1646
|
+
being swallowed.
|
|
1190
1647
|
|
|
1191
1648
|
**Auth:** browser cookie
|
|
1192
1649
|
|
|
@@ -1197,7 +1654,7 @@ Update the shared agents gateway token and push it to all running agent containe
|
|
|
1197
1654
|
|
|
1198
1655
|
**Response:**
|
|
1199
1656
|
```json
|
|
1200
|
-
{ "success": true, "containersUpdated": 2 }
|
|
1657
|
+
{ "success": true, "containersUpdated": 2, "skipped": ["agent_9253eee3"], "failed": [{ "container": "agent_x", "error": "…" }] }
|
|
1201
1658
|
```
|
|
1202
1659
|
|
|
1203
1660
|
**Side effects:**
|
|
@@ -1210,53 +1667,64 @@ Update the shared agents gateway token and push it to all running agent containe
|
|
|
1210
1667
|
## Agent Image Management
|
|
1211
1668
|
|
|
1212
1669
|
### `GET /api/agents/image-status`
|
|
1213
|
-
|
|
1670
|
+
Which OpenClaw versions of the agent image are downloaded, and whether the registry
|
|
1671
|
+
publishes a newer supported one.
|
|
1214
1672
|
|
|
1215
1673
|
**Auth:** browser cookie or bearer token
|
|
1216
1674
|
|
|
1217
|
-
The
|
|
1218
|
-
|
|
1675
|
+
The registry tag list is read over the registry HTTP API (anonymous token for ghcr,
|
|
1676
|
+
plain HTTP for a `localhost` registry), cached for 10 minutes and de-duplicated while
|
|
1677
|
+
in flight, so polling this endpoint does not re-query the registry every time.
|
|
1219
1678
|
|
|
1220
1679
|
**Response:**
|
|
1221
1680
|
```json
|
|
1222
1681
|
{
|
|
1682
|
+
"downloading": false,
|
|
1683
|
+
"downloadingVersion": null,
|
|
1223
1684
|
"exists": true,
|
|
1224
1685
|
"needsUpdate": false,
|
|
1225
|
-
"
|
|
1226
|
-
"
|
|
1686
|
+
"localVersions": ["2026.9.3"],
|
|
1687
|
+
"newestLocal": "2026.9.3",
|
|
1688
|
+
"available": null,
|
|
1689
|
+
"registryReachable": true
|
|
1227
1690
|
}
|
|
1228
1691
|
```
|
|
1229
1692
|
|
|
1230
1693
|
| Field | Description |
|
|
1231
1694
|
|---|---|
|
|
1232
|
-
| `exists` |
|
|
1233
|
-
| `needsUpdate` | `true`
|
|
1234
|
-
| `
|
|
1235
|
-
| `
|
|
1695
|
+
| `exists` | A supported version is downloaded as `openclaw-agent-base:<version>` |
|
|
1696
|
+
| `needsUpdate` | `true` when none is, or when `available` is set |
|
|
1697
|
+
| `localVersions` | Supported versions downloaded, newest first |
|
|
1698
|
+
| `newestLocal` | The version new agents are created on |
|
|
1699
|
+
| `available` | Newest supported version on the registry that is not downloaded and is newer than `newestLocal`, or null |
|
|
1700
|
+
| `downloading` / `downloadingVersion` | A download running, and its version |
|
|
1701
|
+
| `registryReachable` | The tag list could be read |
|
|
1236
1702
|
|
|
1237
1703
|
### `POST /api/agents/download-image`
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
The frontend polls `GET /api/agents/image-status` for progress updates.
|
|
1704
|
+
Downloads one OpenClaw version of the agent image in the background. Returns 202
|
|
1705
|
+
immediately; the frontend polls `GET /api/agents/image-status` for completion.
|
|
1241
1706
|
|
|
1242
1707
|
**Auth:** browser cookie or bearer token
|
|
1243
1708
|
|
|
1709
|
+
**Body (optional):** `{ "version": "2026.9.3" }` — a supported version. Without it, the
|
|
1710
|
+
newest supported version the registry publishes.
|
|
1711
|
+
|
|
1244
1712
|
**Response (202):**
|
|
1245
1713
|
```json
|
|
1246
|
-
{ "
|
|
1714
|
+
{ "started": true, "version": "2026.9.3" }
|
|
1247
1715
|
```
|
|
1248
1716
|
|
|
1249
|
-
**
|
|
1250
|
-
```json
|
|
1251
|
-
{ "error": "A download is already in progress" }
|
|
1252
|
-
```
|
|
1717
|
+
**Errors:** `400` unsupported version; `409` a download is already in progress.
|
|
1253
1718
|
|
|
1254
|
-
|
|
1255
|
-
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
-
|
|
1259
|
-
|
|
1719
|
+
The download is `lib/buildAgentImage.ts` → `downloadAgentImage({ version, onEvent, signal })`:
|
|
1720
|
+
- nothing to do when `openclaw-agent-base:<version>` is already here
|
|
1721
|
+
- `docker pull <registry>:<version>`, tag `openclaw-agent-base:<version>`, untag the
|
|
1722
|
+
registry reference (`pullAgentImage()` in `lib/agent-images.ts`)
|
|
1723
|
+
- when the pull fails and the repository Dockerfile's `ARG OPENCLAW_VERSION` is that
|
|
1724
|
+
version, `docker build --build-arg OPENCLAW_VERSION=<version>` instead; any other
|
|
1725
|
+
version fails
|
|
1726
|
+
- one download at a time (in-process lock, `getIsDownloading()` /
|
|
1727
|
+
`getDownloadingVersion()`); output goes to `/tmp/rev4a-download-<timestamp>.log`
|
|
1260
1728
|
|
|
1261
1729
|
### `GET /api/agents-active`
|
|
1262
1730
|
|
|
@@ -1609,7 +2077,7 @@ List all running Docker containers with resource usage.
|
|
|
1609
2077
|
"containers": [
|
|
1610
2078
|
{
|
|
1611
2079
|
"name": "openclaw-atlas",
|
|
1612
|
-
"image": "openclaw-agent-base:
|
|
2080
|
+
"image": "openclaw-agent-base:2026.9.3",
|
|
1613
2081
|
"status": "running",
|
|
1614
2082
|
"ports": ["0.0.0.0:3731->3000/tcp"],
|
|
1615
2083
|
"created": "2026-06-20T10:00:00Z",
|
|
@@ -1648,16 +2116,16 @@ Update audio or timezone config in `openclaw.json`.
|
|
|
1648
2116
|
## Version & WebSocket
|
|
1649
2117
|
|
|
1650
2118
|
### `GET /api/version`
|
|
1651
|
-
Return the installed
|
|
2119
|
+
Return the installed Rev4a version, read from `package.json`.
|
|
1652
2120
|
|
|
1653
|
-
**Auth:**
|
|
2121
|
+
**Auth:** `rev4a_token` cookie or `Authorization: Bearer <REV4A_TOKEN>`
|
|
1654
2122
|
|
|
1655
2123
|
**Response:**
|
|
1656
2124
|
```json
|
|
1657
|
-
{ "version": "
|
|
2125
|
+
{ "version": "1.4.0" }
|
|
1658
2126
|
```
|
|
1659
2127
|
|
|
1660
|
-
**Fallback:** `{ "version": "
|
|
2128
|
+
**Fallback:** `{ "version": "0.0.0" }` if `package.json` cannot be read.
|
|
1661
2129
|
|
|
1662
2130
|
### Terminal WebSocket
|
|
1663
2131
|
|