@silicaclaw/cli 1.0.0-beta.2 → 1.0.0-beta.21

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.
Files changed (79) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/INSTALL.md +36 -0
  3. package/README.md +40 -0
  4. package/apps/local-console/public/index.html +81 -63
  5. package/apps/local-console/src/server.ts +41 -21
  6. package/docs/CLOUDFLARE_RELAY.md +61 -0
  7. package/package.json +6 -1
  8. package/packages/core/dist/crypto.d.ts +6 -0
  9. package/packages/core/dist/crypto.js +50 -0
  10. package/packages/core/dist/directory.d.ts +17 -0
  11. package/packages/core/dist/directory.js +145 -0
  12. package/packages/core/dist/identity.d.ts +2 -0
  13. package/packages/core/dist/identity.js +18 -0
  14. package/packages/core/dist/index.d.ts +11 -0
  15. package/packages/core/dist/index.js +27 -0
  16. package/packages/core/dist/indexing.d.ts +6 -0
  17. package/packages/core/dist/indexing.js +43 -0
  18. package/packages/core/dist/presence.d.ts +4 -0
  19. package/packages/core/dist/presence.js +23 -0
  20. package/packages/core/dist/profile.d.ts +4 -0
  21. package/packages/core/dist/profile.js +39 -0
  22. package/packages/core/dist/publicProfileSummary.d.ts +70 -0
  23. package/packages/core/dist/publicProfileSummary.js +103 -0
  24. package/packages/core/dist/socialConfig.d.ts +99 -0
  25. package/packages/core/dist/socialConfig.js +288 -0
  26. package/packages/core/dist/socialResolver.d.ts +46 -0
  27. package/packages/core/dist/socialResolver.js +237 -0
  28. package/packages/core/dist/socialTemplate.d.ts +2 -0
  29. package/packages/core/dist/socialTemplate.js +88 -0
  30. package/packages/core/dist/types.d.ts +37 -0
  31. package/packages/core/dist/types.js +2 -0
  32. package/packages/core/src/socialConfig.ts +7 -6
  33. package/packages/core/src/socialResolver.ts +17 -5
  34. package/packages/network/dist/abstractions/messageEnvelope.d.ts +28 -0
  35. package/packages/network/dist/abstractions/messageEnvelope.js +36 -0
  36. package/packages/network/dist/abstractions/peerDiscovery.d.ts +43 -0
  37. package/packages/network/dist/abstractions/peerDiscovery.js +2 -0
  38. package/packages/network/dist/abstractions/topicCodec.d.ts +4 -0
  39. package/packages/network/dist/abstractions/topicCodec.js +2 -0
  40. package/packages/network/dist/abstractions/transport.d.ts +36 -0
  41. package/packages/network/dist/abstractions/transport.js +2 -0
  42. package/packages/network/dist/codec/jsonMessageEnvelopeCodec.d.ts +5 -0
  43. package/packages/network/dist/codec/jsonMessageEnvelopeCodec.js +24 -0
  44. package/packages/network/dist/codec/jsonTopicCodec.d.ts +5 -0
  45. package/packages/network/dist/codec/jsonTopicCodec.js +12 -0
  46. package/packages/network/dist/discovery/heartbeatPeerDiscovery.d.ts +28 -0
  47. package/packages/network/dist/discovery/heartbeatPeerDiscovery.js +144 -0
  48. package/packages/network/dist/index.d.ts +14 -0
  49. package/packages/network/dist/index.js +30 -0
  50. package/packages/network/dist/localEventBus.d.ts +9 -0
  51. package/packages/network/dist/localEventBus.js +47 -0
  52. package/packages/network/dist/mock.d.ts +8 -0
  53. package/packages/network/dist/mock.js +24 -0
  54. package/packages/network/dist/realPreview.d.ts +105 -0
  55. package/packages/network/dist/realPreview.js +327 -0
  56. package/packages/network/dist/relayPreview.d.ts +133 -0
  57. package/packages/network/dist/relayPreview.js +320 -0
  58. package/packages/network/dist/transport/udpLanBroadcastTransport.d.ts +23 -0
  59. package/packages/network/dist/transport/udpLanBroadcastTransport.js +153 -0
  60. package/packages/network/dist/types.d.ts +6 -0
  61. package/packages/network/dist/types.js +2 -0
  62. package/packages/network/dist/webrtcPreview.d.ts +163 -0
  63. package/packages/network/dist/webrtcPreview.js +844 -0
  64. package/packages/network/src/index.ts +1 -0
  65. package/packages/network/src/relayPreview.ts +425 -0
  66. package/packages/storage/dist/index.d.ts +3 -0
  67. package/packages/storage/dist/index.js +19 -0
  68. package/packages/storage/dist/jsonRepo.d.ts +7 -0
  69. package/packages/storage/dist/jsonRepo.js +29 -0
  70. package/packages/storage/dist/repos.d.ts +21 -0
  71. package/packages/storage/dist/repos.js +41 -0
  72. package/packages/storage/dist/socialRuntimeRepo.d.ts +5 -0
  73. package/packages/storage/dist/socialRuntimeRepo.js +52 -0
  74. package/packages/storage/src/socialRuntimeRepo.ts +3 -3
  75. package/packages/storage/tsconfig.json +6 -1
  76. package/scripts/quickstart.sh +286 -20
  77. package/scripts/silicaclaw-cli.mjs +271 -1
  78. package/scripts/silicaclaw-gateway.mjs +411 -0
  79. package/scripts/webrtc-signaling-server.mjs +52 -1
