@silicaclaw/cli 2026.3.19-13 → 2026.3.19-15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  ## v1.0 beta - 2026-03-19
4
4
 
5
+ ### 2026.3.19-15
6
+
7
+ - release build:
8
+ - prepared another fresh date-based package build without publishing
9
+ - regenerated the npm tarball through the release packing workflow
10
+
11
+ ### 2026.3.19-14
12
+
13
+ - release build:
14
+ - prepared another fresh date-based package build without publishing
15
+ - regenerated the npm tarball through the release packing workflow
16
+
5
17
  ### 2026.3.19-13
6
18
 
7
19
  - release build:
package/VERSION CHANGED
@@ -1 +1 @@
1
- v2026.3.19-13
1
+ v2026.3.19-15
@@ -1700,7 +1700,7 @@ class LocalNodeService {
1700
1700
  await this.log("info", "profile.json missing/invalid, initialized from social/default profile");
1701
1701
  }
1702
1702
  await this.profileRepo.set(this.profile);
1703
- this.directory = (0, core_1.dedupeIndex)(await this.cacheRepo.get());
1703
+ this.directory = (0, core_1.createEmptyDirectoryState)();
1704
1704
  this.messageGovernance = {
1705
1705
  ...this.defaultMessageGovernance(),
1706
1706
  ...(await this.socialMessageGovernanceRepo.get()),
@@ -2001,7 +2001,22 @@ class LocalNodeService {
2001
2001
  this.publishedByTopic[topic] = (this.publishedByTopic[topic] ?? 0) + 1;
2002
2002
  }
2003
2003
  async persistCache() {
2004
- await this.cacheRepo.set(this.directory);
2004
+ const persisted = (0, core_1.createEmptyDirectoryState)();
2005
+ if (this.profile) {
2006
+ const selfProfileRecord = {
2007
+ type: "profile",
2008
+ profile: this.profile,
2009
+ };
2010
+ this.directory = (0, core_1.ingestProfileRecord)(this.directory, selfProfileRecord);
2011
+ persisted.profiles[this.profile.agent_id] = this.profile;
2012
+ const selfLastSeenAt = this.directory.presence[this.profile.agent_id];
2013
+ if (typeof selfLastSeenAt === "number" && Number.isFinite(selfLastSeenAt)) {
2014
+ persisted.presence[this.profile.agent_id] = selfLastSeenAt;
2015
+ }
2016
+ const indexed = (0, core_1.rebuildIndexForProfile)(persisted, this.profile);
2017
+ persisted.index = indexed.index;
2018
+ }
2019
+ await this.cacheRepo.set(persisted);
2005
2020
  }
2006
2021
  async persistSocialMessages() {
2007
2022
  await this.socialMessageRepo.set(this.socialMessages);
@@ -203,6 +203,8 @@ root.innerHTML = appTemplate;
203
203
  document.getElementById('socialBridgeTitle').textContent = t('labels.identityBinding');
204
204
  document.getElementById('socialOwnerDeliveryTitle').textContent = t('labels.ownerDelivery');
205
205
  document.getElementById('socialSkillLearningTitle').textContent = t('labels.openclawSkillLearning');
206
+ document.getElementById('socialMessagePathTitle').textContent = t('labels.messagePath');
207
+ document.getElementById('socialMessagePathHint').textContent = t('hints.socialMessagePathHint');
206
208
  document.getElementById('socialGovernanceTitle').textContent = t('labels.messageGovernance');
207
209
  document.getElementById('socialModerationTitle').textContent = t('labels.recentModeration');
208
210
  document.getElementById('socialAdvancedSummary').textContent = t('labels.advancedNetworkDetails');
@@ -268,6 +270,7 @@ root.innerHTML = appTemplate;
268
270
  const shell = createShellController({ resolveThemeMode, t });
269
271
  const {
270
272
  applyTheme,
273
+ clearUiCache,
271
274
  flashButton,
272
275
  hydrateCachedShell,
273
276
  peerStatusText,
@@ -438,6 +441,7 @@ root.innerHTML = appTemplate;
438
441
  parseCsv,
439
442
  profileController,
440
443
  pulseOverviewBroadcastStep,
444
+ clearUiCache,
441
445
  refreshAll,
442
446
  refreshLogs,
443
447
  refreshMessages,
@@ -7,6 +7,7 @@ export function bindAppEvents({
7
7
  parseCsv,
8
8
  profileController,
9
9
  pulseOverviewBroadcastStep,
10
+ clearUiCache,
10
11
  refreshAll,
11
12
  refreshLogs,
12
13
  refreshMessages,
@@ -223,6 +224,7 @@ export function bindAppEvents({
223
224
  document.getElementById("clearDiscoveryCacheBtn").addEventListener("click", async () => {
224
225
  try {
225
226
  await api("/api/cache/clear", { method: "POST" });
227
+ clearUiCache(["silicaclaw_ui_overview", "silicaclaw_ui_network", "silicaclaw_ui_social"]);
226
228
  setFeedback("networkFeedback", t("feedback.discoveryCacheCleared"));
227
229
  toast(t("feedback.discoveryCacheCleared"));
228
230
  setAgentsPage(1);
@@ -143,6 +143,7 @@ export function createOverviewController({
143
143
  </div>
144
144
  `;
145
145
  document.getElementById("snapshot").innerHTML = snapshotHtml;
146
+ const networkDiag = networkStats.adapter_diagnostics_summary || {};
146
147
  const heroModeText = o.social?.network_mode || "-";
147
148
  const heroAdapterText = networkCfg.adapter || "-";
148
149
  const heroRelayText = String(networkDiag.signaling_url || networkCfg.adapter_extra?.signaling_url || "-");
@@ -166,7 +167,6 @@ export function createOverviewController({
166
167
  const openclawDetected = !!bridge.openclaw_installation?.detected || openclawRunning || !!bridge.openclaw_runtime?.gateway_reachable;
167
168
  const skillInstalled = !!bridge.skill_learning?.installed;
168
169
  const globalMode = heroModeText === "global-preview";
169
- const networkDiag = networkStats.adapter_diagnostics_summary || {};
170
170
  const lastNetworkError = String(networkDiag.last_error || o.last_broadcast_error || "").trim();
171
171
  const broadcastHealthy = o.broadcast_enabled && !lastNetworkError;
172
172
  const roleKey = openclawRunning
@@ -1,4 +1,10 @@
1
1
  export function createShellController({ t, resolveThemeMode }) {
2
+ const REALTIME_UI_CACHE_KEYS = [
3
+ "silicaclaw_ui_overview",
4
+ "silicaclaw_ui_network",
5
+ "silicaclaw_ui_social",
6
+ ];
7
+
2
8
  function peerStatusText(status) {
3
9
  if (status === "online") return t("overview.online");
4
10
  if (status === "offline") return t("overview.offline");
@@ -6,48 +12,25 @@ export function createShellController({ t, resolveThemeMode }) {
6
12
  return status || "-";
7
13
  }
8
14
 
9
- function readUiCache(key) {
10
- try {
11
- const raw = localStorage.getItem(key);
12
- return raw ? JSON.parse(raw) : null;
13
- } catch {
14
- return null;
15
- }
15
+ function writeUiCache(key, value) {
16
+ void key;
17
+ void value;
16
18
  }
17
19
 
18
- function writeUiCache(key, value) {
20
+ function clearUiCache(keys) {
21
+ const values = Array.isArray(keys) ? keys : keys ? [keys] : REALTIME_UI_CACHE_KEYS;
19
22
  try {
20
- localStorage.setItem(key, JSON.stringify(value));
23
+ for (const key of values) {
24
+ if (!key) continue;
25
+ localStorage.removeItem(key);
26
+ }
21
27
  } catch {
22
- // ignore cache write failures
28
+ // ignore cache removal failures
23
29
  }
24
30
  }
25
31
 
26
32
  function hydrateCachedShell() {
27
- const overview = readUiCache("silicaclaw_ui_overview");
28
- if (overview) {
29
- if (overview.overviewCardsHtml) document.getElementById("overviewCards").innerHTML = overview.overviewCardsHtml;
30
- if (overview.snapshotText) document.getElementById("snapshot").innerHTML = overview.snapshotText;
31
- if (overview.heroModeText) document.getElementById("heroMode").textContent = overview.heroModeText;
32
- if (overview.pillBroadcastText) document.getElementById("pillBroadcast").textContent = overview.pillBroadcastText;
33
- if (overview.pillBroadcastClassName) document.getElementById("pillBroadcast").className = overview.pillBroadcastClassName;
34
- if (overview.agentsCountHintText) document.getElementById("agentsCountHint").textContent = overview.agentsCountHintText;
35
- if (overview.agentsWrapHtml) document.getElementById("agentsWrap").innerHTML = overview.agentsWrapHtml;
36
- }
37
- const network = readUiCache("silicaclaw_ui_network");
38
- if (network) {
39
- if (network.heroAdapterText) document.getElementById("heroAdapter").textContent = network.heroAdapterText;
40
- if (network.heroRelayText) document.getElementById("heroRelay").textContent = network.heroRelayText;
41
- if (network.heroRoomText) document.getElementById("heroRoom").textContent = network.heroRoomText;
42
- if (network.pillAdapterText) document.getElementById("pillAdapter").textContent = network.pillAdapterText;
43
- }
44
- const social = readUiCache("silicaclaw_ui_social");
45
- if (social) {
46
- if (social.integrationStatusText) document.getElementById("integrationStatusBar").textContent = social.integrationStatusText;
47
- if (social.integrationStatusClassName) document.getElementById("integrationStatusBar").className = social.integrationStatusClassName;
48
- if (social.socialStatusLineText) document.getElementById("socialStatusLine").textContent = social.socialStatusLineText;
49
- if (social.socialStatusSublineText) document.getElementById("socialStatusSubline").textContent = social.socialStatusSublineText;
50
- }
33
+ clearUiCache(REALTIME_UI_CACHE_KEYS);
51
34
  }
52
35
 
53
36
  function toast(msg) {
@@ -130,6 +113,7 @@ export function createShellController({ t, resolveThemeMode }) {
130
113
 
131
114
  return {
132
115
  applyTheme,
116
+ clearUiCache,
133
117
  flashButton,
134
118
  hydrateCachedShell,
135
119
  peerStatusText,
@@ -112,11 +112,12 @@ export function createSocialController({
112
112
  }
113
113
 
114
114
  async function refreshSocial() {
115
- const [socialRes, summaryRes, statusRes, networkCfgRes, governanceRes] = await Promise.all([
115
+ const [socialRes, summaryRes, statusRes, networkCfgRes, networkStatsRes, governanceRes] = await Promise.all([
116
116
  api("/api/social/config"),
117
117
  api("/api/social/integration-summary"),
118
118
  api("/api/integration/status"),
119
119
  api("/api/network/config"),
120
+ api("/api/network/stats"),
120
121
  api("/api/social/message-governance"),
121
122
  ]);
122
123
  const bridgeRes = await api("/api/openclaw/bridge");
@@ -126,6 +127,7 @@ export function createSocialController({
126
127
  const networkCfg = networkCfgRes.data || {};
127
128
  const bridge = bridgeRes.data || {};
128
129
  const governance = governanceRes.data || {};
130
+ const networkStats = networkStatsRes.data || {};
129
131
  const runtime = social.runtime || {};
130
132
  const config = social.social_config || {};
131
133
  const network = config.network || {};
@@ -137,6 +139,7 @@ export function createSocialController({
137
139
  const effectiveNamespace = networkCfg.namespace || runtimeNetwork.namespace || summary.current_namespace || "-";
138
140
  const effectiveRoom = effectiveAdapterExtra.room || runtimeNetwork.room || network.room || "-";
139
141
  const effectiveRelay = effectiveAdapterExtra.signaling_url || runtimeNetwork.signaling_url || network.signaling_url || "-";
142
+ const networkDiag = networkStats.adapter_diagnostics_summary || {};
140
143
  const discoverable = status.discoverable === true;
141
144
  const mode = effectiveMode;
142
145
  const summaryLine = status.status_line || summary.summary_line || `${summary.connected ? t("social.connectedToSilicaClaw") : t("social.notConnectedToSilicaClaw")} · ${discoverable ? t("social.discoverableInCurrentMode") : t("social.notDiscoverableInCurrentMode")} · ${t("social.usingMode", { mode })}`;
@@ -190,6 +193,17 @@ export function createSocialController({
190
193
  [t("social.reuseOpenClawIdentity"), summary.reused_openclaw_identity ? t("common.yes") : t("common.no")],
191
194
  ].map(([k, v]) => `<div class="card"><div class="label">${k}</div><div class="value" style="font-size:17px;">${v}</div></div>`).join("");
192
195
 
196
+ document.getElementById("socialMessagePathCards").innerHTML = [
197
+ [t("social.messageBroadcast"), bridge.message_broadcast_enabled ? t("common.on") : t("common.off")],
198
+ [t("social.publicDiscovery"), status.public_enabled ? t("common.on") : t("common.off")],
199
+ [t("social.namespace"), effectiveNamespace],
200
+ [t("labels.room"), effectiveRoom],
201
+ [t("labels.relay"), effectiveRelay],
202
+ [t("network.lastPoll"), networkDiag.last_poll_at ? new Date(networkDiag.last_poll_at).toLocaleTimeString() : "-"],
203
+ [t("network.lastPublish"), networkDiag.last_publish_at ? new Date(networkDiag.last_publish_at).toLocaleTimeString() : "-"],
204
+ [t("network.lastError"), networkDiag.last_error || t("network.none")],
205
+ ].map(([k, v]) => `<div class="card"><div class="label">${k}</div><div class="value" style="font-size:17px;">${escapeHtml(String(v))}</div></div>`).join("");
206
+
193
207
  const skillLearning = bridge.skill_learning || {};
194
208
  const ownerDelivery = bridge.owner_delivery || {};
195
209
  const installAction = skillLearning.install_action || {};
@@ -689,6 +689,11 @@ export const appTemplate = String.raw`<div class="app" id="appShell">
689
689
  <div class="grid" id="socialIntegrationCards"></div>
690
690
  </div>
691
691
  </div>
692
+ <div class="card">
693
+ <h3 class="title-sm" id="socialMessagePathTitle">Message Path</h3>
694
+ <div class="field-hint" id="socialMessagePathHint">Check this block first when public messages are not showing up on another node.</div>
695
+ <div class="grid" id="socialMessagePathCards"></div>
696
+ </div>
692
697
  </div>
693
698
  <div class="page-column">
694
699
  <div class="card">
@@ -150,6 +150,7 @@ export const TRANSLATIONS = {
150
150
  recentModeration: 'Recent Moderation Activity',
151
151
  ownerDelivery: 'Owner Delivery',
152
152
  openclawSkillLearning: 'OpenClaw Skill Learning',
153
+ messagePath: 'Message Path',
153
154
  skillsEyebrow: 'Skills',
154
155
  skillsFeatured: 'Featured Skill',
155
156
  skillsBundled: 'Bundled Skills',
@@ -225,6 +226,7 @@ export const TRANSLATIONS = {
225
226
  socialBannerBody: 'This page separates current social runtime, bridge health, and learning path, so it is easier to see whether this machine is just broadcasting or also ready to learn and forward.',
226
227
  socialBannerOpenClaw: 'OpenClaw',
227
228
  socialBannerOpenClawValue: 'Use the skill card here to confirm OpenClaw is detected, running, and ready to learn SilicaClaw broadcasts.',
229
+ socialMessagePathHint: 'Check this block first when public messages are not showing up on another node.',
228
230
  skillsBannerTitle: 'See what ships with SilicaClaw and what OpenClaw already exposes here.',
229
231
  skillsBannerBody: 'This page is the skill hub: packaged capabilities from this project on one side, skills already installed into OpenClaw on the other, with a clear path to install the broadcast skill from here.',
230
232
  skillsBannerRuntime: 'Runtime',
@@ -663,6 +665,7 @@ export const TRANSLATIONS = {
663
665
  recentModeration: '最近治理活动',
664
666
  ownerDelivery: '主人转发',
665
667
  openclawSkillLearning: 'OpenClaw 技能学习',
668
+ messagePath: '消息通路',
666
669
  skillsEyebrow: '技能',
667
670
  skillsFeatured: '重点技能',
668
671
  skillsBundled: '项目自带技能',
@@ -738,6 +741,7 @@ export const TRANSLATIONS = {
738
741
  socialBannerBody: '这个页面把当前社交运行态、Bridge 健康度和学习路径拆开呈现,更容易判断这台机器只是广播源,还是已经具备学习与转发能力。',
739
742
  socialBannerOpenClaw: 'OpenClaw',
740
743
  socialBannerOpenClawValue: '在这里看技能卡片,就能确认 OpenClaw 是否已检测到、已启动,并且准备好学习 SilicaClaw 广播。',
744
+ socialMessagePathHint: '如果公开消息没有出现在另一台节点上,先看这里。',
741
745
  skillsBannerTitle: '在这里看清楚 SilicaClaw 自带什么,以及 OpenClaw 这里已经暴露什么。',
742
746
  skillsBannerBody: '这个页面是技能中心:左边是项目打包好的能力,右边是本机 OpenClaw 已安装的技能,同时保留一键安装广播技能的入口。',
743
747
  skillsBannerRuntime: '运行态',
@@ -27,6 +27,7 @@ import {
27
27
  ingestPresenceRecord,
28
28
  ingestProfileRecord,
29
29
  isAgentOnline,
30
+ rebuildIndexForProfile,
30
31
  loadSocialConfig,
31
32
  getSocialConfigSearchPaths,
32
33
  resolveIdentityWithSocial,
@@ -2029,7 +2030,7 @@ export class LocalNodeService {
2029
2030
  }
2030
2031
  await this.profileRepo.set(this.profile);
2031
2032
 
2032
- this.directory = dedupeIndex(await this.cacheRepo.get());
2033
+ this.directory = createEmptyDirectoryState();
2033
2034
  this.messageGovernance = {
2034
2035
  ...this.defaultMessageGovernance(),
2035
2036
  ...(await this.socialMessageGovernanceRepo.get()),
@@ -2366,7 +2367,22 @@ export class LocalNodeService {
2366
2367
  }
2367
2368
 
2368
2369
  private async persistCache(): Promise<void> {
2369
- await this.cacheRepo.set(this.directory);
2370
+ const persisted = createEmptyDirectoryState();
2371
+ if (this.profile) {
2372
+ const selfProfileRecord: SignedProfileRecord = {
2373
+ type: "profile",
2374
+ profile: this.profile,
2375
+ };
2376
+ this.directory = ingestProfileRecord(this.directory, selfProfileRecord);
2377
+ persisted.profiles[this.profile.agent_id] = this.profile;
2378
+ const selfLastSeenAt = this.directory.presence[this.profile.agent_id];
2379
+ if (typeof selfLastSeenAt === "number" && Number.isFinite(selfLastSeenAt)) {
2380
+ persisted.presence[this.profile.agent_id] = selfLastSeenAt;
2381
+ }
2382
+ const indexed = rebuildIndexForProfile(persisted, this.profile);
2383
+ persisted.index = indexed.index;
2384
+ }
2385
+ await this.cacheRepo.set(persisted);
2370
2386
  }
2371
2387
 
2372
2388
  private async persistSocialMessages(): Promise<void> {
@@ -1 +1 @@
1
- 2026.3.19-13
1
+ 2026.3.19-15
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "silicaclaw-broadcast",
3
- "version": "2026.3.19-13",
3
+ "version": "2026.3.19-15",
4
4
  "display_name": "SilicaClaw Broadcast",
5
5
  "description": "OpenClaw skill for reading SilicaClaw public broadcasts, publishing public broadcasts, and forwarding relevant updates to the owner through OpenClaw's native social channel.",
6
6
  "entrypoints": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@silicaclaw/cli",
3
- "version": "2026.3.19-13",
3
+ "version": "2026.3.19-15",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"