@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
@@ -0,0 +1,848 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.WebRTCPreviewAdapter = void 0;
7
+ const crypto_1 = require("crypto");
8
+ const messageEnvelope_1 = require("./abstractions/messageEnvelope");
9
+ const jsonMessageEnvelopeCodec_1 = require("./codec/jsonMessageEnvelopeCodec");
10
+ const jsonTopicCodec_1 = require("./codec/jsonTopicCodec");
11
+ const silicaclaw_defaults_json_1 = __importDefault(require("../../../config/silicaclaw-defaults.json"));
12
+ function now() {
13
+ return Date.now();
14
+ }
15
+ function toBuffer(data) {
16
+ if (Buffer.isBuffer(data))
17
+ return data;
18
+ if (data instanceof ArrayBuffer)
19
+ return Buffer.from(data);
20
+ if (ArrayBuffer.isView(data))
21
+ return Buffer.from(data.buffer, data.byteOffset, data.byteLength);
22
+ if (typeof data === "string")
23
+ return Buffer.from(data, "utf8");
24
+ return null;
25
+ }
26
+ function connectionStateOrUnknown(connection) {
27
+ const value = String(connection?.connectionState ?? connection?.iceConnectionState ?? "unknown");
28
+ if (value === "new" ||
29
+ value === "connecting" ||
30
+ value === "connected" ||
31
+ value === "disconnected" ||
32
+ value === "failed" ||
33
+ value === "closed") {
34
+ return value;
35
+ }
36
+ return "unknown";
37
+ }
38
+ function dataChannelStateOrUnknown(channel) {
39
+ const value = String(channel?.readyState ?? "unknown");
40
+ if (value === "connecting" || value === "open" || value === "closing" || value === "closed") {
41
+ return value;
42
+ }
43
+ return "unknown";
44
+ }
45
+ function iceKey(candidatePayload) {
46
+ return JSON.stringify({
47
+ candidate: candidatePayload?.candidate ?? "",
48
+ sdpMid: candidatePayload?.sdpMid ?? "",
49
+ sdpMLineIndex: candidatePayload?.sdpMLineIndex ?? "",
50
+ });
51
+ }
52
+ function dedupeArray(values) {
53
+ return Array.from(new Set(values.map((value) => value.trim()).filter(Boolean)));
54
+ }
55
+ class WebRTCPreviewAdapter {
56
+ peerId;
57
+ namespace;
58
+ signalingUrl;
59
+ signalingEndpoints;
60
+ room;
61
+ seedPeers;
62
+ bootstrapHints;
63
+ bootstrapSources;
64
+ maxMessageBytes;
65
+ pollIntervalMs;
66
+ maxFutureDriftMs;
67
+ maxPastDriftMs;
68
+ discoveryEventsLimit;
69
+ envelopeCodec;
70
+ topicCodec;
71
+ handlers = new Map();
72
+ sessions = new Map();
73
+ started = false;
74
+ poller = null;
75
+ wrtc = null;
76
+ processedSignalIds = new Map();
77
+ signalingConnectivity = new Map();
78
+ signalingIndex = 0;
79
+ activeSignalingEndpoint = "";
80
+ discoveryEvents = [];
81
+ discoveryEventsTotal = 0;
82
+ lastDiscoveryEventAt = 0;
83
+ signalingMessagesSentTotal = 0;
84
+ signalingMessagesReceivedTotal = 0;
85
+ reconnectAttemptsTotal = 0;
86
+ stats = {
87
+ publish_attempted: 0,
88
+ publish_sent: 0,
89
+ received_total: 0,
90
+ delivered_total: 0,
91
+ dropped_malformed: 0,
92
+ dropped_oversized: 0,
93
+ dropped_namespace_mismatch: 0,
94
+ dropped_timestamp_future_drift: 0,
95
+ dropped_timestamp_past_drift: 0,
96
+ dropped_decode_failed: 0,
97
+ dropped_self: 0,
98
+ dropped_topic_decode_error: 0,
99
+ dropped_handler_error: 0,
100
+ signaling_errors: 0,
101
+ invalid_signaling_payload_total: 0,
102
+ duplicate_sdp_total: 0,
103
+ duplicate_ice_total: 0,
104
+ start_errors: 0,
105
+ stop_errors: 0,
106
+ received_validated: 0,
107
+ };
108
+ constructor(options = {}) {
109
+ this.peerId = options.peerId ?? `webrtc-${process.pid}-${Math.random().toString(36).slice(2, 9)}`;
110
+ this.namespace = (options.namespace ?? silicaclaw_defaults_json_1.default.network.default_namespace).trim() || silicaclaw_defaults_json_1.default.network.default_namespace;
111
+ const configuredSignalingUrls = dedupeArray([
112
+ ...(options.signalingUrls ?? []),
113
+ options.signalingUrl ?? "",
114
+ ]).map((url) => url.replace(/\/+$/, ""));
115
+ this.signalingEndpoints =
116
+ configuredSignalingUrls.length > 0 ? configuredSignalingUrls : ["http://localhost:4510"];
117
+ this.signalingUrl = this.signalingEndpoints[0];
118
+ this.room = (options.room ?? "silicaclaw-room").trim() || "silicaclaw-room";
119
+ this.seedPeers = dedupeArray(options.seedPeers ?? []);
120
+ this.bootstrapHints = dedupeArray(options.bootstrapHints ?? []);
121
+ this.bootstrapSources =
122
+ dedupeArray(options.bootstrapSources ?? []).length > 0
123
+ ? dedupeArray(options.bootstrapSources ?? [])
124
+ : [
125
+ configuredSignalingUrls.length > 0
126
+ ? "config:signaling_urls"
127
+ : options.signalingUrl
128
+ ? "config:signaling_url"
129
+ : "default:signaling_url",
130
+ options.room ? "config:room" : "default:room",
131
+ this.seedPeers.length > 0 ? "config:seed_peers" : "default:seed_peers",
132
+ this.bootstrapHints.length > 0 ? "config:bootstrap_hints" : "default:bootstrap_hints",
133
+ ];
134
+ this.maxMessageBytes = options.maxMessageBytes ?? 64 * 1024;
135
+ this.pollIntervalMs = options.pollIntervalMs ?? 1200;
136
+ this.maxFutureDriftMs = options.maxFutureDriftMs ?? 30_000;
137
+ this.maxPastDriftMs = options.maxPastDriftMs ?? 120_000;
138
+ this.discoveryEventsLimit = Math.max(10, options.discoveryEventsLimit ?? 200);
139
+ this.envelopeCodec = new jsonMessageEnvelopeCodec_1.JsonMessageEnvelopeCodec();
140
+ this.topicCodec = new jsonTopicCodec_1.JsonTopicCodec();
141
+ }
142
+ async start() {
143
+ if (this.started)
144
+ return;
145
+ this.wrtc = this.resolveWebRTCImplementation();
146
+ if (!this.wrtc) {
147
+ this.stats.start_errors += 1;
148
+ throw new Error("WebRTC runtime unavailable: RTCPeerConnection not found. In Node.js install `@roamhq/wrtc` (or `wrtc`) and restart.");
149
+ }
150
+ this.started = true;
151
+ try {
152
+ await this.postJson("/join", { room: this.room, peer_id: this.peerId });
153
+ await this.syncPeersFromSignaling();
154
+ for (const seedPeer of this.seedPeers) {
155
+ if (!seedPeer || seedPeer === this.peerId)
156
+ continue;
157
+ const session = this.ensureSession(seedPeer);
158
+ if (this.isInitiatorFor(seedPeer) && this.shouldAttemptConnect(session)) {
159
+ await this.attemptReconnect(session, "seed_peer_hint");
160
+ }
161
+ }
162
+ this.poller = setInterval(() => {
163
+ this.pollOnce().catch(() => {
164
+ this.stats.signaling_errors += 1;
165
+ });
166
+ }, this.pollIntervalMs);
167
+ }
168
+ catch (error) {
169
+ this.stats.start_errors += 1;
170
+ this.started = false;
171
+ throw new Error(`WebRTC preview start failed: ${error instanceof Error ? error.message : String(error)}`);
172
+ }
173
+ }
174
+ async stop() {
175
+ if (!this.started)
176
+ return;
177
+ this.started = false;
178
+ if (this.poller) {
179
+ clearInterval(this.poller);
180
+ this.poller = null;
181
+ }
182
+ for (const session of this.sessions.values()) {
183
+ this.closePeerSession(session);
184
+ this.recordDiscoveryEvent("peer_removed", {
185
+ peer_id: session.peer_id,
186
+ detail: "adapter_stop",
187
+ });
188
+ }
189
+ this.sessions.clear();
190
+ try {
191
+ await this.postJson("/leave", { room: this.room, peer_id: this.peerId });
192
+ }
193
+ catch {
194
+ this.stats.stop_errors += 1;
195
+ }
196
+ }
197
+ async publish(topic, data) {
198
+ if (!this.started)
199
+ return;
200
+ if (typeof topic !== "string" || !topic.trim() || topic.includes(":"))
201
+ return;
202
+ this.stats.publish_attempted += 1;
203
+ const envelope = {
204
+ version: 1,
205
+ message_id: (0, crypto_1.randomUUID)(),
206
+ topic: `${this.namespace}:${topic}`,
207
+ source_peer_id: this.peerId,
208
+ timestamp: now(),
209
+ payload: this.topicCodec.encode(topic, data),
210
+ };
211
+ const raw = this.envelopeCodec.encode(envelope);
212
+ if (raw.length > this.maxMessageBytes) {
213
+ this.stats.dropped_oversized += 1;
214
+ return;
215
+ }
216
+ let sent = 0;
217
+ for (const session of this.sessions.values()) {
218
+ if (!session.channel || session.channel.readyState !== "open")
219
+ continue;
220
+ try {
221
+ session.channel.send(new Uint8Array(raw));
222
+ sent += 1;
223
+ }
224
+ catch {
225
+ this.stats.signaling_errors += 1;
226
+ }
227
+ }
228
+ if (sent > 0) {
229
+ this.stats.publish_sent += 1;
230
+ }
231
+ }
232
+ subscribe(topic, handler) {
233
+ if (typeof topic !== "string" || !topic.trim() || topic.includes(":"))
234
+ return;
235
+ const key = `${this.namespace}:${topic}`;
236
+ if (!this.handlers.has(key))
237
+ this.handlers.set(key, new Set());
238
+ this.handlers.get(key)?.add(handler);
239
+ }
240
+ getDiagnostics() {
241
+ const connectionStates = {
242
+ new: 0,
243
+ connecting: 0,
244
+ connected: 0,
245
+ disconnected: 0,
246
+ failed: 0,
247
+ closed: 0,
248
+ unknown: 0,
249
+ };
250
+ const dataStates = {
251
+ connecting: 0,
252
+ open: 0,
253
+ closing: 0,
254
+ closed: 0,
255
+ unknown: 0,
256
+ };
257
+ const items = Array.from(this.sessions.values()).map((session) => {
258
+ connectionStates[session.connection_state] += 1;
259
+ dataStates[session.datachannel_state] += 1;
260
+ return {
261
+ peer_id: session.peer_id,
262
+ status: session.status,
263
+ first_seen_at: session.first_seen_at,
264
+ last_seen_at: session.last_seen_at,
265
+ messages_seen: session.messages_seen,
266
+ reconnect_attempts: session.reconnect_attempts,
267
+ connection_state: session.connection_state,
268
+ datachannel_state: session.datachannel_state,
269
+ };
270
+ });
271
+ const online = items.filter((item) => item.status === "online").length;
272
+ const activePeers = items.filter((item) => item.datachannel_state === "open").length;
273
+ return {
274
+ adapter: "webrtc-preview",
275
+ peer_id: this.peerId,
276
+ namespace: this.namespace,
277
+ room: this.room,
278
+ signaling_url: this.activeSignalingEndpoint || this.signalingUrl,
279
+ signaling_endpoints: [...this.signalingEndpoints],
280
+ bootstrap_sources: [...this.bootstrapSources],
281
+ seed_peers_count: this.seedPeers.length,
282
+ bootstrap_hints_count: this.bootstrapHints.length,
283
+ discovery_events_total: this.discoveryEventsTotal,
284
+ last_discovery_event_at: this.lastDiscoveryEventAt,
285
+ discovery_events: [...this.discoveryEvents],
286
+ connection_states_summary: connectionStates,
287
+ datachannel_states_summary: dataStates,
288
+ signaling_messages_sent_total: this.signalingMessagesSentTotal,
289
+ signaling_messages_received_total: this.signalingMessagesReceivedTotal,
290
+ reconnect_attempts_total: this.reconnectAttemptsTotal,
291
+ active_webrtc_peers: activePeers,
292
+ components: {
293
+ transport: "WebRTCDataChannelTransport",
294
+ discovery: "SignalingRoomPolling",
295
+ envelope_codec: this.envelopeCodec.constructor.name,
296
+ topic_codec: this.topicCodec.constructor.name,
297
+ },
298
+ limits: {
299
+ max_message_bytes: this.maxMessageBytes,
300
+ max_future_drift_ms: this.maxFutureDriftMs,
301
+ max_past_drift_ms: this.maxPastDriftMs,
302
+ },
303
+ config: {
304
+ started: this.started,
305
+ topic_handler_count: this.handlers.size,
306
+ poll_interval_ms: this.pollIntervalMs,
307
+ },
308
+ peers: {
309
+ total: items.length,
310
+ online,
311
+ stale: Math.max(0, items.length - online),
312
+ items,
313
+ },
314
+ stats: { ...this.stats },
315
+ };
316
+ }
317
+ resolveWebRTCImplementation() {
318
+ const g = globalThis;
319
+ if (typeof g.RTCPeerConnection === "function") {
320
+ return {
321
+ RTCPeerConnection: g.RTCPeerConnection,
322
+ RTCSessionDescription: g.RTCSessionDescription,
323
+ RTCIceCandidate: g.RTCIceCandidate,
324
+ };
325
+ }
326
+ try {
327
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
328
+ const wrtc = require("@roamhq/wrtc");
329
+ return {
330
+ RTCPeerConnection: wrtc.RTCPeerConnection,
331
+ RTCSessionDescription: wrtc.RTCSessionDescription,
332
+ RTCIceCandidate: wrtc.RTCIceCandidate,
333
+ };
334
+ }
335
+ catch {
336
+ try {
337
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
338
+ const wrtc = require("wrtc");
339
+ return {
340
+ RTCPeerConnection: wrtc.RTCPeerConnection,
341
+ RTCSessionDescription: wrtc.RTCSessionDescription,
342
+ RTCIceCandidate: wrtc.RTCIceCandidate,
343
+ };
344
+ }
345
+ catch {
346
+ return null;
347
+ }
348
+ }
349
+ }
350
+ async pollOnce() {
351
+ if (!this.started)
352
+ return;
353
+ await this.syncPeersFromSignaling();
354
+ const response = await this.getJson(`/poll?room=${encodeURIComponent(this.room)}&peer_id=${encodeURIComponent(this.peerId)}`);
355
+ const messages = Array.isArray(response?.messages) ? response.messages : [];
356
+ for (const message of messages) {
357
+ await this.handleSignalMessage(message);
358
+ }
359
+ this.cleanupProcessedSignalIds();
360
+ const staleThreshold = now() - this.maxPastDriftMs;
361
+ for (const session of this.sessions.values()) {
362
+ if (session.last_seen_at < staleThreshold && session.status !== "stale") {
363
+ session.status = "stale";
364
+ this.recordDiscoveryEvent("peer_stale", {
365
+ peer_id: session.peer_id,
366
+ detail: "presence_timeout",
367
+ });
368
+ }
369
+ }
370
+ }
371
+ async syncPeersFromSignaling() {
372
+ const response = await this.getJson(`/peers?room=${encodeURIComponent(this.room)}`);
373
+ const peers = Array.isArray(response?.peers)
374
+ ? response.peers.map((peer) => String(peer)).filter(Boolean)
375
+ : [];
376
+ const seen = new Set();
377
+ for (const peerId of peers) {
378
+ if (peerId === this.peerId)
379
+ continue;
380
+ seen.add(peerId);
381
+ const session = this.ensureSession(peerId);
382
+ session.last_seen_at = now();
383
+ if (this.isInitiatorFor(peerId) && this.shouldAttemptConnect(session)) {
384
+ await this.attemptReconnect(session, "sync_peers");
385
+ }
386
+ }
387
+ for (const [peerId, session] of this.sessions.entries()) {
388
+ if (!seen.has(peerId) && now() - session.last_seen_at > this.maxPastDriftMs) {
389
+ this.closePeerSession(session);
390
+ this.sessions.delete(peerId);
391
+ this.recordDiscoveryEvent("peer_removed", {
392
+ peer_id: peerId,
393
+ detail: "stale_session_cleanup",
394
+ });
395
+ }
396
+ }
397
+ }
398
+ ensureSession(peerId) {
399
+ const existing = this.sessions.get(peerId);
400
+ if (existing)
401
+ return existing;
402
+ const session = {
403
+ peer_id: peerId,
404
+ status: "connecting",
405
+ first_seen_at: now(),
406
+ last_seen_at: now(),
407
+ messages_seen: 0,
408
+ reconnect_attempts: 0,
409
+ last_reconnect_attempt_at: 0,
410
+ connection_state: "new",
411
+ datachannel_state: "unknown",
412
+ connection: null,
413
+ channel: null,
414
+ remote_description_set: false,
415
+ pending_ice: [],
416
+ seen_ice_keys: new Set(),
417
+ last_offer_sdp: "",
418
+ last_answer_sdp: "",
419
+ };
420
+ this.sessions.set(peerId, session);
421
+ this.recordDiscoveryEvent("peer_joined", { peer_id: peerId });
422
+ return session;
423
+ }
424
+ shouldAttemptConnect(session) {
425
+ if (!this.started)
426
+ return false;
427
+ const cs = session.connection_state;
428
+ if (session.channel && session.channel.readyState === "open")
429
+ return false;
430
+ if (!session.connection)
431
+ return true;
432
+ if (cs === "failed" || cs === "disconnected" || cs === "closed")
433
+ return true;
434
+ return false;
435
+ }
436
+ async attemptReconnect(session, _reason) {
437
+ const nowTs = now();
438
+ if (nowTs - session.last_reconnect_attempt_at < 2000) {
439
+ return;
440
+ }
441
+ session.last_reconnect_attempt_at = nowTs;
442
+ session.reconnect_attempts += 1;
443
+ this.reconnectAttemptsTotal += 1;
444
+ this.recordDiscoveryEvent("reconnect_started", {
445
+ peer_id: session.peer_id,
446
+ detail: _reason,
447
+ });
448
+ try {
449
+ this.closePeerSession(session);
450
+ session.connection = this.createPeerConnection(session);
451
+ if (this.isInitiatorFor(session.peer_id)) {
452
+ const channel = session.connection.createDataChannel("silicaclaw");
453
+ this.bindDataChannel(session, channel);
454
+ const offer = await session.connection.createOffer();
455
+ await session.connection.setLocalDescription(offer);
456
+ session.last_offer_sdp = String(offer?.sdp ?? "");
457
+ await this.sendSignal(session.peer_id, "offer", offer);
458
+ }
459
+ }
460
+ catch (error) {
461
+ this.recordDiscoveryEvent("reconnect_failed", {
462
+ peer_id: session.peer_id,
463
+ detail: error instanceof Error ? error.message : "reconnect_failed",
464
+ });
465
+ throw error;
466
+ }
467
+ }
468
+ createPeerConnection(session) {
469
+ const pc = new this.wrtc.RTCPeerConnection();
470
+ session.connection_state = connectionStateOrUnknown(pc);
471
+ pc.onconnectionstatechange = () => {
472
+ session.connection_state = connectionStateOrUnknown(pc);
473
+ session.last_seen_at = now();
474
+ if (session.connection_state === "connected") {
475
+ session.status = "online";
476
+ if (session.reconnect_attempts > 0) {
477
+ this.recordDiscoveryEvent("reconnect_succeeded", {
478
+ peer_id: session.peer_id,
479
+ detail: "connection_state_connected",
480
+ });
481
+ }
482
+ }
483
+ if ((session.connection_state === "failed" ||
484
+ session.connection_state === "disconnected" ||
485
+ session.connection_state === "closed") &&
486
+ this.isInitiatorFor(session.peer_id)) {
487
+ this.attemptReconnect(session, "connection_state_change").catch(() => {
488
+ this.stats.signaling_errors += 1;
489
+ this.recordDiscoveryEvent("reconnect_failed", {
490
+ peer_id: session.peer_id,
491
+ detail: "connection_state_change",
492
+ });
493
+ });
494
+ }
495
+ };
496
+ pc.onicecandidate = (event) => {
497
+ if (!event?.candidate)
498
+ return;
499
+ this.sendSignal(session.peer_id, "candidate", event.candidate).catch(() => {
500
+ this.stats.signaling_errors += 1;
501
+ });
502
+ };
503
+ pc.ondatachannel = (event) => {
504
+ this.bindDataChannel(session, event.channel);
505
+ };
506
+ return pc;
507
+ }
508
+ bindDataChannel(session, channel) {
509
+ session.channel = channel;
510
+ session.datachannel_state = dataChannelStateOrUnknown(channel);
511
+ channel.binaryType = "arraybuffer";
512
+ channel.onopen = () => {
513
+ session.datachannel_state = "open";
514
+ session.status = "online";
515
+ session.last_seen_at = now();
516
+ if (session.reconnect_attempts > 0) {
517
+ this.recordDiscoveryEvent("reconnect_succeeded", {
518
+ peer_id: session.peer_id,
519
+ detail: "datachannel_open",
520
+ });
521
+ }
522
+ };
523
+ channel.onclose = () => {
524
+ session.datachannel_state = "closed";
525
+ session.status = "stale";
526
+ this.recordDiscoveryEvent("peer_stale", {
527
+ peer_id: session.peer_id,
528
+ detail: "datachannel_closed",
529
+ });
530
+ if (this.isInitiatorFor(session.peer_id)) {
531
+ this.attemptReconnect(session, "datachannel_closed").catch(() => {
532
+ this.stats.signaling_errors += 1;
533
+ });
534
+ }
535
+ };
536
+ channel.onerror = () => {
537
+ this.stats.signaling_errors += 1;
538
+ session.status = "stale";
539
+ };
540
+ channel.onmessage = (event) => {
541
+ const buffer = toBuffer(event?.data);
542
+ if (!buffer) {
543
+ this.stats.dropped_decode_failed += 1;
544
+ return;
545
+ }
546
+ this.onDataMessage(session, buffer);
547
+ };
548
+ }
549
+ async handleSignalMessage(message) {
550
+ if (!message || typeof message !== "object") {
551
+ this.stats.invalid_signaling_payload_total += 1;
552
+ this.recordDiscoveryEvent("malformed_signal_dropped", { detail: "not_object" });
553
+ return;
554
+ }
555
+ const signalId = String(message.id ?? "");
556
+ if (signalId) {
557
+ const already = this.processedSignalIds.get(signalId);
558
+ if (already) {
559
+ this.recordDiscoveryEvent("duplicate_signal_dropped", { detail: "duplicate_signal_id" });
560
+ return;
561
+ }
562
+ this.processedSignalIds.set(signalId, now());
563
+ }
564
+ const fromPeerId = String(message.from_peer_id ?? "");
565
+ const type = String(message.type ?? "");
566
+ const payload = message.payload;
567
+ if (!fromPeerId || fromPeerId === this.peerId || !type) {
568
+ this.stats.invalid_signaling_payload_total += 1;
569
+ this.recordDiscoveryEvent("malformed_signal_dropped", { detail: "missing_required_fields" });
570
+ return;
571
+ }
572
+ this.signalingMessagesReceivedTotal += 1;
573
+ const session = this.ensureSession(fromPeerId);
574
+ session.last_seen_at = now();
575
+ if (!session.connection) {
576
+ session.connection = this.createPeerConnection(session);
577
+ }
578
+ const pc = session.connection;
579
+ try {
580
+ if (type === "offer") {
581
+ const sdp = String(payload?.sdp ?? "");
582
+ if (!sdp) {
583
+ this.stats.invalid_signaling_payload_total += 1;
584
+ this.recordDiscoveryEvent("malformed_signal_dropped", {
585
+ peer_id: fromPeerId,
586
+ detail: "offer_missing_sdp",
587
+ });
588
+ return;
589
+ }
590
+ if (session.last_offer_sdp === sdp) {
591
+ this.stats.duplicate_sdp_total += 1;
592
+ this.recordDiscoveryEvent("duplicate_signal_dropped", {
593
+ peer_id: fromPeerId,
594
+ detail: "duplicate_offer_sdp",
595
+ });
596
+ return;
597
+ }
598
+ session.last_offer_sdp = sdp;
599
+ await pc.setRemoteDescription(new this.wrtc.RTCSessionDescription(payload));
600
+ session.remote_description_set = true;
601
+ await this.flushBufferedIce(session);
602
+ const answer = await pc.createAnswer();
603
+ await pc.setLocalDescription(answer);
604
+ session.last_answer_sdp = String(answer?.sdp ?? "");
605
+ await this.sendSignal(fromPeerId, "answer", answer);
606
+ return;
607
+ }
608
+ if (type === "answer") {
609
+ const sdp = String(payload?.sdp ?? "");
610
+ if (!sdp) {
611
+ this.stats.invalid_signaling_payload_total += 1;
612
+ this.recordDiscoveryEvent("malformed_signal_dropped", {
613
+ peer_id: fromPeerId,
614
+ detail: "answer_missing_sdp",
615
+ });
616
+ return;
617
+ }
618
+ if (session.last_answer_sdp === sdp) {
619
+ this.stats.duplicate_sdp_total += 1;
620
+ this.recordDiscoveryEvent("duplicate_signal_dropped", {
621
+ peer_id: fromPeerId,
622
+ detail: "duplicate_answer_sdp",
623
+ });
624
+ return;
625
+ }
626
+ session.last_answer_sdp = sdp;
627
+ await pc.setRemoteDescription(new this.wrtc.RTCSessionDescription(payload));
628
+ session.remote_description_set = true;
629
+ await this.flushBufferedIce(session);
630
+ return;
631
+ }
632
+ if (type === "candidate") {
633
+ const key = iceKey(payload);
634
+ if (!key || key === "{}") {
635
+ this.stats.invalid_signaling_payload_total += 1;
636
+ this.recordDiscoveryEvent("malformed_signal_dropped", {
637
+ peer_id: fromPeerId,
638
+ detail: "candidate_missing_fields",
639
+ });
640
+ return;
641
+ }
642
+ if (session.seen_ice_keys.has(key)) {
643
+ this.stats.duplicate_ice_total += 1;
644
+ this.recordDiscoveryEvent("duplicate_signal_dropped", {
645
+ peer_id: fromPeerId,
646
+ detail: "duplicate_ice_candidate",
647
+ });
648
+ return;
649
+ }
650
+ session.seen_ice_keys.add(key);
651
+ if (!session.remote_description_set) {
652
+ session.pending_ice.push(payload);
653
+ return;
654
+ }
655
+ await pc.addIceCandidate(new this.wrtc.RTCIceCandidate(payload));
656
+ return;
657
+ }
658
+ this.stats.invalid_signaling_payload_total += 1;
659
+ this.recordDiscoveryEvent("malformed_signal_dropped", {
660
+ peer_id: fromPeerId,
661
+ detail: `unsupported_signal_type:${type}`,
662
+ });
663
+ }
664
+ catch {
665
+ this.stats.signaling_errors += 1;
666
+ }
667
+ }
668
+ async flushBufferedIce(session) {
669
+ if (!session.connection || !session.pending_ice.length)
670
+ return;
671
+ const pending = [...session.pending_ice];
672
+ session.pending_ice = [];
673
+ for (const candidate of pending) {
674
+ try {
675
+ await session.connection.addIceCandidate(new this.wrtc.RTCIceCandidate(candidate));
676
+ }
677
+ catch {
678
+ this.stats.signaling_errors += 1;
679
+ }
680
+ }
681
+ }
682
+ onDataMessage(session, raw) {
683
+ this.stats.received_total += 1;
684
+ session.last_seen_at = now();
685
+ session.messages_seen += 1;
686
+ if (raw.length > this.maxMessageBytes) {
687
+ this.stats.dropped_oversized += 1;
688
+ return;
689
+ }
690
+ const decoded = this.envelopeCodec.decode(raw);
691
+ if (!decoded) {
692
+ this.stats.dropped_decode_failed += 1;
693
+ this.stats.dropped_malformed += 1;
694
+ return;
695
+ }
696
+ const validated = (0, messageEnvelope_1.validateNetworkMessageEnvelope)(decoded.envelope, {
697
+ max_future_drift_ms: this.maxFutureDriftMs,
698
+ max_past_drift_ms: this.maxPastDriftMs,
699
+ });
700
+ if (!validated.ok || !validated.envelope) {
701
+ if (validated.reason === "timestamp_future_drift") {
702
+ this.stats.dropped_timestamp_future_drift += 1;
703
+ }
704
+ else if (validated.reason === "timestamp_past_drift") {
705
+ this.stats.dropped_timestamp_past_drift += 1;
706
+ }
707
+ else {
708
+ this.stats.dropped_malformed += 1;
709
+ }
710
+ return;
711
+ }
712
+ this.stats.received_validated += 1;
713
+ const envelope = validated.envelope;
714
+ if (envelope.source_peer_id === this.peerId) {
715
+ this.stats.dropped_self += 1;
716
+ return;
717
+ }
718
+ if (!envelope.topic.startsWith(`${this.namespace}:`)) {
719
+ this.stats.dropped_namespace_mismatch += 1;
720
+ return;
721
+ }
722
+ const handlers = this.handlers.get(envelope.topic);
723
+ if (!handlers || handlers.size === 0)
724
+ return;
725
+ const logicalTopic = envelope.topic.slice(`${this.namespace}:`.length);
726
+ try {
727
+ const payload = this.topicCodec.decode(logicalTopic, envelope.payload);
728
+ for (const handler of handlers) {
729
+ try {
730
+ handler(payload);
731
+ this.stats.delivered_total += 1;
732
+ }
733
+ catch {
734
+ this.stats.dropped_handler_error += 1;
735
+ }
736
+ }
737
+ }
738
+ catch {
739
+ this.stats.dropped_topic_decode_error += 1;
740
+ }
741
+ }
742
+ cleanupProcessedSignalIds() {
743
+ const threshold = now() - this.maxPastDriftMs;
744
+ for (const [id, ts] of this.processedSignalIds.entries()) {
745
+ if (ts < threshold) {
746
+ this.processedSignalIds.delete(id);
747
+ }
748
+ }
749
+ }
750
+ isInitiatorFor(peerId) {
751
+ return this.peerId < peerId;
752
+ }
753
+ closePeerSession(session) {
754
+ try {
755
+ session.channel?.close?.();
756
+ }
757
+ catch {
758
+ this.stats.stop_errors += 1;
759
+ }
760
+ try {
761
+ session.connection?.close?.();
762
+ }
763
+ catch {
764
+ this.stats.stop_errors += 1;
765
+ }
766
+ session.channel = null;
767
+ session.connection = null;
768
+ session.datachannel_state = "closed";
769
+ session.connection_state = "closed";
770
+ session.remote_description_set = false;
771
+ session.pending_ice = [];
772
+ session.seen_ice_keys.clear();
773
+ }
774
+ async sendSignal(toPeerId, type, payload) {
775
+ this.signalingMessagesSentTotal += 1;
776
+ await this.postJson("/signal", {
777
+ id: (0, crypto_1.randomUUID)(),
778
+ room: this.room,
779
+ from_peer_id: this.peerId,
780
+ to_peer_id: toPeerId,
781
+ type,
782
+ payload,
783
+ });
784
+ }
785
+ async postJson(path, body) {
786
+ return this.requestJson("POST", path, body);
787
+ }
788
+ async getJson(path) {
789
+ return this.requestJson("GET", path);
790
+ }
791
+ async requestJson(method, path, body) {
792
+ const errors = [];
793
+ for (let offset = 0; offset < this.signalingEndpoints.length; offset += 1) {
794
+ const idx = (this.signalingIndex + offset) % this.signalingEndpoints.length;
795
+ const endpoint = this.signalingEndpoints[idx];
796
+ try {
797
+ const res = await fetch(`${endpoint}${path}`, {
798
+ method,
799
+ headers: { "content-type": "application/json" },
800
+ body: method === "POST" ? JSON.stringify(body ?? {}) : undefined,
801
+ });
802
+ if (!res.ok) {
803
+ throw new Error(`HTTP ${res.status}`);
804
+ }
805
+ this.signalingIndex = idx;
806
+ this.activeSignalingEndpoint = endpoint;
807
+ this.markSignalingConnected(endpoint);
808
+ return res.json().catch(() => ({}));
809
+ }
810
+ catch (error) {
811
+ const errMsg = error instanceof Error ? error.message : String(error);
812
+ errors.push(`${endpoint}(${errMsg})`);
813
+ this.markSignalingDisconnected(endpoint, errMsg);
814
+ }
815
+ }
816
+ this.stats.signaling_errors += 1;
817
+ throw new Error(`Signaling ${method} ${path} failed: ${errors.join("; ")}`);
818
+ }
819
+ markSignalingConnected(endpoint) {
820
+ if (this.signalingConnectivity.get(endpoint)) {
821
+ return;
822
+ }
823
+ this.signalingConnectivity.set(endpoint, true);
824
+ this.recordDiscoveryEvent("signaling_connected", { endpoint });
825
+ }
826
+ markSignalingDisconnected(endpoint, detail) {
827
+ if (this.signalingConnectivity.get(endpoint) === false) {
828
+ return;
829
+ }
830
+ this.signalingConnectivity.set(endpoint, false);
831
+ this.recordDiscoveryEvent("signaling_disconnected", { endpoint, detail });
832
+ }
833
+ recordDiscoveryEvent(type, extra = {}) {
834
+ const event = {
835
+ id: (0, crypto_1.randomUUID)(),
836
+ type,
837
+ at: now(),
838
+ ...extra,
839
+ };
840
+ this.discoveryEvents.push(event);
841
+ if (this.discoveryEvents.length > this.discoveryEventsLimit) {
842
+ this.discoveryEvents.splice(0, this.discoveryEvents.length - this.discoveryEventsLimit);
843
+ }
844
+ this.discoveryEventsTotal += 1;
845
+ this.lastDiscoveryEventAt = event.at;
846
+ }
847
+ }
848
+ exports.WebRTCPreviewAdapter = WebRTCPreviewAdapter;