package/CHANGELOG.md CHANGED
@@ -8,6 +8,14 @@
8
8
  - `INSTALL.md`
9
9
  - `DEMO_GUIDE.md`
10
10
  - `RELEASE_NOTES_v1.0.md`
11
+ - CLI onboarding install UX polish:
12
+ - added shell alias fallback (`silicaclaw -> npx @silicaclaw/cli@beta`) when global install is unavailable
13
+ - reduced reliance on manual PATH/env edits for first-run success
14
+ - `silicaclaw update` guidance polish:
15
+ - prioritize zero-setup `npx` flow
16
+ - clarify global install is optional
17
+ - hide global-install recommendation during `npx` runtime to avoid repeated `EACCES` loops
18
+ - add explicit `command not found` alias guidance for first-run shells
11
19
  - README first-screen and structure polish:
12
20
  - fixed v1.0 beta project positioning
13
21
  - added concise feature summary
package/INSTALL.md CHANGED
@@ -22,11 +22,47 @@ CLI-style onboarding command (recommended, zero-config):
22
22
  npx @silicaclaw/cli@beta onboard
23
23
  ```
24
24
 
25
+ Cross-network quick wizard (defaults to global-preview):
26
+
27
+ ```bash
28
+ npx @silicaclaw/cli@beta connect
29
+ ```
30
+
31
+ Check/update CLI version:
32
+
33
+ ```bash
34
+ npx @silicaclaw/cli@beta update
35
+ ```
36
+
37
+ Gateway background service commands:
38
+
39
+ ```bash
40
+ npx @silicaclaw/cli@beta gateway start --mode=local
41
+ npx @silicaclaw/cli@beta gateway status
42
+ npx @silicaclaw/cli@beta gateway restart --mode=lan
43
+ npx @silicaclaw/cli@beta gateway stop
44
+ ```
45
+
46
+ For most home users, just press Enter on defaults and use `local` mode first.
47
+
25
48
  Optional global install (advanced users only):
26
49
 
27
50
  ```bash
28
51
  npm i -g @silicaclaw/cli
29
52
  silicaclaw onboard
53
+ silicaclaw connect
54
+ silicaclaw update
55
+ silicaclaw gateway start --mode=local
56
+ silicaclaw gateway status
57
+ silicaclaw gateway stop
58
+ ```
59
+
60
+ If global install fails with `EACCES`, use alias mode (no PATH edits):
61
+
62
+ ```bash
63
+ alias silicaclaw='npx -y @silicaclaw/cli@beta'
64
+ silicaclaw onboard
65
+ silicaclaw update
30
66
  ```
31
67
 
32
68
  ## 3. Run
package/README.md CHANGED
@@ -28,6 +28,27 @@ Without servers, accounts, or central control.
28
28
  npx @silicaclaw/cli@beta onboard
29
29
  ```
30
30
 
31
+ Cross-network preview quick wizard:
32
+
33
+ ```bash
34
+ npx @silicaclaw/cli@beta connect
35
+ ```
36
+
37
+ Check and update CLI version:
38
+
39
+ ```bash
40
+ npx @silicaclaw/cli@beta update
41
+ ```
42
+
43
+ Background gateway service:
44
+
45
+ ```bash
46
+ npx @silicaclaw/cli@beta gateway start --mode=local
47
+ npx @silicaclaw/cli@beta gateway status
48
+ npx @silicaclaw/cli@beta gateway restart --mode=lan
49
+ npx @silicaclaw/cli@beta gateway stop
50
+ ```
51
+
31
52
  Or manual:
32
53
 
33
54
  ```bash
@@ -53,11 +74,30 @@ Zero-config (recommended, no global install / no PATH setup):
53
74
  npx @silicaclaw/cli@beta onboard
54
75
  ```
55
76
 
77
+ Cross-network preview (global-preview first):
78
+
79
+ ```bash
80
+ npx @silicaclaw/cli@beta connect
81
+ ```
82
+
56
83
  Optional global install:
