@silicaclaw/cli 2026.3.19-9 → 2026.3.20-2

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 (278) hide show
  1. package/CHANGELOG.md +134 -0
  2. package/DEMO_GUIDE.md +1 -1
  3. package/INSTALL.md +47 -13
  4. package/README.md +54 -19
  5. package/VERSION +1 -1
  6. package/apps/local-console/dist/apps/local-console/src/server.d.ts +51 -12
  7. package/apps/local-console/dist/apps/local-console/src/server.js +619 -183
  8. package/apps/local-console/dist/config/silicaclaw-defaults.json +19 -0
  9. package/apps/local-console/dist/packages/core/src/socialConfig.js +9 -5
  10. package/apps/local-console/dist/packages/network/src/realPreview.js +6 -2
  11. package/apps/local-console/dist/packages/network/src/relayPreview.js +8 -2
  12. package/apps/local-console/dist/packages/network/src/transport/udpLanBroadcastTransport.js +2 -1
  13. package/apps/local-console/dist/packages/network/src/webrtcPreview.js +5 -1
  14. package/apps/local-console/dist/packages/storage/config/silicaclaw-defaults.json +19 -0
  15. package/apps/local-console/dist/packages/storage/src/socialRuntimeRepo.js +8 -4
  16. package/apps/local-console/public/app/app.js +21 -1
  17. package/apps/local-console/public/app/events.js +40 -2
  18. package/apps/local-console/public/app/network.js +32 -3
  19. package/apps/local-console/public/app/overview.js +18 -26
  20. package/apps/local-console/public/app/shell.js +18 -34
  21. package/apps/local-console/public/app/social.js +207 -28
  22. package/apps/local-console/public/app/styles.css +182 -14
  23. package/apps/local-console/public/app/template.js +76 -31
  24. package/apps/local-console/public/app/translations.js +147 -51
  25. package/apps/local-console/src/server.ts +652 -189
  26. package/apps/public-explorer/dist/apps/public-explorer/src/server.d.ts +1 -0
  27. package/apps/public-explorer/dist/apps/public-explorer/src/server.js +41 -0
  28. package/apps/public-explorer/dist/config/silicaclaw-defaults.json +19 -0
  29. package/apps/public-explorer/public/app/app.js +22 -2
  30. package/apps/public-explorer/public/app/template.js +4 -4
  31. package/apps/public-explorer/public/app/translations.js +15 -15
  32. package/apps/public-explorer/src/server.ts +11 -1
  33. package/config/silicaclaw-defaults.json +19 -0
  34. package/dist/apps/local-console/src/server.d.ts +1 -0
  35. package/dist/apps/local-console/src/server.js +555 -0
  36. package/docs/NEW_USER_INSTALL.md +14 -10
  37. package/docs/NEW_USER_OPERATIONS.md +4 -4
  38. package/docs/OPENCLAW_BRIDGE.md +15 -0
  39. package/docs/OPENCLAW_BRIDGE_ZH.md +15 -0
  40. package/docs/RELEASE_CHECKLIST.md +95 -0
  41. package/node_modules/@silicaclaw/core/dist/config/silicaclaw-defaults.json +19 -0
  42. package/node_modules/@silicaclaw/core/dist/packages/core/src/crypto.d.ts +6 -0
  43. package/node_modules/@silicaclaw/core/dist/packages/core/src/crypto.js +50 -0
  44. package/node_modules/@silicaclaw/core/dist/packages/core/src/directory.d.ts +17 -0
  45. package/node_modules/@silicaclaw/core/dist/packages/core/src/directory.js +145 -0
  46. package/node_modules/@silicaclaw/core/dist/packages/core/src/identity.d.ts +2 -0
  47. package/node_modules/@silicaclaw/core/dist/packages/core/src/identity.js +18 -0
  48. package/node_modules/@silicaclaw/core/dist/packages/core/src/index.d.ts +12 -0
  49. package/node_modules/@silicaclaw/core/dist/packages/core/src/index.js +28 -0
  50. package/node_modules/@silicaclaw/core/dist/packages/core/src/indexing.d.ts +6 -0
  51. package/node_modules/@silicaclaw/core/dist/packages/core/src/indexing.js +43 -0
  52. package/node_modules/@silicaclaw/core/dist/packages/core/src/presence.d.ts +4 -0
  53. package/node_modules/@silicaclaw/core/dist/packages/core/src/presence.js +23 -0
  54. package/node_modules/@silicaclaw/core/dist/packages/core/src/profile.d.ts +4 -0
  55. package/node_modules/@silicaclaw/core/dist/packages/core/src/profile.js +39 -0
  56. package/node_modules/@silicaclaw/core/dist/packages/core/src/publicProfileSummary.d.ts +70 -0
  57. package/node_modules/@silicaclaw/core/dist/packages/core/src/publicProfileSummary.js +103 -0
  58. package/node_modules/@silicaclaw/core/dist/packages/core/src/socialConfig.d.ts +100 -0
  59. package/node_modules/@silicaclaw/core/dist/packages/core/src/socialConfig.js +300 -0
  60. package/node_modules/@silicaclaw/core/dist/packages/core/src/socialMessage.d.ts +19 -0
  61. package/node_modules/@silicaclaw/core/dist/packages/core/src/socialMessage.js +69 -0
  62. package/node_modules/@silicaclaw/core/dist/packages/core/src/socialResolver.d.ts +46 -0
  63. package/node_modules/@silicaclaw/core/dist/packages/core/src/socialResolver.js +237 -0
  64. package/node_modules/@silicaclaw/core/dist/packages/core/src/socialTemplate.d.ts +2 -0
  65. package/node_modules/@silicaclaw/core/dist/packages/core/src/socialTemplate.js +90 -0
  66. package/node_modules/@silicaclaw/core/dist/packages/core/src/types.d.ts +59 -0
  67. package/node_modules/@silicaclaw/core/dist/packages/core/src/types.js +2 -0
  68. package/node_modules/@silicaclaw/core/src/socialConfig.ts +7 -5
  69. package/node_modules/@silicaclaw/network/dist/config/silicaclaw-defaults.json +19 -0
  70. package/node_modules/@silicaclaw/network/dist/packages/network/src/abstractions/messageEnvelope.d.ts +28 -0
  71. package/node_modules/@silicaclaw/network/dist/packages/network/src/abstractions/messageEnvelope.js +36 -0
  72. package/node_modules/@silicaclaw/network/dist/packages/network/src/abstractions/peerDiscovery.d.ts +43 -0
  73. package/node_modules/@silicaclaw/network/dist/packages/network/src/abstractions/peerDiscovery.js +2 -0
  74. package/node_modules/@silicaclaw/network/dist/packages/network/src/abstractions/topicCodec.d.ts +4 -0
  75. package/node_modules/@silicaclaw/network/dist/packages/network/src/abstractions/topicCodec.js +2 -0
  76. package/node_modules/@silicaclaw/network/dist/packages/network/src/abstractions/transport.d.ts +36 -0
  77. package/node_modules/@silicaclaw/network/dist/packages/network/src/abstractions/transport.js +2 -0
  78. package/node_modules/@silicaclaw/network/dist/packages/network/src/codec/jsonMessageEnvelopeCodec.d.ts +5 -0
  79. package/node_modules/@silicaclaw/network/dist/packages/network/src/codec/jsonMessageEnvelopeCodec.js +24 -0
  80. package/node_modules/@silicaclaw/network/dist/packages/network/src/codec/jsonTopicCodec.d.ts +5 -0
  81. package/node_modules/@silicaclaw/network/dist/packages/network/src/codec/jsonTopicCodec.js +12 -0
  82. package/node_modules/@silicaclaw/network/dist/packages/network/src/discovery/heartbeatPeerDiscovery.d.ts +28 -0
  83. package/node_modules/@silicaclaw/network/dist/packages/network/src/discovery/heartbeatPeerDiscovery.js +144 -0
  84. package/node_modules/@silicaclaw/network/dist/packages/network/src/index.d.ts +14 -0
  85. package/node_modules/@silicaclaw/network/dist/packages/network/src/index.js +30 -0
  86. package/node_modules/@silicaclaw/network/dist/packages/network/src/localEventBus.d.ts +9 -0
  87. package/node_modules/@silicaclaw/network/dist/packages/network/src/localEventBus.js +47 -0
  88. package/node_modules/@silicaclaw/network/dist/packages/network/src/mock.d.ts +8 -0
  89. package/node_modules/@silicaclaw/network/dist/packages/network/src/mock.js +24 -0
  90. package/node_modules/@silicaclaw/network/dist/packages/network/src/realPreview.d.ts +105 -0
  91. package/node_modules/@silicaclaw/network/dist/packages/network/src/realPreview.js +331 -0
  92. package/node_modules/@silicaclaw/network/dist/packages/network/src/relayPreview.d.ts +166 -0
  93. package/node_modules/@silicaclaw/network/dist/packages/network/src/relayPreview.js +448 -0
  94. package/node_modules/@silicaclaw/network/dist/packages/network/src/transport/udpLanBroadcastTransport.d.ts +23 -0
  95. package/node_modules/@silicaclaw/network/dist/packages/network/src/transport/udpLanBroadcastTransport.js +154 -0
  96. package/node_modules/@silicaclaw/network/dist/packages/network/src/types.d.ts +6 -0
  97. package/node_modules/@silicaclaw/network/dist/packages/network/src/types.js +2 -0
  98. package/node_modules/@silicaclaw/network/dist/packages/network/src/webrtcPreview.d.ts +163 -0
  99. package/node_modules/@silicaclaw/network/dist/packages/network/src/webrtcPreview.js +848 -0
  100. package/node_modules/@silicaclaw/network/src/realPreview.ts +3 -2
  101. package/node_modules/@silicaclaw/network/src/relayPreview.ts +5 -2
  102. package/node_modules/@silicaclaw/network/src/transport/udpLanBroadcastTransport.ts +2 -1
  103. package/node_modules/@silicaclaw/network/src/webrtcPreview.ts +2 -1
  104. package/node_modules/@silicaclaw/storage/config/silicaclaw-defaults.json +19 -0
  105. package/node_modules/@silicaclaw/storage/dist/config/silicaclaw-defaults.json +19 -0
  106. package/node_modules/@silicaclaw/storage/dist/packages/core/src/crypto.d.ts +6 -0
  107. package/node_modules/@silicaclaw/storage/dist/packages/core/src/crypto.js +50 -0
  108. package/node_modules/@silicaclaw/storage/dist/packages/core/src/directory.d.ts +17 -0
  109. package/node_modules/@silicaclaw/storage/dist/packages/core/src/directory.js +145 -0
  110. package/node_modules/@silicaclaw/storage/dist/packages/core/src/identity.d.ts +2 -0
  111. package/node_modules/@silicaclaw/storage/dist/packages/core/src/identity.js +18 -0
  112. package/node_modules/@silicaclaw/storage/dist/packages/core/src/index.d.ts +12 -0
  113. package/node_modules/@silicaclaw/storage/dist/packages/core/src/index.js +28 -0
  114. package/node_modules/@silicaclaw/storage/dist/packages/core/src/indexing.d.ts +6 -0
  115. package/node_modules/@silicaclaw/storage/dist/packages/core/src/indexing.js +43 -0
  116. package/node_modules/@silicaclaw/storage/dist/packages/core/src/presence.d.ts +4 -0
  117. package/node_modules/@silicaclaw/storage/dist/packages/core/src/presence.js +23 -0
  118. package/node_modules/@silicaclaw/storage/dist/packages/core/src/profile.d.ts +4 -0
  119. package/node_modules/@silicaclaw/storage/dist/packages/core/src/profile.js +39 -0
  120. package/node_modules/@silicaclaw/storage/dist/packages/core/src/publicProfileSummary.d.ts +70 -0
  121. package/node_modules/@silicaclaw/storage/dist/packages/core/src/publicProfileSummary.js +103 -0
  122. package/node_modules/@silicaclaw/storage/dist/packages/core/src/socialConfig.d.ts +100 -0
  123. package/node_modules/@silicaclaw/storage/dist/packages/core/src/socialConfig.js +300 -0
  124. package/node_modules/@silicaclaw/storage/dist/packages/core/src/socialMessage.d.ts +19 -0
  125. package/node_modules/@silicaclaw/storage/dist/packages/core/src/socialMessage.js +69 -0
  126. package/node_modules/@silicaclaw/storage/dist/packages/core/src/socialResolver.d.ts +46 -0
  127. package/node_modules/@silicaclaw/storage/dist/packages/core/src/socialResolver.js +237 -0
  128. package/node_modules/@silicaclaw/storage/dist/packages/core/src/socialTemplate.d.ts +2 -0
  129. package/node_modules/@silicaclaw/storage/dist/packages/core/src/socialTemplate.js +90 -0
  130. package/node_modules/@silicaclaw/storage/dist/packages/core/src/types.d.ts +59 -0
  131. package/node_modules/@silicaclaw/storage/dist/packages/core/src/types.js +2 -0
  132. package/node_modules/@silicaclaw/storage/dist/packages/storage/config/silicaclaw-defaults.json +19 -0
  133. package/node_modules/@silicaclaw/storage/dist/packages/storage/src/index.d.ts +3 -0
  134. package/node_modules/@silicaclaw/storage/dist/packages/storage/src/index.js +19 -0
  135. package/node_modules/@silicaclaw/storage/dist/packages/storage/src/jsonRepo.d.ts +7 -0
  136. package/node_modules/@silicaclaw/storage/dist/packages/storage/src/jsonRepo.js +29 -0
  137. package/node_modules/@silicaclaw/storage/dist/packages/storage/src/repos.d.ts +61 -0
  138. package/node_modules/@silicaclaw/storage/dist/packages/storage/src/repos.js +67 -0
  139. package/node_modules/@silicaclaw/storage/dist/packages/storage/src/socialRuntimeRepo.d.ts +5 -0
  140. package/node_modules/@silicaclaw/storage/dist/packages/storage/src/socialRuntimeRepo.js +57 -0
  141. package/node_modules/@silicaclaw/storage/dist/socialRuntimeRepo.js +8 -4
  142. package/node_modules/@silicaclaw/storage/src/socialRuntimeRepo.ts +5 -4
  143. package/node_modules/@silicaclaw/storage/tsconfig.json +1 -6
  144. package/openclaw-skills/silicaclaw-bridge-setup/SKILL.md +147 -0
  145. package/openclaw-skills/silicaclaw-bridge-setup/VERSION +1 -0
  146. package/openclaw-skills/silicaclaw-bridge-setup/agents/openai.yaml +6 -0
  147. package/openclaw-skills/silicaclaw-bridge-setup/manifest.json +27 -0
  148. package/openclaw-skills/silicaclaw-bridge-setup/references/owner-dialogue-cheatsheet-zh.md +58 -0
  149. package/openclaw-skills/silicaclaw-bridge-setup/references/runtime-setup.md +43 -0
  150. package/openclaw-skills/silicaclaw-bridge-setup/references/troubleshooting.md +24 -0
  151. package/openclaw-skills/silicaclaw-broadcast/SKILL.md +132 -0
  152. package/openclaw-skills/silicaclaw-broadcast/VERSION +1 -1
  153. package/openclaw-skills/silicaclaw-broadcast/agents/openai.yaml +2 -2
  154. package/openclaw-skills/silicaclaw-broadcast/manifest.json +3 -2
  155. package/openclaw-skills/silicaclaw-broadcast/references/owner-dialogue-cheatsheet-zh.md +81 -0
  156. package/openclaw-skills/silicaclaw-owner-push/SKILL.md +217 -0
  157. package/openclaw-skills/silicaclaw-owner-push/VERSION +1 -0
  158. package/openclaw-skills/silicaclaw-owner-push/agents/openai.yaml +6 -0
  159. package/openclaw-skills/silicaclaw-owner-push/manifest.json +30 -0
  160. package/openclaw-skills/silicaclaw-owner-push/references/owner-dialogue-cheatsheet-zh.md +87 -0
  161. package/openclaw-skills/silicaclaw-owner-push/references/push-routing-policy.md +43 -0
  162. package/openclaw-skills/silicaclaw-owner-push/references/runtime-setup.md +41 -0
  163. package/openclaw-skills/silicaclaw-owner-push/scripts/owner-push-forwarder.mjs +214 -0
  164. package/openclaw-skills/silicaclaw-owner-push/scripts/send-to-owner-via-openclaw.mjs +69 -0
  165. package/package.json +5 -1
  166. package/packages/core/dist/config/silicaclaw-defaults.json +19 -0
  167. package/packages/core/dist/packages/core/src/crypto.d.ts +6 -0
  168. package/packages/core/dist/packages/core/src/crypto.js +50 -0
  169. package/packages/core/dist/packages/core/src/directory.d.ts +17 -0
  170. package/packages/core/dist/packages/core/src/directory.js +145 -0
  171. package/packages/core/dist/packages/core/src/identity.d.ts +2 -0
  172. package/packages/core/dist/packages/core/src/identity.js +18 -0
  173. package/packages/core/dist/packages/core/src/index.d.ts +12 -0
  174. package/packages/core/dist/packages/core/src/index.js +28 -0
  175. package/packages/core/dist/packages/core/src/indexing.d.ts +6 -0
  176. package/packages/core/dist/packages/core/src/indexing.js +43 -0
  177. package/packages/core/dist/packages/core/src/presence.d.ts +4 -0
  178. package/packages/core/dist/packages/core/src/presence.js +23 -0
  179. package/packages/core/dist/packages/core/src/profile.d.ts +4 -0
  180. package/packages/core/dist/packages/core/src/profile.js +39 -0
  181. package/packages/core/dist/packages/core/src/publicProfileSummary.d.ts +70 -0
  182. package/packages/core/dist/packages/core/src/publicProfileSummary.js +103 -0
  183. package/packages/core/dist/packages/core/src/socialConfig.d.ts +100 -0
  184. package/packages/core/dist/packages/core/src/socialConfig.js +300 -0
  185. package/packages/core/dist/packages/core/src/socialMessage.d.ts +19 -0
  186. package/packages/core/dist/packages/core/src/socialMessage.js +69 -0
  187. package/packages/core/dist/packages/core/src/socialResolver.d.ts +46 -0
  188. package/packages/core/dist/packages/core/src/socialResolver.js +237 -0
  189. package/packages/core/dist/packages/core/src/socialTemplate.d.ts +2 -0
  190. package/packages/core/dist/packages/core/src/socialTemplate.js +90 -0
  191. package/packages/core/dist/packages/core/src/types.d.ts +59 -0
  192. package/packages/core/dist/packages/core/src/types.js +2 -0
  193. package/packages/core/src/socialConfig.ts +7 -5
  194. package/packages/network/dist/config/silicaclaw-defaults.json +19 -0
  195. package/packages/network/dist/packages/network/src/abstractions/messageEnvelope.d.ts +28 -0
  196. package/packages/network/dist/packages/network/src/abstractions/messageEnvelope.js +36 -0
  197. package/packages/network/dist/packages/network/src/abstractions/peerDiscovery.d.ts +43 -0
  198. package/packages/network/dist/packages/network/src/abstractions/peerDiscovery.js +2 -0
  199. package/packages/network/dist/packages/network/src/abstractions/topicCodec.d.ts +4 -0
  200. package/packages/network/dist/packages/network/src/abstractions/topicCodec.js +2 -0
  201. package/packages/network/dist/packages/network/src/abstractions/transport.d.ts +36 -0
  202. package/packages/network/dist/packages/network/src/abstractions/transport.js +2 -0
  203. package/packages/network/dist/packages/network/src/codec/jsonMessageEnvelopeCodec.d.ts +5 -0
  204. package/packages/network/dist/packages/network/src/codec/jsonMessageEnvelopeCodec.js +24 -0
  205. package/packages/network/dist/packages/network/src/codec/jsonTopicCodec.d.ts +5 -0
  206. package/packages/network/dist/packages/network/src/codec/jsonTopicCodec.js +12 -0
  207. package/packages/network/dist/packages/network/src/discovery/heartbeatPeerDiscovery.d.ts +28 -0
  208. package/packages/network/dist/packages/network/src/discovery/heartbeatPeerDiscovery.js +144 -0
  209. package/packages/network/dist/packages/network/src/index.d.ts +14 -0
  210. package/packages/network/dist/packages/network/src/index.js +30 -0
  211. package/packages/network/dist/packages/network/src/localEventBus.d.ts +9 -0
  212. package/packages/network/dist/packages/network/src/localEventBus.js +47 -0
  213. package/packages/network/dist/packages/network/src/mock.d.ts +8 -0
  214. package/packages/network/dist/packages/network/src/mock.js +24 -0
  215. package/packages/network/dist/packages/network/src/realPreview.d.ts +105 -0
  216. package/packages/network/dist/packages/network/src/realPreview.js +331 -0
  217. package/packages/network/dist/packages/network/src/relayPreview.d.ts +166 -0
  218. package/packages/network/dist/packages/network/src/relayPreview.js +448 -0
  219. package/packages/network/dist/packages/network/src/transport/udpLanBroadcastTransport.d.ts +23 -0
  220. package/packages/network/dist/packages/network/src/transport/udpLanBroadcastTransport.js +154 -0
  221. package/packages/network/dist/packages/network/src/types.d.ts +6 -0
  222. package/packages/network/dist/packages/network/src/types.js +2 -0
  223. package/packages/network/dist/packages/network/src/webrtcPreview.d.ts +163 -0
  224. package/packages/network/dist/packages/network/src/webrtcPreview.js +848 -0
  225. package/packages/network/src/realPreview.ts +3 -2
  226. package/packages/network/src/relayPreview.ts +5 -2
  227. package/packages/network/src/transport/udpLanBroadcastTransport.ts +2 -1
  228. package/packages/network/src/webrtcPreview.ts +2 -1
  229. package/packages/storage/config/silicaclaw-defaults.json +19 -0
  230. package/packages/storage/dist/config/silicaclaw-defaults.json +19 -0
  231. package/packages/storage/dist/packages/core/src/crypto.d.ts +6 -0
  232. package/packages/storage/dist/packages/core/src/crypto.js +50 -0
  233. package/packages/storage/dist/packages/core/src/directory.d.ts +17 -0
  234. package/packages/storage/dist/packages/core/src/directory.js +145 -0
  235. package/packages/storage/dist/packages/core/src/identity.d.ts +2 -0
  236. package/packages/storage/dist/packages/core/src/identity.js +18 -0
  237. package/packages/storage/dist/packages/core/src/index.d.ts +12 -0
  238. package/packages/storage/dist/packages/core/src/index.js +28 -0
  239. package/packages/storage/dist/packages/core/src/indexing.d.ts +6 -0
  240. package/packages/storage/dist/packages/core/src/indexing.js +43 -0
  241. package/packages/storage/dist/packages/core/src/presence.d.ts +4 -0
  242. package/packages/storage/dist/packages/core/src/presence.js +23 -0
  243. package/packages/storage/dist/packages/core/src/profile.d.ts +4 -0
  244. package/packages/storage/dist/packages/core/src/profile.js +39 -0
  245. package/packages/storage/dist/packages/core/src/publicProfileSummary.d.ts +70 -0
  246. package/packages/storage/dist/packages/core/src/publicProfileSummary.js +103 -0
  247. package/packages/storage/dist/packages/core/src/socialConfig.d.ts +100 -0
  248. package/packages/storage/dist/packages/core/src/socialConfig.js +300 -0
  249. package/packages/storage/dist/packages/core/src/socialMessage.d.ts +19 -0
  250. package/packages/storage/dist/packages/core/src/socialMessage.js +69 -0
  251. package/packages/storage/dist/packages/core/src/socialResolver.d.ts +46 -0
  252. package/packages/storage/dist/packages/core/src/socialResolver.js +237 -0
  253. package/packages/storage/dist/packages/core/src/socialTemplate.d.ts +2 -0
  254. package/packages/storage/dist/packages/core/src/socialTemplate.js +90 -0
  255. package/packages/storage/dist/packages/core/src/types.d.ts +59 -0
  256. package/packages/storage/dist/packages/core/src/types.js +2 -0
  257. package/packages/storage/dist/packages/storage/config/silicaclaw-defaults.json +19 -0
  258. package/packages/storage/dist/packages/storage/src/index.d.ts +3 -0
  259. package/packages/storage/dist/packages/storage/src/index.js +19 -0
  260. package/packages/storage/dist/packages/storage/src/jsonRepo.d.ts +7 -0
  261. package/packages/storage/dist/packages/storage/src/jsonRepo.js +29 -0
  262. package/packages/storage/dist/packages/storage/src/repos.d.ts +61 -0
  263. package/packages/storage/dist/packages/storage/src/repos.js +67 -0
  264. package/packages/storage/dist/packages/storage/src/socialRuntimeRepo.d.ts +5 -0
  265. package/packages/storage/dist/packages/storage/src/socialRuntimeRepo.js +57 -0
  266. package/packages/storage/dist/socialRuntimeRepo.js +8 -4
  267. package/packages/storage/src/socialRuntimeRepo.ts +5 -4
  268. package/packages/storage/tsconfig.json +1 -6
  269. package/scripts/functional-check.mjs +35 -6
  270. package/scripts/install-openclaw-skill.mjs +9 -2
  271. package/scripts/openclaw-bridge-adapter.mjs +3 -1
  272. package/scripts/openclaw-bridge-client.mjs +3 -1
  273. package/scripts/openclaw-runtime-demo.mjs +3 -1
  274. package/scripts/quickstart.sh +14 -10
  275. package/scripts/release-pack.mjs +59 -1
  276. package/scripts/silicaclaw-cli.mjs +162 -50
  277. package/scripts/silicaclaw-gateway.mjs +302 -84
  278. package/scripts/validate-openclaw-skill.mjs +79 -21
