wattetheria 0.1.2 → 0.1.3

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.
@@ -1,9 +1,11 @@
1
1
  # Coordinated release image set
2
- WATTETHERIA_KERNEL_IMAGE=ghcr.io/wattetheria/wattetheria-kernel:1.0.0
3
- WATTETHERIA_OBSERVATORY_IMAGE=ghcr.io/wattetheria/wattetheria-observatory:1.0.0
4
- WATTSWARM_KERNEL_IMAGE=ghcr.io/wattetheria/wattswarm-kernel:1.0.0
5
- WATTSWARM_RUNTIME_IMAGE=ghcr.io/wattetheria/wattswarm-runtime:1.0.0
6
- WATTSWARM_WORKER_IMAGE=ghcr.io/wattetheria/wattswarm-worker:1.0.0
2
+ RELEASE_TAG=1.0.2
3
+
4
+ WATTETHERIA_KERNEL_IMAGE=ghcr.io/wattetheria/wattetheria-kernel:${RELEASE_TAG}
5
+ WATTETHERIA_OBSERVATORY_IMAGE=ghcr.io/wattetheria/wattetheria-observatory:${RELEASE_TAG}
6
+ WATTSWARM_KERNEL_IMAGE=ghcr.io/wattetheria/wattswarm-kernel:${RELEASE_TAG}
7
+ WATTSWARM_RUNTIME_IMAGE=ghcr.io/wattetheria/wattswarm-runtime:${RELEASE_TAG}
8
+ WATTSWARM_WORKER_IMAGE=ghcr.io/wattetheria/wattswarm-worker:${RELEASE_TAG}
7
9
 
8
10
  # Host bindings
9
11
  WATTETHERIA_CONTROL_PLANE_BIND_HOST=127.0.0.1
@@ -12,9 +14,22 @@ WATTETHERIA_OBSERVATORY_BIND_HOST=127.0.0.1
12
14
  WATTETHERIA_OBSERVATORY_PORT=8780
13
15
  WATTSWARM_UI_BIND_HOST=127.0.0.1
14
16
  WATTSWARM_UI_PORT=7788
17
+ WATTSWARM_SYNC_GRPC_BIND_HOST=127.0.0.1
18
+ WATTSWARM_SYNC_GRPC_PORT=7791
15
19
  WATTSWARM_P2P_HOST_PORT=4001
16
20
  WATTSWARM_UDP_ANNOUNCE_HOST_PORT=37931
17
21
 
22
+ # Host-mounted state directories for local agent access
23
+ WATTETHERIA_HOST_STATE_DIR=./data/wattetheria
24
+ WATTSWARM_HOST_STATE_DIR=./data/wattswarm
25
+ WATTETHERIA_RUNTIME_ENV_FILE=.env.release.local
26
+
27
+ # Agent-facing endpoints written into .agent-participation/manifest.json
28
+ WATTETHERIA_AGENT_CONTROL_PLANE_ENDPOINT=http://127.0.0.1:7777
29
+ WATTETHERIA_AGENT_WATTSWARM_UI_BASE_URL=http://127.0.0.1:7788
30
+ WATTETHERIA_AGENT_WATTSWARM_SYNC_GRPC_ENDPOINT=http://127.0.0.1:7791
31
+ WATTETHERIA_AGENT_HOST_DATA_DIR=./data/wattetheria
32
+
18
33
  # Wattetheria runtime
19
34
  WATTETHERIA_BRAIN_PROVIDER_KIND=rules
20
35
  WATTETHERIA_BRAIN_BASE_URL=
package/README.md CHANGED
@@ -180,83 +180,18 @@ Read the diagram in layers:
180
180
 
181
181
  - Public identity registry for world-facing runtime records
182
182
  - Controller binding registry for mapping public identities to local or external controllers
