@flame0510/project-aether 1.9.1 → 1.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/app/agents/CostSection.tsx +94 -0
- package/app/agents/PageClient.tsx +14 -0
- package/app/agents/PanelRow.tsx +9 -0
- package/app/agents/ResourceSection.tsx +105 -0
- package/app/api/assistant/route.ts +10 -3
- package/app/api/containers/route.ts +18 -2
- package/app/api/costs/agent/route.ts +55 -0
- package/app/api/costs/route.ts +19 -77
- package/app/api/costs/usage/route.ts +65 -0
- package/app/api/gateway/sync.ts +18 -4
- package/app/api/metrics/alerts/route.ts +47 -0
- package/app/api/metrics/containers/route.ts +54 -0
- package/app/api/metrics/route.ts +4 -6
- package/app/api/stats/route.ts +2 -3
- package/app/api/stats-since/route.ts +1 -1
- package/app/api/stream/route.ts +3 -5
- package/app/api/system-health/route.ts +33 -32
- package/app/components/CostBreakdown.tsx +1 -1
- package/app/components/Sidebar.tsx +10 -0
- package/app/components/SystemCockpit.tsx +1 -1
- package/app/components/ui/Accordion.tsx +45 -0
- package/app/components/ui/TimeSeriesChart.tsx +47 -11
- package/app/components/ui/index.ts +1 -0
- package/app/containers/ContainersClient.tsx +48 -6
- package/app/costs/CostsSkeleton.tsx +88 -0
- package/app/costs/PageClient.tsx +366 -0
- package/app/costs/loading.tsx +13 -0
- package/app/costs/page.tsx +5 -0
- package/app/globals.css +42 -0
- package/app/system/AgentCharts.tsx +138 -0
- package/app/system/AgentsSection.tsx +281 -0
- package/app/system/PageClient.tsx +7 -7
- package/app/system/RecentAlerts.tsx +72 -0
- package/app/system/SystemSkeleton.tsx +53 -1
- package/app/system/loading.tsx +5 -1
- package/daemon.js +646 -41
- package/docs/ARCHITECTURE.md +72 -31
- package/docs/DESIGN-SYSTEM.md +2 -2
- package/docs/FRONTEND-ARCHITECTURE.md +14 -3
- package/docs/REV4A.md +6 -5
- package/docs/dev/API-REFERENCE.md +208 -38
- package/docs/dev/DATABASE.md +176 -20
- package/docs/dev/GATEWAY.md +53 -9
- package/docs/rag/DATA-FRESHNESS.md +34 -7
- package/docs/rag/GLOSSARY.md +8 -5
- package/docs/rag/REV4A-OVERVIEW.md +10 -3
- package/docs/rag/WHAT-I-CAN-ANSWER.md +6 -3
- package/instrumentation.ts +11 -0
- package/lib/agent-costs.ts +172 -0
- package/lib/container-metrics.ts +340 -0
- package/lib/cost-reconciliation.ts +78 -0
- package/lib/costs-db.ts +31 -0
- package/lib/docker-socket-path.js +133 -0
- package/lib/docker-socket.ts +10 -99
- package/lib/docker-stats.js +284 -0
- package/lib/metrics-db.ts +15 -6
- package/lib/model-pricing.ts +123 -3
- package/lib/price-schedule-sync.ts +133 -0
- package/lib/utils/format.ts +38 -0
- package/model-pricing.json +269 -121
- package/package.json +1 -1
- package/scripts/refresh-model-pricing.mjs +16 -4
- package/scripts/test-docker-stats.mjs +270 -0
- package/lib/billing.ts +0 -100
package/docs/dev/DATABASE.md
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# Rev4a — Database Reference
|
|
2
2
|
|
|
3
|
-
> **Last updated:** 2026-
|
|
3
|
+
> **Last updated:** 2026-10-01
|
|
4
4
|
|
|
5
|
-
Rev4a keeps
|
|
6
|
-
`events.db` (sessions and events, below), `metrics.db` (machine metrics, see
|
|
7
|
-
[Metrics Database](#metrics-database-metricsdb))
|
|
5
|
+
Rev4a keeps four SQLite files in WAL mode, by default all in the data directory's `data/`:
|
|
6
|
+
`events.db` (sessions and events, below), `metrics.db` (machine and container metrics, see
|
|
7
|
+
[Metrics Database](#metrics-database-metricsdb)), `costs.db` (what the agents spent, see
|
|
8
|
+
[Costs Database](#costs-database-costsdb)) and `credentials.db` (see
|
|
8
9
|
[Credentials Database](#credentials-database-credentialsdb)).
|
|
9
10
|
|
|
10
11
|
## Location
|
|
@@ -23,7 +24,7 @@ PRAGMA synchronous = NORMAL;
|
|
|
23
24
|
|
|
24
25
|
- The daemon writes; the Next.js API routes open read-only connections
|
|
25
26
|
- `events.db`: `PRAGMA wal_checkpoint(PASSIVE)` after each daemon poll cycle, `FULL` every 10
|
|
26
|
-
- `metrics.db`: SQLite's automatic checkpoint (every 1000 pages) — no explicit one
|
|
27
|
+
- `metrics.db`, `costs.db`: SQLite's automatic checkpoint (every 1000 pages) — no explicit one
|
|
27
28
|
- WAL files (`*.db-shm`, `*.db-wal`) — do not delete while the daemon is running
|
|
28
29
|
|
|
29
30
|
## Tables
|
|
@@ -38,11 +39,11 @@ CREATE TABLE sessions (
|
|
|
38
39
|
model TEXT,
|
|
39
40
|
tokens_in INTEGER DEFAULT 0,
|
|
40
41
|
tokens_out INTEGER DEFAULT 0,
|
|
41
|
-
cost_usd REAL DEFAULT 0,
|
|
42
|
+
cost_usd REAL DEFAULT 0, -- always 0 since 2026-09-27: agent costs are in costs.db
|
|
42
43
|
status TEXT DEFAULT 'idle',
|
|
43
44
|
task_preview TEXT,
|
|
44
|
-
started_at INTEGER, -- Unix
|
|
45
|
-
ended_at INTEGER, -- Unix
|
|
45
|
+
started_at INTEGER, -- Unix ms
|
|
46
|
+
ended_at INTEGER, -- Unix ms, null if active
|
|
46
47
|
updated_at INTEGER, -- Unix ms, updated only on real changes
|
|
47
48
|
trello_card_url TEXT
|
|
48
49
|
);
|
|
@@ -59,13 +60,19 @@ CREATE TABLE events (
|
|
|
59
60
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
60
61
|
ts INTEGER NOT NULL, -- Unix ms
|
|
61
62
|
session_id TEXT NOT NULL,
|
|
62
|
-
type TEXT NOT NULL, --
|
|
63
|
+
type TEXT NOT NULL, -- see below
|
|
63
64
|
data TEXT -- JSON payload
|
|
64
65
|
);
|
|
65
66
|
|
|
66
67
|
CREATE INDEX idx_events_session ON events(session_id, ts);
|
|
67
68
|
```
|
|
68
69
|
|
|
70
|
+
Types written today: `spawn`, `complete`, `fail`, `spawn_timeout` (the daemon's session poll),
|
|
71
|
+
`system_anomaly` (the daemon's machine metrics, session `system`), `agent_created` and
|
|
72
|
+
`agent_deleted` (the create and delete routes, session `system:agents`). **No retention:**
|
|
73
|
+
rows are never pruned — on a development machine with a nearly full disk, `system_anomaly`
|
|
74
|
+
reached ~4 000 rows in a month.
|
|
75
|
+
|
|
69
76
|
### `lineage` — explicit parent→child declarations
|
|
70
77
|
|
|
71
78
|
```sql
|
|
@@ -90,7 +97,7 @@ CREATE TABLE cost_override (
|
|
|
90
97
|
);
|
|
91
98
|
```
|
|
92
99
|
|
|
93
|
-
|
|
100
|
+
Nothing reads `cost_override` except `/api/cost-override` itself: no page shows an override.
|
|
94
101
|
|
|
95
102
|
The daemon also writes a `system_anomaly` event (session `system`) when the machine
|
|
96
103
|
crosses a threshold — see [Metrics Database](#metrics-database-metricsdb). No feature
|
|
@@ -195,18 +202,21 @@ container started again. Rows are never pruned.
|
|
|
195
202
|
## Useful Queries
|
|
196
203
|
|
|
197
204
|
```sql
|
|
198
|
-
--
|
|
199
|
-
SELECT SUM(
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
205
|
+
-- costs.db: what the agents spent this month (UTC days), by agent
|
|
206
|
+
SELECT s.name, SUM(d.cost) AS spent, SUM(d.missing) AS unpriced
|
|
207
|
+
FROM agent_cost_daily d LEFT JOIN agent_cost_sources s USING (container)
|
|
208
|
+
WHERE d.date >= strftime('%Y-%m-01', 'now')
|
|
209
|
+
GROUP BY d.container ORDER BY spent DESC;
|
|
210
|
+
|
|
211
|
+
-- costs.db: spend by model, last 30 days
|
|
212
|
+
SELECT model, SUM(cost) AS spent, SUM(calls) AS calls
|
|
213
|
+
FROM agent_cost_model_daily WHERE date >= date('now', '-29 days')
|
|
214
|
+
GROUP BY provider, model ORDER BY spent DESC;
|
|
215
|
+
|
|
216
|
+
-- events.db: active sessions right now
|
|
217
|
+
SELECT session_id, label, model, status
|
|
204
218
|
FROM sessions WHERE status = 'working';
|
|
205
219
|
|
|
206
|
-
-- Cost by model (all time)
|
|
207
|
-
SELECT model, SUM(cost_usd) AS total, COUNT(*) AS sessions
|
|
208
|
-
FROM sessions GROUP BY model ORDER BY total DESC;
|
|
209
|
-
|
|
210
220
|
-- Last poll time (freshness check)
|
|
211
221
|
SELECT datetime(MAX(ts)/1000, 'unixepoch', 'localtime') AS last_event
|
|
212
222
|
FROM events;
|
|
@@ -241,6 +251,19 @@ off the machine. `restore.sh` refuses an archive with links in it and a running
|
|
|
241
251
|
SQLite `-wal` cannot mix into the restored state), then resets the permissions. Agent volumes are not included —
|
|
242
252
|
the dashboard's cold backups cover them.
|
|
243
253
|
|
|
254
|
+
### Other tables
|
|
255
|
+
|
|
256
|
+
- `alert_state` — one row per alert key (`lib/alerts.ts`): whether it was sent and resolved, so
|
|
257
|
+
an alert is not sent twice.
|
|
258
|
+
- `tool_calls` and `chat_messages` — created by `lib/db-bootstrap.mjs` and never written by
|
|
259
|
+
any code: `GET /api/tool-calls` reads an always-empty table, nothing reads `chat_messages`.
|
|
260
|
+
Kept until the `events.db` cleanup removes them.
|
|
261
|
+
|
|
262
|
+
`events.db` is Rev4a's own record — what it did to agents (the job tables, created and
|
|
263
|
+
deleted) and what it saw (anomalies, alerts). What agents do is read from OpenClaw: agent
|
|
264
|
+
spend is in `costs.db`; sessions and lineage move to the lineage collector, and with it
|
|
265
|
+
`sessions`, `lineage` and the poll's `spawn`/`complete` events leave this file.
|
|
266
|
+
|
|
244
267
|
## Metrics Database (`metrics.db`)
|
|
245
268
|
|
|
246
269
|
Machine metrics of the host — CPU, RAM, swap, storage — kept apart from `events.db`, so
|
|
@@ -295,6 +318,139 @@ has dropped back under, or once after each daemon start: the state is kept in me
|
|
|
295
318
|
Installs from before this database may still have a `system_metrics` table in
|
|
296
319
|
`events.db`: nothing reads or writes it any more, and it can be dropped.
|
|
297
320
|
|
|
321
|
+
### Container consumption (also in `metrics.db`)
|
|
322
|
+
|
|
323
|
+
What each running container uses, and the disk Docker holds, so the System page can say
|
|
324
|
+
*who* is using the machine. The daemon writes it from the Docker Engine API
|
|
325
|
+
(`lib/docker-stats.js`; where the socket is comes from `lib/docker-socket-path.js`, shared
|
|
326
|
+
with the dashboard's `lib/docker-socket.ts`); `lib/container-metrics.ts` reads it for
|
|
327
|
+
`GET /api/metrics/containers`, the agent panel and the Containers page. The tables are
|
|
328
|
+
created with `CREATE TABLE IF NOT EXISTS` when the daemon starts, so an update adds them;
|
|
329
|
+
until then the API answers `available: false`.
|
|
330
|
+
|
|
331
|
+
**Sampling:** containers every 60 s, on a timer of its own — a priming pass 6 s after the
|
|
332
|
+
daemon starts (it only records the gauges: a CPU figure needs two readings), the first
|
|
333
|
+
complete one 15 s later — and Docker's disk (`/system/df`) every 10 min, the first reading
|
|
334
|
+
25 s after start. **Retention:** 30 days, pruned at every sample. A stopped container has no
|
|
335
|
+
rows: a gap, not zeros. Size, estimated: 60 s × 10 containers × 30 days ≈ 430 000 rows ≈ 35 MB
|
|
336
|
+
(to be measured on the VPS; rollups to 5 min may be added if it grows).
|
|
337
|
+
|
|
338
|
+
```sql
|
|
339
|
+
-- One row per running container per sample. CPU in cores (CPU-seconds per second), rates in
|
|
340
|
+
-- bytes per second; each is NULL on the first sample after a start and when its counter went
|
|
341
|
+
-- backwards (the container restarted) — never a negative rate or a spike. Memory is the
|
|
342
|
+
-- working set: usage − inactive page cache, what `docker stats` shows.
|
|
343
|
+
CREATE TABLE container_metrics (
|
|
344
|
+
ts INTEGER NOT NULL, -- Unix seconds
|
|
345
|
+
container TEXT NOT NULL,
|
|
346
|
+
cpu_cores REAL,
|
|
347
|
+
mem_mb INTEGER,
|
|
348
|
+
pids INTEGER,
|
|
349
|
+
net_rx_bps INTEGER, net_tx_bps INTEGER,
|
|
350
|
+
blk_read_bps INTEGER, blk_write_bps INTEGER
|
|
351
|
+
);
|
|
352
|
+
CREATE INDEX idx_container_metrics_ts ON container_metrics(ts);
|
|
353
|
+
CREATE INDEX idx_container_metrics_c_ts ON container_metrics(container, ts);
|
|
354
|
+
|
|
355
|
+
-- Who each container is: an agent's name (AGENT_NAME), whether it is an agent, and its named
|
|
356
|
+
-- volumes (the link to docker_storage). Forgotten after the retention.
|
|
357
|
+
CREATE TABLE container_sources (
|
|
358
|
+
container TEXT PRIMARY KEY,
|
|
359
|
+
agent_id TEXT, name TEXT, is_agent INTEGER NOT NULL DEFAULT 0,
|
|
360
|
+
volumes TEXT, -- comma-separated volume names
|
|
361
|
+
last_seen INTEGER NOT NULL
|
|
362
|
+
);
|
|
363
|
+
|
|
364
|
+
-- Docker's own cores and memory: the denominators for a container's share (on Docker Desktop
|
|
365
|
+
-- that is its VM — 8 GB of a 16 GB Mac — not the machine). One row.
|
|
366
|
+
CREATE TABLE docker_host (id INTEGER PRIMARY KEY CHECK (id = 1), ncpu INTEGER, mem_total_mb INTEGER, updated_at INTEGER);
|
|
367
|
+
|
|
368
|
+
-- Disk Docker holds, one set of rows per reading: kind 'volume' (name = the volume;
|
|
369
|
+
-- reclaimable_mb = its size when no container uses it), 'layer' (name = a container, its
|
|
370
|
+
-- writable layer), 'images' and 'build_cache' (totals; reclaimable_mb = what no container /
|
|
371
|
+
-- no build uses). A size Docker could not compute is NULL.
|
|
372
|
+
CREATE TABLE docker_storage (ts INTEGER NOT NULL, kind TEXT NOT NULL, name TEXT NOT NULL DEFAULT '', size_mb INTEGER, reclaimable_mb INTEGER);
|
|
373
|
+
CREATE INDEX idx_docker_storage_ts ON docker_storage(ts);
|
|
374
|
+
CREATE INDEX idx_docker_storage_kind_ts ON docker_storage(kind, name, ts);
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
## Costs Database (`costs.db`)
|
|
378
|
+
|
|
379
|
+
What each agent spent, **as its own OpenClaw priced it** — Rev4a computes no cost here.
|
|
380
|
+
Every model in the synced `rev4a` provider block carries its price
|
|
381
|
+
([GATEWAY.md](GATEWAY.md#costs-openclaw-prices-every-call)); each agent's OpenClaw prices
|
|
382
|
+
every call with it, and the daemon copies the figures here, so a stopped or deleted agent
|
|
383
|
+
keeps its history.
|
|
384
|
+
|
|
385
|
+
**Location:** `data/costs.db`, next to `events.db`, resolved like `metrics.db`.
|
|
386
|
+
**Created by** the daemon when it starts, its only writer; the queries are in `lib/agent-costs.ts` (spend) and `lib/cost-reconciliation.ts` (`vendor_balance`), on a read-only handle from `lib/costs-db.ts` — for
|
|
387
|
+
`GET /api/costs/usage`, `GET /api/costs`, `GET /api/costs/agent`, the SSE stream and
|
|
388
|
+
system-health. No file yet → the API answers `available: false`. If the daemon
|
|
389
|
+
cannot open it, it logs `[COSTS] disabled` and the rest of the daemon keeps running.
|
|
390
|
+
**Collection:** 20 s after the daemon starts, then every 5 minutes. First every **running**
|
|
391
|
+
container with an `AGENT_ID` label gets one short call, so that the ones whose index needs
|
|
392
|
+
rebuilding do it in parallel; then, one at a time, two Gateway calls through
|
|
393
|
+
`docker exec … openclaw gateway call` on the agent's port 3000 (token resolved inside the
|
|
394
|
+
container, never on the host's argv), both with `agentScope: "all"` over the last 30 UTC
|
|
395
|
+
days: `usage.cost` (per day, split into input / output / cache read / cache write, and
|
|
396
|
+
the calls it could not price) and `sessions.usage` (per model per day, and the 50 most
|
|
397
|
+
recent sessions). OpenClaw answers from an index of its transcripts that it rebuilds
|
|
398
|
+
after a price change or a restart; an answer whose `cacheStatus` is not `fresh` is never
|
|
399
|
+
stored — asked up to 6 times, 20 s apart (a rebuild took about 60 s on the VPS, from
|
|
400
|
+
4 to 290 transcript files alike), then the previous figures stay and the container's row
|
|
401
|
+
records the error — OpenClaw's own reason ("Gateway not reachable…") or how long the index
|
|
402
|
+
stayed unfresh. An answer missing its lists is refused before anything is deleted. An agent with nothing in the range answers without
|
|
403
|
+
`cacheStatus`, which counts as fresh.
|
|
404
|
+
**Days** are UTC dates, OpenClaw's default and the clock DeepSeek's rates follow.
|
|
405
|
+
**Retention:** the last 30 days are rewritten on every pass (late or repriced calls land in
|
|
406
|
+
the right day); older rows are kept as last read.
|
|
407
|
+
|
|
408
|
+
```sql
|
|
409
|
+
-- One row per container per UTC day with any use: OpenClaw's usage.cost daily entry.
|
|
410
|
+
CREATE TABLE agent_cost_daily (
|
|
411
|
+
container TEXT NOT NULL, date TEXT NOT NULL, -- 'YYYY-MM-DD'
|
|
412
|
+
input INTEGER, output INTEGER, cache_read INTEGER, cache_write INTEGER, total_tokens INTEGER,
|
|
413
|
+
cost REAL, input_cost REAL, output_cost REAL, cache_read_cost REAL, cache_write_cost REAL,
|
|
414
|
+
missing INTEGER, -- calls OpenClaw could not price
|
|
415
|
+
PRIMARY KEY (container, date)
|
|
416
|
+
);
|
|
417
|
+
-- Per model per day: sessions.usage aggregates.modelDaily.
|
|
418
|
+
CREATE TABLE agent_cost_model_daily (
|
|
419
|
+
container TEXT NOT NULL, date TEXT NOT NULL, provider TEXT NOT NULL, model TEXT NOT NULL,
|
|
420
|
+
tokens INTEGER, cost REAL, calls INTEGER,
|
|
421
|
+
PRIMARY KEY (container, date, provider, model)
|
|
422
|
+
);
|
|
423
|
+
-- One row per session seen; its figures are the session's total over the 30-day window.
|
|
424
|
+
CREATE TABLE agent_cost_sessions (
|
|
425
|
+
container TEXT NOT NULL, session_id TEXT NOT NULL,
|
|
426
|
+
session_key TEXT, label TEXT, agent_id TEXT, provider TEXT, model TEXT,
|
|
427
|
+
tokens INTEGER, cost REAL, missing INTEGER,
|
|
428
|
+
first_activity INTEGER, last_activity INTEGER, -- Unix milliseconds
|
|
429
|
+
PRIMARY KEY (container, session_id)
|
|
430
|
+
);
|
|
431
|
+
CREATE INDEX idx_cost_sessions_last ON agent_cost_sessions(last_activity);
|
|
432
|
+
-- The check against the bill: at most once an hour, after a pass, each vendor's own figure
|
|
433
|
+
-- and, read at the same moment, the agents' cumulative priced total for that vendor's
|
|
434
|
+
-- models (agent_cost_model_daily rows whose model starts with '<vendor>/'). Kept 400 days.
|
|
435
|
+
CREATE TABLE vendor_balance (
|
|
436
|
+
ts INTEGER NOT NULL, -- Unix ms
|
|
437
|
+
vendor TEXT NOT NULL, -- 'deepseek' | 'openrouter' (a key must be configured)
|
|
438
|
+
currency TEXT,
|
|
439
|
+
balance REAL, -- DeepSeek: total balance; OpenRouter: credits − usage
|
|
440
|
+
usage REAL, -- OpenRouter: lifetime usage (only grows); NULL for DeepSeek
|
|
441
|
+
agents_cost REAL NOT NULL DEFAULT 0,
|
|
442
|
+
PRIMARY KEY (vendor, ts)
|
|
443
|
+
);
|
|
444
|
+
-- One row per container ever read: its name (AGENT_NAME) and how the last pass went.
|
|
445
|
+
CREATE TABLE agent_cost_sources (
|
|
446
|
+
container TEXT PRIMARY KEY, agent_id TEXT, name TEXT,
|
|
447
|
+
collected_at INTEGER, -- last stored pass, Unix ms
|
|
448
|
+
attempted_at INTEGER, -- last attempt, Unix ms
|
|
449
|
+
error TEXT, -- why the last attempt stored nothing; NULL after a good one
|
|
450
|
+
missing_by_model TEXT -- JSON {"rev4a/<model>": calls} over the 30-day window
|
|
451
|
+
);
|
|
452
|
+
```
|
|
453
|
+
|
|
298
454
|
## Credentials Database (`credentials.db`)
|
|
299
455
|
|
|
300
456
|
The credential hub uses a **separate SQLite file** (`credentials.db`) alongside
|
package/docs/dev/GATEWAY.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Gateway Page
|
|
2
2
|
|
|
3
|
-
> **Last updated:** 2026-09-
|
|
3
|
+
> **Last updated:** 2026-09-27
|
|
4
4
|
|
|
5
5
|
The Gateway page (`/gateway`) is the control panel for provider configuration:
|
|
6
6
|
API keys and the model catalogue offered to every agent container. It does not
|
|
@@ -69,14 +69,15 @@ against the Rev4a provider proxy using this token. See [`PROVIDERS.md`](PROVIDER
|
|
|
69
69
|
|
|
70
70
|
### Sync Flow
|
|
71
71
|
|
|
72
|
-
When a provider key is saved, a model toggle is changed, Sync All is pressed,
|
|
73
|
-
|
|
72
|
+
When a provider key is saved, a model toggle is changed, Sync All is pressed, Rev4a
|
|
73
|
+
starts, or a time-of-day price changes (see [Costs](#costs-openclaw-prices-every-call)),
|
|
74
|
+
the Gateway:
|
|
74
75
|
|
|
75
76
|
1. **Reads** `models.config.json` and the overrides, and keeps the models that are
|
|
76
77
|
enabled and whose provider has a key. If the file cannot be read and no earlier
|
|
77
78
|
read succeeded, the sync stops here and reports `configError`
|
|
78
79
|
2. **Builds** a `models.providers.rev4a` config block (without `rev4a/` prefix on
|
|
79
|
-
model IDs)
|
|
80
|
+
model IDs), each model carrying the `cost` in force now
|
|
80
81
|
3. **Lists** every container with the `AGENT_ID` Docker label, **running or
|
|
81
82
|
not**. Stopped agents are included so that starting one later does not bring
|
|
82
83
|
back an old catalogue
|
|
@@ -92,6 +93,48 @@ Rev4a starts, the Gateway:
|
|
|
92
93
|
the file's original mode, and apply it when started or resumed
|
|
93
94
|
7. **Does NOT restart** the gateway
|
|
94
95
|
|
|
96
|
+
### Costs: OpenClaw prices every call
|
|
97
|
+
|
|
98
|
+
Every model in the `rev4a` block carries a `cost` — `{ input, output, cacheRead,
|
|
99
|
+
cacheWrite }` in $ per 1M tokens, from `priceAt()` in `lib/model-pricing.ts`. That is the
|
|
100
|
+
field OpenClaw's own cost accounting reads first after an agent's `models.json`, so each
|
|
101
|
+
agent prices every call, session and day itself, with the cache split out, and shows it in
|
|
102
|
+
its own Control UI too. Rev4a computes no cost: the daemon reads OpenClaw's figures into
|
|
103
|
+
`costs.db` and the Costs page (`/costs`) shows them (see
|
|
104
|
+
[DATABASE.md](DATABASE.md#costs-database-costsdb)).
|
|
105
|
+
|
|
106
|
+
- **No price, no `cost`.** A model without an entry in `model-pricing.json` or with a
|
|
107
|
+
dynamic router price (`-1`, OpenRouter Auto) gets none, and OpenClaw counts its calls
|
|
108
|
+
as "missing cost" rather than $0. The Costs page lists them, by model.
|
|
109
|
+
- **Free models** (all-zero price) are written with $0.000001 per 1M tokens for every
|
|
110
|
+
rate: OpenClaw treats an all-zero `cost` as no price and would count every call as
|
|
111
|
+
unpriced. A billion tokens come to $0.001.
|
|
112
|
+
- **Unknown cache rate.** An entry without `cacheRead` falls back to the input rate
|
|
113
|
+
(cached tokens priced as if the cache gave no discount); one without `cacheWrite` to
|
|
114
|
+
1.25 × the input rate — vendors that bill cache writes apart charge more than input
|
|
115
|
+
(Anthropic's five-minute cache: 1.25×), and the ones that report no cache writes never
|
|
116
|
+
use it. Either way an overestimate, never an undercount. `refresh:pricing` writes them
|
|
117
|
+
for OpenRouter models when OpenRouter publishes them (104 and 44 of 181 entries when
|
|
118
|
+
added); direct vendors are filled by hand from their pricing page.
|
|
119
|
+
- **Time-of-day prices.** DeepSeek bills half its rates off-peak (peak 01:00–04:00 and
|
|
120
|
+
06:00–10:00 UTC, Monday–Friday). `PRICE_SCHEDULES` in `lib/model-pricing.ts` holds
|
|
121
|
+
that schedule; `priceAt()` writes the rate of the moment, and
|
|
122
|
+
`lib/price-schedule-sync.ts` (started by `instrumentation.ts`) checks at most every
|
|
123
|
+
minute — and exactly at a change due within the minute — whether the bands in force
|
|
124
|
+
differ from the ones last written, and runs a full sync when they do; a sync that
|
|
125
|
+
missed an agent is retried every 5 minutes, at most 3 times for one band — an agent busy
|
|
126
|
+
with an update, recreate, restore or edit writes the prices itself when it finishes; the
|
|
127
|
+
bands count as written after any sync, since a sync that misses one agent writes all the
|
|
128
|
+
others — and a new change is written at once, whatever retry was pending. A check rather than one timer to the next
|
|
129
|
+
change: a timer fires late after the machine sleeps, a check a minute after it wakes
|
|
130
|
+
(simulated: asleep 01:05–05:00 UTC across the 04:00 change → synced at 05:00). OpenClaw keeps the cost it recorded at call
|
|
131
|
+
time, so a later change of rate does not reprice a call (verified with a real call,
|
|
132
|
+
priced off-peak and read again after the switch to peak). Chinese public holidays,
|
|
133
|
+
which DeepSeek bills off-peak all day, are priced as peak — a small overestimate.
|
|
134
|
+
- **Calls made before a model had a price** are priced by OpenClaw when read, at the
|
|
135
|
+
rate in force then; OpenClaw reindexes its transcripts after a price change, which is
|
|
136
|
+
why a read can briefly answer `cacheStatus: refreshing`.
|
|
137
|
+
|
|
95
138
|
### Model ID Convention
|
|
96
139
|
|
|
97
140
|
Inside `models.providers.rev4a.models`, model IDs are stored **without** the
|
|
@@ -503,11 +546,12 @@ The OpenRouter payload carries `pricing.prompt`, `pricing.completion`,
|
|
|
503
546
|
same pass.
|
|
504
547
|
|
|
505
548
|
For direct-provider prices, use the provider's own pricing page. DeepSeek's is
|
|
506
|
-
at `api-docs.deepseek.com/quick_start/pricing
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
549
|
+
at `api-docs.deepseek.com/quick_start/pricing`. Record the **peak** rates, and
|
|
550
|
+
the cache-hit rate as `cacheRead` (`cacheWrite` is the input rate for a vendor that
|
|
551
|
+
bills a cache miss as ordinary input); the off-peak discount is applied by
|
|
552
|
+
`PRICE_SCHEDULES` in `lib/model-pricing.ts`, which is where a schedule change goes.
|
|
553
|
+
These prices are what every agent's OpenClaw computes its costs with (see
|
|
554
|
+
[Costs](#costs-openclaw-prices-every-call)).
|
|
511
555
|
|
|
512
556
|
### What to check, in order
|
|
513
557
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Data Freshness in Rev4a
|
|
2
2
|
|
|
3
|
-
> **Last updated:** 2026-
|
|
3
|
+
> **Last updated:** 2026-10-01
|
|
4
4
|
|
|
5
5
|
Understanding how current the data in Rev4a is — and what is truly real-time vs. periodically updated.
|
|
6
6
|
|
|
@@ -11,7 +11,7 @@ Understanding how current the data in Rev4a is — and what is truly real-time v
|
|
|
11
11
|
**Update frequency:** every 30 seconds
|
|
12
12
|
|
|
13
13
|
The daemon polls `openclaw sessions --json --all-agents` on a fixed 30-second
|
|
14
|
-
timer. Session statuses
|
|
14
|
+
timer. Session statuses and token counts are therefore up to 30 seconds
|
|
15
15
|
behind reality.
|
|
16
16
|
|
|
17
17
|
The interval is fixed: it does not speed up while a session is `working`. Do not tell
|
|
@@ -56,12 +56,39 @@ the collector is not running instead of showing stale numbers as current.
|
|
|
56
56
|
|
|
57
57
|
---
|
|
58
58
|
|
|
59
|
-
##
|
|
59
|
+
## Container consumption (System page, Containers page, agent panel)
|
|
60
60
|
|
|
61
|
-
**Update frequency:** every
|
|
61
|
+
**Update frequency:** every 60 seconds for CPU, memory, processes, network and block I/O of
|
|
62
|
+
each running container; every 10 minutes for the disk Docker holds (volumes, writable layers,
|
|
63
|
+
images, build cache). Both are sampled by the Rev4a daemon on timers of their own and kept 30
|
|
64
|
+
days in `metrics.db`. The first figures after the daemon starts take about 20 seconds (a CPU
|
|
65
|
+
figure needs two readings). A stopped container leaves a gap in its charts, not zeros.
|
|
62
66
|
|
|
63
|
-
|
|
64
|
-
|
|
67
|
+
CPU is a share of Docker's cores and memory a share of Docker's memory: on Docker Desktop that
|
|
68
|
+
is its virtual machine (for example 8 GB of a 16 GB Mac), on a server it is the machine. If the
|
|
69
|
+
newest sample is older than three minutes the System page says it is not collecting — the
|
|
70
|
+
figures still on show are the last known ones, not live. The agent panel stands its "now" rows
|
|
71
|
+
down, and the Containers page clears its usage row rather than leave stale figures. A container
|
|
72
|
+
still seen in Docker's list but without fresh statistics says "no data", not "stopped".
|
|
73
|
+
|
|
74
|
+
## Agent costs (Costs page)
|
|
75
|
+
|
|
76
|
+
**Update frequency:** every 5 minutes. The Rev4a daemon asks each running agent
|
|
77
|
+
what it spent and keeps the figures in their own database (`costs.db`); the page
|
|
78
|
+
refreshes every minute. An agent whose figures could not be read recently is listed
|
|
79
|
+
under **Not current** with the reason.
|
|
80
|
+
|
|
81
|
+
The costs are computed by each agent's own OpenClaw, with the prices Rev4a writes
|
|
82
|
+
into the agent for every model. A call to a model without a price is counted but not
|
|
83
|
+
priced: the page lists those under **Calls without a price**. Days are UTC. DeepSeek
|
|
84
|
+
bills half price off-peak; Rev4a switches the agents' prices at each change, so each
|
|
85
|
+
call is priced at the rate it ran at.
|
|
86
|
+
|
|
87
|
+
## Cost totals (dashboard)
|
|
88
|
+
|
|
89
|
+
The dashboard's "Spent today" card and "Spent by model" list show the same figures as the
|
|
90
|
+
Costs page, for today (UTC day): up to 5 minutes behind the agents. The vendor readings
|
|
91
|
+
behind *Against the bill* on the Costs page are taken once an hour.
|
|
65
92
|
|
|
66
93
|
**Cost override:** `/api/cost-override` exists, but no page in the dashboard
|
|
67
94
|
reads or writes it. There is no UI for setting one, so do not direct a user to
|
|
@@ -143,7 +170,7 @@ The tracked set is `USER.md`, `MEMORY.md`, `AGENTS.md`, `SOUL.md`, `HEARTBEAT.md
|
|
|
143
170
|
|
|
144
171
|
System health is a card on the **Dashboard**, not a page of its own. Its checks
|
|
145
172
|
cover session runtime, the daemon heartbeat, errors in the last 24 hours, today's
|
|
146
|
-
|
|
173
|
+
what the agents spent today, and cron jobs. CPU, RAM and disk are on the System page (`/system`)
|
|
147
174
|
and the Dashboard's Machine card instead.
|
|
148
175
|
|
|
149
176
|
To check directly on the server:
|
package/docs/rag/GLOSSARY.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Rev4a Glossary
|
|
2
2
|
|
|
3
|
-
> **Last updated:** 2026-
|
|
3
|
+
> **Last updated:** 2026-10-01
|
|
4
4
|
|
|
5
5
|
Terms you'll encounter while using the Rev4a dashboard.
|
|
6
6
|
|
|
@@ -22,7 +22,7 @@ A compressed archive (`.tar.gz`) of an agent's persistent volume (`/root/`). Bac
|
|
|
22
22
|
Which browsers may open an agent's Control UI. On OpenClaw 9.x every new browser must be approved once; the approval is remembered per browser. Managed in the "BROWSER ACCESS" section of the agent detail panel: approve or reject waiting browsers, rename or revoke approved ones. The Open button uses a one-time link that skips the approval. The Invite link button gives a link for someone else: their browser still waits for approval.
|
|
23
23
|
|
|
24
24
|
## Container
|
|
25
|
-
A Docker container running on the server. Each agent runs in its own container. The Containers page shows all containers, including infrastructure ones such as databases and reverse proxies, with a web terminal link (a real shell in the browser; a dropped connection is resumed for 2 minutes).
|
|
25
|
+
A Docker container running on the server. Each agent runs in its own container. The Containers page shows all containers (a running one also with its CPU, memory and processes now), including infrastructure ones such as databases and reverse proxies, with a web terminal link (a real shell in the browser; a dropped connection is resumed for 2 minutes).
|
|
26
26
|
|
|
27
27
|
## Channel
|
|
28
28
|
A communication channel (Telegram) configured on an agent. Channels allow users to send DMs to the agent via messaging apps. The Channel Manager modal lets you connect/disconnect Telegram and manage pairings (approve/reject senders).
|
|
@@ -31,7 +31,10 @@ A communication channel (Telegram) configured on an agent. Channels allow users
|
|
|
31
31
|
Modal in the agent detail panel for configuring Telegram channels. Connect with bot token, restart agent, manage pairings (approve pending codes, revoke approved senders). Pairings are polled every 5 seconds with automatic cancellation of stale requests via AbortController.
|
|
32
32
|
|
|
33
33
|
## Cost
|
|
34
|
-
|
|
34
|
+
What an agent spent in USD. Each agent's OpenClaw prices every model call itself, with the prices Rev4a writes into it (input, output, cached input), and Rev4a reads those figures every 5 minutes. The Costs page (`/costs`) shows them for today, 7 or 30 days — by day, by agent, by model, and the most expensive sessions. The dashboard's "Spent today" card shows the same figures for today, and each agent's panel on the Agents page has a COSTS section with its own. The Costs page also compares the agents' figures with what DeepSeek and OpenRouter actually billed (their balance or usage, read every hour).
|
|
35
|
+
|
|
36
|
+
## Off-peak
|
|
37
|
+
DeepSeek bills half price outside its peak hours (peak: 01:00–04:00 and 06:00–10:00 UTC, Monday–Friday). Rev4a writes the lower prices into the agents when off-peak starts and the full ones when it ends, so each call is priced at the rate in force when it ran. The Costs page header shows the current band.
|
|
35
38
|
|
|
36
39
|
## Cost Override
|
|
37
40
|
A manual correction for a month's total cost, stored through `/api/cost-override`. There is currently no screen for it: no page in the dashboard reads or writes an override, so it can only be set by calling the API directly.
|
|
@@ -85,7 +88,7 @@ Replace an agent's persistent volume with a previously created backup. The conta
|
|
|
85
88
|
Undo an agent's latest OpenClaw update: the backup taken just before the update is put back and the agent starts again on its previous version. Anything the agent did after the update is lost. Offered in the agent's OPENCLAW VERSION section while that backup exists.
|
|
86
89
|
|
|
87
90
|
## Session
|
|
88
|
-
One instance of an agent doing work. Tracks: start/end time, tokens used,
|
|
91
|
+
One instance of an agent doing work. Tracks: start/end time, tokens used, model, status, and task description; what a session cost is on the Costs page (most expensive sessions). A session starts when an agent receives a task and ends when it completes or fails.
|
|
89
92
|
|
|
90
93
|
## Pairing
|
|
91
94
|
A security mechanism for Telegram channels. When a user sends `/start` to the bot, a 4-digit pairing code is generated. An admin must approve this code in the Channel Manager to add the user to the allowlist. Approved senders can be revoked at any time.
|
|
@@ -134,6 +137,6 @@ The directory where an agent's operational files live (config, memory files, ski
|
|
|
134
137
|
The page with the machine Rev4a runs on: CPU (with cores and load), memory and swap, and each disk's used and free space, now and over 1 hour, 24 hours, 7 days or 30 days. A bar turns yellow and then red when it reaches a threshold (CPU and memory 85 % / 95 %, disk 80 % / 90 %), and the level is written next to it.
|
|
135
138
|
|
|
136
139
|
## System Health
|
|
137
|
-
A card on the Dashboard reporting whether Rev4a itself is working, not the hardware it runs on. `/api/system-health` returns checks for session runtime, the daemon heartbeat (how old its latest machine sample is), errors in the last 24 hours, today's
|
|
140
|
+
A card on the Dashboard reporting whether Rev4a itself is working, not the hardware it runs on. `/api/system-health` returns checks for session runtime, the daemon heartbeat (how old its latest machine sample is), errors in the last 24 hours, today's what the agents spent today, and cron jobs, each with a health of `ok`, `warning` or `error`, plus a list of recommendations.
|
|
138
141
|
|
|
139
142
|
It reports no CPU, RAM, disk or load average: those are on the System page (`/system`), sampled every 30 seconds by the daemon and kept for 30 days.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# What is Rev4a?
|
|
2
2
|
|
|
3
|
-
> **Last updated:** 2026-
|
|
3
|
+
> **Last updated:** 2026-10-01
|
|
4
4
|
|
|
5
5
|
Rev4a is the control panel for your AI agent infrastructure. It shows you everything your agents are doing, how much they cost, and whether the system is healthy — all in one dashboard.
|
|
6
6
|
|
|
@@ -77,13 +77,20 @@ Interactive graph showing session family trees — which agent spawned which chi
|
|
|
77
77
|
Connects agents to AI providers. Add and remove provider API keys, enable or disable individual models in the catalogue read from `models.config.json`, and push the resulting model list to every agent container. It does not assign models to agents: choosing which model an agent runs is done per agent, in the Model section of the agent's detail panel on the Agents page. Every model row has a **Details** button: the modal shows the model's description, price and provider, its architecture and parameter count when the weights are public, reasoning modes, capabilities, benchmarks with sources, licence and how much disk the weights take — the numbers come from the model card on Hugging Face and from curated entries that cite a source, never from estimates.
|
|
78
78
|
|
|
79
79
|
### Containers (`/containers`)
|
|
80
|
-
Full list of all Docker containers on the server, including stopped ones. Each row shows name, status, image, IP, ports, and an agent pill where applicable. Click "Terminal" to open an interactive shell into that container.
|
|
80
|
+
Full list of all Docker containers on the server, including stopped ones. Each row shows name, status, image, IP, ports, and an agent pill where applicable. Click "Terminal" to open an interactive shell into that container. A running container also shows what it consumes now — CPU share, memory and processes — with a "Charts" link to its history on the System page.
|
|
81
81
|
|
|
82
|
-
The page
|
|
82
|
+
The page has no start, stop, or restart buttons: it is read-only apart from the terminal link. Container lifecycle is managed from the Agents page.
|
|
83
|
+
|
|
84
|
+
### Costs (`/costs`)
|
|
85
|
+
What each agent spent, as its own OpenClaw priced it with the prices Rev4a syncs. Pick today, 7 days or 30 days (UTC days): the total, where the money went (input, output, cache), the tokens and how much input came from cache, a chart of the spend per day, the spend by agent and by model, and the most expensive sessions. Calls that could not be priced are listed by model, and agents whose figures are not current are flagged. When a DeepSeek model is on offer, the header shows whether DeepSeek is billing peak or off-peak (half price) and until when. Figures are read from the agents every 5 minutes. *Against the bill* compares them with what DeepSeek and OpenRouter actually charged (their balance or usage, read every hour); a small gap is normal, since the same key may pay for the assistant too. Each agent's own spend is also in its panel on the Agents page (COSTS).
|
|
83
86
|
|
|
84
87
|
### System (`/system`)
|
|
85
88
|
The machine Rev4a runs on. Three cards — CPU (percent, cores, load average), memory (used, available, swap) and storage (each disk with its used and free space, and whether it is the system disk, where Docker keeps agents, or where Rev4a keeps its data) — then charts of CPU, memory and each disk over 1 hour, 24 hours, 7 days or 30 days (for CPU and memory the average line with the peak shaded). Hover a chart (or focus it and use the arrow keys) to read a point; each chart has a Table view. Bars turn yellow and red when they reach their thresholds. Figures refresh every 30 seconds; history is kept 30 days.
|
|
86
89
|
|
|
90
|
+
### Agents and containers (on the System page)
|
|
91
|
+
Under the machine's charts: what Docker holds on disk (images, volumes, writable layers, build cache, and how much of each no container uses), then one row per container sorted by CPU. A closed row shows its name, whether it is an agent, and its CPU share, memory, storage and processes now, with the average and peak over the chosen range (1 hour to 30 days); open it for the same three charts as the machine — CPU, Memory, Storage — for that container. "Everything else" is what is not a container (the host, Rev4a, other software). Each agent's panel on the Agents page has the same numbers in its RESOURCES section, with a link to its charts. Below, the machine's recent alerts: CPU, memory or a disk that crossed its limit, and when.
|
|
92
|
+
When Docker still sees a container but it has no current statistics, its row says "no data". If collection stops, the current figures are hidden and old rows say "not current" instead of implying a live reading.
|
|
93
|
+
|
|
87
94
|
### Container Terminal (`/containers/terminal/[id]`)
|
|
88
95
|
Live web terminal into a running Docker container — like SSH in the browser: Tab completion, command history, `top`, `vi`, resizing with the window. If the connection drops (network, laptop sleep) the shell keeps running for 2 minutes and reconnecting picks it up; reloading the page resumes it too. **Close** ends the shell. If the container is stopped or missing, the page says so and offers **Reconnect**. Only a logged-in user can open it.
|
|
89
96
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# What PULSE Can Answer
|
|
2
2
|
|
|
3
|
-
> **Last updated:** 2026-
|
|
3
|
+
> **Last updated:** 2026-10-01
|
|
4
4
|
|
|
5
5
|
PULSE is the in-dashboard AI concierge for Rev4a. This document defines what she can and cannot answer.
|
|
6
6
|
|
|
@@ -11,6 +11,9 @@ PULSE is the in-dashboard AI concierge for Rev4a. This document defines what she
|
|
|
11
11
|
- "Where do I find the agents page?"
|
|
12
12
|
- "How do I get to the container list?"
|
|
13
13
|
- "Where do I see CPU, RAM or disk usage of the server?" — The System page (`/system`), and the Machine card on the Dashboard.
|
|
14
|
+
- "Which agent is using the most CPU or memory?" — The "Agents and containers" section of the System page: one row per container, sorted by CPU, with memory, storage and processes; open a row for its charts. An agent's own panel has the same numbers under RESOURCES.
|
|
15
|
+
- "What is filling the disk?" — The "Docker storage" block on the System page: images, volumes (an unused volume is named), writable layers and build cache.
|
|
16
|
+
- "Why is there a red bar or an alert on the System page?" — "Recent alerts" at the bottom of the System page lists what crossed its limit and when.
|
|
14
17
|
- "Where can I see the first-run wizard?"
|
|
15
18
|
- "Is there a page for cron jobs?"
|
|
16
19
|
- "Where can I see my AI providers?" — The Gateway page (`/gateway`) shows providers and the model catalogue. Which model an agent runs is on the Agents page, in that agent's detail panel.
|
|
@@ -20,7 +23,7 @@ PULSE is the in-dashboard AI concierge for Rev4a. This document defines what she
|
|
|
20
23
|
- "How does the custom agent bootstrap work?"
|
|
21
24
|
- "Where is the lineage graph?"
|
|
22
25
|
- "How do I open the file explorer?"
|
|
23
|
-
- "Which page shows costs?"
|
|
26
|
+
- "Which page shows costs?" — The Costs page (`/costs`): what each agent spent, by day, agent, model and session.
|
|
24
27
|
- "Where do I manage credentials (GitHub, Trello, etc.)?"
|
|
25
28
|
- "How do I connect an agent to Telegram?"
|
|
26
29
|
- "How do I approve a Telegram pairing?"
|
|
@@ -93,7 +96,7 @@ PULSE is the in-dashboard AI concierge for Rev4a. This document defines what she
|
|
|
93
96
|
## Troubleshooting Questions
|
|
94
97
|
|
|
95
98
|
- "Why is my agent not showing up?"
|
|
96
|
-
- "Why are costs missing for some sessions?"
|
|
99
|
+
- "Why are costs missing for some sessions?" — On the Costs page, calls to a model without a price are listed under "Calls without a price": the model left the catalogue or has no price yet.
|
|
97
100
|
- "Why can't I connect to the container terminal?"
|
|
98
101
|
- "Why is the gateway sync not working?"
|
|
99
102
|
- "How do I check if the daemon is running?"
|
package/instrumentation.ts
CHANGED
|
@@ -7,12 +7,14 @@
|
|
|
7
7
|
*/
|
|
8
8
|
export async function register() {
|
|
9
9
|
if (process.env.NEXT_RUNTIME === 'nodejs') {
|
|
10
|
+
let startupSynced = false;
|
|
10
11
|
try {
|
|
11
12
|
const { syncAllAgents } = await import('./app/api/gateway/sync');
|
|
12
13
|
// The outcome used to be discarded. syncAllAgents reports failures instead of
|
|
13
14
|
// throwing, so the catch below never saw a sync that reached no container —
|
|
14
15
|
// Docker not yet up at boot left the fleet stale with nothing in the log.
|
|
15
16
|
const outcome = syncAllAgents();
|
|
17
|
+
startupSynced = outcome.ok;
|
|
16
18
|
if (outcome.ok) {
|
|
17
19
|
console.log(`[rev4a] Startup sync: ${outcome.summary}`);
|
|
18
20
|
} else {
|
|
@@ -22,6 +24,15 @@ export async function register() {
|
|
|
22
24
|
// Best-effort — don't block startup if sync fails
|
|
23
25
|
console.warn('[rev4a] Failed to sync agents on startup');
|
|
24
26
|
}
|
|
27
|
+
// Prices that change with the time of day (DeepSeek off-peak): write the new rates
|
|
28
|
+
// into every agent at each change, so OpenClaw prices each call at the rate it ran at.
|
|
29
|
+
// A startup sync that missed an agent is written again by the first check.
|
|
30
|
+
try {
|
|
31
|
+
const { startPriceScheduleSync } = await import('./lib/price-schedule-sync');
|
|
32
|
+
startPriceScheduleSync(startupSynced);
|
|
33
|
+
} catch {
|
|
34
|
+
console.warn('[rev4a] Could not start the price schedule re-sync');
|
|
35
|
+
}
|
|
25
36
|
// Updates cut off by the restart: no job runs in a fresh process, so any update
|
|
26
37
|
// still marked active becomes `interrupted` and waits for the operator.
|
|
27
38
|
try {
|