@@ -4,8 +4,9 @@ import { execFile, spawnSync } from "child_process";
4
4
  import { resolve } from "path";
5
5
  import { accessSync, constants, copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync } from "fs";
6
6
  import { createHash } from "crypto";
7
- import { hostname } from "os";
7
+ import { homedir, hostname } from "os";
8
8
  import { promisify } from "util";
9
+ import defaults from "../../../config/silicaclaw-defaults.json";
9
10
  import {
10
11
  AgentIdentity,
11
12
  DirectoryState,
@@ -27,6 +28,7 @@ import {
27
28
  ingestPresenceRecord,
28
29
  ingestProfileRecord,
29
30
  isAgentOnline,
31
+ rebuildIndexForProfile,
30
32
  loadSocialConfig,
31
33
  getSocialConfigSearchPaths,
32
34
  resolveIdentityWithSocial,
@@ -79,16 +81,22 @@ const NETWORK_MAX_PAST_DRIFT_MS = Number(process.env.NETWORK_MAX_PAST_DRIFT_MS |
79
81
  const NETWORK_HEARTBEAT_INTERVAL_MS = Number(process.env.NETWORK_HEARTBEAT_INTERVAL_MS || 12_000);
80
82
  const NETWORK_PEER_STALE_AFTER_MS = Number(process.env.NETWORK_PEER_STALE_AFTER_MS || 45_000);
81
83
  const OPENCLAW_GATEWAY_HOST = "127.0.0.1";
82
- const OPENCLAW_GATEWAY_PORT = 18_789;
84
+ const DEFAULT_NETWORK_MODE = defaults.network.default_mode as "global-preview";
85
+ const DEFAULT_NETWORK_NAMESPACE = defaults.network.default_namespace;
86
+ const DEFAULT_NETWORK_PORT = defaults.ports.network_default;
87
+ const DEFAULT_GLOBAL_SIGNALING_URL = defaults.network.global_preview.relay_url;
88
+ const DEFAULT_GLOBAL_ROOM = defaults.network.global_preview.room;
89
+ const DEFAULT_BRIDGE_API_BASE = defaults.bridge.api_base;
90
+ const OPENCLAW_GATEWAY_PORT = defaults.ports.openclaw_gateway;
83
91
  const OPENCLAW_GATEWAY_URL = `http://${OPENCLAW_GATEWAY_HOST}:${OPENCLAW_GATEWAY_PORT}/`;
84
92
  const NETWORK_PEER_REMOVE_AFTER_MS = Number(process.env.NETWORK_PEER_REMOVE_AFTER_MS || 180_000);
85
93
  const NETWORK_UDP_BIND_ADDRESS = process.env.NETWORK_UDP_BIND_ADDRESS || "0.0.0.0";
86
94
  const NETWORK_UDP_BROADCAST_ADDRESS = process.env.NETWORK_UDP_BROADCAST_ADDRESS || "255.255.255.255";
87
95
  const NETWORK_PEER_ID = process.env.NETWORK_PEER_ID;
88
96
  const NETWORK_MODE = process.env.NETWORK_MODE || "";
89
- const WEBRTC_SIGNALING_URL = process.env.WEBRTC_SIGNALING_URL || "https://relay.silicaclaw.com";
97
+ const WEBRTC_SIGNALING_URL = process.env.WEBRTC_SIGNALING_URL || DEFAULT_GLOBAL_SIGNALING_URL;
90
98
  const WEBRTC_SIGNALING_URLS = process.env.WEBRTC_SIGNALING_URLS || "";
91
- const WEBRTC_ROOM = process.env.WEBRTC_ROOM || "silicaclaw-global-preview";
99
+ const WEBRTC_ROOM = process.env.WEBRTC_ROOM || DEFAULT_GLOBAL_ROOM;
92
100
  const WEBRTC_SEED_PEERS = process.env.WEBRTC_SEED_PEERS || "";
93
101
  const WEBRTC_BOOTSTRAP_HINTS = process.env.WEBRTC_BOOTSTRAP_HINTS || "";
94
102
  const PROFILE_VERSION = "v0.9";
@@ -105,6 +113,8 @@ const SOCIAL_MESSAGE_DUPLICATE_WINDOW_MS = Number(process.env.SOCIAL_MESSAGE_DUP
105
113
  const SOCIAL_MESSAGE_MAX_FUTURE_MS = Number(process.env.SOCIAL_MESSAGE_MAX_FUTURE_MS || 30_000);
106
114
  const SOCIAL_MESSAGE_MAX_AGE_MS = Number(process.env.SOCIAL_MESSAGE_MAX_AGE_MS || 15 * 60_000);
107
115
  const SOCIAL_MESSAGE_OBSERVATION_HISTORY_LIMIT = Number(process.env.SOCIAL_MESSAGE_OBSERVATION_HISTORY_LIMIT || 500);
116
+ const SOCIAL_MESSAGE_REPLAY_WINDOW_MS = Number(process.env.SOCIAL_MESSAGE_REPLAY_WINDOW_MS || 10 * 60_000);
117
+ const SOCIAL_MESSAGE_REPLAY_MAX_PER_BROADCAST = Number(process.env.SOCIAL_MESSAGE_REPLAY_MAX_PER_BROADCAST || 3);
108
118
  const SOCIAL_MESSAGE_BLOCKED_AGENT_IDS = new Set(
109
119
  dedupeStrings(parseListEnv(process.env.SOCIAL_MESSAGE_BLOCKED_AGENT_IDS || ""))
110
120
  );
@@ -115,23 +125,67 @@ const execFileAsync = promisify(execFile);
115
125
  const OPENCLAW_SKILL_NAME = "silicaclaw-broadcast";
116
126
 
117
127
  function readWorkspaceVersion(workspaceRoot: string): string {
118
- const pkgFile = resolve(workspaceRoot, "package.json");
119
- if (existsSync(pkgFile)) {
120
- try {
121
- const pkg = JSON.parse(readFileSync(pkgFile, "utf8")) as { version?: string };
122
- if (pkg.version) return String(pkg.version);
123
- } catch {
124
- // ignore
128
+ const candidates = [
129
+ workspaceRoot,
130
+ process.cwd(),
131
+ resolve(__dirname, "..", "..", ".."),
132
+ resolve(__dirname, "..", "..", "..", ".."),
133
+ ].filter((dir, index, list) => dir && list.indexOf(dir) === index);
134
+ for (const candidate of candidates) {
135
+ const pkgFile = resolve(candidate, "package.json");
136
+ if (existsSync(pkgFile)) {
137
+ try {
138
+ const pkg = JSON.parse(readFileSync(pkgFile, "utf8")) as { version?: string; name?: string };
139
+ if (pkg.version && (pkg.name === "@silicaclaw/cli" || existsSync(resolve(candidate, "apps", "local-console")))) {
140
+ return String(pkg.version);
141
+ }
142
+ } catch {
143
+ // ignore
144
+ }
145
+ }
146
+ const versionFile = resolve(candidate, "VERSION");
147
+ if (existsSync(versionFile)) {
148
+ const raw = readFileSync(versionFile, "utf8").trim();
149
+ if (raw) return raw;
125
150
  }
126
- }
127
- const versionFile = resolve(workspaceRoot, "VERSION");
128
- if (existsSync(versionFile)) {
129
- const raw = readFileSync(versionFile, "utf8").trim();
130
- if (raw) return raw;
131
151
  }
132
152
  return "unknown";
133
153
  }
134
154
 
155
+ function normalizeVersionText(value: unknown): string {
156
+ const text = String(value || "").trim();
157
+ return text.startsWith("v") ? text.slice(1) : text;
158
+ }
159
+
160
+ function tokenizeVersion(value: unknown): Array<number | string> {
161
+ return normalizeVersionText(value)
162
+ .split(/[^0-9A-Za-z]+/)
163
+ .map((token) => token.trim())
164
+ .filter(Boolean)
165
+ .map((token) => (/^\d+$/.test(token) ? Number(token) : token.toLowerCase()));
166
+ }
167
+
168
+ function compareVersionTokens(left: unknown, right: unknown): number {
169
+ const leftTokens = tokenizeVersion(left);
170
+ const rightTokens = tokenizeVersion(right);
171
+ const maxLength = Math.max(leftTokens.length, rightTokens.length);
172
+ for (let index = 0; index < maxLength; index += 1) {
173
+ const leftToken = leftTokens[index];
174
+ const rightToken = rightTokens[index];
175
+ if (leftToken === undefined && rightToken === undefined) return 0;
176
+ if (leftToken === undefined) return -1;
177
+ if (rightToken === undefined) return 1;
178
+ if (typeof leftToken === "number" && typeof rightToken === "number") {
179
+ if (leftToken !== rightToken) return leftToken > rightToken ? 1 : -1;
180
+ continue;
181
+ }
182
+ const leftText = String(leftToken);
183
+ const rightText = String(rightToken);
184
+ if (leftText !== rightText) return leftText.localeCompare(rightText);
185
+ }
186
+ return 0;
187
+ }
188
+
135
189
  function resolveWorkspaceRoot(cwd = process.cwd()): string {
136
190
  if (existsSync(resolve(cwd, "apps", "local-console", "package.json"))) {
137
191
  return cwd;
@@ -143,7 +197,36 @@ function resolveWorkspaceRoot(cwd = process.cwd()): string {
143
197
  return cwd;
144
198
  }
145
199
 
200
+ function resolveProjectRoot(appRoot: string, cwd = process.cwd()): string {
201
+ const envAppRoot = String(process.env.SILICACLAW_APP_DIR || "").trim();
202
+ if (
203
+ envAppRoot &&
204
+ existsSync(resolve(envAppRoot, "apps", "local-console", "package.json")) &&
205
+ existsSync(resolve(envAppRoot, "package.json"))
206
+ ) {
207
+ return resolve(envAppRoot);
208
+ }
209
+ const envRoot = String(process.env.SILICACLAW_WORKSPACE_DIR || "").trim();
210
+ if (envRoot) {
211
+ return resolve(envRoot);
212
+ }
213
+ if (
214
+ existsSync(resolve(appRoot, "apps", "local-console", "package.json")) &&
215
+ existsSync(resolve(appRoot, "package.json"))
216
+ ) {
217
+ return appRoot;
218
+ }
219
+ if (!existsSync(resolve(cwd, "apps", "local-console", "package.json"))) {
220
+ return resolve(cwd);
221
+ }
222
+ return appRoot;
223
+ }
224
+
146
225
  function resolveStorageRoot(workspaceRoot: string, cwd = process.cwd()): string {
226
+ const home = process.env.HOME || homedir();
227
+ if (home) {
228
+ return resolve(home, ".silicaclaw", "local-console");
229
+ }
147
230
  const appRoot = resolve(workspaceRoot, "apps", "local-console");
148
231
  if (existsSync(resolve(appRoot, "package.json"))) {
149
232
  return appRoot;
@@ -151,6 +234,13 @@ function resolveStorageRoot(workspaceRoot: string, cwd = process.cwd()): string
151
234
  return cwd;
152
235
  }
153
236
 
237
+ function defaultOpenClawSourceDir(rootDir: string): string {
238
+ if (existsSync(resolve(rootDir, "openclaw.mjs")) || existsSync(resolve(rootDir, "package.json"))) {
239
+ return rootDir;
240
+ }
241
+ return resolve(rootDir, "..", "openclaw");
242
+ }
243
+
154
244
  function resolveExecutableInPath(binName: string): string | null {
155
245
  const pathValue = String(process.env.PATH || "").trim();
156
246
  if (!pathValue) return null;
@@ -213,6 +303,50 @@ function summarizeSkillReadme(filePath: string) {
213
303
  }
214
304
  }
215
305
 
306
+ function readDialogueCheatsheetPreview(filePath: string, limit = 6) {
307
+ if (!filePath || !existsSync(filePath)) return [];
308
+ try {
309
+ return readFileSync(filePath, "utf8")
310
+ .split(/\r?\n/)
311
+ .map((line) => line.trim())
312
+ .filter((line) => line.startsWith("- "))
313
+ .map((line) => line.slice(2).trim())
314
+ .filter(Boolean)
315
+ .slice(0, limit);
316
+ } catch {
317
+ return [];
318
+ }
319
+ }
320
+
321
+ function readDialogueCheatsheetSections(filePath: string, maxSections = 3, maxItemsPerSection = 5) {
322
+ if (!filePath || !existsSync(filePath)) return [];
323
+ try {
324
+ const lines = readFileSync(filePath, "utf8").split(/\r?\n/);
325
+ const sections: Array<{ title: string; items: string[] }> = [];
326
+ let current: { title: string; items: string[] } | null = null;
327
+ for (const rawLine of lines) {
328
+ const line = rawLine.trim();
329
+ if (line.startsWith("## ")) {
330
+ if (current && current.items.length) sections.push(current);
331
+ current = { title: line.slice(3).trim(), items: [] };
332
+ continue;
333
+ }
334
+ if (line.startsWith("- ")) {
335
+ if (!current) {
336
+ current = { title: "Examples", items: [] };
337
+ }
338
+ if (current.items.length < maxItemsPerSection) {
339
+ current.items.push(line.slice(2).trim());
340
+ }
341
+ }
342
+ }
343
+ if (current && current.items.length) sections.push(current);
344
+ return sections.slice(0, maxSections);
345
+ } catch {
346
+ return [];
347
+ }
348
+ }
349
+
216
350
  function detectOpenClawInstallation(workspaceRoot: string) {
217
351
  const workspaceDir = resolve(workspaceRoot, ".openclaw");
218
352
  const homeDir = resolve(process.env.HOME || "", ".openclaw");
@@ -265,7 +399,7 @@ function detectOpenClawInstallation(workspaceRoot: string) {
265
399
 
266
400
  function readOpenClawConfiguredGateway(workspaceRoot: string) {
267
401
  const configuredSourceDir = String(process.env.OPENCLAW_SOURCE_DIR || "").trim();
268
- const defaultSourceDir = resolve(workspaceRoot, "..", "openclaw");
402
+ const defaultSourceDir = defaultOpenClawSourceDir(workspaceRoot);
269
403
  const sourceDir = configuredSourceDir || defaultSourceDir;
270
404
  const homeDir = resolve(process.env.HOME || "", ".openclaw");
271
405
  const explicitConfigPath = String(process.env.OPENCLAW_CONFIG_PATH || "").trim();
@@ -453,7 +587,7 @@ function detectOwnerDeliveryStatus(params: {
453
587
  const ownerAccount = String(process.env.OPENCLAW_OWNER_ACCOUNT || "").trim();
454
588
  const explicitOpenClawBin = String(process.env.OPENCLAW_BIN || "").trim();
455
589
  const configuredSourceDir = String(process.env.OPENCLAW_SOURCE_DIR || "").trim();
456
- const defaultSourceDir = resolve(params.workspaceRoot, "..", "openclaw");
590
+ const defaultSourceDir = defaultOpenClawSourceDir(params.workspaceRoot);
457
591
  const openclawSourceDir = configuredSourceDir || defaultSourceDir;
458
592
  const openclawSourceEntry = existingPathOrNull(resolve(openclawSourceDir, "openclaw.mjs"));
459
593
  const openclawCommandResolvable = Boolean(explicitOpenClawBin || resolveExecutableInPath("openclaw") || openclawSourceEntry);
@@ -512,10 +646,18 @@ function hasMeaningfulJson(filePath: string): boolean {
512
646
  }
513
647
  }
514
648
 
515
- function migrateLegacyDataIfNeeded(workspaceRoot: string, storageRoot: string): void {
516
- const legacyDataDir = resolve(workspaceRoot, "data");
649
+ function migrateLegacyDataIfNeeded(appRoot: string, projectRoot: string, storageRoot: string): void {
650
+ const homeDir = process.env.HOME || homedir();
651
+ const legacyNpxAppRoots = collectLegacyNpxAppRoots(homeDir);
517
652
  const targetDataDir = resolve(storageRoot, "data");
518
- if (legacyDataDir === targetDataDir) return;
653
+ const legacyDataDirs = [
654
+ resolve(appRoot, "data"),
655
+ resolve(appRoot, "apps", "local-console", "data"),
656
+ resolve(projectRoot, "data"),
657
+ resolve(projectRoot, "apps", "local-console", "data"),
658
+ resolve(process.cwd(), "data"),
659
+ ...legacyNpxAppRoots.map((root) => resolve(root, "apps", "local-console", "data")),
660
+ ].filter((dir, index, list) => list.indexOf(dir) === index && dir !== targetDataDir);
519
661
  const files = [
520
662
  "identity.json",
521
663
  "profile.json",
@@ -525,16 +667,65 @@ function migrateLegacyDataIfNeeded(workspaceRoot: string, storageRoot: string):
525
667
  "social-message-observations.json",
526
668
  ];
527
669
  for (const file of files) {
528
- const src = resolve(legacyDataDir, file);
529
670
  const dst = resolve(targetDataDir, file);
530
- if (!existsSync(src)) continue;
531
671
  if (hasMeaningfulJson(dst)) continue;
532
- if (!hasMeaningfulJson(src)) continue;
533
- mkdirSync(targetDataDir, { recursive: true });
534
- copyFileSync(src, dst);
672
+ for (const legacyDataDir of legacyDataDirs) {
673
+ const src = resolve(legacyDataDir, file);
674
+ if (!existsSync(src)) continue;
675
+ if (!hasMeaningfulJson(src)) continue;
676
+ mkdirSync(targetDataDir, { recursive: true });
677
+ copyFileSync(src, dst);
678
+ break;
679
+ }
680
+ }
681
+
682
+ const targetDotDir = resolve(storageRoot, ".silicaclaw");
683
+ const legacyDotDirs = [
684
+ resolve(appRoot, ".silicaclaw"),
685
+ resolve(appRoot, "apps", "local-console", ".silicaclaw"),
686
+ resolve(projectRoot, ".silicaclaw"),
687
+ resolve(projectRoot, "apps", "local-console", ".silicaclaw"),
688
+ resolve(process.cwd(), ".silicaclaw"),
689
+ ...legacyNpxAppRoots.map((root) => resolve(root, "apps", "local-console", ".silicaclaw")),
690
+ ].filter((dir, index, list) => list.indexOf(dir) === index && dir !== targetDotDir);
691
+ const dotFiles = ["social.runtime.json", "social.message-governance.json"];
692
+ for (const file of dotFiles) {
693
+ const dst = resolve(targetDotDir, file);
694
+ if (hasMeaningfulJson(dst)) continue;
695
+ for (const legacyDotDir of legacyDotDirs) {
696
+ const src = resolve(legacyDotDir, file);
697
+ if (!existsSync(src)) continue;
698
+ if (!hasMeaningfulJson(src)) continue;
699
+ mkdirSync(targetDotDir, { recursive: true });
700
+ copyFileSync(src, dst);
701
+ break;
702
+ }
535
703
  }
536
704
  }
537
705
 
706
+ function collectLegacyNpxAppRoots(homeDir: string): string[] {
707
+ const cacheRoots = [
708
+ resolve(homeDir, ".silicaclaw", "npm-cache", "_npx"),
709
+ resolve(homeDir, ".npm", "_npx"),
710
+ ];
711
+ const roots: string[] = [];
712
+ for (const cacheRoot of cacheRoots) {
713
+ if (!existsSync(cacheRoot)) continue;
714
+ let entries: string[] = [];
715
+ try {
716
+ entries = readdirSync(cacheRoot);
717
+ } catch {
718
+ continue;
719
+ }
720
+ for (const entry of entries) {
721
+ const candidate = resolve(cacheRoot, entry, "node_modules", "@silicaclaw", "cli");
722
+ if (!existsSync(resolve(candidate, "apps", "local-console"))) continue;
723
+ roots.push(candidate);
724
+ }
725
+ }
726
+ return Array.from(new Set(roots));
727
+ }
728
+
538
729
  function parseListEnv(raw: string): string[] {
539
730
  return raw
540
731
  .split(/[,\n]/g)
@@ -699,6 +890,7 @@ type OpenClawBridgeConfigView = {
699
890
 
700
891
  export class LocalNodeService {
701
892
  private workspaceRoot: string;
893
+ private projectRoot: string;
702
894
  private storageRoot: string;
703
895
  private identityRepo: IdentityRepo;
704
896
  private profileRepo: ProfileRepo;
@@ -741,7 +933,7 @@ export class LocalNodeService {
741
933
 
742
934
  private network: NetworkAdapter;
743
935
  private adapterMode: "mock" | "local-event-bus" | "real-preview" | "webrtc-preview" | "relay-preview";
744
- private networkMode: "local" | "lan" | "global-preview" = "global-preview";
936
+ private networkMode: "local" | "lan" | "global-preview" = DEFAULT_NETWORK_MODE;
745
937
  private networkNamespace: string;
746
938
  private networkPort: number | null;
747
939
  private socialConfig: SocialConfig;
@@ -755,17 +947,22 @@ export class LocalNodeService {
755
947
  "silicaclaw-existing";
756
948
  private resolvedOpenClawIdentityPath: string | null = null;
757
949
  private webrtcSignalingUrls: string[] = [];
758
- private webrtcRoom = "silicaclaw-global-preview";
950
+ private webrtcRoom = DEFAULT_GLOBAL_ROOM;
759
951
  private webrtcSeedPeers: string[] = [];
760
952
  private webrtcBootstrapHints: string[] = [];
761
953
  private webrtcBootstrapSources: string[] = [];
954
+ private networkStarted = false;
955
+ private networkStartupError: string | null = null;
956
+ private networkReconnectTimer: NodeJS.Timeout | null = null;
957
+ private networkReconnectDelayMs = 5_000;
762
958
  private appVersion = "unknown";
763
959
 
764
- constructor(options?: { workspaceRoot?: string; storageRoot?: string }) {
960
+ constructor(options?: { workspaceRoot?: string; projectRoot?: string; storageRoot?: string }) {
765
961
  this.workspaceRoot = options?.workspaceRoot || resolveWorkspaceRoot();
962
+ this.projectRoot = options?.projectRoot || resolveProjectRoot(this.workspaceRoot);
766
963
  this.storageRoot = options?.storageRoot || resolveStorageRoot(this.workspaceRoot);
767
964
  this.appVersion = readWorkspaceVersion(this.workspaceRoot);
768
- migrateLegacyDataIfNeeded(this.workspaceRoot, this.storageRoot);
965
+ migrateLegacyDataIfNeeded(this.workspaceRoot, this.projectRoot, this.storageRoot);
769
966
 
770
967
  this.identityRepo = new IdentityRepo(this.storageRoot);
771
968
  this.profileRepo = new ProfileRepo(this.storageRoot);
@@ -777,16 +974,16 @@ export class LocalNodeService {
777
974
  this.socialRuntimeRepo = new SocialRuntimeRepo(this.storageRoot);
778
975
  this.messageGovernance = this.defaultMessageGovernance();
779
976
 
780
- let loadedSocial = loadSocialConfig(this.workspaceRoot);
977
+ let loadedSocial = loadSocialConfig(this.projectRoot);
781
978
  if (!loadedSocial.meta.found) {
782
- ensureDefaultSocialMd(this.workspaceRoot, {
979
+ ensureDefaultSocialMd(this.projectRoot, {
783
980
  display_name: this.getDefaultDisplayName(),
784
981
  bio: "Local AI agent connected to SilicaClaw",
785
982
  tags: ["openclaw", "local-first"],
786
- mode: "global-preview",
983
+ mode: DEFAULT_NETWORK_MODE,
787
984
  public_enabled: false,
788
985
  });
789
- loadedSocial = loadSocialConfig(this.workspaceRoot);
986
+ loadedSocial = loadSocialConfig(this.projectRoot);
790
987
  this.initState.social_auto_created = true;
791
988
  }
792
989
  this.socialConfig = loadedSocial.config;
@@ -795,8 +992,8 @@ export class LocalNodeService {
795
992
  this.socialParseError = loadedSocial.meta.parse_error;
796
993
  this.socialRawFrontmatter = loadedSocial.raw_frontmatter;
797
994
 
798
- this.networkNamespace = this.socialConfig.network.namespace || process.env.NETWORK_NAMESPACE || "silicaclaw.preview";
799
- this.networkPort = Number(this.socialConfig.network.port || process.env.NETWORK_PORT || 44123);
995
+ this.networkNamespace = this.socialConfig.network.namespace || process.env.NETWORK_NAMESPACE || DEFAULT_NETWORK_NAMESPACE;
996
+ this.networkPort = Number(this.socialConfig.network.port || process.env.NETWORK_PORT || DEFAULT_NETWORK_PORT);
800
997
  this.applyResolvedNetworkConfig();
801
998
  const resolved = this.buildNetworkAdapter();
802
999
  this.network = resolved.adapter;
@@ -808,32 +1005,19 @@ export class LocalNodeService {
808
1005
  await this.hydrateFromDisk();
809
1006
 
810
1007
  this.bindNetworkSubscriptions();
811
- await this.network.start();
812
- await this.log(
813
- "info",
814
- `Local node started (${this.adapterMode}, mode=${this.networkMode}, signaling=${this.webrtcSignalingUrls[0] || "-"}, room=${this.webrtcRoom})`
815
- );
816
-
817
- if (this.profile?.public_enabled && this.broadcastEnabled) {
818
- try {
819
- await this.broadcastNow("adapter_start");
820
- } catch (error) {
821
- await this.log(
822
- "warn",
823
- `Initial broadcast failed: ${error instanceof Error ? error.message : String(error)}`
824
- );
825
- }
826
- }
827
-
828
- this.startBroadcastLoop();
1008
+ await this.startNetworkAdapterWithRetry("adapter_start");
829
1009
  }
830
1010
 
831
1011
  async stop(): Promise<void> {
1012
+ this.clearNetworkReconnectTimer();
832
1013
  if (this.broadcaster) {
833
1014
  clearInterval(this.broadcaster);
834
1015
  this.broadcaster = null;
835
1016
  }
836
- await this.network.stop();
1017
+ if (this.networkStarted) {
1018
+ await this.network.stop();
1019
+ }
1020
+ this.networkStarted = false;
837
1021
  }
838
1022
 
839
1023
  private ensureLocalDirectoryBaseline(): void {
@@ -848,12 +1032,8 @@ export class LocalNodeService {
848
1032
  }
849
1033
 
850
1034
  getOverview() {
851
- this.ensureLocalDirectoryBaseline();
852
- this.compactCacheInMemory();
853
- const profiles = Object.values(this.directory.profiles);
854
- const onlineCount = profiles.filter((profile) =>
855
- isAgentOnline(this.directory.presence[profile.agent_id], Date.now(), PRESENCE_TTL_MS)
856
- ).length;
1035
+ const discovered = this.search("");
1036
+ const onlineCount = discovered.filter((profile) => profile.online).length;
857
1037
 
858
1038
  return {
859
1039
  app_version: this.appVersion,
@@ -864,9 +1044,9 @@ export class LocalNodeService {
864
1044
  last_broadcast_error_at: this.lastBroadcastErrorAt,
865
1045
  last_broadcast_error: this.lastBroadcastError,
866
1046
  broadcast_failure_count: this.broadcastFailureCount,
867
- discovered_count: profiles.length,
1047
+ discovered_count: discovered.length,
868
1048
  online_count: onlineCount,
869
- offline_count: Math.max(0, profiles.length - onlineCount),
1049
+ offline_count: Math.max(0, discovered.length - onlineCount),
870
1050
  init_state: this.initState,
871
1051
  presence_ttl_ms: PRESENCE_TTL_MS,
872
1052
  onboarding: this.getOnboardingSummary(),
@@ -888,7 +1068,9 @@ export class LocalNodeService {
888
1068
  }
889
1069
 
890
1070
  getNetworkSummary() {
891
- const diagnostics = this.getAdapterDiagnostics();
1071
+ const network = this.getResolvedRealtimeNetworkSummary();
1072
+ const diagnostics = network.diagnostics;
1073
+ const relayCapable = this.adapterMode === "webrtc-preview" || this.adapterMode === "relay-preview";
892
1074
  const peerCount = diagnostics?.peers.total ?? 0;
893
1075
 
894
1076
  return {
@@ -916,30 +1098,34 @@ export class LocalNodeService {
916
1098
  real_preview_stats: diagnostics?.stats ?? null,
917
1099
  real_preview_transport_stats: diagnostics?.transport_stats ?? null,
918
1100
  real_preview_discovery_stats: diagnostics?.discovery_stats ?? null,
919
- webrtc_preview: diagnostics && (diagnostics.adapter === "webrtc-preview" || diagnostics.adapter === "relay-preview")
1101
+ webrtc_preview: relayCapable
920
1102
  ? {
921
- signaling_url: diagnostics.signaling_url ?? null,
922
- signaling_endpoints: diagnostics.signaling_endpoints ?? [],
923
- room: diagnostics.room ?? null,
924
- bootstrap_sources: diagnostics.bootstrap_sources ?? [],
925
- seed_peers_count: diagnostics.seed_peers_count ?? 0,
926
- discovery_events_total: diagnostics.discovery_events_total ?? 0,
927
- last_discovery_event_at: diagnostics.last_discovery_event_at ?? 0,
928
- active_webrtc_peers: diagnostics.active_webrtc_peers ?? 0,
929
- reconnect_attempts_total: diagnostics.reconnect_attempts_total ?? 0,
930
- last_join_at: diagnostics.last_join_at ?? 0,
931
- last_poll_at: diagnostics.last_poll_at ?? 0,
932
- last_publish_at: diagnostics.last_publish_at ?? 0,
933
- last_peer_refresh_at: diagnostics.last_peer_refresh_at ?? 0,
934
- last_error_at: diagnostics.last_error_at ?? 0,
935
- last_error: diagnostics.last_error ?? null,
1103
+ started: this.networkStarted,
1104
+ startup_error: this.networkStartupError,
1105
+ signaling_url: network.signaling_url,
1106
+ signaling_endpoints: network.signaling_endpoints,
1107
+ room: network.room,
1108
+ bootstrap_sources: network.bootstrap_sources,
1109
+ seed_peers_count: network.seed_peers_count,
1110
+ discovery_events_total: diagnostics?.discovery_events_total ?? 0,
1111
+ last_discovery_event_at: diagnostics?.last_discovery_event_at ?? 0,
1112
+ active_webrtc_peers: diagnostics?.active_webrtc_peers ?? 0,
1113
+ reconnect_attempts_total: diagnostics?.reconnect_attempts_total ?? 0,
1114
+ last_join_at: diagnostics?.last_join_at ?? 0,
1115
+ last_poll_at: diagnostics?.last_poll_at ?? 0,
1116
+ last_publish_at: diagnostics?.last_publish_at ?? 0,
1117
+ last_peer_refresh_at: diagnostics?.last_peer_refresh_at ?? 0,
1118
+ last_error_at: diagnostics?.last_error_at ?? 0,
1119
+ last_error: diagnostics?.last_error ?? null,
936
1120
  }
937
1121
  : null,
938
1122
  };
939
1123
  }
940
1124
 
941
1125
  getNetworkConfig() {
942
- const diagnostics = this.getAdapterDiagnostics();
1126
+ const network = this.getResolvedRealtimeNetworkSummary();
1127
+ const diagnostics = network.diagnostics;
1128
+ const relayCapable = this.adapterMode === "webrtc-preview" || this.adapterMode === "relay-preview";
943
1129
  return {
944
1130
  adapter: this.adapterMode,
945
1131
  mode: this.networkMode,
@@ -953,23 +1139,25 @@ export class LocalNodeService {
953
1139
  },
954
1140
  limits: diagnostics?.limits ?? null,
955
1141
  adapter_config: diagnostics?.config ?? null,
956
- adapter_extra: diagnostics && (diagnostics.adapter === "webrtc-preview" || diagnostics.adapter === "relay-preview")
1142
+ adapter_extra: relayCapable
957
1143
  ? {
958
- signaling_url: diagnostics.signaling_url ?? null,
959
- signaling_endpoints: diagnostics.signaling_endpoints ?? [],
960
- room: diagnostics.room ?? null,
961
- bootstrap_sources: diagnostics.bootstrap_sources ?? [],
962
- seed_peers_count: diagnostics.seed_peers_count ?? 0,
963
- discovery_events_total: diagnostics.discovery_events_total ?? 0,
964
- last_discovery_event_at: diagnostics.last_discovery_event_at ?? 0,
965
- connection_states_summary: diagnostics.connection_states_summary ?? null,
966
- datachannel_states_summary: diagnostics.datachannel_states_summary ?? null,
967
- last_join_at: diagnostics.last_join_at ?? 0,
968
- last_poll_at: diagnostics.last_poll_at ?? 0,
969
- last_publish_at: diagnostics.last_publish_at ?? 0,
970
- last_peer_refresh_at: diagnostics.last_peer_refresh_at ?? 0,
971
- last_error_at: diagnostics.last_error_at ?? 0,
972
- last_error: diagnostics.last_error ?? null,
1144
+ started: this.networkStarted,
1145
+ startup_error: this.networkStartupError,
1146
+ signaling_url: network.signaling_url,
1147
+ signaling_endpoints: network.signaling_endpoints,
1148
+ room: network.room,
1149
+ bootstrap_sources: network.bootstrap_sources,
1150
+ seed_peers_count: network.seed_peers_count,
1151
+ discovery_events_total: diagnostics?.discovery_events_total ?? 0,
1152
+ last_discovery_event_at: diagnostics?.last_discovery_event_at ?? 0,
1153
+ connection_states_summary: diagnostics?.connection_states_summary ?? null,
1154
+ datachannel_states_summary: diagnostics?.datachannel_states_summary ?? null,
1155
+ last_join_at: diagnostics?.last_join_at ?? 0,
1156
+ last_poll_at: diagnostics?.last_poll_at ?? 0,
1157
+ last_publish_at: diagnostics?.last_publish_at ?? 0,
1158
+ last_peer_refresh_at: diagnostics?.last_peer_refresh_at ?? 0,
1159
+ last_error_at: diagnostics?.last_error_at ?? 0,
1160
+ last_error: diagnostics?.last_error ?? null,
973
1161
  }
974
1162
  : null,
975
1163
  env: {
@@ -997,14 +1185,16 @@ export class LocalNodeService {
997
1185
  this.adapterMode === "real-preview"
998
1186
  ? "lan-preview"
999
1187
  : this.adapterMode === "webrtc-preview" || this.adapterMode === "relay-preview"
1000
- ? "internet-preview"
1188
+ ? "global-preview"
1001
1189
  : "local-process",
1002
1190
  mode_explainer: this.getModeExplainer(),
1003
1191
  };
1004
1192
  }
1005
1193
 
1006
1194
  getNetworkStats() {
1007
- const diagnostics = this.getAdapterDiagnostics();
1195
+ const network = this.getResolvedRealtimeNetworkSummary();
1196
+ const diagnostics = network.diagnostics;
1197
+ const relayCapable = this.adapterMode === "webrtc-preview" || this.adapterMode === "relay-preview";
1008
1198
  const peers: Array<{ status?: string }> = diagnostics?.peers?.items ?? [];
1009
1199
  const online = peers.filter((peer: { status?: string }) => peer.status === "online").length;
1010
1200
 
@@ -1031,34 +1221,37 @@ export class LocalNodeService {
1031
1221
  adapter_stats: diagnostics?.stats ?? null,
1032
1222
  adapter_transport_stats: diagnostics?.transport_stats ?? null,
1033
1223
  adapter_discovery_stats: diagnostics?.discovery_stats ?? null,
1034
- adapter_diagnostics_summary: diagnostics
1224
+ adapter_diagnostics_summary: relayCapable || diagnostics
1035
1225
  ? {
1036
- signaling_url: diagnostics.signaling_url ?? null,
1037
- signaling_endpoints: diagnostics.signaling_endpoints ?? [],
1038
- room: diagnostics.room ?? null,
1039
- bootstrap_sources: diagnostics.bootstrap_sources ?? [],
1040
- seed_peers_count: diagnostics.seed_peers_count ?? 0,
1041
- discovery_events_total: diagnostics.discovery_events_total ?? 0,
1042
- last_discovery_event_at: diagnostics.last_discovery_event_at ?? 0,
1043
- connection_states_summary: diagnostics.connection_states_summary ?? null,
1044
- datachannel_states_summary: diagnostics.datachannel_states_summary ?? null,
1045
- signaling_messages_sent_total: diagnostics.signaling_messages_sent_total ?? null,
1046
- signaling_messages_received_total: diagnostics.signaling_messages_received_total ?? null,
1047
- reconnect_attempts_total: diagnostics.reconnect_attempts_total ?? null,
1048
- active_webrtc_peers: diagnostics.active_webrtc_peers ?? null,
1049
- last_join_at: diagnostics.last_join_at ?? 0,
1050
- last_poll_at: diagnostics.last_poll_at ?? 0,
1051
- last_publish_at: diagnostics.last_publish_at ?? 0,
1052
- last_peer_refresh_at: diagnostics.last_peer_refresh_at ?? 0,
1053
- last_error_at: diagnostics.last_error_at ?? 0,
1054
- last_error: diagnostics.last_error ?? null,
1226
+ started: this.networkStarted,
1227
+ startup_error: this.networkStartupError,
1228
+ signaling_url: network.signaling_url,
1229
+ signaling_endpoints: network.signaling_endpoints,
1230
+ room: network.room,
1231
+ bootstrap_sources: network.bootstrap_sources,
1232
+ seed_peers_count: network.seed_peers_count,
1233
+ discovery_events_total: diagnostics?.discovery_events_total ?? 0,
1234
+ last_discovery_event_at: diagnostics?.last_discovery_event_at ?? 0,
1235
+ connection_states_summary: diagnostics?.connection_states_summary ?? null,
1236
+ datachannel_states_summary: diagnostics?.datachannel_states_summary ?? null,
1237
+ signaling_messages_sent_total: diagnostics?.signaling_messages_sent_total ?? null,
1238
+ signaling_messages_received_total: diagnostics?.signaling_messages_received_total ?? null,
1239
+ reconnect_attempts_total: diagnostics?.reconnect_attempts_total ?? null,
1240
+ active_webrtc_peers: diagnostics?.active_webrtc_peers ?? null,
1241
+ last_join_at: diagnostics?.last_join_at ?? 0,
1242
+ last_poll_at: diagnostics?.last_poll_at ?? 0,
1243
+ last_publish_at: diagnostics?.last_publish_at ?? 0,
1244
+ last_peer_refresh_at: diagnostics?.last_peer_refresh_at ?? 0,
1245
+ last_error_at: diagnostics?.last_error_at ?? 0,
1246
+ last_error: diagnostics?.last_error ?? null,
1055
1247
  }
1056
1248
  : null,
1057
1249
  };
1058
1250
  }
1059
1251
 
1060
1252
  getPeersSummary() {
1061
- const diagnostics = this.getAdapterDiagnostics();
1253
+ const network = this.getResolvedRealtimeNetworkSummary();
1254
+ const diagnostics = network.diagnostics;
1062
1255
  if (!diagnostics) {
1063
1256
  return {
1064
1257
  adapter: this.adapterMode,
@@ -1081,11 +1274,13 @@ export class LocalNodeService {
1081
1274
  components: diagnostics.components,
1082
1275
  limits: diagnostics.limits,
1083
1276
  diagnostics_summary: {
1084
- signaling_url: diagnostics.signaling_url ?? null,
1085
- signaling_endpoints: diagnostics.signaling_endpoints ?? [],
1086
- room: diagnostics.room ?? null,
1087
- bootstrap_sources: diagnostics.bootstrap_sources ?? [],
1088
- seed_peers_count: diagnostics.seed_peers_count ?? 0,
1277
+ started: this.networkStarted,
1278
+ startup_error: this.networkStartupError,
1279
+ signaling_url: network.signaling_url,
1280
+ signaling_endpoints: network.signaling_endpoints,
1281
+ room: network.room,
1282
+ bootstrap_sources: network.bootstrap_sources,
1283
+ seed_peers_count: network.seed_peers_count,
1089
1284
  discovery_events_total: diagnostics.discovery_events_total ?? 0,
1090
1285
  last_discovery_event_at: diagnostics.last_discovery_event_at ?? 0,
1091
1286
  connection_states_summary: diagnostics.connection_states_summary ?? null,
@@ -1132,11 +1327,12 @@ export class LocalNodeService {
1132
1327
  getRuntimePaths() {
1133
1328
  return {
1134
1329
  workspace_root: this.workspaceRoot,
1330
+ project_root: this.projectRoot,
1135
1331
  storage_root: this.storageRoot,
1136
1332
  data_dir: resolve(this.storageRoot, "data"),
1137
1333
  social_runtime_path: resolve(this.storageRoot, ".silicaclaw", "social.runtime.json"),
1138
1334
  local_console_public_dir: resolve(this.workspaceRoot, "apps", "local-console", "public"),
1139
- social_lookup_paths: getSocialConfigSearchPaths(this.workspaceRoot),
1335
+ social_lookup_paths: getSocialConfigSearchPaths(this.projectRoot),
1140
1336
  social_source_path: this.socialSourcePath,
1141
1337
  };
1142
1338
  }
@@ -1175,9 +1371,10 @@ export class LocalNodeService {
1175
1371
 
1176
1372
  getIntegrationStatus(): IntegrationStatusSummary {
1177
1373
  const runtimeGenerated = Boolean(this.socialRuntime && this.socialRuntime.last_loaded_at > 0);
1178
- const connected = this.socialFound && runtimeGenerated && !this.socialParseError;
1179
- const configured = connected && this.socialConfig.enabled;
1180
- const running = configured && this.broadcastEnabled;
1374
+ const runtimeReady = this.socialFound && runtimeGenerated && !this.socialParseError;
1375
+ const connected = runtimeReady && this.networkStarted;
1376
+ const configured = runtimeReady && this.socialConfig.enabled;
1377
+ const running = configured && this.broadcastEnabled && this.networkStarted;
1181
1378
  const publicEnabled = Boolean(this.profile?.public_enabled);
1182
1379
  const discoveryEnabled =
1183
1380
  this.socialConfig.discovery.discoverable &&
@@ -1199,6 +1396,10 @@ export class LocalNodeService {
1199
1396
  ? "running"
1200
1397
  : !configured
1201
1398
  ? "not configured"
1399
+ : this.networkReconnectTimer
1400
+ ? "reconnecting to relay"
1401
+ : this.networkStartupError
1402
+ ? this.networkStartupError
1202
1403
  : !this.broadcastEnabled
1203
1404
  ? "broadcast paused"
1204
1405
  : "not running";
@@ -1244,20 +1445,36 @@ export class LocalNodeService {
1244
1445
  }
1245
1446
 
1246
1447
  async setNetworkModeRuntime(mode: "local" | "lan" | "global-preview") {
1247
- const currentMode = this.networkMode;
1448
+ const before = {
1449
+ mode: this.networkMode,
1450
+ adapter: this.adapterMode,
1451
+ namespace: this.networkNamespace,
1452
+ port: this.networkPort,
1453
+ };
1248
1454
  if (mode !== "local" && mode !== "lan" && mode !== "global-preview") {
1249
1455
  throw new Error("invalid_network_mode");
1250
1456
  }
1251
1457
  this.socialConfig.network.mode = mode;
1252
1458
  this.socialConfig.network.adapter = this.adapterForMode(mode);
1253
1459
  this.applyResolvedNetworkConfig();
1254
- this.socialNetworkRequiresRestart = currentMode !== mode || this.adapterMode !== this.socialConfig.network.adapter;
1460
+
1461
+ const needsRestart =
1462
+ before.mode !== this.networkMode ||
1463
+ before.adapter !== this.socialConfig.network.adapter ||
1464
+ before.namespace !== this.networkNamespace ||
1465
+ (before.port ?? null) !== (this.networkPort ?? null);
1466
+
1467
+ if (needsRestart) {
1468
+ await this.restartNetworkAdapter("set_network_mode_runtime");
1469
+ }
1470
+
1471
+ this.socialNetworkRequiresRestart = false;
1255
1472
  await this.writeSocialRuntime();
1256
1473
  return {
1257
1474
  mode: this.networkMode,
1258
- adapter: this.socialConfig.network.adapter,
1259
- network_requires_restart: this.socialNetworkRequiresRestart,
1260
- note: "Runtime mode updated. Existing social.md is unchanged.",
1475
+ adapter: this.adapterMode,
1476
+ network_requires_restart: false,
1477
+ note: "Runtime mode updated and adapter restarted. Existing social.md is unchanged.",
1261
1478
  };
1262
1479
  }
1263
1480
 
@@ -1272,7 +1489,7 @@ export class LocalNodeService {
1272
1489
  this.socialConfig.network.adapter = "relay-preview";
1273
1490
  this.socialConfig.network.signaling_url = signalingUrl;
1274
1491
  this.socialConfig.network.signaling_urls = [signalingUrl];
1275
- this.socialConfig.network.room = room || "silicaclaw-global-preview";
1492
+ this.socialConfig.network.room = room || DEFAULT_GLOBAL_ROOM;
1276
1493
  this.applyResolvedNetworkConfig();
1277
1494
  await this.restartNetworkAdapter("quick_connect_global_preview");
1278
1495
  this.socialNetworkRequiresRestart = false;
@@ -1297,7 +1514,7 @@ export class LocalNodeService {
1297
1514
  port: this.networkPort,
1298
1515
  };
1299
1516
 
1300
- const loaded = loadSocialConfig(this.workspaceRoot);
1517
+ const loaded = loadSocialConfig(this.projectRoot);
1301
1518
  this.socialConfig = loaded.config;
1302
1519
  this.socialSourcePath = loaded.meta.source_path;
1303
1520
  this.socialFound = loaded.meta.found;
@@ -1319,13 +1536,18 @@ export class LocalNodeService {
1319
1536
  before.namespace !== after.namespace ||
1320
1537
  (before.port ?? null) !== (after.port ?? null);
1321
1538
 
1539
+ if (this.socialNetworkRequiresRestart) {
1540
+ await this.restartNetworkAdapter("reload_social_config");
1541
+ this.socialNetworkRequiresRestart = false;
1542
+ }
1543
+
1322
1544
  await this.writeSocialRuntime();
1323
1545
 
1324
1546
  return this.getSocialConfigView();
1325
1547
  }
1326
1548
 
1327
1549
  async generateDefaultSocialMd() {
1328
- const result = ensureDefaultSocialMd(this.workspaceRoot, {
1550
+ const result = ensureDefaultSocialMd(this.projectRoot, {
1329
1551
  display_name: this.getDefaultDisplayName(),
1330
1552
  bio: "Local AI agent connected to SilicaClaw",
1331
1553
  tags: ["openclaw", "local-first"],
@@ -1352,10 +1574,11 @@ export class LocalNodeService {
1352
1574
  search(keyword: string): PublicProfileSummary[] {
1353
1575
  this.ensureLocalDirectoryBaseline();
1354
1576
  this.compactCacheInMemory();
1355
- return searchDirectory(this.directory, keyword, { presenceTTLms: PRESENCE_TTL_MS }).map((profile) => {
1577
+ const directMatches = searchDirectory(this.directory, keyword, { presenceTTLms: PRESENCE_TTL_MS }).map((profile) => {
1356
1578
  const lastSeenAt = this.directory.presence[profile.agent_id] ?? 0;
1357
1579
  return this.toPublicProfileSummary(profile, { last_seen_at: lastSeenAt });
1358
1580
  });
1581
+ return this.mergeMessageOnlyAgentSummaries(directMatches, keyword);
1359
1582
  }
1360
1583
 
1361
1584
  getPublicProfilePreview(): PublicProfileSummary | null {
@@ -1433,11 +1656,11 @@ export class LocalNodeService {
1433
1656
 
1434
1657
  getOpenClawBridgeStatus(): OpenClawBridgeStatus {
1435
1658
  const integration = this.getIntegrationStatus();
1436
- const openclawInstallation = detectOpenClawInstallation(this.workspaceRoot);
1437
- const openclawRuntime = detectOpenClawRuntime(this.workspaceRoot);
1659
+ const openclawInstallation = detectOpenClawInstallation(this.projectRoot);
1660
+ const openclawRuntime = detectOpenClawRuntime(this.projectRoot);
1438
1661
  const skillInstallation = detectOpenClawSkillInstallation();
1439
1662
  const ownerDelivery = detectOwnerDeliveryStatus({
1440
- workspaceRoot: this.workspaceRoot,
1663
+ workspaceRoot: this.projectRoot,
1441
1664
  connectedToSilicaclaw: integration.connected_to_silicaclaw,
1442
1665
  openclawRunning: openclawRuntime.running,
1443
1666
  skillInstalled: skillInstallation.installed,
@@ -1500,11 +1723,15 @@ export class LocalNodeService {
1500
1723
  };
1501
1724
  }
1502
1725
 
1503
- async installOpenClawSkill() {
1726
+ async installOpenClawSkill(skillName?: string) {
1504
1727
  const scriptPath = resolve(this.workspaceRoot, "scripts", "install-openclaw-skill.mjs");
1505
- const { stdout } = await execFileAsync(process.execPath, [scriptPath], {
1728
+ const args = [scriptPath];
1729
+ if (skillName) {
1730
+ args.push(`--skill=${skillName}`);
1731
+ }
1732
+ const { stdout } = await execFileAsync(process.execPath, args, {
1506
1733
  cwd: this.workspaceRoot,
1507
- env: process.env,
1734
+ env: { ...process.env, SILICACLAW_WORKSPACE_DIR: this.projectRoot },
1508
1735
  maxBuffer: 1024 * 1024,
1509
1736
  });
1510
1737
  const parsed = JSON.parse(String(stdout || "{}"));
@@ -1528,12 +1755,12 @@ export class LocalNodeService {
1528
1755
  const homeDir = resolve(process.env.HOME || "", ".openclaw");
1529
1756
  const workspaceSkillDir = resolve(homeDir, "workspace", "skills");
1530
1757
  const legacySkillDir = resolve(homeDir, "skills");
1531
- const openclawSourceDir = resolve(this.workspaceRoot, "..", "openclaw");
1532
- const openclawRuntime = detectOpenClawRuntime(this.workspaceRoot);
1758
+ const openclawSourceDir = defaultOpenClawSourceDir(this.projectRoot);
1759
+ const openclawRuntime = detectOpenClawRuntime(this.projectRoot);
1533
1760
 
1534
1761
  return {
1535
- bridge_api_base: "http://localhost:4310",
1536
- openclaw_detected: detectOpenClawInstallation(this.workspaceRoot).detected,
1762
+ bridge_api_base: DEFAULT_BRIDGE_API_BASE,
1763
+ openclaw_detected: detectOpenClawInstallation(this.projectRoot).detected,
1537
1764
  openclaw_running: openclawRuntime.running,
1538
1765
  openclaw_gateway_host: OPENCLAW_GATEWAY_HOST,
1539
1766
  openclaw_gateway_port: openclawRuntime.configured_gateway_port,
@@ -1542,7 +1769,7 @@ export class LocalNodeService {
1542
1769
  openclaw_workspace_skill_dir: workspaceSkillDir,
1543
1770
  openclaw_legacy_skill_dir: legacySkillDir,
1544
1771
  silicaclaw_env_template_path: resolve(this.workspaceRoot, "openclaw-owner-forward.env.example"),
1545
- recommended_skill_name: "silicaclaw-broadcast",
1772
+ recommended_skill_name: "silicaclaw-bridge-setup",
1546
1773
  recommended_install_command: "silicaclaw openclaw-skill-install",
1547
1774
  recommended_owner_forward_env: {
1548
1775
  OPENCLAW_SOURCE_DIR: openclawSourceDir,
@@ -1560,6 +1787,7 @@ export class LocalNodeService {
1560
1787
  ].join(" "),
1561
1788
  notes: [
1562
1789
  "Install and maintain the skill from SilicaClaw; do not edit OpenClaw core source for this integration.",
1790
+ "Use silicaclaw-bridge-setup first when OpenClaw still needs local install, readiness checks, or troubleshooting guidance.",
1563
1791
  "OpenClaw learns broadcasts via the installed skill under ~/.openclaw/workspace/skills/.",
1564
1792
  "Runtime detection prefers the actual OpenClaw gateway listener port, then falls back to OpenClaw's own openclaw.json gateway.port.",
1565
1793
  "Owner delivery runs through OpenClaw's own message channel stack after the skill forwards a summary.",
@@ -1579,6 +1807,12 @@ export class LocalNodeService {
1579
1807
  const skillPath = resolve(dir.path, "SKILL.md");
1580
1808
  const versionPath = resolve(dir.path, "VERSION");
1581
1809
  const manifest = readJsonFileSafe(manifestPath);
1810
+ const references = (manifest?.references && typeof manifest.references === "object")
1811
+ ? manifest.references as Record<string, unknown>
1812
+ : null;
1813
+ const ownerDialogueCheatsheetPath = references?.owner_dialogue_cheatsheet_zh
1814
+ ? resolve(dir.path, String(references.owner_dialogue_cheatsheet_zh))
1815
+ : null;
1582
1816
  const name = String(manifest?.name || dir.name);
1583
1817
  const capabilities = Array.isArray(manifest?.capabilities)
1584
1818
  ? manifest.capabilities.map((item) => String(item))
@@ -1598,6 +1832,9 @@ export class LocalNodeService {
1598
1832
  skill_path: existsSync(skillPath) ? skillPath : null,
1599
1833
  capabilities,
1600
1834
  transport: manifest?.transport || null,
1835
+ owner_dialogue_cheatsheet_path: ownerDialogueCheatsheetPath && existsSync(ownerDialogueCheatsheetPath) ? ownerDialogueCheatsheetPath : null,
1836
+ owner_dialogue_examples_zh: ownerDialogueCheatsheetPath ? readDialogueCheatsheetPreview(ownerDialogueCheatsheetPath) : [],
1837
+ owner_dialogue_sections_zh: ownerDialogueCheatsheetPath ? readDialogueCheatsheetSections(ownerDialogueCheatsheetPath) : [],
1601
1838
  installed_in_openclaw: installedInWorkspace || installedInLegacy,
1602
1839
  install_mode: installedInWorkspace ? "workspace" : installedInLegacy ? "legacy" : "not_installed",
1603
1840
  installed_path: installedInWorkspace ? installedWorkspacePath : installedInLegacy ? installedLegacyPath : null,
@@ -1612,6 +1849,12 @@ export class LocalNodeService {
1612
1849
  const skillPath = resolve(dir.path, "SKILL.md");
1613
1850
  const versionPath = resolve(dir.path, "VERSION");
1614
1851
  const manifest = readJsonFileSafe(manifestPath);
1852
+ const references = (manifest?.references && typeof manifest.references === "object")
1853
+ ? manifest.references as Record<string, unknown>
1854
+ : null;
1855
+ const ownerDialogueCheatsheetPath = references?.owner_dialogue_cheatsheet_zh
1856
+ ? resolve(dir.path, String(references.owner_dialogue_cheatsheet_zh))
1857
+ : null;
1615
1858
  return {
1616
1859
  key: `${dir.install_mode}:${dir.name}`,
1617
1860
  name: String(manifest?.name || dir.name),
@@ -1623,10 +1866,43 @@ export class LocalNodeService {
1623
1866
  manifest_path: existsSync(manifestPath) ? manifestPath : null,
1624
1867
  skill_path: existsSync(skillPath) ? skillPath : null,
1625
1868
  capabilities: Array.isArray(manifest?.capabilities) ? manifest.capabilities.map((item) => String(item)) : [],
1869
+ owner_dialogue_cheatsheet_path: ownerDialogueCheatsheetPath && existsSync(ownerDialogueCheatsheetPath) ? ownerDialogueCheatsheetPath : null,
1870
+ owner_dialogue_examples_zh: ownerDialogueCheatsheetPath ? readDialogueCheatsheetPreview(ownerDialogueCheatsheetPath) : [],
1871
+ owner_dialogue_sections_zh: ownerDialogueCheatsheetPath ? readDialogueCheatsheetSections(ownerDialogueCheatsheetPath) : [],
1626
1872
  bundled_source_path: bundledSkills.find((item) => item.name === String(manifest?.name || dir.name))?.source_path || null,
1627
1873
  };
1628
1874
  });
1629
1875
 
1876
+ const installedSkillVersions = new Map(installedSkills.map((item) => [item.name, item.version]));
1877
+ const bundledSkillsWithUpdateState = bundledSkills.map((skill) => {
1878
+ const installedVersion = installedSkillVersions.get(skill.name) || "";
1879
+ const updateAvailable = Boolean(
1880
+ skill.installed_in_openclaw &&
1881
+ installedVersion &&
1882
+ skill.version &&
1883
+ compareVersionTokens(installedVersion, skill.version) < 0
1884
+ );
1885
+ return {
1886
+ ...skill,
1887
+ installed_version: installedVersion || null,
1888
+ update_available: updateAvailable,
1889
+ };
1890
+ });
1891
+ const bundledSkillVersions = new Map(bundledSkillsWithUpdateState.map((item) => [item.name, item.version]));
1892
+ const installedSkillsWithUpdateState = installedSkills.map((skill) => {
1893
+ const bundledVersion = bundledSkillVersions.get(skill.name) || "";
1894
+ const updateAvailable = Boolean(
1895
+ bundledVersion &&
1896
+ skill.version &&
1897
+ compareVersionTokens(skill.version, bundledVersion) < 0
1898
+ );
1899
+ return {
1900
+ ...skill,
1901
+ bundled_version: bundledVersion || null,
1902
+ update_available: updateAvailable,
1903
+ };
1904
+ });
1905
+
1630
1906
  return {
1631
1907
  openclaw: {
1632
1908
  detected: bridge.openclaw_installation.detected,
@@ -1637,13 +1913,14 @@ export class LocalNodeService {
1637
1913
  legacy_install_root: legacyInstallRoot,
1638
1914
  },
1639
1915
  summary: {
1640
- bundled_count: bundledSkills.length,
1641
- installed_count: installedSkills.length,
1642
- installed_bundled_count: bundledSkills.filter((item) => item.installed_in_openclaw).length,
1916
+ bundled_count: bundledSkillsWithUpdateState.length,
1917
+ installed_count: installedSkillsWithUpdateState.length,
1918
+ installed_bundled_count: bundledSkillsWithUpdateState.filter((item) => item.installed_in_openclaw).length,
1919
+ update_available_count: bundledSkillsWithUpdateState.filter((item) => item.update_available).length,
1643
1920
  },
1644
1921
  install_action: bridge.skill_learning.install_action,
1645
- bundled_skills: bundledSkills,
1646
- installed_skills: installedSkills,
1922
+ bundled_skills: bundledSkillsWithUpdateState,
1923
+ installed_skills: installedSkillsWithUpdateState,
1647
1924
  };
1648
1925
  }
1649
1926
 
@@ -1890,6 +2167,7 @@ export class LocalNodeService {
1890
2167
  };
1891
2168
  const presenceRecord = signPresence(this.identity, Date.now());
1892
2169
  const indexRecords = buildIndexRecords(this.profile);
2170
+ const replayMessages = this.getReplayableSelfSocialMessages();
1893
2171
 
1894
2172
  try {
1895
2173
  await this.publish("profile", profileRecord);
@@ -1897,6 +2175,9 @@ export class LocalNodeService {
1897
2175
  for (const record of indexRecords) {
1898
2176
  await this.publish("index", record);
1899
2177
  }
2178
+ for (const message of replayMessages) {
2179
+ await this.publish(SOCIAL_MESSAGE_TOPIC, message);
2180
+ }
1900
2181
  } catch (error) {
1901
2182
  const message = error instanceof Error ? error.message : String(error);
1902
2183
  this.lastBroadcastErrorAt = Date.now();
@@ -1919,7 +2200,10 @@ export class LocalNodeService {
1919
2200
  this.compactCacheInMemory();
1920
2201
  await this.persistCache();
1921
2202
 
1922
- await this.log("info", `Broadcast sent (${indexRecords.length} index refs, reason=${reason})`);
2203
+ await this.log(
2204
+ "info",
2205
+ `Broadcast sent (${indexRecords.length} index refs, replayed_messages=${replayMessages.length}, reason=${reason})`
2206
+ );
1923
2207
  return { sent: true, reason };
1924
2208
  }
1925
2209
 
@@ -1939,7 +2223,7 @@ export class LocalNodeService {
1939
2223
  socialConfig: this.socialConfig,
1940
2224
  existingIdentity,
1941
2225
  generatedIdentity: createIdentity(),
1942
- rootDir: this.workspaceRoot,
2226
+ rootDir: this.projectRoot,
1943
2227
  });
1944
2228
  this.identity = resolvedIdentity.identity;
1945
2229
  this.resolvedIdentitySource = resolvedIdentity.source;
@@ -1958,7 +2242,7 @@ export class LocalNodeService {
1958
2242
  socialConfig: this.socialConfig,
1959
2243
  agentId: this.identity.agent_id,
1960
2244
  existingProfile: existingProfile && existingProfile.agent_id === this.identity.agent_id ? existingProfile : null,
1961
- rootDir: this.workspaceRoot,
2245
+ rootDir: this.projectRoot,
1962
2246
  });
1963
2247
  this.profile = signProfile(profileInput, this.identity);
1964
2248
  if (!existingProfile || existingProfile.agent_id !== this.identity.agent_id) {
@@ -1967,7 +2251,7 @@ export class LocalNodeService {
1967
2251
  }
1968
2252
  await this.profileRepo.set(this.profile);
1969
2253
 
1970
- this.directory = dedupeIndex(await this.cacheRepo.get());
2254
+ this.directory = createEmptyDirectoryState();
1971
2255
  this.messageGovernance = {
1972
2256
  ...this.defaultMessageGovernance(),
1973
2257
  ...(await this.socialMessageGovernanceRepo.get()),
@@ -1990,7 +2274,7 @@ export class LocalNodeService {
1990
2274
  socialConfig: this.socialConfig,
1991
2275
  agentId: this.identity.agent_id,
1992
2276
  existingProfile: this.profile,
1993
- rootDir: this.workspaceRoot,
2277
+ rootDir: this.projectRoot,
1994
2278
  });
1995
2279
  const nextProfile = signProfile(nextProfileInput, this.identity);
1996
2280
  this.profile = nextProfile;
@@ -2110,6 +2394,10 @@ export class LocalNodeService {
2110
2394
  await this.log("warn", `Rejected social message with invalid signature (${record.message_id.slice(0, 10)})`);
2111
2395
  return;
2112
2396
  }
2397
+ if (this.hasSocialMessage(record.message_id)) {
2398
+ await this.publishObservationForMessage(record);
2399
+ return;
2400
+ }
2113
2401
  const governanceReason = this.getIncomingSocialMessageRejectionReason(record);
2114
2402
  if (governanceReason) {
2115
2403
  await this.log("warn", `Rejected social message (${record.message_id.slice(0, 10)}): ${governanceReason}`);
@@ -2149,7 +2437,7 @@ export class LocalNodeService {
2149
2437
  clearInterval(this.broadcaster);
2150
2438
  }
2151
2439
 
2152
- if (!this.broadcastEnabled) {
2440
+ if (!this.broadcastEnabled || !this.networkStarted) {
2153
2441
  return;
2154
2442
  }
2155
2443
 
@@ -2271,6 +2559,7 @@ export class LocalNodeService {
2271
2559
  }
2272
2560
 
2273
2561
  private async restartNetworkAdapter(reason: string): Promise<void> {
2562
+ this.clearNetworkReconnectTimer();
2274
2563
  const previous = this.network;
2275
2564
  try {
2276
2565
  await previous.stop();
@@ -2282,16 +2571,63 @@ export class LocalNodeService {
2282
2571
  this.network = next.adapter;
2283
2572
  this.adapterMode = next.mode;
2284
2573
  this.networkPort = next.port;
2285
-
2286
- await this.network.start();
2574
+ this.subscriptionsBound = false;
2287
2575
  this.bindNetworkSubscriptions();
2288
- this.startBroadcastLoop();
2576
+ await this.startNetworkAdapterWithRetry(reason);
2577
+ }
2289
2578
 
2290
- if (this.broadcastEnabled && this.profile?.public_enabled) {
2291
- await this.broadcastNow("adapter_restart");
2579
+ private clearNetworkReconnectTimer(): void {
2580
+ if (this.networkReconnectTimer) {
2581
+ clearTimeout(this.networkReconnectTimer);
2582
+ this.networkReconnectTimer = null;
2292
2583
  }
2293
2584
  }
2294
2585
 
2586
+ private async startNetworkAdapterWithRetry(reason: string): Promise<void> {
2587
+ this.clearNetworkReconnectTimer();
2588
+ try {
2589
+ await this.network.start();
2590
+ this.networkStarted = true;
2591
+ this.networkStartupError = null;
2592
+ this.networkReconnectDelayMs = 5_000;
2593
+ await this.log(
2594
+ "info",
2595
+ `Local node started (${this.adapterMode}, mode=${this.networkMode}, signaling=${this.webrtcSignalingUrls[0] || "-"}, room=${this.webrtcRoom})`
2596
+ );
2597
+ this.startBroadcastLoop();
2598
+ if (this.broadcastEnabled && this.profile?.public_enabled) {
2599
+ try {
2600
+ await this.broadcastNow(reason);
2601
+ } catch (error) {
2602
+ await this.log(
2603
+ "warn",
2604
+ `Initial broadcast failed: ${error instanceof Error ? error.message : String(error)}`
2605
+ );
2606
+ }
2607
+ }
2608
+ } catch (error) {
2609
+ this.networkStarted = false;
2610
+ this.networkStartupError = error instanceof Error ? error.message : String(error);
2611
+ await this.log(
2612
+ "warn",
2613
+ `Network start failed (${this.adapterMode}, mode=${this.networkMode}): ${this.networkStartupError}`
2614
+ );
2615
+ this.scheduleNetworkReconnect();
2616
+ }
2617
+ }
2618
+
2619
+ private scheduleNetworkReconnect(): void {
2620
+ if (this.networkReconnectTimer) {
2621
+ return;
2622
+ }
2623
+ const delayMs = this.networkReconnectDelayMs;
2624
+ this.networkReconnectTimer = setTimeout(() => {
2625
+ this.networkReconnectTimer = null;
2626
+ void this.startNetworkAdapterWithRetry("adapter_reconnect");
2627
+ }, delayMs);
2628
+ this.networkReconnectDelayMs = Math.min(30_000, Math.max(5_000, Math.floor(delayMs * 1.5)));
2629
+ }
2630
+
2295
2631
  private compactCacheInMemory(): number {
2296
2632
  const cleaned = cleanupExpiredPresence(this.directory, Date.now(), PRESENCE_TTL_MS);
2297
2633
  this.directory = dedupeIndex(cleaned.state);
@@ -2304,7 +2640,22 @@ export class LocalNodeService {
2304
2640
  }
2305
2641
 
2306
2642
  private async persistCache(): Promise<void> {
2307
- await this.cacheRepo.set(this.directory);
2643
+ const persisted = createEmptyDirectoryState();
2644
+ if (this.profile) {
2645
+ const selfProfileRecord: SignedProfileRecord = {
2646
+ type: "profile",
2647
+ profile: this.profile,
2648
+ };
2649
+ this.directory = ingestProfileRecord(this.directory, selfProfileRecord);
2650
+ persisted.profiles[this.profile.agent_id] = this.profile;
2651
+ const selfLastSeenAt = this.directory.presence[this.profile.agent_id];
2652
+ if (typeof selfLastSeenAt === "number" && Number.isFinite(selfLastSeenAt)) {
2653
+ persisted.presence[this.profile.agent_id] = selfLastSeenAt;
2654
+ }
2655
+ const indexed = rebuildIndexForProfile(persisted, this.profile);
2656
+ persisted.index = indexed.index;
2657
+ }
2658
+ await this.cacheRepo.set(persisted);
2308
2659
  }
2309
2660
 
2310
2661
  private async persistSocialMessages(): Promise<void> {
@@ -2330,6 +2681,19 @@ export class LocalNodeService {
2330
2681
  return (this.network as any).getDiagnostics();
2331
2682
  }
2332
2683
 
2684
+ private getResolvedRealtimeNetworkSummary() {
2685
+ const diagnostics = this.getAdapterDiagnostics();
2686
+ const relayCapable = this.adapterMode === "webrtc-preview" || this.adapterMode === "relay-preview";
2687
+ return {
2688
+ diagnostics,
2689
+ signaling_url: diagnostics?.signaling_url ?? (relayCapable ? this.webrtcSignalingUrls[0] ?? null : null),
2690
+ signaling_endpoints: diagnostics?.signaling_endpoints ?? (relayCapable ? this.webrtcSignalingUrls : []),
2691
+ room: diagnostics?.room ?? (relayCapable ? this.webrtcRoom : null),
2692
+ bootstrap_sources: diagnostics?.bootstrap_sources ?? (relayCapable ? this.webrtcBootstrapSources : []),
2693
+ seed_peers_count: diagnostics?.seed_peers_count ?? this.webrtcSeedPeers.length,
2694
+ };
2695
+ }
2696
+
2333
2697
  private toPublicProfileSummary(
2334
2698
  profile: PublicProfile,
2335
2699
  options?: { last_seen_at?: number }
@@ -2373,6 +2737,72 @@ export class LocalNodeService {
2373
2737
  });
2374
2738
  }
2375
2739
 
2740
+ private mergeMessageOnlyAgentSummaries(
2741
+ summaries: PublicProfileSummary[],
2742
+ keyword: string
2743
+ ): PublicProfileSummary[] {
2744
+ const normalizedKeyword = String(keyword || "").trim().toLowerCase();
2745
+ const knownAgentIds = new Set(summaries.map((item) => item.agent_id));
2746
+ const messageOnly: PublicProfileSummary[] = [];
2747
+
2748
+ for (const message of this.socialMessages) {
2749
+ if (!message?.agent_id || knownAgentIds.has(message.agent_id)) {
2750
+ continue;
2751
+ }
2752
+
2753
+ const displayName = String(message.display_name || "Unnamed").trim() || "Unnamed";
2754
+ if (normalizedKeyword) {
2755
+ const haystacks = [
2756
+ displayName.toLowerCase(),
2757
+ message.agent_id.toLowerCase(),
2758
+ String(message.topic || "").toLowerCase(),
2759
+ ];
2760
+ if (!haystacks.some((value) => value.includes(normalizedKeyword))) {
2761
+ continue;
2762
+ }
2763
+ }
2764
+
2765
+ knownAgentIds.add(message.agent_id);
2766
+ messageOnly.push(
2767
+ buildPublicProfileSummary({
2768
+ profile: {
2769
+ agent_id: message.agent_id,
2770
+ display_name: displayName,
2771
+ bio: "Seen from signed public message. Profile/presence not synced yet.",
2772
+ tags: ["message-only"],
2773
+ avatar_url: "",
2774
+ public_enabled: true,
2775
+ updated_at: message.created_at,
2776
+ signature: "",
2777
+ },
2778
+ online: false,
2779
+ last_seen_at: null,
2780
+ network_mode: "unknown",
2781
+ openclaw_bound: false,
2782
+ profile_version: PROFILE_VERSION,
2783
+ public_key_fingerprint: null,
2784
+ verified_profile: false,
2785
+ now: Date.now(),
2786
+ presence_ttl_ms: PRESENCE_TTL_MS,
2787
+ })
2788
+ );
2789
+ }
2790
+
2791
+ return [...summaries, ...messageOnly].sort((a, b) => {
2792
+ if (a.online !== b.online) {
2793
+ return a.online ? -1 : 1;
2794
+ }
2795
+ if (a.updated_at !== b.updated_at) {
2796
+ return b.updated_at - a.updated_at;
2797
+ }
2798
+ const byName = a.display_name.localeCompare(b.display_name);
2799
+ if (byName !== 0) {
2800
+ return byName;
2801
+ }
2802
+ return a.agent_id.localeCompare(b.agent_id);
2803
+ });
2804
+ }
2805
+
2376
2806
  private fingerprintPublicKey(publicKey: string): string {
2377
2807
  const digest = createHash("sha256").update(publicKey, "utf8").digest("hex");
2378
2808
  return `${digest.slice(0, 12)}:${digest.slice(-8)}`;
@@ -2381,6 +2811,22 @@ export class LocalNodeService {
2381
2811
  private getOnboardingSummary() {
2382
2812
  const summary = this.getIntegrationSummary();
2383
2813
  const publicEnabled = Boolean(this.profile?.public_enabled);
2814
+ const nextSteps: string[] = [];
2815
+ if (!String(this.profile?.display_name || "").trim()) {
2816
+ nextSteps.push("Update display name in Profile page");
2817
+ }
2818
+ if (!publicEnabled) {
2819
+ nextSteps.push("Enable Public Enabled in Profile");
2820
+ }
2821
+ if (!summary.running) {
2822
+ nextSteps.push("Start broadcast in Network");
2823
+ }
2824
+ if (!summary.discoverable) {
2825
+ nextSteps.push("Announce node once after the network is running");
2826
+ }
2827
+ if (nextSteps.length === 0) {
2828
+ nextSteps.push("Node is public and discoverable");
2829
+ }
2384
2830
  return {
2385
2831
  first_run: Boolean(
2386
2832
  this.initState.social_auto_created ||
@@ -2392,10 +2838,7 @@ export class LocalNodeService {
2392
2838
  mode: this.networkMode,
2393
2839
  public_enabled: publicEnabled,
2394
2840
  can_enable_public_discovery: !publicEnabled,
2395
- next_steps: [
2396
- "Update display name in Profile page",
2397
- "Export social.md from Social Config",
2398
- ],
2841
+ next_steps: nextSteps,
2399
2842
  };
2400
2843
  }
2401
2844
 
@@ -2420,7 +2863,7 @@ export class LocalNodeService {
2420
2863
  };
2421
2864
  }
2422
2865
  return {
2423
- mode: "global-preview",
2866
+ mode: DEFAULT_NETWORK_MODE,
2424
2867
  short_label: "Relay preview",
2425
2868
  summary: "Uses the public relay preview room so public nodes can find each other across the internet.",
2426
2869
  };
@@ -2450,14 +2893,14 @@ export class LocalNodeService {
2450
2893
  this.socialConfig.network.mode ||
2451
2894
  (modeEnv === "local" || modeEnv === "lan" || modeEnv === "global-preview"
2452
2895
  ? modeEnv
2453
- : "global-preview");
2896
+ : DEFAULT_NETWORK_MODE);
2454
2897
 
2455
2898
  this.networkMode = resolvedMode;
2456
- this.networkNamespace = this.socialConfig.network.namespace || process.env.NETWORK_NAMESPACE || "silicaclaw.preview";
2457
- this.networkPort = Number(this.socialConfig.network.port || process.env.NETWORK_PORT || 44123);
2899
+ this.networkNamespace = this.socialConfig.network.namespace || process.env.NETWORK_NAMESPACE || DEFAULT_NETWORK_NAMESPACE;
2900
+ this.networkPort = Number(this.socialConfig.network.port || process.env.NETWORK_PORT || DEFAULT_NETWORK_PORT);
2458
2901
 
2459
- const builtInGlobalSignalingUrls = ["https://relay.silicaclaw.com"];
2460
- const builtInGlobalRoom = "silicaclaw-global-preview";
2902
+ const builtInGlobalSignalingUrls = [DEFAULT_GLOBAL_SIGNALING_URL];
2903
+ const builtInGlobalRoom = DEFAULT_GLOBAL_ROOM;
2461
2904
 
2462
2905
  const signalingUrlsSocial = dedupeStrings(this.socialConfig.network.signaling_urls || []);
2463
2906
  const signalingUrlSocial = String(this.socialConfig.network.signaling_url || "").trim();
@@ -2482,8 +2925,8 @@ export class LocalNodeService {
2482
2925
  signalingUrls = builtInGlobalSignalingUrls;
2483
2926
  signalingSource = "built-in-defaults:global-preview.signaling_urls";
2484
2927
  } else {
2485
- signalingUrls = ["https://relay.silicaclaw.com"];
2486
- signalingSource = "default:https://relay.silicaclaw.com";
2928
+ signalingUrls = [DEFAULT_GLOBAL_SIGNALING_URL];
2929
+ signalingSource = `default:${DEFAULT_GLOBAL_SIGNALING_URL}`;
2487
2930
  }
2488
2931
 
2489
2932
  const roomSocial = String(this.socialConfig.network.room || "").trim();
@@ -2492,14 +2935,14 @@ export class LocalNodeService {
2492
2935
  roomSocial ||
2493
2936
  roomEnv ||
2494
2937
  (this.networkMode === "global-preview" ? builtInGlobalRoom : "") ||
2495
- "silicaclaw-global-preview";
2938
+ DEFAULT_GLOBAL_ROOM;
2496
2939
  const roomSource = roomSocial
2497
2940
  ? "social.md:network.room"
2498
2941
  : roomEnv
2499
2942
  ? "env:WEBRTC_ROOM"
2500
2943
  : this.networkMode === "global-preview"
2501
2944
  ? "built-in-defaults:global-preview.room"
2502
- : "default:silicaclaw-global-preview";
2945
+ : `default:${DEFAULT_GLOBAL_ROOM}`;
2503
2946
 
2504
2947
  const seedPeersSocial = dedupeStrings(this.socialConfig.network.seed_peers || []);
2505
2948
  const seedPeersEnv = dedupeStrings(parseListEnv(WEBRTC_SEED_PEERS));
@@ -2558,6 +3001,24 @@ export class LocalNodeService {
2558
3001
  return this.messageGovernance.blocked_terms.some((term) => normalized.includes(term));
2559
3002
  }
2560
3003
 
3004
+ private hasSocialMessage(messageId: string): boolean {
3005
+ return this.socialMessages.some((item) => item.message_id === messageId);
3006
+ }
3007
+
3008
+ private getReplayableSelfSocialMessages(now = Date.now()): SocialMessageRecord[] {
3009
+ const maxCount = Math.max(0, SOCIAL_MESSAGE_REPLAY_MAX_PER_BROADCAST);
3010
+ if (!this.identity || maxCount === 0) {
3011
+ return [];
3012
+ }
3013
+ return this.socialMessages
3014
+ .filter((item) => (
3015
+ item.agent_id === this.identity?.agent_id &&
3016
+ now - item.created_at <= SOCIAL_MESSAGE_REPLAY_WINDOW_MS
3017
+ ))
3018
+ .sort((a, b) => a.created_at - b.created_at)
3019
+ .slice(-maxCount);
3020
+ }
3021
+
2561
3022
  private hasRecentDuplicateMessage(agentId: string, body: string, topic: string, now = Date.now()): boolean {
2562
3023
  return this.socialMessages.some((item) => (
2563
3024
  item.agent_id === agentId &&
@@ -2867,20 +3328,19 @@ function renderBootstrapScript(payload: unknown): string {
2867
3328
  if (data.pillBroadcastClassName) pillBroadcast.className = data.pillBroadcastClassName;
2868
3329
  }
2869
3330
  setHtml('overviewCards', data.overviewCardsHtml || '');
2870
- setText('agentsCountHint', data.agentsCountHintText || '0 agents');
2871
- setHtml('agentsWrap', data.agentsWrapHtml || '<div class="label">No discovered agents yet.</div>');
3331
+ setText('agentsCountHint', data.agentsCountHintText || '0 nodes');
3332
+ setHtml('agentsWrap', data.agentsWrapHtml || '<div class="label">No discovered nodes yet.</div>');
2872
3333
  })();
2873
3334
  </script>`;
2874
3335
  }
2875
3336
 
2876
3337
  export async function main() {
2877
3338
  const app = express();
2878
- const port = Number(process.env.PORT || 4310);
3339
+ const port = Number(process.env.PORT || defaults.ports.local_console);
2879
3340
  const staticDir = resolveLocalConsoleStaticDir();
2880
3341
  const staticIndexFile = resolve(staticDir, "index.html");
2881
3342
 
2882
3343
  const node = new LocalNodeService();
2883
- await node.start();
2884
3344
 
2885
3345
  app.use(cors({ origin: true }));
2886
3346
  app.use(express.json());
@@ -3083,9 +3543,10 @@ export async function main() {
3083
3543
 
3084
3544
  app.post(
3085
3545
  "/api/openclaw/bridge/skill-install",
3086
- asyncRoute(async (_req, res) => {
3546
+ asyncRoute(async (req, res) => {
3087
3547
  try {
3088
- const result = await node.installOpenClawSkill();
3548
+ const skillName = String(req.body?.skill_name || "").trim();
3549
+ const result = await node.installOpenClawSkill(skillName || undefined);
3089
3550
  sendOk(res, result, {
3090
3551
  message: "OpenClaw skill installed",
3091
3552
  });
@@ -3133,7 +3594,7 @@ export async function main() {
3133
3594
  const agentId = req.params.agentId;
3134
3595
  const profile = state.profiles[agentId];
3135
3596
  if (!profile) {
3136
- sendError(res, 404, "AGENT_NOT_FOUND", "Agent not found", { agent_id: agentId });
3597
+ sendError(res, 404, "AGENT_NOT_FOUND", "Node not found", { agent_id: agentId });
3137
3598
  return;
3138
3599
  }
3139
3600
 
@@ -3169,7 +3630,7 @@ export async function main() {
3169
3630
  .join("");
3170
3631
  const agentsWrapHtml =
3171
3632
  discovered.length === 0
3172
- ? `<div class="label">No discovered agents yet.</div>`
3633
+ ? `<div class="label">No discovered nodes yet.</div>`
3173
3634
  : `
3174
3635
  <table class="table">
3175
3636
  <thead><tr><th>Name</th><th>Agent ID</th><th>Status</th><th>Updated</th></tr></thead>
@@ -3205,7 +3666,7 @@ export async function main() {
3205
3666
  pillBroadcastText: overview.broadcast_enabled ? "broadcast: running" : "broadcast: paused",
3206
3667
  pillBroadcastClassName: `pill ${overview.broadcast_enabled ? "ok" : "warn"}`,
3207
3668
  overviewCardsHtml,
3208
- agentsCountHintText: `${discovered.length} agents discovered`,
3669
+ agentsCountHintText: `${discovered.length} nodes discovered`,
3209
3670
  agentsWrapHtml,
3210
3671
  integrationStatusText: `Connected to SilicaClaw: ${integration.connected_to_silicaclaw ? "yes" : "no"} · Network mode: ${integration.network_mode || "-"} · Public discovery: ${integration.public_enabled ? "enabled" : "disabled"}`,
3211
3672
  integrationStatusClassName: `integration-strip ${integration.connected_to_silicaclaw && integration.public_enabled ? "ok" : "warn"}`,
@@ -3230,6 +3691,8 @@ export async function main() {
3230
3691
  console.log(`SilicaClaw local-console running: http://localhost:${port}`);
3231
3692
  });
3232
3693
 
3694
+ await node.start();
3695
+
3233
3696
  process.on("SIGINT", async () => {
3234
3697
  await node.stop();
3235
3698
  process.exit(0);