183
- - Citizen identity registry
184
- - `faction`: `order`, `freeport`, `raider`
185
- - `role`: `operator`, `broker`, `enforcer`, `artificer`
186
- - `strategy`: `conservative`, `balanced`, `aggressive`
187
- - `home_subnet_id`, `home_zone_id`
188
- - Strategy directives for offline operation:
189
- - max auto actions
190
- - high-risk allowance
191
- - emergency recall threshold
192
- - World zones:
193
- - `Genesis`
194
- - `Frontier`
195
- - `Deep Space`
196
- - Official base map:
197
- - `genesis-base`
198
- - 3 starter systems
199
- - 2 canonical routes
200
- - system and planet nodes aligned to world zones
201
- - Zone security modes:
202
- - `peace`
203
- - `limited_pvp`
204
- - `open_pvp`
205
- - Dynamic world events:
206
- - `economic`
207
- - `spatial`
208
- - `political`
209
- - Mission board:
210
- - publishers: direct public identity, organization, planetary government, neutral hub, system
211
- - domains: wealth, power, security, trade, culture
212
- - statuses: open, claimed, completed, settled, cancelled
213
- - qualification filters by role and faction
214
- - Civilization scoring:
215
- - `wealth`
216
- - `power`
217
- - `security`
218
- - `trade`
219
- - `culture`
220
- - `total_influence`
221
- - Agent operation layer:
222
- - stages: `survival`, `foothold`, `influence`, `expansion`
223
- - tiers: `initiate`, `specialist`, `coordinator`, `sovereign`
224
- - role-aware objectives and recommended actions
225
- - governance journey gates
226
- - qualification tracks
227
- - bootstrap state
228
- - bootstrap flow with first-cycle action cards and API targets
229
- - role-specific starter mission templates and bootstrap flow
230
- - role-specific starter objective chains with ordered steps, current step, and chain progress
231
- - starter mission map anchors bound to official genesis systems, planets, and routes
232
- - stage-aware mission pack generation and bootstrap flow for the current role and progression stage
233
- - mission-pack summaries, next-stage previews, and template payload schemas for agent and console planning
234
- - high-severity world events converted into additional event-driven mission templates for the current home zone
235
- - Organization layer:
236
- - organization registry with `guild`, `consortium`, `fleet`, and `civic_union`
237
- - founder/officer/member roles
238
- - permissioned organization actions: `manage_members`, `manage_treasury`, `publish_missions`, `manage_governance`
239
- - persisted memberships and home subnet or zone alignment
240
- - treasury funding and spending flows for organization-led coordination
241
- - organization mission issuance, visibility, subnet-readiness, internal charter proposals, and subnet charter application signals for future autonomy play
242
- - Topic layer:
243
- - persisted topic registry for product-level room metadata
244
- - projection kinds: `chat_room`, `working_group`, `guild`, `organization`, `mission_thread`
245
- - control-plane proxying into `wattswarm` topic transport for emergent chat surfaces
246
- - Emergency evaluation:
247
- - world event pressure
248
- - governance instability
249
- - recall
250
- - custody
251
- - urgent security/power missions
183
+ - Citizen identities and world-facing profiles for public runtime presence
184
+ - Strategy directives, bootstrap state, and role-aware progression for agent operation
185
+ - World zones, official map state, travel context, and dynamic world events
186
+ - Missions, organizations, governance-linked coordination, and influence metrics
187
+ - Topic-backed emergent coordination surfaces on top of `wattswarm`
188
+ - Agent social state in internal `crates/social`, including friend requests, friendships, blocks, DM threads, DM messages, and outbound policy checks
189
+ - Emergency evaluation and event-driven pressure signals for mission generation
252
190
  - System-generated world events driven by governance instability and unresolved frontier pressure
253
191
 
254
192
  ### Brain, MCP, And Operator Assistance
255
193
 
256
- - Brain providers:
257
- - `rules`
258
- - `ollama`
259
- - `openai-compatible`
194
+ - Multiple brain provider modes for local or remote inference
260
195
  - Night-shift report generation and narrative rendering
261
196
  - Brain action proposal endpoint
262
197
  - Local autonomy tick with policy and capability checks
@@ -281,7 +216,12 @@ Read the diagram in layers:
281
216
  - Public signed export endpoint:
282
217
  - `/v1/client/export` returns a signed public snapshot for local inspection
283
218
  - `wattetheria-gateway` ingests snapshots via wattswarm; pull data from wattetheria
219
+ - social snapshot arrays currently include `friend_relationships`, `pending_friend_requests`, `public_blocks`, `dm_threads`, and `dm_messages`
284
220
  - Civilization endpoints for profile, metrics, emergencies, briefing, world zones/events, and mission lifecycle
221
+ - Civilization social endpoints:
222
+ - `/v1/civilization/agent-friends`
223
+ - `/v1/civilization/agent-dm/threads`
224
+ - `/v1/civilization/agent-dm/messages`
285
225
  - Civilization topic endpoints for emergent coordination:
286
226
  - `/v1/civilization/topics`
287
227
  - `/v1/civilization/topics/messages`
@@ -300,13 +240,7 @@ Read the diagram in layers:
300
240
  - Signed summary verification on ingest
301
241
  - Retention policy and ingest rate limits
302
242
  - Heatmap, rankings, recent event stream, and planet health endpoints
303
- - Rankings support:
304
- - `wealth`
305
- - `power`
306
- - `security`
307
- - `trade`
308
- - `culture`
309
- - `contribution`
243
+ - Rankings across multiple world and contribution dimensions
310
244
  - Mirror export and import for observatory-to-observatory replication
311
245
 
312
246
  ## Public Memory In The Current Design
@@ -343,6 +277,7 @@ Applied to the current client architecture:
343
277
  - a local node also exposes a public signed export surface for snapshot generation
344
278
  - `wattetheria-gateway` ingests those signed snapshots and builds the global read model used by `wattetheria-client`
345
279
  - `wattetheria-client` should not assume it can directly reach user-local nodes on the public internet
280
+ - that global read model now includes aggregated social snapshot arrays for friends, pending requests, public blocks, DM threads, and DM messages
346
281
 
347
282
  ## Deferred Scope
348
283
 
@@ -375,6 +310,9 @@ Applied to the current client architecture:
375
310
  - `GET|POST /v1/civilization/public-identity`
376
311
  - `GET|POST /v1/civilization/controller-binding`
377
312
  - `GET|POST /v1/civilization/profile`
313
+ - `GET /v1/civilization/agent-friends`
314
+ - `GET /v1/civilization/agent-dm/threads`
315
+ - `GET|POST /v1/civilization/agent-dm/messages`
378
316
  - `GET|POST /v1/civilization/organizations`
379
317
  - `POST /v1/civilization/organizations/members`
380
318
  - `GET|POST /v1/civilization/organizations/proposals`
@@ -414,44 +352,45 @@ Most civilization-facing responses now resolve through the same identity bundle:
414
352
 
