@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
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Rev4a Architecture — Design & Vision
|
|
2
2
|
|
|
3
3
|
> **Status:** Active — `main` branch
|
|
4
|
-
> **Last updated:** 2026-09-
|
|
4
|
+
> **Last updated:** 2026-09-15
|
|
5
5
|
> **Goal:** Transform Rev4a from a monitoring dashboard into a central orchestrator for a distributed multi-container agency.
|
|
6
6
|
|
|
7
7
|
---
|
|
@@ -130,6 +130,101 @@ The central container, running the Next.js dashboard + orchestration API.
|
|
|
130
130
|
a key), `isModelOffered()` (enforced by the proxy and the assistant) and
|
|
131
131
|
`catalogueStatus()`. A failed read of `models.config.json` falls back to the last
|
|
132
132
|
good copy and never deletes overrides.
|
|
133
|
+
- **Agent images and OpenClaw versions.** `lib/agent-versions.json` lists the supported
|
|
134
|
+
OpenClaw versions, newest first, with the model `input` list for each; the server reads
|
|
135
|
+
it through `lib/agent-versions.ts`, the `rev4a` CLI with `require`. The provider sync
|
|
136
|
+
takes each agent's own list (`containerVersionSync()`: image tag, else the entrypoint's
|
|
137
|
+
`.last-version` file), so a 2026.7.x agent is never sent an `input` value it would
|
|
138
|
+
reject — one unknown value discards the whole generated catalogue. The previous
|
|
139
|
+
versions stay listed while agents still run them: that is also what lets a rollback
|
|
140
|
+
pull the previous image. `lib/agent-images.ts`
|
|
141
|
+
lists local `openclaw-agent-base:<version>` images through the Docker API, reads an
|
|
142
|
+
agent's version (image label or tag, else `openclaw --version` in the container, cached
|
|
143
|
+
per image id), reads the registry tag list over HTTP (cached 10 min), pulls a version
|
|
144
|
+
and resolves the image a recreate uses. Images are never addressed as `:latest`: create
|
|
145
|
+
takes the newest supported version downloaded, recreate the agent's own.
|
|
146
|
+
- **Cold backups** are `lib/cold-backup.ts`. The agent is stopped and a detached helper
|
|
147
|
+
container, `rev4a-backup-<id>` from the agent's own image, archives its volume to
|
|
148
|
+
`rev4a-backups` as `.partial`, reads it back and renames it; the agent is started
|
|
149
|
+
again if it was running. The helper and its labels are the job, so no request waits
|
|
150
|
+
on it and a Rev4a restart does not lose it (`reconcileColdBackups()` in
|
|
151
|
+
`instrumentation.ts`). While a helper runs, the routes that change the agent answer
|
|
152
|
+
409 (`isColdBackupRunning()`), and `syncAllAgents()` skips the agent.
|
|
153
|
+
- **Agent updates** are `lib/agent-update.ts`, with their state in the `agent_upgrades`
|
|
154
|
+
table (`lib/agent-update-state.ts`). An update counts the agent's transcript events per
|
|
155
|
+
session and its cron jobs, takes a cold pre-update backup, recreates the container on
|
|
156
|
+
the newer version (`lib/agent-recreate.ts`, argv only, env values outside the process
|
|
157
|
+
table), waits for `/startupz` to report that version, re-applies Rev4a's config and
|
|
158
|
+
checks the counts. A rollback restores the pre-update backup on the previous version.
|
|
159
|
+
Both run inside the Rev4a process; `markInterruptedUpdates()` at startup turns an
|
|
160
|
+
unfinished one into `interrupted`. `lib/agent-busy.ts` gives the routes one 409 reason
|
|
161
|
+
for "edit, update, recreate, restore or backup running". After a successful update, `pruneAgentImages()`
|
|
162
|
+
keeps only images in use plus the newest and previous versions.
|
|
163
|
+
- **Agent recreates** are `lib/agent-recreate.ts`, with their state in the
|
|
164
|
+
`agent_recreates` table (`lib/agent-recreate-state.ts`): the same-version counterpart
|
|
165
|
+
of an update. A cold backup, then the container rebuilt on the image the agent already
|
|
166
|
+
runs, then `/startupz`, then the runtime config. It answers `202` and runs in the
|
|
167
|
+
background, so a page reload or a Rev4a restart does not lose it; at startup a job
|
|
168
|
+
still active becomes `interrupted` and the agent is started again — after its backup
|
|
169
|
+
helper has finished, never while the archive is written. On success the older
|
|
170
|
+
`prerecreate` archives are pruned to the newest two. The same `recreateAgentContainer()`
|
|
171
|
+
serves the edit route (`PATCH` → 202, no backup) and the Update action.
|
|
172
|
+
`agentBusyReason()` (`lib/agent-busy.ts`) covers it for every route that changes an agent.
|
|
173
|
+
- **Host ports** have one source of truth: `lib/agent-ports.ts` holds the pure rules
|
|
174
|
+
(`validatePortInput`, `findAvailablePortBlock`, `portMappingArgs`,
|
|
175
|
+
`hostPortsFromBindings`, `hostPortsFromArgs`) and `lib/agent-ports-server.ts` answers
|
|
176
|
+
which ports are taken (`getHostPortHolders()`, `getUsedHostPorts()`; server-only, it
|
|
177
|
+
talks to the Docker socket). Creation, the create form, the edit flow and every
|
|
178
|
+
container rebuild (update, recreate, rollback) read their numbers from there. One block
|
|
179
|
+
maps to the container's gateway port 3000 (`3700-3709` → `3700-3709:3000-3009`); all
|
|
180
|
+
flows produce the same validation messages, and a flow rebuilding a container excludes
|
|
181
|
+
that container's own ports, so its block never conflicts with itself.
|
|
182
|
+
**A stopped container still holds its block**, but neither `docker ps -a --format
|
|
183
|
+
'{{.Ports}}'` nor the Docker API's container list reports its ports (verified on Docker
|
|
184
|
+
29: both return nothing). The lookup therefore reads each container's
|
|
185
|
+
`HostConfig.PortBindings` — the only source covering the stopped ones. Without that, a
|
|
186
|
+
new agent could be handed the block of a stopped agent, and the clash only surfaced
|
|
187
|
+
later, when that agent was started again (`Bind for 0.0.0.0:3730 failed: port is
|
|
188
|
+
already allocated`), after the container it was replacing had been removed.
|
|
189
|
+
`recreateAgentContainer()` refuses such a clash **before** removing anything, naming the
|
|
190
|
+
container that holds each port, so the agent stays up and the operator changes the range
|
|
191
|
+
from the Edit panel; a failed `docker run` no longer leaves a container behind.
|
|
192
|
+
- **Agent edits** are `lib/agent-edit.ts`, with their state in the `agent_edits` table
|
|
193
|
+
(`lib/agent-edit-state.ts`): the display name (`AGENT_NAME`) and/or the port range are
|
|
194
|
+
applied by rebuilding the container on the image it already runs — **no backup**, the
|
|
195
|
+
volume is never touched. Same 202-and-background shape as a restore; the parameters
|
|
196
|
+
live in the row, so recovery can tell what was being applied. The panel shows a banner
|
|
197
|
+
and the agent card an `EDITING` chip.
|
|
198
|
+
- **Agent restores** are `lib/agent-restore.ts`, with their state in the `agent_restores`
|
|
199
|
+
table (`lib/agent-restore-state.ts`): the archive replaces the volume (stop, clear,
|
|
200
|
+
extract, start), up to 30 minutes. Same 202-and-background shape as a recreate, and the
|
|
201
|
+
same guarantees — a reload or a Rev4a restart does not lose the job, a second restore
|
|
202
|
+
is refused, an interrupted one starts the container again. The extract has no
|
|
203
|
+
percentage (it is a single `tar xzf`); the panel and the agent card show `RESTORING`.
|
|
204
|
+
- **Browser access** to an agent's Control UI goes through `lib/agent-devices.ts`:
|
|
205
|
+
`openclaw devices list | approve | reject | rename | remove` and
|
|
206
|
+
`openclaw dashboard --json`, run inside the container with the async `dockerExec`.
|
|
207
|
+
The gateway token is resolved inside the container (`/root/.agent-token`, then its
|
|
208
|
+
environment), never passed in argv. Whether approval applies is decided by the
|
|
209
|
+
OpenClaw version: 9.x answers `/startupz` with JSON and always requires it;
|
|
210
|
+
2026.7.x follows `gateway.controlUi.dangerouslyDisableDeviceAuth`.
|
|
211
|
+
`buildInviteLink()` builds the plain token link for someone else; the invite route
|
|
212
|
+
refuses agents without approval. Every link's token is read inside the container,
|
|
213
|
+
and its host is the request's `Host` header (`hostnameFromHostHeader()`), never a
|
|
214
|
+
caller-supplied value.
|
|
215
|
+
- **Telegram DM pairing** lives in `lib/channelManager.ts`. The bot binding is config
|
|
216
|
+
(`channels.telegram.botToken`/`dmPolicy`); *who* may talk is OpenClaw's pairing
|
|
217
|
+
store — on 2026.9.3 the SQLite rows in `~/.openclaw/state/openclaw.sqlite`, not the
|
|
218
|
+
`credentials/*.json` files older releases used. Reading and revoking go through the
|
|
219
|
+
store's own functions (`readChannelAllowFromStoreSync`,
|
|
220
|
+
`removeChannelAllowFromStoreEntry`): no CLI, RPC or documented export exposes them,
|
|
221
|
+
so a small script run inside the container finds OpenClaw's `pairing-store` module by
|
|
222
|
+
glob and its functions by name (the names survive minification; no hash or minified
|
|
223
|
+
symbol is hardcoded) and calls them, which uses the same state transaction the CLI
|
|
224
|
+
does. If a release stops exposing the store, the revoke fails with a message pointing
|
|
225
|
+
at `/allowlist remove` — it never reports success without changing anything. Approving
|
|
226
|
+
a pairing request (`openclaw pairing approve`) also bootstraps
|
|
227
|
+
`commands.ownerAllowFrom` for the first owner (OpenClaw's own behaviour).
|
|
133
228
|
|
|
134
229
|
**Running commands inside containers** (`lib/docker-exec.ts`):
|
|
135
230
|
|
|
@@ -149,6 +244,7 @@ the rules for anything you touch, not as a description of the whole tree:
|
|
|
149
244
|
container cannot blank an entire page. Callers: `app/api/skills/route.js`,
|
|
150
245
|
`app/api/agents/models-summary/route.ts`,
|
|
151
246
|
`app/api/agents/channels-summary/route.ts`,
|
|
247
|
+
`app/api/agents/devices-summary/route.ts`,
|
|
152
248
|
`lib/openclaw-cron.ts`, `app/api/credentials/detect/route.ts`,
|
|
153
249
|
`lib/credentials/delivery.ts`.
|
|
154
250
|
- **Never interpolate a secret into a command string.** Pass it through the `env`
|
|
@@ -195,7 +291,9 @@ agent-{name}-data (named vol) → /root (rw,
|
|
|
195
291
|
All shared volumes are defined centrally in `lib/agent-setup.ts`
|
|
196
292
|
(`REV4A_VOLUMES`) and used by both the create and recreate API routes.
|
|
197
293
|
The `bootstrap-extra-files` hook is pre-wired in the base image Dockerfile
|
|
198
|
-
and guaranteed at runtime by `applyRuntimeConfig()
|
|
294
|
+
and guaranteed at runtime by `applyRuntimeConfig()`, which also applies the Control
|
|
295
|
+
UI browser-origin policy (`withControlUiPolicy()`): the Host-header fallback on, an
|
|
296
|
+
`allowedOrigins: ["*"]` list and the retired `dangerouslyDisableDeviceAuth` removed.
|
|
199
297
|
|
|
200
298
|
**Injected environment variables:**
|
|
201
299
|
```
|
|
@@ -337,7 +435,7 @@ Rev4a Dashboard UI
|
|
|
337
435
|
POST /api/agents/create Create new container agent (template + model)
|
|
338
436
|
DELETE /api/agents/{id} Destructive: remove container + volume + backups
|
|
339
437
|
POST /api/agents/{id}/restart Restart container
|
|
340
|
-
POST /api/agents/{id}/recreate Rebuild
|
|
438
|
+
POST /api/agents/{id}/recreate Rebuild on the agent's own version (volume preserved)
|
|
341
439
|
GET /api/agents List all agents (Docker containers with AGENT_ID)
|
|
342
440
|
```
|
|
343
441
|
|
|
@@ -422,7 +520,7 @@ networks:
|
|
|
422
520
|
- Agent → Rev4a Gateway: `http://rev4a-control:3721`
|
|
423
521
|
- Agent → Rev4a Dashboard: `http://rev4a-control:3720`
|
|
424
522
|
- Rev4a → Agent (healthcheck): `http://agent-{id}:3000`
|
|
425
|
-
-
|
|
523
|
+
- Browser → Agent Control UI: the container's published host port, `http://<host>:<port>`. Rev4a sets no reverse-proxy labels
|
|
426
524
|
|
|
427
525
|
---
|
|
428
526
|
|
|
@@ -573,10 +671,10 @@ ALTER TABLE sessions ADD COLUMN ended_at INTEGER;
|
|
|
573
671
|
- Distributed: per-agent memory, Rev4a indexes centrally — more resilient
|
|
574
672
|
- Recommendation: distributed with central index
|
|
575
673
|
|
|
576
|
-
3. **
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
674
|
+
3. **External access to agents** — decided: each agent's Control UI is published on a
|
|
675
|
+
host port, and Rev4a sets no reverse-proxy labels. HTTPS or a domain means the
|
|
676
|
+
operator's own proxy in front of that port. A first-class integration, with routing
|
|
677
|
+
labels applied at `docker run`, would be a separate feature.
|
|
580
678
|
|
|
581
679
|
4. **Hermes and other frameworks: dashboard integration?**
|
|
582
680
|
- Hermes has its own session format
|
|
@@ -628,6 +726,6 @@ for the full rationale.
|
|
|
628
726
|
| Memory | Per-agent SQLite + central index | New |
|
|
629
727
|
| Config | Generated YAML/JSON | New |
|
|
630
728
|
| Credential stores | SQLite + JSON | credentials.db for service tokens, provider-keys.json for provider API keys. Neither is encrypted |
|
|
631
|
-
| Reverse proxy |
|
|
729
|
+
| Reverse proxy | Optional, operator's choice | Rev4a sets no reverse-proxy labels on agent containers |
|
|
632
730
|
| Monitoring | Rev4a daemon (extended) | Evolution of current daemon |
|
|
633
731
|
| Version control | Git + GitHub | `github.com/Flame0510/rev4a.git` |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Rev4a Frontend Architecture
|
|
2
2
|
|
|
3
|
-
> **Last updated:** 2026-09-
|
|
3
|
+
> **Last updated:** 2026-09-15
|
|
4
4
|
|
|
5
5
|
## Layering
|
|
6
6
|
|
|
@@ -42,6 +42,13 @@ All shared UI primitives live in `app/components/ui/` and are exported from `app
|
|
|
42
42
|
| `Page` / `PageHeader` | `Page.tsx` | Full-page layout shell. |
|
|
43
43
|
| `Icons` | `Icons.tsx` | SVG icons (`EyeIcon`, `EyeOffIcon`), 16/20px shared. |
|
|
44
44
|
| `PasswordInput` | `PasswordInput.tsx` | Password input with inline show/hide toggle (`<button type="button">` with `aria-label`). |
|
|
45
|
+
| `UpdateSection` | `app/agents/UpdateSection.tsx` | OPENCLAW VERSION section of the agent detail panel: the version the agent runs, **Update to <version>** when a newer supported version is downloaded (confirm modal, disabled while the agent is stopped), the running update's steps with backup progress, the outcome, and **Roll back to <version>** after an update. Polls `/api/agents/[id]/update` every 2 s while an update or rollback runs; one action at a time (keyed busy state). |
|
|
46
|
+
| `BackupSection` | inline in `app/agents/PageClient.tsx` | BACKUP section of the agent detail panel, on the cold backup and the restore. **Backup Now** starts `POST /api/agents/[id]/cold-backup`; while the job runs a banner shows the file and its live percent with **Cancel** (`DELETE /cold-backup`). **Restore** (after a confirm) starts `POST /restore` and a banner shows `Restoring <file>…` (no percent: the extract is a single `tar xzf`, and there is no Cancel). Both sections poll their `GET` every 2 s while running, and on mount pick up a job that is already running — a backup lives in a Docker helper, a restore in `agent_restores`, so reloading the page or navigating away never loses them nor allows a second one (the server answers 409 anyway). Delete per row; all actions disabled while one runs; keyed busy state `{ kind, file }` so only the row in action shows the spinner. On the agent list, an activity Badge (fed by `/api/agents/activity-summary`, polled at 2 s only while something runs, otherwise riding the 15 s list poll) reads `BACKUP nn%`, `RESTORING`, `RECREATING`, `EDITING` or `UPDATING`. |
|
|
47
|
+
| `RecreateSection` | inline in `app/agents/PageClient.tsx` | RECREATE section of the agent detail panel. **Recreate Container** starts `POST /api/agents/[id]/recreate` (202) after a confirm; a banner then shows the phase — *Backing up … nn%* while the cold backup runs, *Recreating container…* while the container is rebuilt and the gateway starts. The section polls `GET /recreate` every 2 s, and on mount picks up a recreate that is already running, so a reload or navigation never loses it; it refetches the agent once the job reports `done`. |
|
|
48
|
+
| `ImageDownloadBanner` | `app/agents/ImageDownloadBanner.tsx` | Agent image banner on the Agents page. Polls `/api/agents/image-status` every 2 s; offers **Download Image** when no supported version is downloaded, **Download <version>** when the registry publishes a newer one, and shows the download in progress and its completion. Downloading changes no agent. |
|
|
49
|
+
| `BrowserAccessSection` / `OpenControlUiButton` | `app/agents/BrowserAccessSection.tsx` | Browser access to one agent's Control UI, in its detail panel: requests waiting for approval (Approve / Reject) and approved browsers (Rename / Revoke), refreshed every 5 s while mounted. A successful approve, reject, rename or revoke updates the list at once, since the refresh behind it runs the OpenClaw CLI and takes seconds; a read started before the mutation is discarded. On agents that require approval, "Invite link" fetches `/api/agents/[id]/invite-link` and shows the link in a read-only field with Copy, which uses the Clipboard API in a secure context and the field's selection over plain HTTP, plus a warning when the link uses localhost. `OpenControlUiButton` opens `/api/agents/[id]/open-control-ui` in a new tab inside the click; that route redirects to a one-time link that pairs the browser with no approval, or to the plain token link when none can be issued. Used on the agent cards and in the panel. |
|
|
50
|
+
| `ChannelManager` / `ChannelSection` | `app/agents/ChannelManager.tsx`, `ChannelSection` inline in `app/agents/PageClient.tsx` | Telegram, in the agent detail panel (`ChannelSection` is the card that opens the modal; the modal title is the agent's display name). Reads `GET /channels`; lists pending pairing requests with **Approve** (`POST /channels/pairing`) and approved senders with **Revoke** after a confirm (`DELETE /channels/pairing?senderId=`). Pending comes from `openclaw pairing list`, approved from OpenClaw's pairing store (`lib/channelManager.ts`). Polls pairings every 5 s while open; one keyed busy state per action. |
|
|
51
|
+
| `EditAgentModal` / `EditBanner` | `app/agents/PageClient.tsx` | Rename/ports editing. The modal has **Display Name** and **Host Port** (same input and validation messages as the create wizard, from `lib/agent-ports.ts`), sends `PATCH /api/agents/[id]` and closes on `202`; `EditBanner` (top of the agent detail) polls `GET /api/agents/[id]` every 2 s while an edit is `rebuilding`, resumes on mount, and refetches the agent once it finishes, so a reload or a navigation shows the running edit instead of allowing a second (the server answers 409). No backup is taken: the volume is untouched. |
|
|
45
52
|
| `ModelSection` | `app/agents/ModelSection.tsx` | Primary model and fallbacks for one agent, in its detail panel. Explicit save, no restart. The model is a property of the agent, not of the gateway. Tags models the catalogue marks `deprecated`. |
|
|
46
53
|
| `ModelsProvider` / `useModels` | `app/lib/models-context.tsx` | The client's single model list, from `/api/models`. Whatever changes what is offered calls `refresh()`: the Gateway page after a toggle, a key save or removal, or a sync, and the first-run wizard after saving keys. Everything else only reads, PulseChat included. |
|
|
47
54
|
| `WizardPageClient` + step components | `app/wizard/PageClient.tsx` | Multi-step first-run wizard plus its frame shell, with mobile-first CSS. |
|
package/docs/REV4A.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Rev4a — VPS Dashboard
|
|
2
2
|
|
|
3
|
-
> **Last updated:** 2026-09-
|
|
3
|
+
> **Last updated:** 2026-09-15
|
|
4
4
|
|
|
5
5
|
A Next.js 16 dashboard for monitoring and managing the OpenClaw ecosystem.
|
|
6
6
|
|
|
@@ -68,6 +68,7 @@ Rev4a reads the following environment variables. Set them in `/config` (UI) or d
|
|
|
68
68
|
| `REV4A_ROOT` | No | Installation root, used by `rev4a update` to detect a git checkout |
|
|
69
69
|
| `REV4A_TIMEZONE` | No | IANA timezone used for scheduling and timestamps |
|
|
70
70
|
| `REV4A_FORCE_INSECURE_COOKIE` | No | Allow a non-Secure session cookie — plain-HTTP deployments only |
|
|
71
|
+
| `REV4A_AGENT_IMAGE_REGISTRY` | No | Registry repository agent images are pulled from (default: `ghcr.io/flame0510/rev4a/openclaw-agent-base`); a `localhost` registry is reached over plain HTTP |
|
|
71
72
|
|
|
72
73
|
See [Alerts](#alerts-telegram) below for the Telegram alert variables.
|
|
73
74
|
|
|
@@ -76,9 +77,9 @@ See [Alerts](#alerts-telegram) below for the Telegram alert variables.
|
|
|
76
77
|
Every agent is created with:
|
|
77
78
|
|
|
78
79
|
- A host port mapping (`-p <port>:3000`) for direct access
|
|
79
|
-
-
|
|
80
|
+
- Control UI at `http://<host>:<port>`, opened from the Agents page through `GET /api/agents/[id]/open-control-ui`
|
|
80
81
|
- The provider gateway at `http://host.docker.internal:3740/api/provider/v1`
|
|
81
|
-
- `
|
|
82
|
+
- `gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback: true`, so the Control UI opens on whatever host the agent is reached on, and pages from other origins are refused
|
|
82
83
|
|
|
83
84
|
The port is auto-assigned starting from 3000 (or user-specified).
|
|
84
85
|
|
|
@@ -302,9 +303,10 @@ Lists all agents (Docker containers with `AGENT_ID` label), with:
|
|
|
302
303
|
- Name, image, template, status, ports, IP
|
|
303
304
|
- TG channel status chip (green = connected, hidden if not configured)
|
|
304
305
|
- **Shared gateway token** — text input with Save & Sync. Changing the token saves it to `data/agents-token.json` and pushes it to all running containers.
|
|
305
|
-
- **Control UI link** —
|
|
306
|
+
- **Control UI link** — the **Open** buttons go through `GET /api/agents/[id]/open-control-ui`, which redirects to the agent's published port on the host the dashboard was reached on: a one-time link on OpenClaw 9.x, the plain `#token=` link otherwise, with the token read inside the container.
|
|
306
307
|
- **Agent creation wizard** — multi-step form at `/agents/create`.
|
|
307
|
-
- **Channel Manager** — each agent detail panel has a Channels section with Telegram configuration and pairing management.
|
|
308
|
+
- **Channel Manager** — each agent detail panel has a Channels section with Telegram configuration and pairing management. Pending requests (a sender's `/start` creates one) show an **Approve** button; approved senders show **Revoke** with a confirmation. Approved senders are OpenClaw's pairing store, not the agent config: on 2026.9.x the SQLite store, read and revoked through OpenClaw's own store functions (see [ARCHITECTURE.md](ARCHITECTURE.md)).
|
|
309
|
+
- **Browser access** — the detail panel section right after Channels. From OpenClaw 9.x each new browser must be approved once before the Control UI connects: the section lists requests waiting for approval (Approve / Reject) and approved browsers (Rename / Revoke), and the agent card shows a "browser waiting" badge. The **Open** buttons first ask the agent for a one-time link that pairs the browser with no approval, and open the plain token link when none can be issued (every 2026.7.x agent). **Invite link** (9.x agents only) gives a link to send to someone else: their browser passes gateway auth and waits in the approval list until approved. The link carries the gateway token every agent shares; changing the agents token invalidates every link sent. All of it runs the OpenClaw CLI inside the container asynchronously.
|
|
308
310
|
|
|
309
311
|
### Agent Creation Wizard (`/agents/create`)
|
|
310
312
|
|
|
@@ -318,20 +320,20 @@ Available templates:
|
|
|
318
320
|
|
|
319
321
|
**Step 2 — Config:** Enter agent name, optional port, select a primary model from the available providers (pre-filtered by configured provider keys). The first model is pre-selected.
|
|
320
322
|
|
|
321
|
-
**Step 3 — Deploy:** `POST /api/agents/create` creates the Docker container and returns
|
|
323
|
+
**Step 3 — Deploy:** `POST /api/agents/create` creates the Docker container and returns its port and the shared gateway token; the wizard links to `http://<host>:<port>#token=<token>`.
|
|
322
324
|
|
|
323
325
|
**Post-creation pipeline:**
|
|
324
|
-
1. The container boots with the
|
|
326
|
+
1. The container boots with the newest supported OpenClaw version downloaded here, `openclaw-agent-base:<version>`; the create answers `409` when none is — see [Agent Templates](#agent-templates) below.
|
|
325
327
|
2. OpenClaw gateway starts automatically with `--allow-unconfigured`, generating its own default config.
|
|
326
328
|
3. The route waits for the gateway to finish starting with `waitForGatewayReady()`, up
|
|
327
329
|
to 60 s. On OpenClaw 9.x it waits for `/startupz` to report `started`. The
|
|
328
330
|
2026.7.1-2 image has no `/startupz`, so there it waits for `/health`, which only
|
|
329
331
|
shows the server is listening.
|
|
330
332
|
4. Once ready, the route writes:
|
|
331
|
-
- `gateway.controlUi.
|
|
333
|
+
- `gateway.controlUi` with the browser-origin policy (`withControlUiPolicy()` in `lib/agent-setup.ts`): the Host-header fallback on, so the Control UI is accepted from the host the browser connected to — a public IP is refused without it — while pages from other origins are refused; an `allowedOrigins: ["*"]` list and the retired `dangerouslyDisableDeviceAuth` are dropped, any other allowlist is kept. Recreate re-applies the policy. Each new browser still needs a one-time device approval
|
|
332
334
|
- `agents.defaults.model.primary` + fallbacks — the primary model selected in the wizard
|
|
333
335
|
5. The route builds `models.providers.rev4a` with `buildRev4aProviderConfig()` and writes it with `openclaw config patch --stdin`.
|
|
334
|
-
6. The agent's
|
|
336
|
+
6. The agent's Control UI is reachable at `http://<host>:<port>#token=<gateway-token>`. For HTTPS or a domain, put your own reverse proxy in front of the published port: Rev4a sets no reverse-proxy labels on agent containers.
|
|
335
337
|
|
|
336
338
|
---
|
|
337
339
|
|
|
@@ -341,8 +343,9 @@ Each agent is created with a **persistent Docker volume** `agent-<name>-data`
|
|
|
341
343
|
mounted at `/root`. This cleanly separates two planes:
|
|
342
344
|
|
|
343
345
|
- **Software → image.** OpenClaw, `gh`, `vercel`, `supabase`, `trello`, Node — all
|
|
344
|
-
live in `openclaw-agent-base
|
|
345
|
-
|
|
346
|
+
live in the agent image `openclaw-agent-base:<version>` (outside `/root`). A recreate
|
|
347
|
+
keeps the agent's OpenClaw version; moving an agent to a newer version is a separate
|
|
348
|
+
update.
|
|
346
349
|
- **Data → volume.** Config (`openclaw.json`), state DB (`openclaw.sqlite`),
|
|
347
350
|
workspace files, and CLI credentials (`.config/gh`, `.local/share/com.vercel.cli`,
|
|
348
351
|
`.supabase`, …) live in `/root` and survive container removal.
|
|
@@ -354,9 +357,12 @@ a re-created agent keeps the workspace the user edited.
|
|
|
354
357
|
|
|
355
358
|
| Action | What it does |
|
|
356
359
|
|---|---|
|
|
357
|
-
| **Backup** | `tar` of `/root` (excl. npm cache) → `.tar.gz` in the `rev4a-backups` volume |
|
|
360
|
+
| **Backup** | `tar` of `/root` (excl. npm cache) → `.tar.gz` in the `rev4a-backups` volume, while the agent runs |
|
|
361
|
+
| **Cold backup** | stop → archive `/root` (excl. npm cache) as `.partial` in a helper container → read the archive back → rename → start again if it was running. Other operations on the agent answer 409 meanwhile. Survives a Rev4a restart. API only for now (`/api/agents/[id]/cold-backup`) |
|
|
362
|
+
| **Update** | the agent panel's OPENCLAW VERSION section, when a newer supported version is downloaded: count transcript events and cron jobs → cold pre-update backup → recreate on the new version (its data migrates one way) → wait for that version → check nothing went missing. Unused agent images are removed afterwards |
|
|
363
|
+
| **Rollback** | after an update: stop → restore the pre-update backup → recreate on the previous version. Everything since the backup is lost |
|
|
358
364
|
| **Restore** | stop → replace `/root` with the backup (real wipe incl. dotfiles) → restart. Software/image untouched — only data goes back in time |
|
|
359
|
-
| **Recreate** | auto-backup first (aborts if it fails) → rebuild
|
|
365
|
+
| **Recreate** | auto-backup first (aborts if it fails) → rebuild on the agent's own OpenClaw version (`openclaw-agent-base:<version>`, tagged or pulled when missing) keeping the volume. Never changes version |
|
|
360
366
|
| **Restart** | `docker restart`, nothing else |
|
|
361
367
|
| **Delete** | **destructive**: removes container **+ volume + all backups**. Cannot be undone |
|
|
362
368
|
|
|
@@ -368,19 +374,50 @@ agent means rebuilding the image + Recreate, never `update` inside the container
|
|
|
368
374
|
|
|
369
375
|
## Agent Templates
|
|
370
376
|
|
|
371
|
-
### Base Image (`openclaw-agent-base
|
|
377
|
+
### Base Image (`openclaw-agent-base:<version>`)
|
|
372
378
|
|
|
373
379
|
**Dockerfile:** `agent-templates/base-image/Dockerfile`
|
|
374
380
|
|
|
381
|
+
**Versions.** Images are kept locally per OpenClaw version, `openclaw-agent-base:<version>`;
|
|
382
|
+
`:latest` is not used. The versions this release supports, newest first, and the model
|
|
383
|
+
`input` list for each, are in `lib/agent-versions.json`, read by the server and the
|
|
384
|
+
`rev4a` CLI. The Agents page banner offers the newest supported version the registry
|
|
385
|
+
publishes (`ghcr.io/flame0510/rev4a/openclaw-agent-base`, or `REV4A_AGENT_IMAGE_REGISTRY`);
|
|
386
|
+
downloading touches no agent. Create uses the newest supported version downloaded; recreate
|
|
387
|
+
keeps the agent's version. An agent card shows **UPDATE AVAILABLE** when a newer supported
|
|
388
|
+
version is downloaded.
|
|
389
|
+
|
|
375
390
|
Built from `node:24-bookworm-slim`, includes:
|
|
376
|
-
- OpenClaw
|
|
391
|
+
- OpenClaw, pinned by the `OPENCLAW_VERSION` build argument and recorded as the
|
|
392
|
+
`org.opencontainers.image.version` label
|
|
393
|
+
- `gh`, `vercel`, `trello`, `supabase`
|
|
394
|
+
- Plugins under `/opt/openclaw-plugins`, outside the agent volume and pinned to the same
|
|
395
|
+
OpenClaw release: DuckDuckGo web search
|
|
377
396
|
- `openssl` (for local token generation)
|
|
378
397
|
- Custom entrypoint `/agent-entrypoint.sh`
|
|
379
398
|
|
|
399
|
+
See also [agent-templates/README.md](../agent-templates/README.md).
|
|
400
|
+
|
|
380
401
|
**Entrypoint behavior:**
|
|
381
402
|
- Resolves the auth token (file > env > local random)
|
|
382
403
|
- Does NOT generate `openclaw.json` — OpenClaw creates its own default config on first boot
|
|
383
|
-
- **Version-gated migration:** if the OpenClaw version differs from the one recorded in
|
|
404
|
+
- **Version-gated migration:** if the OpenClaw version differs from the one recorded in
|
|
405
|
+
`/root/.openclaw/.last-version` (a newer image, or a new volume), runs
|
|
406
|
+
`openclaw doctor --fix --non-interactive` **before** starting the gateway. `--fix` is
|
|
407
|
+
required from OpenClaw 9.x: the agent database schema is migrated only with the gateway
|
|
408
|
+
stopped, and the gateway refuses the old schema rather than migrating it. The version is
|
|
409
|
+
recorded only after a successful run, so a failed migration is retried on the next boot.
|
|
410
|
+
A failure does not block boot.
|
|
411
|
+
- **Image defaults**, before the gateway starts, written only where missing:
|
|
412
|
+
- the image's plugin directories in `plugins.load.paths`
|
|
413
|
+
- `tools.web.search.provider: "duckduckgo"` when no provider is set
|
|
414
|
+
- `agents.defaults.heartbeat.every: "0m"` when unset: every heartbeat tick is a
|
|
415
|
+
model-calling turn, so the recurring cadence is opt-in
|
|
416
|
+
|
|
417
|
+
An explicit value, such as a heartbeat turned on for one agent, is never overwritten.
|
|
418
|
+
The config is read with one node process and written through `openclaw config patch`,
|
|
419
|
+
which validates; if `openclaw.json` cannot be read as JSON, the defaults are skipped and
|
|
420
|
+
the log says so.
|
|
384
421
|
- Starts OpenClaw gateway with `--allow-unconfigured`: `openclaw gateway --bind lan --port 3000 --auth token --token "<token>" --allow-unconfigured`
|
|
385
422
|
- Everything else (controlUi, model refs, providers) is handled by the create route and the Gateway sync module
|
|
386
423
|
|
|
@@ -437,7 +474,7 @@ Only models whose provider has a key in `data/provider-keys.json` are synced.
|
|
|
437
474
|
|
|
438
475
|
| Property | Value |
|
|
439
476
|
|---|---|
|
|
440
|
-
| Image | `openclaw-agent-base
|
|
477
|
+
| Image | `openclaw-agent-base:<version>` (Node 24-bookworm-slim + OpenClaw) |
|
|
441
478
|
| Port | `0.0.0.0:3731 → 3000` |
|
|
442
479
|
| IP | `172.19.0.3` |
|
|
443
480
|
| AGENT_ID | `atlas` |
|