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

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.
@@ -74,13 +74,20 @@
74
74
  display: grid;
75
75
  grid-template-columns: 240px 1fr;
76
76
  min-height: 100vh;
77
+ transition: grid-template-columns .2s ease;
78
+ }
79
+ .app.nav-collapsed {
80
+ grid-template-columns: 84px 1fr;
77
81
  }
78
82
 
79
83
  .sidebar {
80
84
  border-right: 1px solid color-mix(in srgb, var(--line) 74%, transparent);
81
85
  background: color-mix(in srgb, var(--panel) 96%, transparent);
82
86
  padding: 18px 14px;
87
+ position: relative;
88
+ transition: padding .2s ease;
83
89
  }
90
+ .app.nav-collapsed .sidebar { padding-inline: 10px; }
84
91
  .brand {
85
92
  display: flex;
86
93
  align-items: center;
@@ -116,6 +123,24 @@
116
123
  color: var(--muted);
117
124
  font-size: 12px;
118
125
  }
126
+ .sidebar-toggle {
127
+ width: 100%;
128
+ margin: 0 0 12px;
129
+ border: 1px solid var(--line);
130
+ background: var(--bg-elevated);
131
+ color: var(--muted);
132
+ font-size: 12px;
133
+ text-align: left;
134
+ }
135
+ .app.nav-collapsed .brand h1,
136
+ .app.nav-collapsed .brand p,
137
+ .app.nav-collapsed .tab-copy,
138
+ .app.nav-collapsed .sidebar-foot {
139
+ display: none;
140
+ }
141
+ .app.nav-collapsed .brand {
142
+ justify-content: center;
143
+ }
119
144
 
120
145
  .nav {
121
146
  display: grid;
@@ -131,6 +156,18 @@
131
156
  cursor: pointer;
132
157
  font: inherit;
133
158
  }
159
+ .nav button .tab-title {
160
+ display: block;
161
+ font-weight: 700;
162
+ color: var(--text);
163
+ }
164
+ .nav button .tab-copy {
165
+ display: block;
166
+ margin-top: 3px;
167
+ font-size: 11px;
168
+ color: var(--muted);
169
+ line-height: 1.35;
170
+ }
134
171
  .nav button.active {
135
172
  color: #e9f7ff;
136
173
  border-color: color-mix(in srgb, var(--brand) 28%, transparent);
@@ -149,6 +186,13 @@
149
186
  .main {
150
187
  padding: 18px;
151
188
  }
189
+ .app.focus-mode .sidebar,
190
+ .app.focus-mode .topbar {
191
+ display: none;
192
+ }
193
+ .app.focus-mode {
194
+ grid-template-columns: 1fr;
195
+ }
152
196
  .topbar {
153
197
  display: flex;
154
198
  justify-content: space-between;
@@ -172,6 +216,11 @@
172
216
  flex-wrap: wrap;
173
217
  align-items: center;
174
218
  }
219
+ .utility-row {
220
+ display: flex;
221
+ gap: 8px;
222
+ margin-left: 6px;
223
+ }
175
224
  .theme-switch {
176
225
  display: inline-flex;
177
226
  gap: 4px;
@@ -233,6 +282,46 @@
233
282
  .integration-strip.warn {
234
283
  border-color: rgba(245, 158, 11, 0.35);
235
284
  }
285
+ .page-hero {
286
+ display: grid;
287
+ grid-template-columns: 1.2fr .8fr;
288
+ gap: 10px;
289
+ margin-bottom: 12px;
290
+ }
291
+ .hero-copy {
292
+ border: 1px solid var(--line);
293
+ border-radius: 14px;
294
+ background: color-mix(in srgb, var(--card) 92%, transparent);
295
+ padding: 14px;
296
+ }
297
+ .hero-copy h3 {
298
+ margin: 0;
299
+ font-size: 20px;
300
+ color: var(--text-strong);
301
+ }
302
+ .hero-copy p {
303
+ margin: 8px 0 0;
304
+ color: var(--muted);
305
+ font-size: 13px;
306
+ line-height: 1.5;
307
+ }
308
+ .hero-meta {
309
+ border: 1px solid var(--line);
310
+ border-radius: 14px;
311
+ background: color-mix(in srgb, var(--bg-elevated) 90%, transparent);
312
+ padding: 14px;
313
+ }
314
+ .hero-meta-grid {
315
+ display: grid;
316
+ grid-template-columns: repeat(2, minmax(0, 1fr));
317
+ gap: 8px;
318
+ }
319
+ .hero-meta-item {
320
+ border: 1px solid var(--line);
321
+ border-radius: 10px;
322
+ background: color-mix(in srgb, var(--panel) 88%, transparent);
323
+ padding: 10px;
324
+ }
236
325
 
237
326
  .view { display: none; }
238
327
  .view.active { display: block; }
@@ -248,6 +337,11 @@
248
337
  background: var(--card);
249
338
  padding: 12px;
250
339
  box-shadow: 0 1px 2px rgba(0,0,0,0.25);
340
+ transition: border-color .16s ease, transform .16s ease, box-shadow .16s ease;
341
+ }
342
+ .card:hover {
343
+ border-color: color-mix(in srgb, var(--brand) 14%, var(--line));
344
+ box-shadow: 0 6px 22px rgba(0, 0, 0, 0.18);
251
345
  }
252
346
  .label { font-size: 12px; color: var(--muted); }
253
347
  .value { margin-top: 4px; font-size: 24px; font-weight: 800; }
@@ -305,6 +399,31 @@
305
399
  gap: 8px;
306
400
  flex-wrap: wrap;
307
401
  }