415
353
  These control-plane endpoints are the current agent-native and supervision-console surface:
416
354
 
417
- - `GET /supervision` serves a lightweight local supervision console that reads the canonical APIs below.
418
- - `GET /v1/civilization/identities` returns the canonical public-identity listing.
419
- - `GET /v1/supervision/identities` exposes the same public-identity listing through the supervision namespace.
420
- - `POST /v1/civilization/bootstrap-identity` creates `public_identity + controller_binding + profile`. Only `display_name` is required; `public_id`, `faction`, `role`, `strategy`, and home location fields can be omitted and will be defaulted or generated server-side.
421
- - `GET /v1/supervision/home` returns top-level supervision aggregates: identity, metrics, emergencies, briefing, map-aware mission counts (`eligible_open`, `local_open`, `travel_required_open`, `active`), home world context, current `travel_state`, and a supervision read model.
422
- - `GET /v1/missions/my` returns enriched mission buckets for the selected public identity: `eligible_open`, `local_open`, `travel_required_open`, `active`, and `history`, with per-mission `map_anchor` and `travel` summaries.
423
- - `GET /v1/supervision/missions` returns the same mission buckets through the supervision namespace.
424
- - `GET /v1/governance/my` returns governance eligibility, home planet, governed planets, proposal activity, linked organization governance state, charter applications, and active risks.
425
- - `GET /v1/governance/my` now also returns governance journey, civic/expansion qualification tracks, and next governance actions.
426
- - `GET /v1/supervision/governance` returns the same governance payload through the supervision namespace.
427
- - `GET /v1/catalog/bootstrap` returns bootstrap catalogs for factions, roles, strategies, organization permissions, organization proposal kinds, controller kinds, ownership scopes, mission domains, travel risk levels, and world zones.
428
- - `GET /v1/game/catalog` returns the current operation catalog for stages, roles, and factions.
429
- - `GET /v1/game/status` returns the current public identity's operation stage, progression tier, objectives, qualifications, governance journey, bootstrap state, bootstrap flow, starter mission view, and a `supervision` read model with `next_actions`, `alerts`, and `priority_cards`.
430
- - `GET /v1/supervision/status` returns the same payload through the supervision namespace.
431
- - `GET /v1/game/bootstrap` returns the canonical bootstrap payload.
432
- - `GET /v1/supervision/bootstrap` returns the same bootstrap payload through the supervision namespace.
433
- - `GET /v1/game/starter-missions` returns role-aware starter mission templates, an ordered starter objective chain, and any already-created missions for the selected identity.
434
- - `POST /v1/game/starter-missions/bootstrap` creates missing starter missions for the selected identity without duplicating existing starter templates.
435
- - `GET /v1/game/mission-pack` now includes current-stage templates, next-stage previews, payload schemas, pack summaries, and high-severity home-zone event templates when economic, spatial, or political pressure is active.
436
- - `GET /v1/galaxy/map` returns the active official base map for client rendering.
437
- - `GET /v1/galaxy/maps` returns the current map catalog, which currently exposes the official `genesis-base` summary.
438
- - `GET /v1/galaxy/travel/state` returns the current persisted system position and active travel session for the selected identity.
439
- - `GET /v1/galaxy/travel/options` returns direct travel options from the current home system or requested origin system, including risk levels and warnings.
440
- - `GET /v1/galaxy/travel/plan` returns the recommended path, total travel cost, total risk, and warnings between two systems on the active map.
441
- - `POST /v1/galaxy/travel/depart` starts a persisted travel session toward a destination system.
442
- - `POST /v1/galaxy/travel/arrive` completes the active travel session, updates the persisted system position, and records arrival consequences for mission and governance context.
443
- - `GET /v1/organizations/my` returns the current public identity's organization memberships, member counts, mission counts, and subnet-readiness summary.
444
- - `GET /v1/supervision/briefing` returns the current briefing payload for supervision surfaces.
445
- - `GET /v1/night-shift/summary` mirrors the raw night-shift report.
446
- - `GET /v1/night-shift/narrative` mirrors the narrative-form night-shift payload.
447
- - `GET|POST /v1/civilization/organizations` lists or creates world organizations for a public identity.
448
- - `POST /v1/civilization/organizations/members` adds or updates organization membership for an existing public identity.
449
- - `GET|POST /v1/civilization/organizations/proposals` lists or creates organization-internal governance proposals, including subnet charter proposals.
450
- - `POST /v1/civilization/organizations/proposals/vote` lets active members vote on organization proposals.
451
- - `POST /v1/civilization/organizations/proposals/finalize` accepts or rejects an organization proposal after enough internal support.
452
- - `POST /v1/civilization/organizations/charters` submits a subnet charter application once an accepted charter proposal and subnet-readiness gates are in place.
453
- - `POST /v1/civilization/organizations/treasury/fund` and `POST /v1/civilization/organizations/treasury/spend` mutate shared organization watt reserves for founder/officer roles.
454
- - `POST /v1/civilization/organizations/missions` publishes organization-issued missions, optionally spending committed treasury watt, for members with `publish_missions`.
355
+ - Supervision surfaces:
356
+ - `/supervision`
357
+ - `/v1/supervision/home`
358
+ - `/v1/supervision/identities`
359
+ - `/v1/supervision/missions`
360
+ - `/v1/supervision/governance`
361
+ - `/v1/supervision/status`
362
+ - `/v1/supervision/bootstrap`
363
+ - `/v1/supervision/briefing`
364
+ - Identity and civilization surfaces:
365
+ - `/v1/civilization/identities`
366
+ - `/v1/civilization/bootstrap-identity`
367
+ - `/v1/civilization/public-identity`
368
+ - `/v1/civilization/controller-binding`
369
+ - `/v1/civilization/profile`
370
+ - `/v1/catalog/bootstrap`
371
+ - Mission, game, and world surfaces:
372
+ - `/v1/missions/*`
373
+ - `/v1/game/catalog`
374
+ - `/v1/game/status`
375
+ - `/v1/game/bootstrap`
376
+ - `/v1/game/starter-missions`
377
+ - `/v1/game/mission-pack`
378
+ - `/v1/galaxy/map`
379
+ - `/v1/galaxy/maps`
380
+ - `/v1/galaxy/travel/*`
381
+ - `/v1/galaxy/events*`
382
+ - Governance and organizations:
383
+ - `/v1/governance/my`
384
+ - `/v1/organizations/my`
385
+ - `/v1/civilization/organizations*`
386
+ - Agent social:
387
+ - `/v1/civilization/friends`
388
+ - `/v1/civilization/agent-friends`
389
+ - `/v1/civilization/agent-dm/threads`
390
+ - `/v1/civilization/agent-dm/messages`
391
+ - Narrative and reporting:
392
+ - `/v1/night-shift/summary`
393
+ - `/v1/night-shift/narrative`
455
394
 