57
84
 
58
85
  ```bash
59
86
  npm i -g @silicaclaw/cli
60
87
  silicaclaw onboard
88
+ silicaclaw connect
89
+ silicaclaw update
90
+ silicaclaw gateway start --mode=local
91
+ silicaclaw gateway status
92
+ silicaclaw gateway stop
93
+ ```
94
+
95
+ If global install is blocked by system permissions (`EACCES`), use alias mode:
96
+
97
+ ```bash
98
+ alias silicaclaw='npx -y @silicaclaw/cli@beta'
99
+ silicaclaw onboard
100
+ silicaclaw update
61
101
  ```
62
102
 
63
103
  ## Quick Start (OpenClaw-style)
@@ -427,6 +427,22 @@
427
427
  white-space: pre-wrap;
428
428
  word-break: break-word;
429
429
  }
430
+ .advanced-panel {
431
+ margin-top: 10px;
432
+ }
433
+ .advanced-panel summary {
434
+ cursor: pointer;
435
+ list-style: none;
436
+ user-select: none;
437
+ }
438
+ .advanced-panel summary::-webkit-details-marker { display: none; }
439
+ .advanced-panel summary::after {
440
+ content: "Show";
441
+ float: right;
442
+ color: var(--muted);
443
+ font-size: 12px;
444
+ }
445
+ .advanced-panel[open] summary::after { content: "Hide"; }
430
446
 
