@flame0510/project-aether 1.6.2 → 1.8.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/LICENSE +21 -0
- package/README.md +6 -2
- package/agent-templates/atlas/HEARTBEAT.md +1 -1
- package/app/agents/ImageDownloadBanner.tsx +171 -37
- package/app/api/agents/download-image/route.ts +29 -5
- package/app/api/agents/image-status/route.ts +17 -2
- package/app/api/assistant/route.ts +21 -5
- package/app/api/auth/login/route.ts +2 -2
- package/app/api/metrics/route.ts +126 -23
- package/app/api/setup/agent-image/route.ts +6 -4
- package/app/api/system-health/route.ts +25 -21
- package/app/components/DashboardToolbar.tsx +2 -2
- package/app/components/LineageGraphPage.tsx +4 -4
- package/app/components/SessionDrawer.tsx +8 -8
- package/app/components/Sidebar.tsx +10 -0
- package/app/components/Skeleton.tsx +4 -1
- package/app/components/SystemCockpit.tsx +83 -3
- package/app/components/ui/Meter.tsx +34 -0
- package/app/components/ui/TimeSeriesChart.tsx +226 -0
- package/app/components/ui/index.ts +2 -0
- package/app/globals.css +42 -1
- package/app/setup/PageClient.tsx +1 -1
- package/app/system/PageClient.tsx +263 -0
- package/app/system/SystemSkeleton.tsx +115 -0
- package/app/system/loading.tsx +13 -0
- package/app/system/page.tsx +5 -0
- package/bin/postinstall.js +5 -1
- package/daemon.js +274 -214
- package/docs/ARCHITECTURE.md +65 -34
- package/docs/CONTAINER-TERMINAL.md +17 -8
- package/docs/DESIGN-SYSTEM.md +22 -12
- package/docs/FRONTEND-ARCHITECTURE.md +8 -4
- package/docs/REV4A.md +37 -86
- package/docs/dev/API-REFERENCE.md +102 -19
- package/docs/dev/DATABASE.md +79 -28
- package/docs/dev/SESSION-MAINTENANCE-PLAN.md +6 -6
- package/docs/rag/DATA-FRESHNESS.md +31 -15
- package/docs/rag/GLOSSARY.md +8 -5
- package/docs/rag/REV4A-OVERVIEW.md +14 -7
- package/docs/rag/WHAT-I-CAN-ANSWER.md +4 -3
- package/lib/agent-images.ts +43 -14
- package/lib/buildAgentImage.ts +142 -6
- package/lib/db-bootstrap.mjs +0 -11
- package/lib/metrics-db.ts +48 -0
- package/lib/patterns/sessionPresentation.ts +4 -2
- package/lib/rev4a-auth.d.ts +1 -0
- package/lib/rev4a-auth.js +18 -2
- package/next.config.mjs +9 -1
- package/package.json +2 -2
- package/scripts/backup.sh +48 -54
- package/scripts/check-language.mjs +21 -3
- package/scripts/restore.sh +77 -59
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-26
|
|
5
5
|
> **Goal:** Transform Rev4a from a monitoring dashboard into a central orchestrator for a distributed multi-container agency.
|
|
6
6
|
|
|
7
7
|
---
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
|
|
36
36
|
```
|
|
37
37
|
┌────────────────────────────────────────────────────────────────┐
|
|
38
|
-
│ DOCKER HOST
|
|
38
|
+
│ DOCKER HOST │
|
|
39
39
|
│ │
|
|
40
40
|
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────┐ │
|
|
41
41
|
│ │ rev4a-control │ │ agent-argus │ │ agent-atlas │ │
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
|
|
73
73
|
**Credentials vault:** The `/credentials` page stores third-party service tokens in `credentials.db` and installs them into containers on explicit sync. Provider API keys live separately in `provider-keys.json`, managed from the Gateway UI.
|
|
74
74
|
|
|
75
|
-
**Container management UI:** The `/containers` page lists all
|
|
75
|
+
**Container management UI:** The `/containers` page lists all Docker containers, running or stopped, with status, image, IP and ports, and opens a web terminal into any of them. Fully functional. (Machine CPU, RAM and disk are on `/system`.)
|
|
76
76
|
|
|
77
77
|
**Workspace API:** Lazy-loaded file tree explorer with real-time reads (no caching), supports both host and container workspaces via `docker exec`.
|
|
78
78
|
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
| Feature | Status | Notes |
|
|
82
82
|
|---|---|---|
|
|
83
83
|
| Credentials (UI + API) | 🟢 Implemented | SQLite, no encryption yet |
|
|
84
|
-
| Container page | 🟢 Functional | Lists all containers,
|
|
84
|
+
| Container page | 🟢 Functional | Lists all containers, web terminal |
|
|
85
85
|
| Agent creation wizard | 🟢 Implemented | One-click create with model/template selection |
|
|
86
86
|
| Provider proxy | 🟢 Implemented | Agents route through Rev4a provider gateway |
|
|
87
87
|
| Shared volumes | 🟢 Implemented | Skills + repos mounted on all agents |
|
|
@@ -132,6 +132,18 @@ The central container, running the Next.js dashboard + orchestration API.
|
|
|
132
132
|
a key), `isModelOffered()` (enforced by the proxy and the assistant) and
|
|
133
133
|
`catalogueStatus()`. A failed read of `models.config.json` falls back to the last
|
|
134
134
|
good copy and never deletes overrides.
|
|
135
|
+
- **The model data has three files and three scripts.** `models.config.json` is curated by
|
|
136
|
+
hand (identity, `enabled`, `deprecated`, and an optional `info` block for what no API
|
|
137
|
+
publishes: the vendor's size claim, benchmarks, the docs link, notes).
|
|
138
|
+
`model-pricing.json` and `model-details.json` are **generated** by
|
|
139
|
+
`npm run refresh:pricing` (`scripts/refresh-model-pricing.mjs`): prices from OpenRouter,
|
|
140
|
+
and per model the description, architecture and benchmarks from OpenRouter plus the size,
|
|
141
|
+
weight mix, licence and dates from the Hugging Face card. Two scripts back the curation
|
|
142
|
+
of `info`: `npm run info:suggest` (`scripts/model-info-suggest.mjs`) prints the candidate
|
|
143
|
+
facts from a card, and `npm run info:verify` (`scripts/model-info-verify.mjs`) refuses a
|
|
144
|
+
proposal the card does not support — evidence per field, every benchmark value under the
|
|
145
|
+
column that names this model. `GET /api/models/details` merges the three files for the
|
|
146
|
+
details modal (`docs/dev/GATEWAY.md`).
|
|
135
147
|
- **Agent images and OpenClaw versions.** `lib/agent-versions.json` lists the supported
|
|
136
148
|
OpenClaw versions, newest first, with the model `input` list for each; the server reads
|
|
137
149
|
it through `lib/agent-versions.ts`, the `rev4a` CLI with `require`. The provider sync
|
|
@@ -560,19 +572,19 @@ The Rev4a daemon (`daemon.js`) is a standalone Node.js process that bridges the
|
|
|
560
572
|
|
|
561
573
|
### Responsibilities
|
|
562
574
|
|
|
563
|
-
1. Poll `openclaw sessions --json --all-agents`
|
|
575
|
+
1. Poll `openclaw sessions --json --all-agents` every 30 s
|
|
564
576
|
2. Upsert session rows into `events.db`
|
|
565
|
-
3. Emit `spawn` / `complete` / `
|
|
566
|
-
4.
|
|
567
|
-
|
|
568
|
-
|
|
577
|
+
3. Emit `spawn` / `complete` / `fail` / `spawn_timeout` events into the `events` table
|
|
578
|
+
4. Sample the host machine (CPU, RAM, swap, storage) every 30 s into `metrics.db`, on a
|
|
579
|
+
timer of its own
|
|
580
|
+
5. Detect anomalies (CPU > 85%, RAM > 90%, a disk > 90%) and record them as events
|
|
581
|
+
6. Manage DB lifecycle (WAL mode; events.db checkpointed after each poll cycle)
|
|
569
582
|
|
|
570
|
-
### Poll
|
|
583
|
+
### Poll Interval
|
|
571
584
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
| ≥ 1 session with status `working` | 15 s |
|
|
585
|
+
A fixed 30 s timer, whether or not sessions are working. When the OpenClaw CLI is not on
|
|
586
|
+
the host, the session poll logs it once and stays idle; the machine metrics keep being
|
|
587
|
+
sampled, since they have their own timer.
|
|
576
588
|
|
|
577
589
|
### Cost Estimation
|
|
578
590
|
|
|
@@ -614,28 +626,45 @@ The daemon uses `INSERT … ON CONFLICT DO UPDATE` with these rules:
|
|
|
614
626
|
|
|
615
627
|
### System Metrics
|
|
616
628
|
|
|
617
|
-
Every
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
- **
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
629
|
+
Every 30 s (`METRICS_INTERVAL_MS`) the daemon samples the machine it runs on and writes a
|
|
630
|
+
`system_metrics` row plus one `system_disks` row per filesystem into `metrics.db` — its
|
|
631
|
+
own database next to `events.db` (schema in `docs/dev/DATABASE.md`). Machine-wide sources:
|
|
632
|
+
|
|
633
|
+
- **CPU**: busy share of all cores since the previous sample, from host ticks
|
|
634
|
+
(`os.cpus()`, i.e. `/proc/stat` on Linux) — the whole machine, not Rev4a's cgroup
|
|
635
|
+
- **RAM / swap**: `/proc/meminfo` on Linux, used = `MemTotal − MemAvailable` (what `free`
|
|
636
|
+
shows); on macOS (development) active + wired + compressed pages from `vm_stat` and
|
|
637
|
+
`vm.swapusage`; elsewhere `os.totalmem()` / `os.freemem()` and no swap
|
|
638
|
+
- **Storage**: `fs.statfsSync()` on `/`, Docker's data root (`docker info`, asked once and
|
|
639
|
+
retried every 10 min while Docker is down) and Rev4a's data directory, each device once;
|
|
640
|
+
a path that cannot be read (Docker Desktop's root inside its VM) is skipped
|
|
641
|
+
- **Load**: `os.loadavg()` 1 / 5 / 15 min
|
|
642
|
+
|
|
643
|
+
The first sample comes about 5 s after start (the CPU figure needs a delta from the baseline
|
|
644
|
+
taken at load), then one every 30 s. Samples older than 30 days are pruned at every sample.
|
|
645
|
+
`metrics.db` is opened in a try: if it cannot be (corrupt, unwritable), sampling is off —
|
|
646
|
+
`[METRICS] disabled` in the log — and the session poll keeps running. The API side reads
|
|
647
|
+
it through `lib/metrics-db.ts` (read-only, `null` when there is nothing to read);
|
|
648
|
+
`GET /api/metrics` serves the System page (`/system`) and the dashboard's Machine card.
|
|
625
649
|
|
|
626
650
|
### Anomaly Detection
|
|
627
651
|
|
|
628
|
-
|
|
652
|
+
After each sample the daemon inserts a `system_anomaly` event into `events.db`
|
|
653
|
+
(`{ metric, values, threshold, message }`) and logs `[ANOMALY] <message>` when:
|
|
629
654
|
|
|
630
|
-
| Metric |
|
|
631
|
-
|
|
632
|
-
| CPU | >
|
|
633
|
-
| RAM | > 90% |
|
|
655
|
+
| Metric | Condition | Repeats |
|
|
656
|
+
|---|---|---|
|
|
657
|
+
| CPU | > 85% on two consecutive samples | at most once per 5 min |
|
|
658
|
+
| RAM | > 90% on two consecutive samples | at most once per 5 min |
|
|
659
|
+
| Disk (each filesystem) | ≥ 90% | once when it reaches it, no cooldown — again only after it drops back under, or after the daemon restarts |
|
|
660
|
+
|
|
661
|
+
No feature consumes `system_anomaly` events yet; they only pass through the generic
|
|
662
|
+
event feeds.
|
|
634
663
|
|
|
635
664
|
### DB Safety
|
|
636
665
|
|
|
637
666
|
- WAL mode + `PRAGMA synchronous = NORMAL` for concurrent read safety
|
|
638
|
-
- `PRAGMA wal_checkpoint(PASSIVE)` runs after each poll cycle
|
|
667
|
+
- `PRAGMA wal_checkpoint(PASSIVE)` runs after each poll cycle, `FULL` every 10 cycles
|
|
639
668
|
- On `uncaughtException` the daemon logs but does NOT exit — relies on systemd Restart=always for recovery
|
|
640
669
|
|
|
641
670
|
### Migrations
|
|
@@ -722,17 +751,19 @@ ALTER TABLE sessions ADD COLUMN ended_at INTEGER;
|
|
|
722
751
|
|
|
723
752
|
Rev4a provides a real, interactive terminal for any agent container via
|
|
724
753
|
a WebSocket-connected PTY. The implementation is documented in detail in
|
|
725
|
-
[docs/
|
|
754
|
+
[docs/CONTAINER-TERMINAL.md](CONTAINER-TERMINAL.md).
|
|
726
755
|
|
|
727
756
|
### Two-process architecture
|
|
728
757
|
|
|
729
758
|
| Process | Port | Role |
|
|
730
759
|
|---|---|---|
|
|
731
|
-
| `
|
|
732
|
-
| `
|
|
760
|
+
| Next.js (`next start`) | 3740 | Serves the terminal page, auth, API |
|
|
761
|
+
| `terminal-ws-server.js` | 3741 (127.0.0.1) | WebSocket PTY server via `node-pty` |
|
|
733
762
|
|
|
734
|
-
|
|
735
|
-
contention with Next.js during high-throughput I/O.
|
|
763
|
+
Both are started by `rev4a serve`; the terminal server is a process of its own to avoid
|
|
764
|
+
event-loop contention with Next.js during high-throughput I/O. It has no authentication
|
|
765
|
+
of its own and is reachable only through a reverse proxy route (see
|
|
766
|
+
[CONTAINER-TERMINAL.md](CONTAINER-TERMINAL.md#routing)).
|
|
736
767
|
|
|
737
768
|
### Why custom DOM over xterm.js
|
|
738
769
|
|
|
@@ -742,7 +773,7 @@ CSS conflicts. The current implementation uses a plain `<div>` with native
|
|
|
742
773
|
browser scrolling and a hidden `<textarea>` for input — stable under any
|
|
743
774
|
output volume.
|
|
744
775
|
|
|
745
|
-
See [
|
|
776
|
+
See [CONTAINER-TERMINAL.md](CONTAINER-TERMINAL.md#terminal-client-browser)
|
|
746
777
|
for the full rationale.
|
|
747
778
|
|
|
748
779
|
## 12. Technology Stack
|
|
@@ -15,7 +15,7 @@ via `node-pty` and streaming I/O over a dedicated WebSocket server.
|
|
|
15
15
|
Browser (xterm-compatible DOM terminal)
|
|
16
16
|
│ WebSocket wss://rev4a/containers/terminal/{id}
|
|
17
17
|
▼
|
|
18
|
-
terminal-ws-server.js (
|
|
18
|
+
terminal-ws-server.js (127.0.0.1:3741, reached through a reverse proxy — see Routing)
|
|
19
19
|
│ node-pty
|
|
20
20
|
▼
|
|
21
21
|
docker exec -it {containerId} env TERM=xterm-256color bash
|
|
@@ -30,20 +30,30 @@ Container Shell (bash, interactive, full PTY)
|
|
|
30
30
|
|
|
31
31
|
| Process | Port | Role |
|
|
32
32
|
|------------------|-------|------|
|
|
33
|
-
| `
|
|
34
|
-
| `
|
|
33
|
+
| Next.js (`next start`) | 3740 | Next.js app (pages, API routes, auth) |
|
|
34
|
+
| `terminal-ws-server.js` | 3741 | Standalone WebSocket server for terminal sessions, bound to 127.0.0.1 |
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
Both are child processes of `rev4a serve` (with `daemon.js`), under the single
|
|
37
|
+
`rev4a.service` unit in production. The terminal server is its own process, isolated from
|
|
37
38
|
the Next.js app to avoid event-loop contention during high-throughput I/O (fast
|
|
38
39
|
`seq`, `cat` on large files, interactive shell sessions).
|
|
39
40
|
|
|
40
|
-
###
|
|
41
|
+
### Routing
|
|
41
42
|
|
|
42
43
|
```
|
|
43
44
|
/containers/terminal/{id} → Next.js (page serving TerminalClient)
|
|
44
|
-
/api/terminal-ws?id={containerId} →
|
|
45
|
+
/api/terminal-ws?id={containerId} → reverse proxy → ws://127.0.0.1:3741
|
|
45
46
|
```
|
|
46
47
|
|
|
48
|
+
The browser opens the socket on the dashboard's own host (`/api/terminal-ws`). Next.js
|
|
49
|
+
has no route for that path, so the terminal works only when a reverse proxy in front of
|
|
50
|
+
Rev4a forwards it to port 3741; without one the socket gets a 404.
|
|
51
|
+
|
|
52
|
+
**No authentication of its own.** `terminal-ws-server.js` checks neither the session
|
|
53
|
+
cookie nor the token: whoever reaches it gets a shell in the container named by `id`.
|
|
54
|
+
It listens on 127.0.0.1 only, so the exposure is exactly what the proxy route above
|
|
55
|
+
opens. An open issue.
|
|
56
|
+
|
|
47
57
|
### Data flow
|
|
48
58
|
|
|
49
59
|
1. User opens `/containers/terminal/openclaw-atlas`
|
|
@@ -257,8 +267,7 @@ app/containers/terminal/
|
|
|
257
267
|
├── [id]/
|
|
258
268
|
│ ├── page.tsx # Next.js page (auth-protected)
|
|
259
269
|
│ └── TerminalClient.tsx # Client-side terminal component
|
|
260
|
-
terminal-ws-server.js # WebSocket PTY server
|
|
261
|
-
rev4a.service # Systemd target for all three services
|
|
270
|
+
terminal-ws-server.js # WebSocket PTY server, started by `rev4a serve`
|
|
262
271
|
```
|
|
263
272
|
|
|
264
273
|
## Known Limitations
|
package/docs/DESIGN-SYSTEM.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Source of truth: `app/design-system.ts` + CSS tokens in `app/globals.css`.
|
|
4
4
|
|
|
5
|
-
> **Last updated:** 2026-
|
|
5
|
+
> **Last updated:** 2026-09-26
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -44,19 +44,22 @@ Rev4a uses a **square/sharp** visual language:
|
|
|
44
44
|
Defined in `app/globals.css` as CSS custom properties:
|
|
45
45
|
|
|
46
46
|
```css
|
|
47
|
-
--bg:
|
|
48
|
-
--bg2:
|
|
49
|
-
--border:
|
|
50
|
-
--text:
|
|
51
|
-
--text-dim: #888
|
|
52
|
-
--violet:
|
|
53
|
-
--violet-bg:
|
|
54
|
-
--green:
|
|
55
|
-
--red:
|
|
56
|
-
--yellow:
|
|
57
|
-
--blue:
|
|
47
|
+
--bg: var(--color-bg-950) /* #0A0A0B Page background */
|
|
48
|
+
--bg2: var(--color-bg-900) /* #111114 Slightly lighter surface */
|
|
49
|
+
--border: var(--color-border-700) /* #222228 Default border */
|
|
50
|
+
--text: var(--color-text-100) /* #E8E8E8 Primary text */
|
|
51
|
+
--text-dim: var(--color-text-400) /* #888 Muted/secondary text */
|
|
52
|
+
--violet: var(--color-accent-500) /* #925BFC Brand accent */
|
|
53
|
+
--violet-bg: rgba(146, 91, 252, 0.08) /* Violet-tinted hover/active background */
|
|
54
|
+
--green: var(--color-success-500) /* #22c55e Success */
|
|
55
|
+
--red: var(--color-danger-500) /* #ef4444 Error */
|
|
56
|
+
--yellow: var(--color-warning-500) /* #f59e0b Warning */
|
|
57
|
+
--blue: var(--color-blue-400) /* #60a5fa Info / links */
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
+
The semantic tokens above point at the palette tokens (`--color-*`) defined at the top of
|
|
61
|
+
`app/globals.css`; the hex values are the palette's, quoted for reference.
|
|
62
|
+
|
|
60
63
|
### Typography
|
|
61
64
|
|
|
62
65
|
- Monospace by default: `var(--font-mono-stack)`
|
|
@@ -66,6 +69,12 @@ Defined in `app/globals.css` as CSS custom properties:
|
|
|
66
69
|
|
|
67
70
|
## Component design rules
|
|
68
71
|
|
|
72
|
+
A shared component grows by **variant, never by copy**. `Metric` is the example: `size="sm"`
|
|
73
|
+
is the compact form for a grid inside a dialog (smaller value, tighter spacing), and it lives
|
|
74
|
+
on the primitive so the modal and the dashboard cannot drift apart. When a new page needs a
|
|
75
|
+
pattern that does not exist yet, add it here and to `docs/FRONTEND-ARCHITECTURE.md`'s catalog,
|
|
76
|
+
then use it — a one-off copy of markup is the thing this section exists to prevent.
|
|
77
|
+
|
|
69
78
|
1. **Border radius: 0 everywhere.** `border-radius: 0` or sharp corners. Exceptions: `Pill`, avatar circles, status dots.
|
|
70
79
|
2. **No shadows.** Flat design. Use `border: 1px solid var(--border)` to define surfaces.
|
|
71
80
|
3. **Violet is the only accent.** `var(--violet)` for active states, primary actions, headings. `var(--violet-bg)` for hover/active backgrounds.
|
|
@@ -73,6 +82,7 @@ Defined in `app/globals.css` as CSS custom properties:
|
|
|
73
82
|
5. **CSS vars over hardcoded colors.** Never inline `#22c55e`, `#ef4444`, `#888` — use the CSS token.
|
|
74
83
|
6. **Loading feedback.** Use `loading` prop on `Button` (shows inline spinner). Do not render separate loader elements.
|
|
75
84
|
7. **Minimal animation.** Transitions on hover/active (0.1s–0.12s), spin on loading spinner. No bounce, shake, or decorative animations.
|
|
85
|
+
8. **Data display: violet for the data, status colours for levels.** A chart series is `var(--violet)` (2px line, 10% wash), grid and axis text are `var(--border)` / `var(--text-dim)`, never the series colour. A `Meter` fill is violet when fine and `var(--yellow)` / `var(--red)` at its warning / critical level, with the level also written out — status never rests on colour alone. Focus on a chart is a 1px `outline` in `var(--violet-dim)`, not a shadow. See `TimeSeriesChart` and `Meter` in `docs/FRONTEND-ARCHITECTURE.md`.
|
|
76
86
|
|
|
77
87
|
### React hook
|
|
78
88
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Rev4a Frontend Architecture
|
|
2
2
|
|
|
3
|
-
> **Last updated:** 2026-09-
|
|
3
|
+
> **Last updated:** 2026-09-26
|
|
4
4
|
|
|
5
5
|
## Layering
|
|
6
6
|
|
|
@@ -38,6 +38,8 @@ All shared UI primitives live in `app/components/ui/` and are exported from `app
|
|
|
38
38
|
| `ModalityIcons` | `ModalityIcons.tsx` | Capability badges parsed from a model's `modality` string (`"text+image->text"`). Input types render as icons; a non-text **output** is called out separately, since reading an image and generating one are different capabilities. Pass `dynamic` for router models, which advertise the union of everything they might route to and so show "varies" instead. |
|
|
39
39
|
| `Metric` | `Metric.tsx` | Metric card with title, value, subtitle, tone, and a `size` (`md` default, `sm` for a grid inside a dialog: smaller value, tighter spacing). |
|
|
40
40
|
| `StatusCard` | `StatusCard.tsx` | Health/status report card. |
|
|
41
|
+
| `Meter` / `meterLevel` | `Meter.tsx` | Horizontal 0-100 % fill whose colour carries the level (`ok` accent, `warning` yellow, `critical` red); the level is also written out beside it, never colour alone. `meterLevel(percent, warning, critical)` picks the level. ARIA `role="meter"`. |
|
|
42
|
+
| `TimeSeriesChart` | `TimeSeriesChart.tsx` | One series over time in plain SVG measured to its container (text never scaled): a 2px accent line for the value, a 10 % wash up to the bucket's peak, a hairline grid at 0 / 50 / 100 %, the latest value labelled at the line's end. Points further apart than 1.5 buckets are not joined (a gap means no samples). Hover or keyboard focus (← → Esc) shows a crosshair snapped to the nearest point with value and peak; a **Table** disclosure holds the same data. `stale` dims the previous render while a new range loads. Used by the System page. |
|
|
41
43
|
| `Surface` | `Surface.tsx` | Shared panel/card surface, variant prop. |
|
|
42
44
|
| `Page` / `PageHeader` | `Page.tsx` | Full-page layout shell. |
|
|
43
45
|
| `Icons` | `Icons.tsx` | SVG icons (`EyeIcon`, `EyeOffIcon`), 16/20px shared. |
|
|
@@ -45,7 +47,9 @@ All shared UI primitives live in `app/components/ui/` and are exported from `app
|
|
|
45
47
|
| `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
48
|
| `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
49
|
| `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
|
-
|
|
|
50
|
+
| System page | `app/system/PageClient.tsx` | `/system`: CPU, memory and storage of the host, from `GET /api/metrics`. Three cards (CPU with cores and load; memory with available and swap; one `Meter` per filesystem with its roles) — thresholds 85/95 % for CPU and memory, 80/90 % for storage — then a range switch (`Tabs`: 1h · 24h · 7d · 30d) scoping the charts below it: CPU and memory average with peak, one chart per filesystem. Polls every 30 s with an `AbortController` ref (a range change aborts the previous fetch). Times in the configured Rev4a timezone (`useRev4aTimezone`). Before the first answer the cards and charts are skeletons shaped like them (`app/system/SystemSkeleton.tsx`, also the route's `loading.tsx`), so nothing jumps; the hostname line is cut with an ellipsis and never widens the page. Says *No samples yet* before the daemon's first sample and *Not collecting* when the latest sample is older than three intervals. Two columns of charts from lg (992px) up, one below. |
|
|
51
|
+
| Machine card | `app/components/SystemCockpit.tsx` | On the dashboard, next to Active sessions: CPU, RAM and the fullest disk now, each figure coloured only when past its threshold (same thresholds as the System page), the issues named in words, and a link to `/system`. It says *No samples yet* before the first sample, *Not collecting* when the newest is stale, and *Metrics unavailable* when a refresh fails (keeping the last figures). A `Surface`, not a `Metric`: a danger `Metric` paints its whole value red. |
|
|
52
|
+
| `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 while a download runs shows the percent of layers finished and the latest line of docker's output (the bar is indeterminate until a percent can be computed). **Cancel** (`DELETE /api/agents/download-image`, own loading state) appears once `image-status` reports the download running; before that the button reads *Starting…* and is disabled. Every outcome — ready and cancelled for 3 s, a failure until the next action — comes from the server's `lastResult`, so one that ended while the page was closed or reloading is still reported if it is less than 30 s old (aged with `serverTime`). Each outcome is announced once per page load, although the Agents page mounts the banner in three places (mobile list, mobile detail, desktop). Downloading changes no agent. |
|
|
49
53
|
| `VersionBanner` | `app/components/VersionBanner.tsx` | "Update available" banner for Rev4a itself, when `GET /api/update-check?check=1` reports a newer published version (dismissable per version, remembered in `localStorage`). **Update now** starts `POST /api/update-check`; because that update restarts the server, the banner cannot be told the outcome by the response: it records what it asked for in `sessionStorage`, polls `/api/update-check` until the installed version moves (two minutes at most) and reloads, then on the next mount either confirms "Updated to vX" or reports that the update did not complete and points at `update.log` (the update's own output). It never reloads blindly onto the same version. |
|
|
50
54
|
| `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. |
|
|
51
55
|
| `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`). When that store cannot be read the panel shows the reason instead of "No approved senders" (`PairingState.error`), so an empty list is never a guess. Polls pairings every 5 s while open; one keyed busy state per action. |
|
|
@@ -58,7 +62,7 @@ All shared UI primitives live in `app/components/ui/` and are exported from `app
|
|
|
58
62
|
| Wizard icons | `app/wizard/icons.tsx` | Shared SVG icons (flyweight pattern): `ArrowRightIcon`, `CheckIcon`, `CheckCircleIcon`, `DockerIcon`, `GatewayIcon`, `AgentIcon`, `TemplateIcon`, `LinkIcon`, `ConfigIcon`, `InformationIcon`. |
|
|
59
63
|
| `tokens` | `tokens.ts` | TypeScript types for `Tone` and related token values. |
|
|
60
64
|
| `Badge` | `Badge.tsx` | Inline status tag with tone variants (success, danger, warning, neutral). Used for channel chips, pairing labels, error/success messages. |
|
|
61
|
-
| `Skeleton` + shape helpers | `app/components/Skeleton.tsx` | Shimmer placeholders. `Skeleton` is the primitive; the rest mirror one specific layout each: `TreeSkeleton`, `CodeSkeleton`, `CronJobsSkeleton`, `CronRunsSkeleton`, `CredentialCardsSkeleton`, `AgentSyncRowsSkeleton`, `SkeletonLines`, `SkeletonMetric`, `CardRowSkeleton`. A shape helper must match the real markup it stands in for — same row structure, same paddings, same element count where the count is known — so nothing reflows when data replaces it. |
|
|
65
|
+
| `Skeleton` + shape helpers | `app/components/Skeleton.tsx` | Shimmer placeholders. `Skeleton` is the primitive; the rest mirror one specific layout each: `TreeSkeleton`, `CodeSkeleton`, `CronJobsSkeleton`, `CronRunsSkeleton`, `CredentialCardsSkeleton`, `AgentSyncRowsSkeleton`, `SkeletonLines`, `SkeletonMetric`, `CardRowSkeleton`. A shape helper must match the real markup it stands in for — same row structure, same paddings, same element count where the count is known — so nothing reflows when data replaces it. Inside text (a `<p>`, a `<span>`) pass `as="span"`: a `<div>` there is invalid HTML, and the parser splits the paragraph in the server-rendered page. The System page keeps its own shapes in `app/system/SystemSkeleton.tsx`, each shimmer inside the class of the text it replaces so the line boxes are the real ones — measured to 0 px of movement on desktop and mobile. |
|
|
62
66
|
|
|
63
67
|
### Rules
|
|
64
68
|
|
|
@@ -153,7 +157,7 @@ For interactive pages that can change view/file/tab quickly:
|
|
|
153
157
|
For every new/changed page:
|
|
154
158
|
|
|
155
159
|
1. No new one-off card styles unless justified.
|
|
156
|
-
2. Use `app/components/ui/*` primitives first — `Button`, `Input`, `Select`, `Modal`, `Toast`, `Surface`, `Metric`, `StatusCard`, `Pill`, `Page`, `Tabs`, `ItemList`, `FilterBar`, `PropertyList`, `TemplateOption`, `LoadingSpinner`.
|
|
160
|
+
2. Use `app/components/ui/*` primitives first — `Button`, `Input`, `Select`, `Modal`, `Toast`, `Surface`, `Metric`, `StatusCard`, `Meter`, `TimeSeriesChart`, `Pill`, `Page`, `Tabs`, `ItemList`, `FilterBar`, `PropertyList`, `TemplateOption`, `LoadingSpinner`.
|
|
157
161
|
3. Async data must render skeleton, not fake zero values.
|
|
158
162
|
4. Business logic stays in `lib`/API routes; UI consumes typed payloads.
|
|
159
163
|
5. If a pattern is introduced, name it and keep it in `lib/patterns` or `app/components/ui`.
|
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-26
|
|
4
4
|
|
|
5
5
|
A Next.js 16 dashboard for monitoring and managing the OpenClaw ecosystem.
|
|
6
6
|
|
|
@@ -9,50 +9,34 @@ A Next.js 16 dashboard for monitoring and managing the OpenClaw ecosystem.
|
|
|
9
9
|
**Process manager:** `rev4a serve` (single command). Production: systemd service (`rev4a.service`).
|
|
10
10
|
**Repository:** `github.com/Flame0510/rev4a.git` (branch: `main`)
|
|
11
11
|
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
## Overview
|
|
15
|
-
|
|
16
|
-
| Field | Value |
|
|
17
|
-
|---|---|
|
|
18
|
-
| VPS IP | `187.77.156.41` |
|
|
19
|
-
| OS | Ubuntu 24.04.4 LTS |
|
|
20
|
-
| Resources | 4 CPU, 15 GB RAM, 193 GB disk |
|
|
21
|
-
|
|
22
|
-
|
|
23
12
|
---
|
|
24
13
|
|
|
25
14
|
## Architecture
|
|
26
15
|
|
|
27
16
|
```
|
|
28
|
-
|
|
17
|
+
Browser (directly, or through a reverse proxy of your choice)
|
|
29
18
|
│
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
19
|
+
┌──────────┴──────────────────┐
|
|
20
|
+
│ rev4a serve │
|
|
21
|
+
│ (systemd: rev4a.service) │
|
|
22
|
+
│ Next.js 16 :3740 │
|
|
23
|
+
│ daemon.js │
|
|
24
|
+
│ terminal WS :3741 │
|
|
25
|
+
└──────────┬──────────────────┘
|
|
34
26
|
│
|
|
35
|
-
|
|
36
|
-
│ rev4a │
|
|
37
|
-
│ systemd (rev4a.service) │
|
|
38
|
-
│ :3740 │
|
|
39
|
-
│ Next.js 16 │
|
|
40
|
-
└─────────────────────┘
|
|
27
|
+
docker exec / Docker API
|
|
41
28
|
│
|
|
42
29
|
┌─────────────┴─────────────┐
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
│ :3700-3708 │ │ :3731 → 3000 │
|
|
49
|
-
│ AGENT_ID=core│ │ AGENT_ID=atlas │
|
|
50
|
-
└──────────────┘ └─────────────────┘
|
|
30
|
+
┌──────┴──────┐ ┌──────┴──────┐
|
|
31
|
+
│ agent │ … │ agent │
|
|
32
|
+
│ container │ │ container │
|
|
33
|
+
│ (OpenClaw) │ │ (OpenClaw) │
|
|
34
|
+
└─────────────┘ └─────────────┘
|
|
51
35
|
```
|
|
52
36
|
|
|
53
37
|
### Environment
|
|
54
38
|
|
|
55
|
-
Rev4a reads the following environment variables. Set them in `/config` (UI) or directly in `.env`. See [.env.example](../.env.example) for the canonical, always-current list.
|
|
39
|
+
Rev4a reads the following environment variables. Set them in `/config` (UI) or directly in `.env`, except the three read before `.env` is loaded — `REV4A_DATA_DIR`, `REV4A_DOCKER_WAIT_SECONDS` (both from the process environment: a shell export or a systemd `Environment=` line) and `REV4A_SKIP_POSTINSTALL_BUILD` (read by `npm install`). See [.env.example](../.env.example) for the canonical, always-current list.
|
|
56
40
|
|
|
57
41
|
| Variable | Required | Description |
|
|
58
42
|
|---|---|---|
|
|
@@ -67,8 +51,11 @@ Rev4a reads the following environment variables. Set them in `/config` (UI) or d
|
|
|
67
51
|
| `REV4A_WORKSPACE_ROOT` | No | Filesystem path for the local file explorer |
|
|
68
52
|
| `REV4A_ROOT` | No | Installation root, used by `rev4a update` to detect a git checkout |
|
|
69
53
|
| `REV4A_TIMEZONE` | No | IANA timezone used for scheduling and timestamps |
|
|
70
|
-
| `REV4A_FORCE_INSECURE_COOKIE` | No |
|
|
54
|
+
| `REV4A_FORCE_INSECURE_COOKIE` | No | `1` allows a non-Secure session cookie — plain-HTTP deployments only (any other value, `true` included, does nothing) |
|
|
71
55
|
| `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 |
|
|
56
|
+
| `REV4A_DOCKER_WAIT_SECONDS` | No | How long `rev4a serve` waits for the Docker daemon at start-up (default `90`, capped at 600) |
|
|
57
|
+
| `REV4A_DEV_ORIGINS` | No | Extra comma-separated hostnames or IPs allowed to reach `next dev`; no effect on a production build |
|
|
58
|
+
| `REV4A_SKIP_POSTINSTALL_BUILD` | No | `1` in CI: `npm ci` then skips the post-install `next build` (the workflows build as a separate step). Any non-empty value skips it, `0` included — leave it unset to build |
|
|
72
59
|
|
|
73
60
|
See [Alerts](#alerts-telegram) below for the Telegram alert variables.
|
|
74
61
|
|
|
@@ -91,10 +78,12 @@ to any specific reverse proxy.
|
|
|
91
78
|
|
|
92
79
|
When environment variables are saved via the Config page (Save & Restart):
|
|
93
80
|
|
|
94
|
-
1. Values are
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
81
|
+
1. Values are merged into the data directory's `.env` (`~/.config/rev4a/.env`; the
|
|
82
|
+
`.env` in the installation root is a link to it). An empty value removes the key.
|
|
83
|
+
2. `sudo systemctl daemon-reload` is attempted; it is harmless where there is no systemd.
|
|
84
|
+
3. **Save & Restart** runs `sudo systemctl restart rev4a.service`: the restart only works
|
|
85
|
+
when Rev4a runs as that systemd service. Started any other way, restart `rev4a serve`
|
|
86
|
+
yourself — `serve` reads `.env` at start-up, so nothing changes until then.
|
|
98
87
|
|
|
99
88
|
## Components
|
|
100
89
|
|
|
@@ -108,13 +97,15 @@ When environment variables are saved via the Config page (Save & Restart):
|
|
|
108
97
|
|
|
109
98
|
| Route | Description |
|
|
110
99
|
|---|---|
|
|
111
|
-
| `/` | System overview —
|
|
100
|
+
| `/` | System overview — health, sessions, the machine's CPU / RAM / disk (links to `/system`), cost, live feed |
|
|
112
101
|
| `/agents` | Running agents (Docker containers with `AGENT_ID`), gateway token management, agent creation wizard, channel manager (Telegram pairing) |
|
|
113
102
|
| `/containers` | All Docker containers on the host |
|
|
103
|
+
| `/system` | The host machine: CPU, memory and swap, storage per filesystem — current values and 1h / 24h / 7d / 30d history (from `metrics.db`, sampled by the daemon every 30 s) |
|
|
114
104
|
| `/wizard` | First-run setup wizard (Welcome → Providers → Agent → Ready) |
|
|
115
105
|
| `/workspace` | File explorer with tree view + editor — VPS host or container workspaces |
|
|
116
106
|
| `/lineage` | Agent lineage / orchestration tree |
|
|
117
107
|
| `/memory` | Agent memory browser |
|
|
108
|
+
| `/gateway` (model rows) | Each model has a **Details** (`⋯`) action opening a modal with what is known about it: description, price and the provider it comes from, architecture and parameters (mixture of experts, layers, hidden size, attention heads, vision encoder, precision, weights on disk), reasoning modes, capabilities, benchmarks with their sources, licence and popularity. The facts come from the model's card on Hugging Face and — for the vendor's own size claim and benchmarks — from hand-curated entries that always cite a source and a date; nothing is estimated. |
|
|
118
109
|
| `/crons` | Scheduled cron jobs — auto-discovered from the host gateway and every running agent container (Docker containers with `AGENT_ID` label), on/off toggle, run history per job |
|
|
119
110
|
| `/credentials` | Third-party tool credentials (GitHub, Trello, Vercel, Supabase, Notion) — SQLite vault, synced into agent containers as CLI config or, for the REST-only providers, as a config file the agent reads |
|
|
120
111
|
| `/setup` | First-run password setup — sets `REV4A_PASSWORD` and the JWT secret before the dashboard is reachable |
|
|
@@ -159,7 +150,7 @@ REV4A_TELEGRAM_BOT_TOKEN= # Telegram bot token
|
|
|
159
150
|
REV4A_TELEGRAM_CHAT_ID= # Telegram chat to notify
|
|
160
151
|
REV4A_ALERT_COOLDOWN_MS=600000 # Minimum time between repeat alerts for the same condition
|
|
161
152
|
REV4A_ALERT_STALE_SECONDS=120 # How long without a DB write before the system is considered stale
|
|
162
|
-
REV4A_ALERT_SMOKE=false #
|
|
153
|
+
REV4A_ALERT_SMOKE=false # true enables GET /api/alerts/smoke (sends a test alert) in production
|
|
163
154
|
```
|
|
164
155
|
|
|
165
156
|
**Behavior:**
|
|
@@ -220,14 +211,16 @@ echo 'vm.swappiness=10' > /etc/sysctl.d/99-rev4a-swap.conf
|
|
|
220
211
|
|
|
221
212
|
### Systemd environment override
|
|
222
213
|
|
|
223
|
-
File: `/etc/systemd/system/rev4a
|
|
214
|
+
File: `/etc/systemd/system/rev4a.service.d/override.conf` — a drop-in for the single
|
|
215
|
+
`rev4a.service` unit (`systemctl edit rev4a`). It is for the variables `.env` does not
|
|
216
|
+
carry: `rev4a serve` reads `~/.config/rev4a/.env` itself, and a key present there wins
|
|
217
|
+
over the same key set here — so secrets such as `REV4A_PASSWORD` or `REV4A_TOKEN`
|
|
218
|
+
belong in `.env` (or `/config`), not in the drop-in. It is also the only place for the
|
|
219
|
+
variables read before `.env` is loaded (`REV4A_DATA_DIR`, `REV4A_DOCKER_WAIT_SECONDS`).
|
|
224
220
|
|
|
225
221
|
```ini
|
|
226
222
|
[Service]
|
|
227
|
-
Environment=
|
|
228
|
-
Environment=REV4A_TOKEN=***
|
|
229
|
-
Environment=REV4A_JWT_SECRET=***
|
|
230
|
-
Environment=REV4A_DB=/path/to/rev4a/data/events.db
|
|
223
|
+
Environment=REV4A_DOCKER_WAIT_SECONDS=180
|
|
231
224
|
Environment=NODE_ENV=production
|
|
232
225
|
Environment=NEXT_TELEMETRY_DISABLED=1
|
|
233
226
|
```
|
|
@@ -484,48 +477,6 @@ Only models whose provider has a key in `data/provider-keys.json` are synced.
|
|
|
484
477
|
|
|
485
478
|
---
|
|
486
479
|
|
|
487
|
-
## Containers
|
|
488
|
-
|
|
489
|
-
### openclaw-atlas
|
|
490
|
-
|
|
491
|
-
| Property | Value |
|
|
492
|
-
|---|---|
|
|
493
|
-
| Image | `openclaw-agent-base:<version>` (Node 24-bookworm-slim + OpenClaw) |
|
|
494
|
-
| Port | `0.0.0.0:3731 → 3000` |
|
|
495
|
-
| IP | `172.19.0.3` |
|
|
496
|
-
| AGENT_ID | `atlas` |
|
|
497
|
-
| Config mount | `/docker/atlas-data/openclaw-fixed.json → /root/.openclaw/openclaw.json` |
|
|
498
|
-
| Auth mode | `token` |
|
|
499
|
-
|
|
500
|
-
**Control UI:** `http://<vps-ip>:3731` — enter the gateway token to log in.
|
|
501
|
-
Rev4a-generated direct links should use `#token=<gateway-token>` for automatic Control UI sign-in.
|
|
502
|
-
|
|
503
|
-
### openclaw-core
|
|
504
|
-
|
|
505
|
-
| Property | Value |
|
|
506
|
-
|---|---|
|
|
507
|
-
| Image | `ghcr.io/hostinger/hvps-openclaw:latest` |
|
|
508
|
-
| Ports | `:3711-3719` (mapped to container `:3700-3708`) |
|
|
509
|
-
| AGENT_ID | `core` |
|
|
510
|
-
|
|
511
|
-
### openclaw-giacomo
|
|
512
|
-
|
|
513
|
-
| Property | Value |
|
|
514
|
-
|---|---|
|
|
515
|
-
| Image | `ghcr.io/hostinger/hvps-openclaw:latest` |
|
|
516
|
-
| Port | `:3730` |
|
|
517
|
-
| AGENT_ID | `giacomo` |
|
|
518
|
-
|
|
519
|
-
### Other containers
|
|
520
|
-
|
|
521
|
-
| Name | Image | Notes |
|
|
522
|
-
|---|---|---|
|
|
523
|
-
| `hermes` | `nousresearch/hermes-agent:latest` | Hermes agent |
|
|
524
|
-
| `hermes-dashboard` | `nousresearch/hermes-agent:latest` | Hermes dashboard |
|
|
525
|
-
| `traefik-traefik-1` | `traefik:latest` | Reverse proxy |
|
|
526
|
-
|
|
527
|
-
---
|
|
528
|
-
|
|
529
480
|
## Key Gotchas
|
|
530
481
|
|
|
531
482
|
1. **`auth.mode: "none"` + `0.0.0.0:3000`** → OpenClaw refuses to bind. Must be `auth.mode: "token"`.
|