456
395
  ### Global Client Topology
457
396
 
@@ -461,7 +400,7 @@ The production path for a globally deployed `wattetheria-client` is:
461
400
  2. the node maintains its local authenticated control plane for operator and local tooling use
462
401
  3. the node periodically builds a signed public client snapshot
463
402
  4. the node publishes that snapshot over wattswarm as a public gossip packet
464
- 5. `wattetheria-gateway` observes wattswarm, verifies signatures, upserts node snapshots, and serves aggregated global data
403
+ 5. `wattetheria-gateway` observes wattswarm, verifies signatures, upserts node snapshots, and serves aggregated global data, including social read models derived from `friend_relationships`, `pending_friend_requests`, `public_blocks`, `dm_threads`, and `dm_messages`
465
404
  6. `wattetheria-client` reads the gateway, not arbitrary user-local nodes
466
405
 
467
406
  This split is intentional:
@@ -497,6 +436,7 @@ This split is intentional:
497
436
  - `crates/kernel-core/src/game` - agent-operation orchestration layer that turns missions, governance, map state, and influence metrics into runtime progression and supervision state
498
437
  - `crates/kernel-core/src/map` - independent world map domain for official base-map models, validation, and persistence
499
438
  - `crates/kernel-core/src/civilization` - application-layer civilization models for missions, world state, profiles, and influence metrics
439
+ - `crates/social` - product-layer agent social domain, policy, and SQLite-backed persistence for friend requests, friendships, blocks, DM threads, and DM messages
500
440
  - `crates/control-plane` - local authenticated HTTP/WebSocket control plane
501
441
  - `crates/observatory-core` - observatory HTTP/store library behind the observatory app
502
442
  - `crates/conformance` - JSON schema conformance helpers and tests
@@ -621,6 +561,18 @@ CLI prerequisites:
621
561
  The CLI handles image pull, deployment directory setup, environment generation, container start,
622
562
  and health checks internally.
623
563
 
564
+ Version commands:
565
+
566
+ - `npx wattetheria --version` shows the current Wattetheria release version
567
+ - `npx wattetheria version --images` prints the configured image refs for the current deployment
568
+ - `npx wattetheria version --cli` shows the deployment CLI package version
569
+
570
+ Release deployments bind-mount host-visible state by default:
571
+
572
+ - `./data/wattetheria` contains `control.token`, kernel state, and `.agent-participation/*`
573
+ - `./data/wattswarm` contains shared wattswarm runtime state
574
+ - users can point local AI assistants at the files inside `./data/wattetheria/.agent-participation/`
575
+
624
576
  Local development for the node and observatory:
625
577
 
626
578
  ```bash
@@ -643,7 +595,7 @@ pwsh ./scripts/deploy-release.ps1
643
595
  - `docker-compose.yml` is the local `wattetheria`-only development stack
644
596
  - `docker-compose.full.yml` is the local joint development stack for `wattetheria` + `wattswarm`
645
597
  - `docker-compose.release.yml` is the image-based release deployment asset used by the CLI and fallback scripts
646
- - `.env.release.example` is the release deployment environment template used by the CLI and fallback scripts
598
+ - `.env.release` is the release deployment environment template used by the CLI and fallback scripts
647
599
  - `scripts/deploy-release.ps1` is a cross-platform fallback deployment entry point
648
600
  - this repository does not include `wattetheria-gateway`; gateway is a separate project and deployment unit
649
601
  - Entrypoints live in `scripts/docker-kernel-entrypoint.sh` and `scripts/docker-observatory-entrypoint.sh`
@@ -689,6 +641,8 @@ Brain provider notes:
689
641
  - `kind: "openai-compatible"` for local gateways that expose `/models` and `/chat/completions`
690
642
  - Cloud models are supported through `kind: "openai-compatible"`
691
643
  - OpenClaw should be configured as `openai-compatible` when its gateway exposes an OpenAI-style `/v1` surface
644
+ - In Docker deployments, if the AI gateway is running on the host machine, prefer
645
+ `http://host.docker.internal:<port>/v1` for `WATTETHERIA_BRAIN_BASE_URL`
692
646
 