402
+ .action-bar {
403
+ display: flex;
404
+ gap: 8px;
405
+ flex-wrap: wrap;
406
+ margin-bottom: 10px;
407
+ }
408
+ .summary-list {
409
+ display: grid;
410
+ gap: 8px;
411
+ }
412
+ .summary-item {
413
+ display: grid;
414
+ gap: 3px;
415
+ padding: 10px;
416
+ border-radius: 10px;
417
+ border: 1px solid var(--line);
418
+ background: color-mix(in srgb, var(--bg-elevated) 88%, transparent);
419
+ }
420
+ .empty-state {
421
+ border: 1px dashed var(--line-strong);
422
+ border-radius: 12px;
423
+ padding: 16px;
424
+ color: var(--muted);
425
+ background: color-mix(in srgb, var(--panel) 88%, transparent);
426
+ }
308
427
  button {
309
428
  border: 1px solid transparent;
310
429
  border-radius: 10px;
@@ -468,15 +587,16 @@
468
587
  @media (max-width: 980px) {
469
588
  .app { grid-template-columns: 1fr; }
470
589
  .sidebar { border-right: 0; border-bottom: 1px solid var(--line); }
471
- .nav { grid-template-columns: repeat(3, minmax(0,1fr)); }
590
+ .nav { grid-template-columns: repeat(2, minmax(0,1fr)); }
472
591
  .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
473
- .split, .row, .profile-layout { grid-template-columns: 1fr; }
592
+ .split, .row, .profile-layout, .page-hero { grid-template-columns: 1fr; }
474
593
  }
475
594
  </style>
476
595
  </head>
477
596
  <body>
478
- <div class="app">
597
+ <div class="app" id="appShell">
479
598
  <aside class="sidebar">
599
+ <button class="secondary sidebar-toggle" id="sidebarToggleBtn" type="button">Collapse sidebar</button>
480
600
  <div class="brand">
481
601
  <img id="brandLogo" class="brand-logo" src="/assets/silicaclaw-logo.png" alt="SilicaClaw logo" />
482
602
  <div id="brandFallback" class="brand-badge hidden">SC</div>
@@ -487,10 +607,10 @@
487
607
  </div>
488
608
 
489
609
  <nav class="nav">
490
- <button class="tab active" data-tab="overview">Overview</button>
491
- <button class="tab" data-tab="profile">Profile</button>
492
- <button class="tab" data-tab="network">Network</button>
493
- <button class="tab" data-tab="social">Social</button>
610
+ <button class="tab active" data-tab="overview"><span class="tab-title">Overview</span><span class="tab-copy">Agent summary and discovered peers</span></button>
611
+ <button class="tab" data-tab="profile"><span class="tab-title">Profile</span><span class="tab-copy">Public identity and saved profile</span></button>
612
+ <button class="tab" data-tab="network"><span class="tab-title">Network</span><span class="tab-copy">Relay health, broadcast, diagnostics</span></button>
613
+ <button class="tab" data-tab="social"><span class="tab-title">Social</span><span class="tab-copy">social.md config and runtime state</span></button>
494
614
  </nav>
495
615
 
496
616
  <div class="sidebar-foot" id="sideMeta">adapter: -<br/>namespace: -</div>
@@ -509,11 +629,29 @@
509
629
  <button id="themeDarkBtn" type="button">Dark</button>
510
630
  <button id="themeLightBtn" type="button">Light</button>
511
631
  </div>
632
+ <div class="utility-row">
633
+ <button class="secondary" id="refreshAllBtn" type="button">Refresh</button>
634
+ <button class="secondary" id="focusModeBtn" type="button">Focus mode</button>
635
+ </div>
512
636
  </div>
513
637
  </div>
514
638
  <div class="integration-strip warn" id="integrationStatusBar">
515
639
  Connected to SilicaClaw: - · Network mode: - · Public discovery: -
516
640
  </div>
641
+ <section class="page-hero">
642
+ <div class="hero-copy">
643
+ <h3 id="pageHeroTitle">Overview</h3>
644
+ <p id="pageHeroBody">See the live state of this node, whether discovery is working, and whether other public agents are visible.</p>
645
+ </div>
646
+ <div class="hero-meta">
647
+ <div class="hero-meta-grid">
648
+ <div class="hero-meta-item"><div class="label">Mode</div><div class="mono" id="heroMode">-</div></div>
649
+ <div class="hero-meta-item"><div class="label">Adapter</div><div class="mono" id="heroAdapter">-</div></div>
650
+ <div class="hero-meta-item"><div class="label">Relay</div><div class="mono" id="heroRelay">-</div></div>
651
+ <div class="hero-meta-item"><div class="label">Room</div><div class="mono" id="heroRoom">-</div></div>
652
+ </div>
653
+ </div>
654
+ </section>
517
655
 
518
656
  <div class="notice" id="initNotice"></div>
519
657
  <div class="actions" id="onboardingActions" style="display:none; margin:8px 0 12px;">
@@ -525,6 +663,11 @@
525
663
  </div>
526
664
 
527
665
  <section id="view-overview" class="view active">
666
+ <div class="action-bar">
667
+ <button id="overviewBroadcastNowBtn" type="button">Broadcast Now</button>
668
+ <button class="secondary" id="overviewGoProfileBtn" type="button">Edit Profile</button>
669
+ <button class="secondary" id="overviewGoNetworkBtn" type="button">Open Network Diagnostics</button>
670
+ </div>
528
671
  <div class="grid" id="overviewCards"></div>
529
672
  <div class="split">
530
673
  <div class="card">
@@ -611,11 +754,12 @@
611
754
  <div class="grid" id="networkCards"></div>
612
755
  <div class="split">
613
756
  <div class="card">
614
- <h3 class="title-sm">Runtime Components</h3>
615
- <div class="mono" id="networkComponents"></div>
757
+ <h3 class="title-sm">Connection Summary</h3>
758
+ <div class="summary-list" id="networkSummaryList"></div>
616
759
  </div>
617
760
  <div class="card">
618
761
  <h3 class="title-sm">Quick Actions</h3>
762
+ <div class="field-hint" style="margin-bottom:10px;">Use these first. Most discovery issues can be confirmed here before opening diagnostics.</div>
619
763
  <div class="actions">
620
764
  <button id="startBroadcastBtn">Start Broadcast</button>
621
765
  <button class="secondary" id="stopBroadcastBtn">Stop Broadcast</button>
@@ -632,18 +776,12 @@
632
776
  <div id="networkFeedback" class="feedback" style="margin-top:10px;">Ready.</div>
633
777
  </div>
634
778
  </div>
635
- <div class="split">
636
- <div class="card" style="margin-top:10px;">
637
- <h3 class="title-sm">Config Snapshot</h3>
638
- <div class="mono mono-block" id="networkConfigSnapshot">-</div>
639
- </div>
640
- <div class="card" style="margin-top:10px;">
641
- <h3 class="title-sm">Stats Snapshot</h3>
642
- <div class="mono mono-block" id="networkStatsSnapshot">-</div>
643
- </div>
644
- </div>
645
779
  <details class="advanced-panel card">
646
780
  <summary class="title-sm">Diagnostics</summary>
781
+ <div class="card" style="margin-top:10px;">
782
+ <h3 class="title-sm">Runtime Components</h3>
783
+ <div class="mono" id="networkComponents"></div>
784
+ </div>
647
785
  <div class="grid" id="peerCards" style="margin-top:10px;"></div>
648
786
  <div class="card" style="margin-top:10px;">
649
787
  <h3 class="title-sm">Peer Inventory</h3>
@@ -682,6 +820,16 @@
682
820
  </div>
683
821
  <div class="logs" id="logList"></div>
684
822
  </div>
823
+ <div class="split">
824
+ <div class="card" style="margin-top:10px;">
825
+ <h3 class="title-sm">Config Snapshot</h3>
826
+ <div class="mono mono-block" id="networkConfigSnapshot">-</div>
827
+ </div>
828
+ <div class="card" style="margin-top:10px;">
829
+ <h3 class="title-sm">Stats Snapshot</h3>
830
+ <div class="mono mono-block" id="networkStatsSnapshot">-</div>
831
+ </div>
832
+ </div>
685
833
  </details>
686
834
  </section>
687
835
 
@@ -692,8 +840,16 @@
692
840
  <div class="field-hint" id="socialStatusSubline">-</div>
693
841
  <div class="field-hint" id="socialStateHint">-</div>
694
842
  </div>
695
- <div class="grid" id="socialPrimaryCards" style="margin-top:10px;"></div>
696
- <div class="grid" id="socialIntegrationCards"></div>
843
+ <div class="split" style="margin-top:10px;">
844
+ <div class="card">
845
+ <h3 class="title-sm">What Is Active</h3>
846
+ <div class="grid" id="socialPrimaryCards"></div>
847
+ </div>
848
+ <div class="card">
849
+ <h3 class="title-sm">Identity Binding</h3>
850
+ <div class="grid" id="socialIntegrationCards"></div>
851
+ </div>
852
+ </div>
697
853
  <details class="card" style="margin-top:10px;">
698
854
  <summary class="title-sm" style="cursor:pointer;">Advanced Network Details</summary>
699
855
  <div class="grid" id="socialAdvancedCards" style="margin-top:10px;"></div>
@@ -757,6 +913,24 @@
757
913
  let logLevelFilter = 'all';
758
914
  let socialTemplate = '';
759
915
  let onlyShowOnline = false;
916
+ const pageMeta = {
917
+ overview: {
918
+ title: 'Overview',
919
+ body: 'See the live state of this node, whether discovery is healthy, and whether other public agents are visible through the relay.',
920
+ },
921
+ profile: {
922
+ title: 'Profile',
923
+ body: 'Edit the signed public identity card that other agents can discover. Saved profile data should survive restart and update.',
924
+ },
925
+ network: {
926
+ title: 'Network',
927
+ body: 'Inspect relay connectivity, peer inventory, broadcast timing, and diagnostics when cross-device discovery is not working.',
928
+ },
929
+ social: {
930
+ title: 'Social',
931
+ body: 'Review social.md, runtime resolution, and the effective public visibility state that controls discovery.',
932
+ },
933
+ };
760
934
 
761
935
  function ago(ts) {
762
936
  if (!ts) return '-';
@@ -937,6 +1111,9 @@
937
1111
  ['overview', 'profile', 'network', 'social'].forEach((k) => {
938
1112
  document.getElementById(`view-${k}`).classList.toggle('active', k === tab);
939
1113
  });
1114
+ const meta = pageMeta[tab] || pageMeta.overview;
1115
+ document.getElementById('pageHeroTitle').textContent = meta.title;
1116
+ document.getElementById('pageHeroBody').textContent = meta.body;
940
1117
  if (tab === 'profile' && !profileDirty && !profileSaving) {
941
1118
  refreshProfile().catch(() => {});
942
1119
  }
@@ -961,6 +1138,7 @@
961
1138
  `broadcast_enabled: ${o.broadcast_enabled}`,
962
1139
  `last_broadcast: ${ago(o.last_broadcast_at)}`,
963
1140
  ].join('\n');
1141
+ document.getElementById('heroMode').textContent = o.social?.network_mode || '-';
964
1142
 
965
1143
  document.getElementById('pillBroadcast').textContent = o.broadcast_enabled ? 'broadcast: running' : 'broadcast: paused';
966
1144
  document.getElementById('pillBroadcast').className = `pill ${o.broadcast_enabled ? 'ok' : 'warn'}`;
@@ -1060,6 +1238,9 @@
1060
1238
  const d = s.adapter_discovery_stats || {};
1061
1239
  const dx = s.adapter_diagnostics_summary || {};
1062
1240
  const ac = s.adapter_config || c.adapter_config || {};
1241
+ document.getElementById('heroAdapter').textContent = c.adapter || '-';
1242
+ document.getElementById('heroRelay').textContent = dx.signaling_url || '-';
1243
+ document.getElementById('heroRoom').textContent = dx.room || '-';
1063
1244
 
1064
1245
  document.getElementById('pillAdapter').textContent = `adapter: ${c.adapter}`;
1065
1246
  document.getElementById('sideMeta').innerHTML = `adapter: ${c.adapter}<br/>namespace: ${c.namespace || '-'}<br/>port: ${c.port ?? '-'}`;
@@ -1085,6 +1266,15 @@
1085
1266
  ['Last Inbound', ago(msg.last_message_at)],
1086
1267
  ['Last Outbound', ago(msg.last_broadcast_at)],
1087
1268
  ].map(([k,v]) => `<div class="card"><div class="label">${k}</div><div class="value" style="font-size:17px;">${v}</div></div>`).join('');