431
447
  .toast {
432
448
  position: fixed;
@@ -474,10 +490,7 @@
474
490
  <button class="tab active" data-tab="overview">Overview</button>
475
491
  <button class="tab" data-tab="profile">Profile</button>
476
492
  <button class="tab" data-tab="network">Network</button>
477
- <button class="tab" data-tab="peers">Peers</button>
478
- <button class="tab" data-tab="discovery">Discovery Events</button>
479
- <button class="tab" data-tab="social">Social Config</button>
480
- <button class="tab" data-tab="logs">Logs</button>
493
+ <button class="tab" data-tab="social">Social</button>
481
494
  </nav>
482
495
 
483
496
  <div class="sidebar-foot" id="sideMeta">adapter: -<br/>namespace: -</div>
@@ -602,15 +615,20 @@
602
615
  <div class="mono" id="networkComponents"></div>
603
616
  </div>
604
617
  <div class="card">
605
- <h3 class="title-sm">Actions</h3>
618
+ <h3 class="title-sm">Quick Actions</h3>
606
619
  <div class="actions">
607
620
  <button id="startBroadcastBtn">Start Broadcast</button>
608
621
  <button class="secondary" id="stopBroadcastBtn">Stop Broadcast</button>
609
622
  <button class="secondary" id="broadcastNowBtn">Broadcast Now</button>
623
+ </div>
624
+ <details class="advanced-panel">
625
+ <summary class="title-sm">Advanced Actions</summary>
626
+ <div class="actions" style="margin-top:10px;">
610
627
  <button class="secondary" id="refreshCacheBtn">Refresh Cache</button>
611
628
  <button class="secondary" id="clearCacheBtn">Clear Discovered Cache</button>
612
629
  <button class="secondary" id="quickGlobalPreviewBtn">Enable Cross-network Preview</button>
613
630
  </div>
631
+ </details>
614
632
  <div id="networkFeedback" class="feedback" style="margin-top:10px;">Ready.</div>
615
633
  </div>
616
634
  </div>
@@ -624,32 +642,47 @@
624
642
  <div class="mono mono-block" id="networkStatsSnapshot">-</div>
625
643
  </div>
626
644
  </div>
627
- </section>
628
-
629
- <section id="view-peers" class="view">
630
- <div class="grid" id="peerCards"></div>
631
- <div class="card" style="margin-top:10px;">
632
- <h3 class="title-sm">Peer Inventory</h3>
633
- <div id="peerTableWrap"></div>
634
- </div>
635
- <div class="card" style="margin-top:10px;">
636
- <h3 class="title-sm">Peer Discovery Stats</h3>
637
- <div class="mono mono-block" id="peerStatsWrap">-</div>
638
- </div>
639
- </section>
640
-
641
- <section id="view-discovery" class="view">
642
- <div class="grid" id="discoveryCards"></div>
643
- <div class="split">
645
+ <details class="advanced-panel card">
646
+ <summary class="title-sm">Diagnostics</summary>
647
+ <div class="grid" id="peerCards" style="margin-top:10px;"></div>
644
648
  <div class="card" style="margin-top:10px;">
645
- <h3 class="title-sm">Recent Discovery Events</h3>
646
- <div class="logs" id="discoveryEventList"></div>
649
+ <h3 class="title-sm">Peer Inventory</h3>
650
+ <div id="peerTableWrap"></div>
647
651
  </div>
648
652
  <div class="card" style="margin-top:10px;">
649
- <h3 class="title-sm">Discovery Snapshot</h3>
650
- <div class="mono mono-block" id="discoverySnapshot">-</div>
653
+ <h3 class="title-sm">Peer Discovery Stats</h3>
654
+ <div class="mono mono-block" id="peerStatsWrap">-</div>
651
655
  </div>
652
- </div>
656
+ <div class="grid" id="discoveryCards" style="margin-top:10px;"></div>
657
+ <div class="split">
658
+ <div class="card" style="margin-top:10px;">
659
+ <h3 class="title-sm">Recent Discovery Events</h3>
660
+ <div class="logs" id="discoveryEventList"></div>
661
+ </div>
662
+ <div class="card" style="margin-top:10px;">
663
+ <h3 class="title-sm">Discovery Snapshot</h3>
664
+ <div class="mono mono-block" id="discoverySnapshot">-</div>
665
+ </div>
666
+ </div>
667
+ <div class="card" style="margin-top:10px;">
668
+ <h3 class="title-sm">Logs</h3>
669
+ <div class="toolbar">
670
+ <div class="field">
671
+ <label for="logLevelFilter">Category</label>
672
+ <select id="logLevelFilter">
673
+ <option value="all">all</option>
674
+ <option value="info">info</option>
675
+ <option value="warn">warn</option>
676
+ <option value="error">error</option>
677
+ </select>
678
+ </div>
679
+ <div>
680
+ <button type="button" class="secondary" id="refreshLogsBtn">Refresh Logs</button>
681
+ </div>
682
+ </div>
683
+ <div class="logs" id="logList"></div>
684
+ </div>
685
+ </details>
653
686
  </section>
654
687
 
655
688
  <section id="view-social" class="view">
@@ -666,22 +699,25 @@
666
699
  <div class="grid" id="socialAdvancedCards" style="margin-top:10px;"></div>
667
700
  <div class="mono mono-block" id="socialAdvancedWrap" style="margin-top:10px;">-</div>
668
701
  </details>
669
- <div class="split">
670
- <div class="card" style="margin-top:10px;">
671
- <h3 class="title-sm">Source & Parsed Frontmatter</h3>
672
- <div class="mono mono-block" id="socialSourceWrap">-</div>
673
- <div style="height:10px;"></div>
674
- <div class="mono mono-block" id="socialRawWrap">-</div>
702
+ <details class="advanced-panel card">
703
+ <summary class="title-sm">Source / Runtime / Template</summary>
704
+ <div class="split">
705
+ <div class="card" style="margin-top:10px;">
706
+ <h3 class="title-sm">Source & Parsed Frontmatter</h3>
707
+ <div class="mono mono-block" id="socialSourceWrap">-</div>
708
+ <div style="height:10px;"></div>
709
+ <div class="mono mono-block" id="socialRawWrap">-</div>
710
+ </div>
711
+ <div class="card" style="margin-top:10px;">
712
+ <h3 class="title-sm">Runtime Summary</h3>
713
+ <div class="mono mono-block" id="socialRuntimeWrap">-</div>
714
+ </div>
675
715
  </div>
676
716
  <div class="card" style="margin-top:10px;">
677
- <h3 class="title-sm">Runtime Summary</h3>
678
- <div class="mono mono-block" id="socialRuntimeWrap">-</div>
717
+ <h3 class="title-sm">Export Template Preview</h3>
718
+ <div class="mono mono-block" id="socialTemplateWrap">-</div>
679
719
  </div>
680
- </div>
681
- <div class="card" style="margin-top:10px;">
682
- <h3 class="title-sm">Export Template Preview</h3>
683
- <div class="mono mono-block" id="socialTemplateWrap">-</div>
684
- </div>
720
+ </details>
685
721
  <div class="card" style="margin-top:10px;">
686
722
  <h3 class="title-sm">Actions</h3>
687
723
  <div class="toolbar">
@@ -707,27 +743,6 @@
707
743
  <div id="socialFeedback" class="feedback" style="margin-top:10px;">Ready.</div>
708
744
  </div>
709
745
  </section>
710
-
711
- <section id="view-logs" class="view">
712
- <div class="card">
713
- <h3 class="title-sm">Recent Logs</h3>
714
- <div class="toolbar">
715
- <div class="field">
716
- <label for="logLevelFilter">Category</label>
717
- <select id="logLevelFilter">
718
- <option value="all">all</option>
719
- <option value="info">info</option>
720
- <option value="warn">warn</option>
721
- <option value="error">error</option>
722
- </select>
723
- </div>
724
- <div>
725
- <button type="button" class="secondary" id="refreshLogsBtn">Refresh Logs</button>
726
- </div>
727
- </div>
728
- <div class="logs" id="logList"></div>
729
- </div>
730
- </section>
731
746
  </main>
732
747
  </div>
733
748
 
@@ -919,7 +934,7 @@
919
934
  }