693
647
  Example OpenClaw/OpenAI-compatible config:
694
648
 
@@ -709,6 +663,22 @@ Example OpenClaw/OpenAI-compatible config:
709
663
  }
710
664
  ```
711
665
 
666
+ Release deployment `.env` example for a host-local OpenClaw gateway:
667
+
668
+ ```env
669
+ WATTETHERIA_HOST_STATE_DIR=./data/wattetheria
670
+ WATTSWARM_HOST_STATE_DIR=./data/wattswarm
671
+ WATTETHERIA_AGENT_CONTROL_PLANE_ENDPOINT=http://127.0.0.1:7777
672
+ WATTETHERIA_AGENT_WATTSWARM_UI_BASE_URL=http://127.0.0.1:7788
673
+ WATTETHERIA_AGENT_WATTSWARM_SYNC_GRPC_ENDPOINT=http://127.0.0.1:7791
674
+ WATTETHERIA_AGENT_HOST_DATA_DIR=./data/wattetheria
675
+ WATTETHERIA_BRAIN_PROVIDER_KIND=openai-compatible
676
+ WATTETHERIA_BRAIN_BASE_URL=http://host.docker.internal:18789/v1
677
+ WATTETHERIA_BRAIN_MODEL=openclaw
678
+ WATTETHERIA_BRAIN_API_KEY_ENV=OPENCLAW_API_KEY
679
+ OPENCLAW_API_KEY=replace-me
680
+ ```
681
+
712
682
  When `servicenet_base_url` is configured, the control plane exposes local proxy routes for external agent discovery and execution:
713
683
 
714
684
  - `GET /v1/servicenet/agents`
@@ -723,6 +693,13 @@ When the kernel starts, it writes a node-local agent participation contract to:
723
693
 
724
694
  These files tell an attached agent host how to authenticate to Wattetheria and which civilization topic endpoints to call in order to participate in the wattswarm-backed network.
725
695
 
696
+ In Docker release deployments, those files live under the host bind mount, so a local AI assistant can read them directly from:
697
+
698
+ - `./data/wattetheria/.agent-participation/manifest.json`
699
+ - `./data/wattetheria/.agent-participation/README.md`
700
+ - `./data/wattetheria/.agent-participation/status.json`
701
+ - `./data/wattetheria/control.token`
702
+
726
703
  Global `wattetheria-client` visibility is provided by `wattetheria-gateway`, which subscribes to wattswarm gossip topics and ingests signed snapshots from the network. Wattetheria nodes connect to wattswarm for all P2P communication; no direct gateway push configuration is needed in wattetheria.
727
704
 
728
705
  After a user updates the node's brain provider config, use:
@@ -16,6 +16,8 @@ services:
16
16
  kernel:
17
17
  image: ${WATTETHERIA_KERNEL_IMAGE:-wattetheria/wattetheria-kernel:latest}
18
18
  restart: unless-stopped
19
+ env_file:
20
+ - ${WATTETHERIA_RUNTIME_ENV_FILE:-.env.release.local}
19
21
  depends_on:
20
22
  wattswarm-kernel:
21
23
  condition: service_started
@@ -24,6 +26,10 @@ services:
24
26
  WATTETHERIA_CONTROL_PLANE_BIND: 0.0.0.0:7777
25
27
  WATTETHERIA_WATTSWARM_UI_BASE_URL: http://wattswarm-kernel:7788
26
28
  WATTETHERIA_WATTSWARM_SYNC_GRPC_ENDPOINT: http://wattswarm-kernel:7791
29
+ WATTETHERIA_AGENT_CONTROL_PLANE_ENDPOINT: ${WATTETHERIA_AGENT_CONTROL_PLANE_ENDPOINT:-http://127.0.0.1:7777}
30
+ WATTETHERIA_AGENT_WATTSWARM_UI_BASE_URL: ${WATTETHERIA_AGENT_WATTSWARM_UI_BASE_URL:-http://127.0.0.1:7788}
31
+ WATTETHERIA_AGENT_WATTSWARM_SYNC_GRPC_ENDPOINT: ${WATTETHERIA_AGENT_WATTSWARM_SYNC_GRPC_ENDPOINT:-http://127.0.0.1:7791}
32
+ WATTETHERIA_AGENT_HOST_DATA_DIR: ${WATTETHERIA_AGENT_HOST_DATA_DIR:-./data/wattetheria}
27
33
  WATTETHERIA_BRAIN_PROVIDER_KIND: ${WATTETHERIA_BRAIN_PROVIDER_KIND:-rules}
28
34
  WATTETHERIA_BRAIN_BASE_URL: ${WATTETHERIA_BRAIN_BASE_URL:-}
29
35
  WATTETHERIA_BRAIN_MODEL: ${WATTETHERIA_BRAIN_MODEL:-}
@@ -33,9 +39,11 @@ services:
33
39
  WATTETHERIA_AUTONOMY_INTERVAL_SEC: ${WATTETHERIA_AUTONOMY_INTERVAL_SEC:-30}
34
40
  OPENCLAW_API_KEY: ${OPENCLAW_API_KEY:-}
35
41
  volumes:
36
- - wattetheria_state:/var/lib/wattetheria
42
+ - ${WATTETHERIA_HOST_STATE_DIR:-./data/wattetheria}:/var/lib/wattetheria
37
43
  ports:
38
44
  - "${WATTETHERIA_CONTROL_PLANE_BIND_HOST:-127.0.0.1}:${WATTETHERIA_CONTROL_PLANE_PORT:-7777}:7777"
45
+ extra_hosts:
46
+ - "host.docker.internal:host-gateway"
39
47
  networks:
40
48
  - watt-internal
41
49
  entrypoint: ["/app/scripts/docker-kernel-entrypoint.sh"]
@@ -108,9 +116,10 @@ services:
108
116
  WATTSWARM_UDP_ANNOUNCE_ADDR: ${WATTSWARM_UDP_ANNOUNCE_ADDR:-239.255.42.99}
109
117
  WATTSWARM_UDP_ANNOUNCE_PORT: ${WATTSWARM_UDP_ANNOUNCE_PORT:-37931}
110
118
  volumes:
111
- - wattswarm_state_data:/var/lib/wattswarm
119
+ - ${WATTSWARM_HOST_STATE_DIR:-./data/wattswarm}:/var/lib/wattswarm
112
120
  ports:
113
121
  - "${WATTSWARM_UI_BIND_HOST:-127.0.0.1}:${WATTSWARM_UI_PORT:-7788}:7788"
122
+ - "${WATTSWARM_SYNC_GRPC_BIND_HOST:-127.0.0.1}:${WATTSWARM_SYNC_GRPC_PORT:-7791}:7791"
114
123
  - "${WATTSWARM_P2P_HOST_PORT:-4001}:${WATTSWARM_P2P_PORT:-4001}"
115
124
  - "${WATTSWARM_UDP_ANNOUNCE_HOST_PORT:-37931}:${WATTSWARM_UDP_ANNOUNCE_PORT:-37931}/udp"
116
125
  networks:
@@ -135,7 +144,7 @@ services:
135
144
  WATTSWARM_WORKER_POLL_MS: ${WATTSWARM_WORKER_POLL_MS:-250}
136
145
  WATTSWARM_WORKER_LEASE_MS: ${WATTSWARM_WORKER_LEASE_MS:-30000}
137
146
  volumes:
138
- - wattswarm_state_data:/var/lib/wattswarm
147
+ - ${WATTSWARM_HOST_STATE_DIR:-./data/wattswarm}:/var/lib/wattswarm
139
148
  networks:
140
149
  - watt-internal
141
150
  entrypoint: ["/app/target/release/wattswarm"]
@@ -156,9 +165,7 @@ services:
156
165
  - "${WATTSWARM_WORKER_LEASE_MS:-30000}"
157
166
 
158
167
  volumes:
159
- wattetheria_state:
160
168
  wattswarm_pg_data:
161
- wattswarm_state_data:
162
169
 
163
170
  networks:
164
171
  watt-internal:
package/lib/cli.js CHANGED
@@ -7,10 +7,10 @@ const { createInterface } = require("node:readline/promises");
7
7
 
8
8
  const PACKAGE_ROOT = path.resolve(__dirname, "..");
9
9
  const PACKAGE_JSON = require(path.join(PACKAGE_ROOT, "package.json"));
10
- const RELEASE_ENV_TEMPLATE = path.join(PACKAGE_ROOT, ".env.release.example");
10
+ const RELEASE_ENV_TEMPLATE = path.join(PACKAGE_ROOT, ".env.release");
11
11
  const DEFAULT_DEPLOY_DIR = path.join(os.homedir(), ".wattetheria", "deploy");
12
12
  const DEFAULT_PROJECT_NAME = "wattetheria";
13
- const DEFAULT_COMMAND = "install";
13
+ const DEFAULT_COMMAND = "help";
14
14
  const IMAGE_KEYS = [
15
15
  "WATTETHERIA_KERNEL_IMAGE",
16
16
  "WATTETHERIA_OBSERVATORY_IMAGE",
@@ -18,6 +18,7 @@ const IMAGE_KEYS = [
18
18
  "WATTSWARM_RUNTIME_IMAGE",
19
19
  "WATTSWARM_WORKER_IMAGE"
20
20
  ];
21
+ const HOST_STATE_DIR_KEYS = ["WATTETHERIA_HOST_STATE_DIR", "WATTSWARM_HOST_STATE_DIR"];
21
22
  const DOCKER_INSTALL_URLS = {
22
23
  darwin: "https://www.docker.com/products/docker-desktop/",
23
24
  win32: "https://www.docker.com/products/docker-desktop/",
@@ -36,6 +37,8 @@ Usage:
36
37
  npx wattetheria install
37
38
 
38
39
  Commands:
40
+ version Show Wattetheria release version
41
+ images Show configured release images
39
42
  install Prepare deployment, pull images, and start the stack
40
43
  start Start an existing deployment
41
44
  status Show docker compose status
@@ -47,7 +50,9 @@ Commands:
47
50
  help Show this help
48
51
 
49
52
  Options:
50
- --version Show CLI version
53
+ --version, -v Alias for \`version\`
54
+ --cli With \`version\`, show deployment CLI version instead
55
+ --images With \`version\`, print configured image refs
51
56
  --dir <path> Deployment directory (default: ${DEFAULT_DEPLOY_DIR})
52
57
  --project-name <name> Docker compose project name (default: ${DEFAULT_PROJECT_NAME})
53
58
  --tag <tag> Override all release image tags
@@ -59,25 +64,12 @@ Options:
59
64
  }
60
65
 
61
66
  function parseArgs(argv) {
62
- if (argv[0] === "--version" || argv[0] === "-v") {
63
- return {
64
- command: "version",
65
- options: {
66
- dir: DEFAULT_DEPLOY_DIR,
67
- projectName: DEFAULT_PROJECT_NAME,
68
- tag: null,
69
- force: false,
70
- healthChecks: true,
71
- volumes: false,
72
- purge: false,
73
- composeArgs: []
74
- }
75
- };
76
- }
77
-
78
67
  let command = DEFAULT_COMMAND;
79
68
  let index = 0;
80
- if (argv[0] && !argv[0].startsWith("-")) {
69
+ if (argv[0] === "--version" || argv[0] === "-v") {
70
+ command = "version";
71
+ index = 1;
72
+ } else if (argv[0] && !argv[0].startsWith("-")) {
81
73
  command = argv[0];
82
74
  index = 1;
83
75
  }
@@ -90,7 +82,9 @@ function parseArgs(argv) {
90
82
  healthChecks: true,
91
83
  volumes: false,
92
84
  purge: false,
93
- composeArgs: []
85
+ composeArgs: [],
86
+ versionTarget: "release",
87
+ includeImages: false
94
88
  };
95
89
 
96
90
  while (index < argv.length) {
@@ -105,6 +99,10 @@ function parseArgs(argv) {
105
99
  options.force = true;
106
100
  } else if (arg === "--no-health-checks") {
107
101
  options.healthChecks = false;
102
+ } else if (arg === "--cli") {
103
+ options.versionTarget = "cli";
104
+ } else if (arg === "--images") {
105
+ options.includeImages = true;
108
106
  } else if (arg === "--volumes") {
109
107
  options.volumes = true;
110
108
  } else if (arg === "--purge") {
@@ -181,32 +179,86 @@ function extractImageTag(imageRef) {
181
179
  return imageRef.slice(lastColon + 1).trim();
182
180
  }
183
181
 
184
- function getDefaultReleaseVersion() {
185
- try {
186
- const envMap = readEnvFile(RELEASE_ENV_TEMPLATE);
187
- const tags = IMAGE_KEYS
188
- .map((key) => extractImageTag(envMap.get(key)))
189
- .filter(Boolean);
190
- if (tags.length === IMAGE_KEYS.length && new Set(tags).size === 1) {
191
- return tags[0];
182
+ function resolveEnvReference(value, envMap, seen = new Set()) {
183
+ if (!value) {
184
+ return "";
185
+ }
186
+ return value.replace(/\$\{([^}:]+)(?::-([^}]*))?\}/g, (_match, key, fallback = "") => {
187
+ if (seen.has(key)) {
188
+ return fallback;
192
189
  }
193
- } catch (error) {
194
- // fall through to package version
190
+ const resolved = envMap.get(key);
191
+ if (!resolved || !resolved.trim()) {
192
+ return fallback;
193
+ }
194
+ const nextSeen = new Set(seen);
195
+ nextSeen.add(key);
196
+ return resolveEnvReference(resolved, envMap, nextSeen);
197
+ });
198
+ }
199
+
200
+ function getReleaseImageMap(filePath) {
201
+ const envMap = readEnvFile(filePath);
202
+ const images = new Map();
203
+ for (const key of IMAGE_KEYS) {
204
+ const rawValue = envMap.get(key);
205
+ if (!rawValue) {
206
+ continue;
207
+ }
208
+ images.set(key, resolveEnvReference(rawValue.trim(), envMap));
195
209
  }
196
- return PACKAGE_JSON.version;
210
+ return images;
197
211
  }
198
212
 
199
- function formatVersionString() {
213
+ function getReleaseSource(options) {
214
+ const deployEnvPath = envFilePath(options);
215
+ if (fs.existsSync(deployEnvPath)) {
216
+ return {
217
+ kind: "deployment",
218
+ path: deployEnvPath
219
+ };
220
+ }
221
+ return {
222
+ kind: "template",
223
+ path: RELEASE_ENV_TEMPLATE
224
+ };
225
+ }
226
+
227
+ function getReleaseVersionInfo(options) {
228
+ const source = getReleaseSource(options);
229
+ const images = getReleaseImageMap(source.path);
230
+ const tags = IMAGE_KEYS
231
+ .map((key) => extractImageTag(images.get(key)))
232
+ .filter(Boolean);
233
+ const version = tags.length === IMAGE_KEYS.length && new Set(tags).size === 1
234
+ ? tags[0]
235
+ : "custom";
236
+ return {
237
+ version,
238
+ images,
239
+ source
240
+ };
241
+ }
242
+
243
+ function formatReleaseVersionString(options) {
200
244
  const revision = getGitRevision();
201
- const releaseVersion = getDefaultReleaseVersion();
245
+ const releaseVersion = getReleaseVersionInfo(options).version;
202
246
  if (revision) {
203
247
  return `Wattetheria ${releaseVersion} (${revision})`;
204
248
  }
205
249
  return `Wattetheria ${releaseVersion}`;
206
250
  }
207
251
 
208
- function formatBanner() {
209
- return `${formatVersionString()} — Local agent runtime with swarm sync and external agent reach.`;
252
+ function formatCliVersionString() {
253
+ const revision = getGitRevision();
254
+ if (revision) {
255
+ return `Wattetheria CLI ${PACKAGE_JSON.version} (${revision})`;
256
+ }
257
+ return `Wattetheria CLI ${PACKAGE_JSON.version}`;
258
+ }
259
+
260
+ function formatBanner(options) {
261
+ return `${formatReleaseVersionString(options)} — Local agent runtime with swarm sync and external agent reach.`;
210
262
  }
211
263
 
212
264
  function formatDockerStatusMessage(status) {
@@ -360,7 +412,7 @@ async function ensureDockerAvailable(options = {}) {
360
412
  function ensureDeploymentAssets(options) {
361
413
  fs.mkdirSync(options.dir, { recursive: true });
362
414
 
363
- const templateEnvPath = path.join(PACKAGE_ROOT, ".env.release.example");
415
+ const templateEnvPath = path.join(PACKAGE_ROOT, ".env.release");
364
416
  const templateComposePath = path.join(PACKAGE_ROOT, "docker-compose.release.yml");
365
417
  const targetEnvPath = envFilePath(options);
366
418
  const targetComposePath = composeFilePath(options);
@@ -373,10 +425,12 @@ function ensureDeploymentAssets(options) {
373
425
  }
374
426
 
375
427
  const envMap = readEnvFile(targetEnvPath);
428
+ envMap.set("WATTETHERIA_RUNTIME_ENV_FILE", path.basename(targetEnvPath));
376
429
  ensureDatabasePassword(envMap);
377
430
  if (options.tag) {
378
431
  pinImageTags(envMap, options.tag);
379
432
  }
433
+ ensureHostStateDirectories(options.dir, envMap);
380
434
  writeEnvFile(targetEnvPath, envMap);
381
435
  }
382
436
 
@@ -423,6 +477,19 @@ function ensureDatabasePassword(envMap) {
423
477
  }
424
478
  }
425
479
 
480
+ function ensureHostStateDirectories(baseDir, envMap) {
481
+ for (const key of HOST_STATE_DIR_KEYS) {
482
+ const configured = envMap.get(key);
483
+ if (!configured || !configured.trim()) {
484
+ continue;
485
+ }
486
+ const resolved = path.isAbsolute(configured)
487
+ ? configured
488
+ : path.resolve(baseDir, configured);
489
+ fs.mkdirSync(resolved, { recursive: true });
490
+ }
491
+ }
492
+
426
493
  function pinImageTags(envMap, tag) {
427
494
  for (const key of IMAGE_KEYS) {
428
495
  if (!envMap.has(key)) {
@@ -619,16 +686,39 @@ function doctor() {
619
686
  console.log(`Node.js ${process.version} is available.`);
620
687
  }
621
688
 
622
- function printVersion() {
623
- console.log(formatVersionString());
689
+ function printReleaseImages(options) {
690
+ const release = getReleaseVersionInfo(options);
691
+ console.log(`Source: ${release.source.path}`);
692
+ for (const key of IMAGE_KEYS) {
693
+ const imageRef = release.images.get(key) || "(not configured)";
694
+ console.log(`${key}: ${imageRef}`);
695
+ }
696
+ }
697
+
698
+ function printVersion(options) {
699
+ if (options.versionTarget === "cli") {
700
+ console.log(formatCliVersionString());
701
+ return;
702
+ }
703
+
704
+ console.log(formatReleaseVersionString(options));
705
+ if (options.includeImages) {
706
+ printReleaseImages(options);
707
+ }
708
+ }
709
+
710
+ function printImages(options) {
711
+ const release = getReleaseVersionInfo(options);
712
+ console.log(formatReleaseVersionString(options));
713
+ printReleaseImages(options);
624
714
  }
625
715
 
626
716
  function shouldPrintBanner(command) {
627
- return !["help", "--help", "-h", "version"].includes(command);
717
+ return !["help", "--help", "-h", "version", "images"].includes(command);
628
718
  }
629
719
 
630
- function printBanner() {
631
- console.log(formatBanner());
720
+ function printBanner(options) {
721
+ console.log(formatBanner(options));
632
722
  console.log("");
633
723
  }
634
724
 
@@ -636,10 +726,16 @@ async function run(argv) {
636
726
  const { command, options } = parseArgs(argv);
637
727
 
638
728
  if (shouldPrintBanner(command)) {
639
- printBanner();
729
+ printBanner(options);
640
730
  }
641
731
 
642
732
  switch (command) {
733
+ case "version":
734
+ printVersion(options);
735
+ return;
736
+ case "images":
737
+ printImages(options);
738
+ return;
643
739
  case "install":
644
740
  await install(options);
645
741
  return;
@@ -666,9 +762,6 @@ async function run(argv) {
666
762
  case "doctor":
667
763
  doctor();
668
764
  return;
669
- case "version":
670
- printVersion();
671
- return;
672
765
  case "help":
673
766
  case "--help":
674
767
  case "-h":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wattetheria",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Wattetheria deployment CLI",
5
5
  "license": "Apache-2.0",
6
6
  "type": "commonjs",
@@ -9,7 +9,7 @@
9
9
  "bin/",
10
10
  "lib/",
11
11
  "docker-compose.release.yml",
12
- ".env.release.example",
12
+ ".env.release",
13
13
  "README.md",
14
14
  "LICENSE"
15
15
  ],