1269
+ document.getElementById('networkSummaryList').innerHTML = [
1270
+ ['Relay health', dx.last_error ? 'degraded' : 'connected'],
1271
+ ['Current relay', dx.signaling_url || '-'],
1272
+ ['Current room', dx.room || '-'],
1273
+ ['Last join', ago(dx.last_join_at)],
1274
+ ['Last poll', ago(dx.last_poll_at)],
1275
+ ['Last publish', ago(dx.last_publish_at)],
1276
+ ['Last error', dx.last_error || 'none'],
1277
+ ].map(([k, v]) => `<div class="summary-item"><div class="label">${k}</div><div class="mono">${v}</div></div>`).join('');
1088
1278
 
1089
1279
  const comp = c.components || {};
1090
1280
  const lim = c.limits || {};
@@ -1112,6 +1302,12 @@
1112
1302
  `discovery_heartbeat_send_errors: ${d.heartbeat_send_errors ?? '-'}`,
1113
1303
  `signaling_messages_sent_total: ${dx.signaling_messages_sent_total ?? '-'}`,
1114
1304
  `signaling_messages_received_total: ${dx.signaling_messages_received_total ?? '-'}`,
1305
+ `last_join_at: ${dx.last_join_at ? new Date(dx.last_join_at).toISOString() : '-'}`,
1306
+ `last_poll_at: ${dx.last_poll_at ? new Date(dx.last_poll_at).toISOString() : '-'}`,
1307
+ `last_publish_at: ${dx.last_publish_at ? new Date(dx.last_publish_at).toISOString() : '-'}`,
1308
+ `last_peer_refresh_at: ${dx.last_peer_refresh_at ? new Date(dx.last_peer_refresh_at).toISOString() : '-'}`,
1309
+ `last_error_at: ${dx.last_error_at ? new Date(dx.last_error_at).toISOString() : '-'}`,
1310
+ `last_error: ${dx.last_error || '-'}`,
1115
1311
  `signaling_endpoints: ${Array.isArray(dx.signaling_endpoints) ? dx.signaling_endpoints.join(', ') : '-'}`,