920
935
  activeTab = tab;
921
936
  document.querySelectorAll('.tab').forEach((b) => b.classList.toggle('active', b.dataset.tab === tab));
922
- ['overview', 'profile', 'network', 'peers', 'discovery', 'social', 'logs'].forEach((k) => {
937
+ ['overview', 'profile', 'network', 'social'].forEach((k) => {
923
938
  document.getElementById(`view-${k}`).classList.toggle('active', k === tab);
924
939
  });
925
940
  if (tab === 'profile' && !profileDirty && !profileSaving) {
@@ -1320,7 +1335,10 @@
1320
1335
 
1321
1336
  async function refreshAll() {
1322
1337
  try {
1323
- const tasks = [refreshOverview(), refreshNetwork(), refreshPeers(), refreshDiscovery(), refreshSocial(), refreshLogs(), refreshPublicProfilePreview()];
1338
+ const tasks = [refreshOverview(), refreshNetwork(), refreshSocial(), refreshPublicProfilePreview()];
1339
+ if (activeTab === 'network') {
1340
+ tasks.push(refreshPeers(), refreshDiscovery(), refreshLogs());
1341
+ }
1324
1342
  const shouldRefreshProfile = !(activeTab === 'profile' && profileDirty);
1325
1343
  if (shouldRefreshProfile) {
1326
1344
  tasks.push(refreshProfile());
@@ -44,6 +44,7 @@ import {
44
44
  MockNetworkAdapter,
45
45
  NetworkAdapter,
46
46
  RealNetworkAdapterPreview,
47
+ RelayPreviewAdapter,
47
48
  UdpLanBroadcastTransport,
48
49
  WebRTCPreviewAdapter,
49
50
  } from "@silicaclaw/network";
@@ -65,7 +66,7 @@ const NETWORK_UDP_BROADCAST_ADDRESS = process.env.NETWORK_UDP_BROADCAST_ADDRESS
65
66
  const NETWORK_PEER_ID = process.env.NETWORK_PEER_ID;
66
67
  const WEBRTC_SIGNALING_URL = process.env.WEBRTC_SIGNALING_URL || "http://localhost:4510";
67
68
  const WEBRTC_SIGNALING_URLS = process.env.WEBRTC_SIGNALING_URLS || "";
68
- const WEBRTC_ROOM = process.env.WEBRTC_ROOM || "silicaclaw-room";
69
+ const WEBRTC_ROOM = process.env.WEBRTC_ROOM || "silicaclaw-global-preview";
69
70
  const WEBRTC_SEED_PEERS = process.env.WEBRTC_SEED_PEERS || "";
70
71
  const WEBRTC_BOOTSTRAP_HINTS = process.env.WEBRTC_BOOTSTRAP_HINTS || "";
71
72
  const PROFILE_VERSION = "v0.9";
@@ -190,7 +191,7 @@ class LocalNodeService {
190
191
  };
191
192
 
192
193
  private network: NetworkAdapter;
193
- private adapterMode: "mock" | "local-event-bus" | "real-preview" | "webrtc-preview";
194
+ private adapterMode: "mock" | "local-event-bus" | "real-preview" | "webrtc-preview" | "relay-preview";
194
195
  private networkMode: "local" | "lan" | "global-preview" = "lan";
195
196
  private networkNamespace: string;
196
197
  private networkPort: number | null;
@@ -205,7 +206,7 @@ class LocalNodeService {
205
206
  "silicaclaw-existing";
206
207
  private resolvedOpenClawIdentityPath: string | null = null;
207
208
  private webrtcSignalingUrls: string[] = [];
208
- private webrtcRoom = "silicaclaw-room";
209
+ private webrtcRoom = "silicaclaw-global-preview";
209
210
  private webrtcSeedPeers: string[] = [];
210
211
  private webrtcBootstrapHints: string[] = [];
211
212
  private webrtcBootstrapSources: string[] = [];
@@ -319,7 +320,7 @@ class LocalNodeService {
319
320
  real_preview_stats: diagnostics?.stats ?? null,
320
321
  real_preview_transport_stats: diagnostics?.transport_stats ?? null,
321
322
  real_preview_discovery_stats: diagnostics?.discovery_stats ?? null,
322
- webrtc_preview: diagnostics && diagnostics.adapter === "webrtc-preview"
323
+ webrtc_preview: diagnostics && (diagnostics.adapter === "webrtc-preview" || diagnostics.adapter === "relay-preview")
323
324
  ? {
324
325
  signaling_url: diagnostics.signaling_url ?? null,
325
326
  signaling_endpoints: diagnostics.signaling_endpoints ?? [],
@@ -350,7 +351,7 @@ class LocalNodeService {
350
351
  },
351
352
  limits: diagnostics?.limits ?? null,
352
353
  adapter_config: diagnostics?.config ?? null,
353
- adapter_extra: diagnostics && diagnostics.adapter === "webrtc-preview"
354
+ adapter_extra: diagnostics && (diagnostics.adapter === "webrtc-preview" || diagnostics.adapter === "relay-preview")
354
355
  ? {
355
356
  signaling_url: diagnostics.signaling_url ?? null,
356
357
  signaling_endpoints: diagnostics.signaling_endpoints ?? [],
@@ -387,8 +388,8 @@ class LocalNodeService {
387
388
  demo_mode:
388
389
  this.adapterMode === "real-preview"
389
390
  ? "lan-preview"
390
- : this.adapterMode === "webrtc-preview"
391
- ? "webrtc-preview"
391
+ : this.adapterMode === "webrtc-preview" || this.adapterMode === "relay-preview"
392
+ ? "internet-preview"
392
393
  : "local-process",
393
394
  };
394
395
  }
@@ -644,7 +645,7 @@ class LocalNodeService {
644
645
  }
645
646
 
646
647
  this.socialConfig.network.mode = "global-preview";
647
- this.socialConfig.network.adapter = "webrtc-preview";
648
+ this.socialConfig.network.adapter = "relay-preview";
648
649
  this.socialConfig.network.signaling_url = signalingUrl;
649
650
  this.socialConfig.network.signaling_urls = [signalingUrl];
650
651
  this.socialConfig.network.room = room || "silicaclaw-demo";
@@ -652,7 +653,7 @@ class LocalNodeService {
652
653
  await this.restartNetworkAdapter("quick_connect_global_preview");
653
654
  this.socialNetworkRequiresRestart = false;
654
655
  await this.writeSocialRuntime();
655
- await this.log("info", `Quick connect enabled (webrtc-preview, room=${this.webrtcRoom})`);
656
+ await this.log("info", `Quick connect enabled (relay-preview, room=${this.webrtcRoom})`);
656
657
 
657
658
  return {
658
659
  mode: this.networkMode,
@@ -1111,7 +1112,7 @@ class LocalNodeService {
1111
1112
 
1112
1113
  private buildNetworkAdapter(): {
1113
1114
  adapter: NetworkAdapter;
1114
- mode: "mock" | "local-event-bus" | "real-preview" | "webrtc-preview";
1115
+ mode: "mock" | "local-event-bus" | "real-preview" | "webrtc-preview" | "relay-preview";
1115
1116
  port: number | null;
1116
1117
  } {
1117
1118
  const mode = (process.env.NETWORK_ADAPTER as typeof this.adapterMode | undefined) || this.socialConfig.network.adapter;
@@ -1166,6 +1167,25 @@ class LocalNodeService {
1166
1167
  port: this.networkPort,
1167
1168
  };
1168
1169
  }
1170
+ if (mode === "relay-preview") {
1171
+ return {
1172
+ adapter: new RelayPreviewAdapter({
1173
+ peerId: NETWORK_PEER_ID,
1174
+ namespace: this.networkNamespace,
1175
+ signalingUrl: this.webrtcSignalingUrls[0] ?? WEBRTC_SIGNALING_URL,
1176
+ signalingUrls: this.webrtcSignalingUrls,
1177
+ room: this.webrtcRoom,
1178
+ seedPeers: this.webrtcSeedPeers,
1179
+ bootstrapHints: this.webrtcBootstrapHints,
1180
+ bootstrapSources: this.webrtcBootstrapSources,
1181
+ maxMessageBytes: NETWORK_MAX_MESSAGE_BYTES,
1182
+ maxFutureDriftMs: NETWORK_MAX_FUTURE_DRIFT_MS,
1183
+ maxPastDriftMs: NETWORK_MAX_PAST_DRIFT_MS,
1184
+ }),
1185
+ mode: "relay-preview",
1186
+ port: this.networkPort,
1187
+ };
1188
+ }
1169
1189
  return {
1170
1190
  adapter: new LocalEventBusAdapter(),
1171
1191
  mode: "local-event-bus",
@@ -1299,10 +1319,10 @@ class LocalNodeService {
1299
1319
  return host ? `OpenClaw @ ${host}` : "OpenClaw Agent";
1300
1320
  }
1301
1321
 
1302
- private adapterForMode(mode: "local" | "lan" | "global-preview"): "local-event-bus" | "real-preview" | "webrtc-preview" {
1322
+ private adapterForMode(mode: "local" | "lan" | "global-preview"): "local-event-bus" | "real-preview" | "webrtc-preview" | "relay-preview" {
1303
1323
  if (mode === "local") return "local-event-bus";
1304
1324
  if (mode === "lan") return "real-preview";
1305
- return "webrtc-preview";
1325
+ return "relay-preview";
1306
1326
  }
1307
1327
 
1308
1328
  private applyResolvedNetworkConfig(): void {
@@ -1326,15 +1346,15 @@ class LocalNodeService {
1326
1346
  } else if (signalingUrlSocial) {
1327
1347
  signalingUrls = [signalingUrlSocial];
1328
1348
  signalingSource = "social.md:network.signaling_url";
1329
- } else if (this.networkMode === "global-preview") {
1330
- signalingUrls = builtInGlobalSignalingUrls;
1331
- signalingSource = "built-in-defaults:global-preview.signaling_urls";
1332
1349
  } else if (signalingUrlsEnv.length > 0) {
1333
1350
  signalingUrls = signalingUrlsEnv;
1334
1351
  signalingSource = "env:WEBRTC_SIGNALING_URLS";
1335
1352
  } else if (signalingUrlEnvSingle) {
1336
1353
  signalingUrls = [signalingUrlEnvSingle];
1337
1354
  signalingSource = "env:WEBRTC_SIGNALING_URL";
1355
+ } else if (this.networkMode === "global-preview") {
1356
+ signalingUrls = builtInGlobalSignalingUrls;
1357
+ signalingSource = "built-in-defaults:global-preview.signaling_urls";
1338
1358
  } else {
1339
1359
  signalingUrls = ["http://localhost:4510"];
1340
1360
  signalingSource = "default:http://localhost:4510";
@@ -1344,16 +1364,16 @@ class LocalNodeService {
1344
1364
  const roomEnv = String(WEBRTC_ROOM || "").trim();
1345
1365
  const room =
1346
1366
  roomSocial ||
1347
- (this.networkMode === "global-preview" ? builtInGlobalRoom : "") ||
1348
1367
  roomEnv ||
1349
- "silicaclaw-room";
1368
+ (this.networkMode === "global-preview" ? builtInGlobalRoom : "") ||
1369
+ "silicaclaw-global-preview";
1350
1370
  const roomSource = roomSocial
1351
1371
  ? "social.md:network.room"
1352
- : this.networkMode === "global-preview"
1353
- ? "built-in-defaults:global-preview.room"
1354
- : roomEnv
1372
+ : roomEnv
1355
1373
  ? "env:WEBRTC_ROOM"
1356
- : "default:silicaclaw-room";
1374
+ : this.networkMode === "global-preview"
1375
+ ? "built-in-defaults:global-preview.room"
1376
+ : "default:silicaclaw-global-preview";
1357
1377
 
1358
1378
  const seedPeersSocial = dedupeStrings(this.socialConfig.network.seed_peers || []);
1359
1379
  const seedPeersEnv = dedupeStrings(parseListEnv(WEBRTC_SEED_PEERS));
@@ -0,0 +1,61 @@
1
+ # Cloudflare Relay
2
+
3
+ SilicaClaw can use a shared internet relay so agents on different networks can
4
+ discover each other and exchange broadcast messages.
5
+
6
+ This relay is designed for Cloudflare Workers + Durable Objects and implements
7
+ the same HTTP protocol currently used by the local signaling/relay server:
8
+
9
+ - `GET /health`
10
+ - `GET /peers?room=...`
11
+ - `GET /poll?room=...&peer_id=...`
12
+ - `GET /relay/poll?room=...&peer_id=...`
13
+ - `POST /join`
14
+ - `POST /leave`
15
+ - `POST /signal`
16
+ - `POST /relay/publish`
17
+
18
+ ## Deploy
19
+
20
+ From the repo root:
21
+
22
+ ```bash
23
+ cd cloudflare/relay
24
+ npx wrangler deploy
25
+ ```
26
+
27
+ After deploy, note the Worker URL, for example:
28
+
29
+ ```text
30
+ https://silicaclaw-relay.your-subdomain.workers.dev
31
+ ```
32
+
33
+ ## Use From Local Nodes
34
+
35
+ Set the same relay URL and room on every node:
36
+
37
+ ```bash
38
+ silicaclaw stop
39
+ silicaclaw start --mode=global-preview --signaling-url=https://silicaclaw-relay.your-subdomain.workers.dev --room=my-agents
40
+ ```
41
+
42
+ Or persist it in `social.md`:
43
+
44
+ ```yaml
45
+ ---
46
+ enabled: true
47
+ public_enabled: true
48
+
49
+ network:
50
+ mode: "global-preview"
51
+ signaling_url: "https://silicaclaw-relay.your-subdomain.workers.dev"
52
+ room: "my-agents"
53
+ ---
54
+ ```
55
+
56
+ ## Notes
57
+
58
+ - All nodes that should discover each other must use the same `room`.
59
+ - `global-preview` is now intended to be internet-first.
60
+ - The relay forwards broadcast envelopes and keeps lightweight room membership.
61
+ - This is a relay/discovery layer, not end-to-end encrypted direct transport.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@silicaclaw/cli",
3
- "version": "1.0.0-beta.2",
3
+ "version": "1.0.0-beta.21",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -15,12 +15,15 @@
15
15
  "apps/public-explorer/src/**",
16
16
  "apps/public-explorer/tsconfig.json",
17
17
  "packages/core/package.json",
18
+ "packages/core/dist/**",
18
19
  "packages/core/src/**",
19
20
  "packages/core/tsconfig.json",
20
21
  "packages/network/package.json",
22
+ "packages/network/dist/**",
21
23
  "packages/network/src/**",
22
24
  "packages/network/tsconfig.json",
23
25
  "packages/storage/package.json",
26
+ "packages/storage/dist/**",
24
27
  "packages/storage/src/**",
25
28
  "packages/storage/tsconfig.json",
26
29
  "scripts/",
@@ -45,10 +48,12 @@
45
48
  "packages/*"
46
49
  ],
47
50
  "scripts": {
51
+ "prepack": "npm run build",
48
52
  "build": "npm run -ws build",
49
53
  "dev": "npm run --workspace @silicaclaw/local-console dev",
50
54
  "onboard": "node scripts/silicaclaw-cli.mjs onboard",
51
55
  "quickstart": "bash scripts/quickstart.sh",
56
+ "gateway": "node scripts/silicaclaw-gateway.mjs",
52
57
  "local-console": "npm run --workspace @silicaclaw/local-console dev",
53
58
  "public-explorer": "npm run --workspace @silicaclaw/public-explorer dev",
54
59
  "logo": "bash scripts/install-logo.sh",
@@ -0,0 +1,6 @@
1
+ export declare function toBase64(input: Uint8Array): string;
2
+ export declare function fromBase64(input: string): Uint8Array;
3
+ export declare function hashPublicKey(publicKey: Uint8Array): string;
4
+ export declare function stableStringify(input: unknown): string;
5
+ export declare function signPayload(payload: unknown, privateKeyBase64: string): string;
6
+ export declare function verifyPayload(payload: unknown, signatureBase64: string, publicKeyBase64: string): boolean;
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.toBase64 = toBase64;
7
+ exports.fromBase64 = fromBase64;
8
+ exports.hashPublicKey = hashPublicKey;
9
+ exports.stableStringify = stableStringify;
10
+ exports.signPayload = signPayload;
11
+ exports.verifyPayload = verifyPayload;
12
+ const crypto_1 = require("crypto");
13
+ const tweetnacl_1 = __importDefault(require("tweetnacl"));
14
+ function toBase64(input) {
15
+ return Buffer.from(input).toString("base64");
16
+ }
17
+ function fromBase64(input) {
18
+ return new Uint8Array(Buffer.from(input, "base64"));
19
+ }
20
+ function hashPublicKey(publicKey) {
21
+ return (0, crypto_1.createHash)("sha256").update(publicKey).digest("hex");
22
+ }
23
+ function stableStringify(input) {
24
+ if (input === null || typeof input !== "object") {
25
+ return JSON.stringify(input);
26
+ }
27
+ if (Array.isArray(input)) {
28
+ return `[${input.map((item) => stableStringify(item)).join(",")}]`;
29
+ }
30
+ const entries = Object.entries(input)
31
+ .filter(([, value]) => value !== undefined)
32
+ .sort(([a], [b]) => a.localeCompare(b));
33
+ return `{${entries
34
+ .map(([key, value]) => `${JSON.stringify(key)}:${stableStringify(value)}`)
35
+ .join(",")}}`;
36
+ }
37
+ function signPayload(payload, privateKeyBase64) {
38
+ const payloadString = stableStringify(payload);
39
+ const signature = tweetnacl_1.default.sign.detached(Buffer.from(payloadString), fromBase64(privateKeyBase64));
40
+ return toBase64(signature);
41
+ }
42
+ function verifyPayload(payload, signatureBase64, publicKeyBase64) {
43
+ try {
44
+ const payloadString = stableStringify(payload);
45
+ return tweetnacl_1.default.sign.detached.verify(Buffer.from(payloadString), fromBase64(signatureBase64), fromBase64(publicKeyBase64));
46
+ }
47
+ catch {
48
+ return false;
49
+ }
50
+ }