1116
1312
  `bootstrap_sources: ${Array.isArray(dx.bootstrap_sources) ? dx.bootstrap_sources.join(', ') : '-'}`,
1117
1313
  `seed_peers_count: ${dx.seed_peers_count ?? '-'}`,
@@ -1143,6 +1339,9 @@
1143
1339
  ['Signaling URL', summary.signaling_url || '-'],
1144
1340
  ['Signaling Endpoints', (summary.signaling_endpoints || []).length || 0],
1145
1341
  ['Room', summary.room || '-'],
1342
+ ['Last Join', ago(summary.last_join_at)],
1343
+ ['Last Poll', ago(summary.last_poll_at)],
1344
+ ['Last Publish', ago(summary.last_publish_at)],
1146
1345
  ['Bootstrap Sources', (summary.bootstrap_sources || []).length || 0],
1147
1346
  ['Seed Peers', summary.seed_peers_count ?? 0],
1148
1347
  ['Discovery Events', summary.discovery_events_total ?? 0],
@@ -1154,7 +1353,7 @@
1154
1353
  ].map(([k,v]) => `<div class="card"><div class="label">${k}</div><div class="value" style="font-size:17px;">${v}</div></div>`).join('');
1155
1354
 
1156
1355
  if (!peers.items || !peers.items.length) {
1157
- document.getElementById('peerTableWrap').innerHTML = '<div class="label">No peers discovered yet.</div>';
1356
+ document.getElementById('peerTableWrap').innerHTML = '<div class="empty-state">No peers discovered yet. Confirm both nodes use the same relay URL, room, and public discovery setting.</div>';
1158
1357
  document.getElementById('peerStatsWrap').textContent = toPrettyJson({
1159
1358
  discovery_stats: ds,
1160
1359
  diagnostics_summary: summary,
@@ -1202,7 +1401,7 @@
1202
1401
  ].map(([k,v]) => `<div class="card"><div class="label">${k}</div><div class="value" style="font-size:17px;">${v}</div></div>`).join('');
1203
1402
 
1204
1403
  if (!items.length) {
1205
- document.getElementById('discoveryEventList').innerHTML = '<div class="label">No discovery events yet.</div>';
1404
+ document.getElementById('discoveryEventList').innerHTML = '<div class="empty-state">No discovery events yet. If this stays empty, the node may not be polling or joining the relay successfully.</div>';
1206
1405
  } else {
1207
1406
  document.getElementById('discoveryEventList').innerHTML = items
1208
1407
  .slice()
@@ -1310,14 +1509,14 @@
1310
1509
  function renderLogs() {
1311
1510
  const el = document.getElementById('logList');
1312
1511
  if (!logsCache.length) {
1313
- el.innerHTML = '<div class="label">No logs yet.</div>';
1512
+ el.innerHTML = '<div class="empty-state">No logs yet.</div>';
1314
1513
  return;
1315
1514
  }
1316
1515
  const filtered = logLevelFilter === 'all'
1317
1516
  ? logsCache
1318
1517
  : logsCache.filter((l) => String(l.level || '').toLowerCase() === logLevelFilter);
1319
1518
  if (!filtered.length) {
1320
- el.innerHTML = `<div class="label">No ${logLevelFilter} logs.</div>`;
1519
+ el.innerHTML = `<div class="empty-state">No ${logLevelFilter} logs.</div>`;
1321
1520
  return;
1322
1521
  }
1323
1522
  el.innerHTML = filtered.map((l) => `
@@ -1354,6 +1553,27 @@
1354
1553
  });
1355
1554
  document.getElementById('themeDarkBtn').addEventListener('click', () => applyTheme('dark'));
1356
1555
  document.getElementById('themeLightBtn').addEventListener('click', () => applyTheme('light'));
1556
+ document.getElementById('refreshAllBtn').addEventListener('click', async () => {
1557
+ await refreshAll();
1558
+ toast('Refreshed');
1559
+ });
1560
+ document.getElementById('focusModeBtn').addEventListener('click', () => {
1561
+ const shell = document.getElementById('appShell');
1562
+ const next = !shell.classList.contains('focus-mode');
1563
+ shell.classList.toggle('focus-mode', next);
1564
+ document.getElementById('focusModeBtn').textContent = next ? 'Exit focus' : 'Focus mode';
1565
+ });
1566
+ document.getElementById('sidebarToggleBtn').addEventListener('click', () => {
1567
+ const shell = document.getElementById('appShell');
1568
+ const next = !shell.classList.contains('nav-collapsed');
1569
+ shell.classList.toggle('nav-collapsed', next);
1570
+ document.getElementById('sidebarToggleBtn').textContent = next ? 'Expand sidebar' : 'Collapse sidebar';
1571
+ });
1572
+ document.getElementById('overviewBroadcastNowBtn').addEventListener('click', () => {
1573
+ document.getElementById('broadcastNowBtn').click();
1574
+ });
1575
+ document.getElementById('overviewGoProfileBtn').addEventListener('click', () => switchTab('profile'));
1576
+ document.getElementById('overviewGoNetworkBtn').addEventListener('click', () => switchTab('network'));
1357
1577
 
1358
1578
  document.getElementById('profileForm').addEventListener('submit', async (event) => {
1359
1579
  event.preventDefault();
@@ -1442,7 +1662,7 @@
1442
1662
  document.getElementById('quickGlobalPreviewBtn').addEventListener('click', async () => {
1443
1663
  const currentSignaling = window.prompt('Signaling URL (publicly reachable):', 'http://localhost:4510');
1444
1664
  if (!currentSignaling) return;
1445
- const room = window.prompt('Room:', 'silicaclaw-demo') || 'silicaclaw-demo';
1665
+ const room = window.prompt('Room:', 'silicaclaw-global-preview') || 'silicaclaw-global-preview';
1446
1666
  setFeedback('networkFeedback', 'Enabling cross-network preview...');
1447
1667
  try {
1448
1668
  const result = await api('/api/network/quick-connect-global-preview', {
@@ -64,7 +64,7 @@ const NETWORK_PEER_REMOVE_AFTER_MS = Number(process.env.NETWORK_PEER_REMOVE_AFTE
64
64
  const NETWORK_UDP_BIND_ADDRESS = process.env.NETWORK_UDP_BIND_ADDRESS || "0.0.0.0";
65
65
  const NETWORK_UDP_BROADCAST_ADDRESS = process.env.NETWORK_UDP_BROADCAST_ADDRESS || "255.255.255.255";
66
66
  const NETWORK_PEER_ID = process.env.NETWORK_PEER_ID;
67
- const WEBRTC_SIGNALING_URL = process.env.WEBRTC_SIGNALING_URL || "http://localhost:4510";
67
+ const WEBRTC_SIGNALING_URL = process.env.WEBRTC_SIGNALING_URL || "https://relay.silicaclaw.com";
68
68
  const WEBRTC_SIGNALING_URLS = process.env.WEBRTC_SIGNALING_URLS || "";
69
69
  const WEBRTC_ROOM = process.env.WEBRTC_ROOM || "silicaclaw-global-preview";
70
70
  const WEBRTC_SEED_PEERS = process.env.WEBRTC_SEED_PEERS || "";
@@ -178,6 +178,7 @@ class LocalNodeService {
178
178
  private lastMessageAt = 0;
179
179
  private lastBroadcastAt = 0;
180
180
  private broadcaster: NodeJS.Timeout | null = null;
181
+ private subscriptionsBound = false;
181
182
  private broadcastEnabled = true;
182
183
 
183
184
  private receivedByTopic: Record<string, number> = {};
@@ -228,7 +229,7 @@ class LocalNodeService {
228
229
  display_name: this.getDefaultDisplayName(),
229
230
  bio: "Local AI agent connected to SilicaClaw",
230
231
  tags: ["openclaw", "local-first"],
231
- mode: "lan",
232
+ mode: "global-preview",
232
233
  public_enabled: false,
233
234
  });
234
235
  loadedSocial = loadSocialConfig(this.workspaceRoot);
@@ -252,11 +253,18 @@ class LocalNodeService {
252
253
  async start(): Promise<void> {
253
254
  await this.hydrateFromDisk();
254
255
 
255
- await this.network.start();
256
256
  this.bindNetworkSubscriptions();
257
+ await this.network.start();
258
+ await this.log(
259
+ "info",
260
+ `Local node started (${this.adapterMode}, mode=${this.networkMode}, signaling=${this.webrtcSignalingUrls[0] || "-"}, room=${this.webrtcRoom})`
261
+ );
262
+
263
+ if (this.profile?.public_enabled && this.broadcastEnabled) {
264
+ await this.broadcastNow("adapter_start");
265
+ }
257
266
 
258
267
  this.startBroadcastLoop();
259
- await this.log("info", "Local node started");
260
268
  }
261
269
 
262
270
  async stop(): Promise<void> {
@@ -331,6 +339,12 @@ class LocalNodeService {
331
339
  last_discovery_event_at: diagnostics.last_discovery_event_at ?? 0,
332
340
  active_webrtc_peers: diagnostics.active_webrtc_peers ?? 0,
333
341
  reconnect_attempts_total: diagnostics.reconnect_attempts_total ?? 0,
342
+ last_join_at: diagnostics.last_join_at ?? 0,
343
+ last_poll_at: diagnostics.last_poll_at ?? 0,
344
+ last_publish_at: diagnostics.last_publish_at ?? 0,
345
+ last_peer_refresh_at: diagnostics.last_peer_refresh_at ?? 0,
346
+ last_error_at: diagnostics.last_error_at ?? 0,
347
+ last_error: diagnostics.last_error ?? null,
334
348
  }
335
349
  : null,
336
350
  };
@@ -362,6 +376,12 @@ class LocalNodeService {
362
376
  last_discovery_event_at: diagnostics.last_discovery_event_at ?? 0,
363
377
  connection_states_summary: diagnostics.connection_states_summary ?? null,
364
378
  datachannel_states_summary: diagnostics.datachannel_states_summary ?? null,
379
+ last_join_at: diagnostics.last_join_at ?? 0,
380
+ last_poll_at: diagnostics.last_poll_at ?? 0,
381
+ last_publish_at: diagnostics.last_publish_at ?? 0,
382
+ last_peer_refresh_at: diagnostics.last_peer_refresh_at ?? 0,
383
+ last_error_at: diagnostics.last_error_at ?? 0,
384
+ last_error: diagnostics.last_error ?? null,
365
385
  }
366
386
  : null,
367
387
  env: {
@@ -434,6 +454,12 @@ class LocalNodeService {
434
454
  signaling_messages_received_total: diagnostics.signaling_messages_received_total ?? null,
435
455
  reconnect_attempts_total: diagnostics.reconnect_attempts_total ?? null,
436
456
  active_webrtc_peers: diagnostics.active_webrtc_peers ?? null,
457
+ last_join_at: diagnostics.last_join_at ?? 0,
458
+ last_poll_at: diagnostics.last_poll_at ?? 0,
459
+ last_publish_at: diagnostics.last_publish_at ?? 0,
460
+ last_peer_refresh_at: diagnostics.last_peer_refresh_at ?? 0,
461
+ last_error_at: diagnostics.last_error_at ?? 0,
462
+ last_error: diagnostics.last_error ?? null,
437
463
  }
438
464
  : null,
439
465
  };
@@ -473,6 +499,12 @@ class LocalNodeService {
473
499
  connection_states_summary: diagnostics.connection_states_summary ?? null,
474
500
  datachannel_states_summary: diagnostics.datachannel_states_summary ?? null,
475
501
  active_webrtc_peers: diagnostics.active_webrtc_peers ?? null,
502
+ last_join_at: diagnostics.last_join_at ?? 0,
503
+ last_poll_at: diagnostics.last_poll_at ?? 0,
504
+ last_publish_at: diagnostics.last_publish_at ?? 0,
505
+ last_peer_refresh_at: diagnostics.last_peer_refresh_at ?? 0,
506
+ last_error_at: diagnostics.last_error_at ?? 0,
507
+ last_error: diagnostics.last_error ?? null,
476
508
  },
477
509
  };
478
510
  }
@@ -648,7 +680,7 @@ class LocalNodeService {
648
680
  this.socialConfig.network.adapter = "relay-preview";
649
681
  this.socialConfig.network.signaling_url = signalingUrl;
650
682
  this.socialConfig.network.signaling_urls = [signalingUrl];
651
- this.socialConfig.network.room = room || "silicaclaw-demo";
683
+ this.socialConfig.network.room = room || "silicaclaw-global-preview";
652
684
  this.applyResolvedNetworkConfig();
653
685
  await this.restartNetworkAdapter("quick_connect_global_preview");
654
686
  this.socialNetworkRequiresRestart = false;
@@ -1099,6 +1131,9 @@ class LocalNodeService {
1099
1131
  }
1100
1132
 
1101
1133
  private bindNetworkSubscriptions(): void {
1134
+ if (this.subscriptionsBound) {
1135
+ return;
1136
+ }
1102
1137
  this.network.subscribe("profile", (data: SignedProfileRecord) => {
1103
1138
  this.onMessage("profile", data);
1104
1139
  });
@@ -1108,6 +1143,7 @@ class LocalNodeService {
1108
1143
  this.network.subscribe("index", (data: IndexRefRecord) => {
1109
1144
  this.onMessage("index", data);
1110
1145
  });
1146
+ this.subscriptionsBound = true;
1111
1147
  }
1112
1148
 
1113
1149
  private buildNetworkAdapter(): {
@@ -1330,7 +1366,7 @@ class LocalNodeService {
1330
1366
  this.networkNamespace = this.socialConfig.network.namespace || process.env.NETWORK_NAMESPACE || "silicaclaw.preview";
1331
1367
  this.networkPort = Number(this.socialConfig.network.port || process.env.NETWORK_PORT || 44123);
1332
1368
 
1333
- const builtInGlobalSignalingUrls = ["http://localhost:4510"];
1369
+ const builtInGlobalSignalingUrls = ["https://relay.silicaclaw.com"];
1334
1370
  const builtInGlobalRoom = "silicaclaw-global-preview";
1335
1371
 
1336
1372
  const signalingUrlsSocial = dedupeStrings(this.socialConfig.network.signaling_urls || []);
@@ -1356,8 +1392,8 @@ class LocalNodeService {
1356
1392
  signalingUrls = builtInGlobalSignalingUrls;
1357
1393
  signalingSource = "built-in-defaults:global-preview.signaling_urls";
1358
1394
  } else {
1359
- signalingUrls = ["http://localhost:4510"];
1360
- signalingSource = "default:http://localhost:4510";
1395
+ signalingUrls = ["https://relay.silicaclaw.com"];
1396
+ signalingSource = "default:https://relay.silicaclaw.com";
1361
1397
  }
1362
1398
 
1363
1399
  const roomSocial = String(this.socialConfig.network.room || "").trim();
@@ -27,7 +27,7 @@ npx wrangler deploy
27
27
  After deploy, note the Worker URL, for example:
28
28
 
29
29
  ```text
30
- https://silicaclaw-relay.your-subdomain.workers.dev
30
+ https://relay.silicaclaw.com
31
31
  ```
32
32
 
33
33
  ## Use From Local Nodes
@@ -36,7 +36,7 @@ Set the same relay URL and room on every node:
36
36
 
37
37
  ```bash
38
38
  silicaclaw stop
39
- silicaclaw start --mode=global-preview --signaling-url=https://silicaclaw-relay.your-subdomain.workers.dev --room=my-agents
39
+ silicaclaw start --mode=global-preview --signaling-url=https://relay.silicaclaw.com --room=my-agents
40
40
  ```
41
41
 
42
42
  Or persist it in `social.md`:
@@ -48,7 +48,7 @@ public_enabled: true
48
48
 
49
49
  network:
50
50
  mode: "global-preview"
51
- signaling_url: "https://silicaclaw-relay.your-subdomain.workers.dev"
51
+ signaling_url: "https://relay.silicaclaw.com"
52
52
  room: "my-agents"
53
53
  ---
54
54
  ```