@pllla/openclaw-plugin 0.1.0

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 (283) hide show
  1. package/dist/accounts.js +66 -0
  2. package/dist/bridges.js +110 -0
  3. package/dist/index.js +215 -0
  4. package/dist/lane.js +158 -0
  5. package/dist/pairing.js +36 -0
  6. package/dist/stateStore.js +88 -0
  7. package/node_modules/@socket.io/component-emitter/LICENSE +24 -0
  8. package/node_modules/@socket.io/component-emitter/Readme.md +79 -0
  9. package/node_modules/@socket.io/component-emitter/lib/cjs/index.d.ts +179 -0
  10. package/node_modules/@socket.io/component-emitter/lib/cjs/index.js +176 -0
  11. package/node_modules/@socket.io/component-emitter/lib/cjs/package.json +4 -0
  12. package/node_modules/@socket.io/component-emitter/lib/esm/index.d.ts +179 -0
  13. package/node_modules/@socket.io/component-emitter/lib/esm/index.js +169 -0
  14. package/node_modules/@socket.io/component-emitter/lib/esm/package.json +4 -0
  15. package/node_modules/@socket.io/component-emitter/package.json +28 -0
  16. package/node_modules/debug/LICENSE +20 -0
  17. package/node_modules/debug/README.md +481 -0
  18. package/node_modules/debug/package.json +64 -0
  19. package/node_modules/debug/src/browser.js +272 -0
  20. package/node_modules/debug/src/common.js +292 -0
  21. package/node_modules/debug/src/index.js +10 -0
  22. package/node_modules/debug/src/node.js +263 -0
  23. package/node_modules/engine.io-client/LICENSE +22 -0
  24. package/node_modules/engine.io-client/README.md +331 -0
  25. package/node_modules/engine.io-client/build/cjs/browser-entrypoint.d.ts +3 -0
  26. package/node_modules/engine.io-client/build/cjs/browser-entrypoint.js +4 -0
  27. package/node_modules/engine.io-client/build/cjs/contrib/has-cors.d.ts +1 -0
  28. package/node_modules/engine.io-client/build/cjs/contrib/has-cors.js +14 -0
  29. package/node_modules/engine.io-client/build/cjs/contrib/parseqs.d.ts +15 -0
  30. package/node_modules/engine.io-client/build/cjs/contrib/parseqs.js +38 -0
  31. package/node_modules/engine.io-client/build/cjs/contrib/parseuri.d.ts +1 -0
  32. package/node_modules/engine.io-client/build/cjs/contrib/parseuri.js +67 -0
  33. package/node_modules/engine.io-client/build/cjs/globals.d.ts +4 -0
  34. package/node_modules/engine.io-client/build/cjs/globals.js +26 -0
  35. package/node_modules/engine.io-client/build/cjs/globals.node.d.ts +21 -0
  36. package/node_modules/engine.io-client/build/cjs/globals.node.js +97 -0
  37. package/node_modules/engine.io-client/build/cjs/index.d.ts +15 -0
  38. package/node_modules/engine.io-client/build/cjs/index.js +32 -0
  39. package/node_modules/engine.io-client/build/cjs/package.json +10 -0
  40. package/node_modules/engine.io-client/build/cjs/socket.d.ts +482 -0
  41. package/node_modules/engine.io-client/build/cjs/socket.js +766 -0
  42. package/node_modules/engine.io-client/build/cjs/transport.d.ts +106 -0
  43. package/node_modules/engine.io-client/build/cjs/transport.js +153 -0
  44. package/node_modules/engine.io-client/build/cjs/transports/index.d.ts +8 -0
  45. package/node_modules/engine.io-client/build/cjs/transports/index.js +11 -0
  46. package/node_modules/engine.io-client/build/cjs/transports/polling-fetch.d.ts +15 -0
  47. package/node_modules/engine.io-client/build/cjs/transports/polling-fetch.js +60 -0
  48. package/node_modules/engine.io-client/build/cjs/transports/polling-xhr.d.ts +108 -0
  49. package/node_modules/engine.io-client/build/cjs/transports/polling-xhr.js +285 -0
  50. package/node_modules/engine.io-client/build/cjs/transports/polling-xhr.node.d.ts +11 -0
  51. package/node_modules/engine.io-client/build/cjs/transports/polling-xhr.node.js +44 -0
  52. package/node_modules/engine.io-client/build/cjs/transports/polling.d.ts +52 -0
  53. package/node_modules/engine.io-client/build/cjs/transports/polling.js +165 -0
  54. package/node_modules/engine.io-client/build/cjs/transports/websocket.d.ts +36 -0
  55. package/node_modules/engine.io-client/build/cjs/transports/websocket.js +136 -0
  56. package/node_modules/engine.io-client/build/cjs/transports/websocket.node.d.ts +14 -0
  57. package/node_modules/engine.io-client/build/cjs/transports/websocket.node.js +68 -0
  58. package/node_modules/engine.io-client/build/cjs/transports/webtransport.d.ts +18 -0
  59. package/node_modules/engine.io-client/build/cjs/transports/webtransport.js +94 -0
  60. package/node_modules/engine.io-client/build/cjs/util.d.ts +7 -0
  61. package/node_modules/engine.io-client/build/cjs/util.js +65 -0
  62. package/node_modules/engine.io-client/build/esm/browser-entrypoint.d.ts +3 -0
  63. package/node_modules/engine.io-client/build/esm/browser-entrypoint.js +2 -0
  64. package/node_modules/engine.io-client/build/esm/contrib/has-cors.d.ts +1 -0
  65. package/node_modules/engine.io-client/build/esm/contrib/has-cors.js +11 -0
  66. package/node_modules/engine.io-client/build/esm/contrib/parseqs.d.ts +15 -0
  67. package/node_modules/engine.io-client/build/esm/contrib/parseqs.js +34 -0
  68. package/node_modules/engine.io-client/build/esm/contrib/parseuri.d.ts +1 -0
  69. package/node_modules/engine.io-client/build/esm/contrib/parseuri.js +64 -0
  70. package/node_modules/engine.io-client/build/esm/globals.d.ts +4 -0
  71. package/node_modules/engine.io-client/build/esm/globals.js +22 -0
  72. package/node_modules/engine.io-client/build/esm/globals.node.d.ts +21 -0
  73. package/node_modules/engine.io-client/build/esm/globals.node.js +91 -0
  74. package/node_modules/engine.io-client/build/esm/index.d.ts +15 -0
  75. package/node_modules/engine.io-client/build/esm/index.js +15 -0
  76. package/node_modules/engine.io-client/build/esm/package.json +10 -0
  77. package/node_modules/engine.io-client/build/esm/socket.d.ts +482 -0
  78. package/node_modules/engine.io-client/build/esm/socket.js +728 -0
  79. package/node_modules/engine.io-client/build/esm/transport.d.ts +106 -0
  80. package/node_modules/engine.io-client/build/esm/transport.js +142 -0
  81. package/node_modules/engine.io-client/build/esm/transports/index.d.ts +8 -0
  82. package/node_modules/engine.io-client/build/esm/transports/index.js +8 -0
  83. package/node_modules/engine.io-client/build/esm/transports/polling-fetch.d.ts +15 -0
  84. package/node_modules/engine.io-client/build/esm/transports/polling-fetch.js +56 -0
  85. package/node_modules/engine.io-client/build/esm/transports/polling-xhr.d.ts +108 -0
  86. package/node_modules/engine.io-client/build/esm/transports/polling-xhr.js +271 -0
  87. package/node_modules/engine.io-client/build/esm/transports/polling-xhr.node.d.ts +11 -0
  88. package/node_modules/engine.io-client/build/esm/transports/polling-xhr.node.js +17 -0
  89. package/node_modules/engine.io-client/build/esm/transports/polling.d.ts +52 -0
  90. package/node_modules/engine.io-client/build/esm/transports/polling.js +145 -0
  91. package/node_modules/engine.io-client/build/esm/transports/websocket.d.ts +36 -0
  92. package/node_modules/engine.io-client/build/esm/transports/websocket.js +125 -0
  93. package/node_modules/engine.io-client/build/esm/transports/websocket.node.d.ts +14 -0
  94. package/node_modules/engine.io-client/build/esm/transports/websocket.node.js +41 -0
  95. package/node_modules/engine.io-client/build/esm/transports/webtransport.d.ts +18 -0
  96. package/node_modules/engine.io-client/build/esm/transports/webtransport.js +80 -0
  97. package/node_modules/engine.io-client/build/esm/util.d.ts +7 -0
  98. package/node_modules/engine.io-client/build/esm/util.js +59 -0
  99. package/node_modules/engine.io-client/build/esm-debug/browser-entrypoint.d.ts +3 -0
  100. package/node_modules/engine.io-client/build/esm-debug/browser-entrypoint.js +2 -0
  101. package/node_modules/engine.io-client/build/esm-debug/contrib/has-cors.d.ts +1 -0
  102. package/node_modules/engine.io-client/build/esm-debug/contrib/has-cors.js +11 -0
  103. package/node_modules/engine.io-client/build/esm-debug/contrib/parseqs.d.ts +15 -0
  104. package/node_modules/engine.io-client/build/esm-debug/contrib/parseqs.js +34 -0
  105. package/node_modules/engine.io-client/build/esm-debug/contrib/parseuri.d.ts +1 -0
  106. package/node_modules/engine.io-client/build/esm-debug/contrib/parseuri.js +64 -0
  107. package/node_modules/engine.io-client/build/esm-debug/globals.d.ts +4 -0
  108. package/node_modules/engine.io-client/build/esm-debug/globals.js +22 -0
  109. package/node_modules/engine.io-client/build/esm-debug/globals.node.d.ts +21 -0
  110. package/node_modules/engine.io-client/build/esm-debug/globals.node.js +91 -0
  111. package/node_modules/engine.io-client/build/esm-debug/index.d.ts +15 -0
  112. package/node_modules/engine.io-client/build/esm-debug/index.js +15 -0
  113. package/node_modules/engine.io-client/build/esm-debug/package.json +10 -0
  114. package/node_modules/engine.io-client/build/esm-debug/socket.d.ts +482 -0
  115. package/node_modules/engine.io-client/build/esm-debug/socket.js +757 -0
  116. package/node_modules/engine.io-client/build/esm-debug/transport.d.ts +106 -0
  117. package/node_modules/engine.io-client/build/esm-debug/transport.js +145 -0
  118. package/node_modules/engine.io-client/build/esm-debug/transports/index.d.ts +8 -0
  119. package/node_modules/engine.io-client/build/esm-debug/transports/index.js +8 -0
  120. package/node_modules/engine.io-client/build/esm-debug/transports/polling-fetch.d.ts +15 -0
  121. package/node_modules/engine.io-client/build/esm-debug/transports/polling-fetch.js +56 -0
  122. package/node_modules/engine.io-client/build/esm-debug/transports/polling-xhr.d.ts +108 -0
  123. package/node_modules/engine.io-client/build/esm-debug/transports/polling-xhr.js +276 -0
  124. package/node_modules/engine.io-client/build/esm-debug/transports/polling-xhr.node.d.ts +11 -0
  125. package/node_modules/engine.io-client/build/esm-debug/transports/polling-xhr.node.js +17 -0
  126. package/node_modules/engine.io-client/build/esm-debug/transports/polling.d.ts +52 -0
  127. package/node_modules/engine.io-client/build/esm-debug/transports/polling.js +158 -0
  128. package/node_modules/engine.io-client/build/esm-debug/transports/websocket.d.ts +36 -0
  129. package/node_modules/engine.io-client/build/esm-debug/transports/websocket.js +128 -0
  130. package/node_modules/engine.io-client/build/esm-debug/transports/websocket.node.d.ts +14 -0
  131. package/node_modules/engine.io-client/build/esm-debug/transports/websocket.node.js +41 -0
  132. package/node_modules/engine.io-client/build/esm-debug/transports/webtransport.d.ts +18 -0
  133. package/node_modules/engine.io-client/build/esm-debug/transports/webtransport.js +87 -0
  134. package/node_modules/engine.io-client/build/esm-debug/util.d.ts +7 -0
  135. package/node_modules/engine.io-client/build/esm-debug/util.js +59 -0
  136. package/node_modules/engine.io-client/dist/engine.io.esm.min.js +7 -0
  137. package/node_modules/engine.io-client/dist/engine.io.esm.min.js.map +1 -0
  138. package/node_modules/engine.io-client/dist/engine.io.js +3094 -0
  139. package/node_modules/engine.io-client/dist/engine.io.js.map +1 -0
  140. package/node_modules/engine.io-client/dist/engine.io.min.js +7 -0
  141. package/node_modules/engine.io-client/dist/engine.io.min.js.map +1 -0
  142. package/node_modules/engine.io-client/package.json +95 -0
  143. package/node_modules/engine.io-parser/LICENSE +22 -0
  144. package/node_modules/engine.io-parser/Readme.md +158 -0
  145. package/node_modules/engine.io-parser/build/cjs/commons.d.ts +14 -0
  146. package/node_modules/engine.io-parser/build/cjs/commons.js +19 -0
  147. package/node_modules/engine.io-parser/build/cjs/contrib/base64-arraybuffer.d.ts +2 -0
  148. package/node_modules/engine.io-parser/build/cjs/contrib/base64-arraybuffer.js +48 -0
  149. package/node_modules/engine.io-parser/build/cjs/decodePacket.browser.d.ts +2 -0
  150. package/node_modules/engine.io-parser/build/cjs/decodePacket.browser.js +66 -0
  151. package/node_modules/engine.io-parser/build/cjs/decodePacket.d.ts +2 -0
  152. package/node_modules/engine.io-parser/build/cjs/decodePacket.js +59 -0
  153. package/node_modules/engine.io-parser/build/cjs/encodePacket.browser.d.ts +4 -0
  154. package/node_modules/engine.io-parser/build/cjs/encodePacket.browser.js +72 -0
  155. package/node_modules/engine.io-parser/build/cjs/encodePacket.d.ts +3 -0
  156. package/node_modules/engine.io-parser/build/cjs/encodePacket.js +38 -0
  157. package/node_modules/engine.io-parser/build/cjs/index.d.ts +9 -0
  158. package/node_modules/engine.io-parser/build/cjs/index.js +164 -0
  159. package/node_modules/engine.io-parser/build/cjs/package.json +8 -0
  160. package/node_modules/engine.io-parser/build/esm/commons.d.ts +14 -0
  161. package/node_modules/engine.io-parser/build/esm/commons.js +14 -0
  162. package/node_modules/engine.io-parser/build/esm/contrib/base64-arraybuffer.d.ts +2 -0
  163. package/node_modules/engine.io-parser/build/esm/contrib/base64-arraybuffer.js +43 -0
  164. package/node_modules/engine.io-parser/build/esm/decodePacket.browser.d.ts +2 -0
  165. package/node_modules/engine.io-parser/build/esm/decodePacket.browser.js +62 -0
  166. package/node_modules/engine.io-parser/build/esm/decodePacket.d.ts +2 -0
  167. package/node_modules/engine.io-parser/build/esm/decodePacket.js +55 -0
  168. package/node_modules/engine.io-parser/build/esm/encodePacket.browser.d.ts +4 -0
  169. package/node_modules/engine.io-parser/build/esm/encodePacket.browser.js +68 -0
  170. package/node_modules/engine.io-parser/build/esm/encodePacket.d.ts +3 -0
  171. package/node_modules/engine.io-parser/build/esm/encodePacket.js +33 -0
  172. package/node_modules/engine.io-parser/build/esm/index.d.ts +9 -0
  173. package/node_modules/engine.io-parser/build/esm/index.js +156 -0
  174. package/node_modules/engine.io-parser/build/esm/package.json +8 -0
  175. package/node_modules/engine.io-parser/package.json +46 -0
  176. package/node_modules/ms/index.js +162 -0
  177. package/node_modules/ms/license.md +21 -0
  178. package/node_modules/ms/package.json +38 -0
  179. package/node_modules/ms/readme.md +59 -0
  180. package/node_modules/socket.io-client/LICENSE +21 -0
  181. package/node_modules/socket.io-client/README.md +29 -0
  182. package/node_modules/socket.io-client/build/cjs/browser-entrypoint.d.ts +2 -0
  183. package/node_modules/socket.io-client/build/cjs/browser-entrypoint.js +4 -0
  184. package/node_modules/socket.io-client/build/cjs/contrib/backo2.d.ts +12 -0
  185. package/node_modules/socket.io-client/build/cjs/contrib/backo2.js +69 -0
  186. package/node_modules/socket.io-client/build/cjs/index.d.ts +29 -0
  187. package/node_modules/socket.io-client/build/cjs/index.js +76 -0
  188. package/node_modules/socket.io-client/build/cjs/manager.d.ts +295 -0
  189. package/node_modules/socket.io-client/build/cjs/manager.js +416 -0
  190. package/node_modules/socket.io-client/build/cjs/on.d.ts +2 -0
  191. package/node_modules/socket.io-client/build/cjs/on.js +9 -0
  192. package/node_modules/socket.io-client/build/cjs/socket.d.ts +593 -0
  193. package/node_modules/socket.io-client/build/cjs/socket.js +909 -0
  194. package/node_modules/socket.io-client/build/cjs/url.d.ts +33 -0
  195. package/node_modules/socket.io-client/build/cjs/url.js +69 -0
  196. package/node_modules/socket.io-client/build/esm/browser-entrypoint.d.ts +2 -0
  197. package/node_modules/socket.io-client/build/esm/browser-entrypoint.js +2 -0
  198. package/node_modules/socket.io-client/build/esm/contrib/backo2.d.ts +12 -0
  199. package/node_modules/socket.io-client/build/esm/contrib/backo2.js +66 -0
  200. package/node_modules/socket.io-client/build/esm/index.d.ts +29 -0
  201. package/node_modules/socket.io-client/build/esm/index.js +58 -0
  202. package/node_modules/socket.io-client/build/esm/manager.d.ts +295 -0
  203. package/node_modules/socket.io-client/build/esm/manager.js +367 -0
  204. package/node_modules/socket.io-client/build/esm/on.d.ts +2 -0
  205. package/node_modules/socket.io-client/build/esm/on.js +6 -0
  206. package/node_modules/socket.io-client/build/esm/package.json +5 -0
  207. package/node_modules/socket.io-client/build/esm/socket.d.ts +593 -0
  208. package/node_modules/socket.io-client/build/esm/socket.js +880 -0
  209. package/node_modules/socket.io-client/build/esm/url.d.ts +33 -0
  210. package/node_modules/socket.io-client/build/esm/url.js +59 -0
  211. package/node_modules/socket.io-client/build/esm-debug/browser-entrypoint.d.ts +2 -0
  212. package/node_modules/socket.io-client/build/esm-debug/browser-entrypoint.js +2 -0
  213. package/node_modules/socket.io-client/build/esm-debug/contrib/backo2.d.ts +12 -0
  214. package/node_modules/socket.io-client/build/esm-debug/contrib/backo2.js +66 -0
  215. package/node_modules/socket.io-client/build/esm-debug/index.d.ts +29 -0
  216. package/node_modules/socket.io-client/build/esm-debug/index.js +62 -0
  217. package/node_modules/socket.io-client/build/esm-debug/manager.d.ts +295 -0
  218. package/node_modules/socket.io-client/build/esm-debug/manager.js +386 -0
  219. package/node_modules/socket.io-client/build/esm-debug/on.d.ts +2 -0
  220. package/node_modules/socket.io-client/build/esm-debug/on.js +6 -0
  221. package/node_modules/socket.io-client/build/esm-debug/package.json +5 -0
  222. package/node_modules/socket.io-client/build/esm-debug/socket.d.ts +593 -0
  223. package/node_modules/socket.io-client/build/esm-debug/socket.js +902 -0
  224. package/node_modules/socket.io-client/build/esm-debug/url.d.ts +33 -0
  225. package/node_modules/socket.io-client/build/esm-debug/url.js +63 -0
  226. package/node_modules/socket.io-client/dist/socket.io.esm.min.js +7 -0
  227. package/node_modules/socket.io-client/dist/socket.io.esm.min.js.map +1 -0
  228. package/node_modules/socket.io-client/dist/socket.io.js +4955 -0
  229. package/node_modules/socket.io-client/dist/socket.io.js.map +1 -0
  230. package/node_modules/socket.io-client/dist/socket.io.min.js +7 -0
  231. package/node_modules/socket.io-client/dist/socket.io.min.js.map +1 -0
  232. package/node_modules/socket.io-client/dist/socket.io.msgpack.min.js +7 -0
  233. package/node_modules/socket.io-client/dist/socket.io.msgpack.min.js.map +1 -0
  234. package/node_modules/socket.io-client/package.json +101 -0
  235. package/node_modules/socket.io-parser/LICENSE +20 -0
  236. package/node_modules/socket.io-parser/Readme.md +81 -0
  237. package/node_modules/socket.io-parser/build/cjs/binary.d.ts +20 -0
  238. package/node_modules/socket.io-parser/build/cjs/binary.js +90 -0
  239. package/node_modules/socket.io-parser/build/cjs/index.d.ts +101 -0
  240. package/node_modules/socket.io-parser/build/cjs/index.js +366 -0
  241. package/node_modules/socket.io-parser/build/cjs/is-binary.d.ts +7 -0
  242. package/node_modules/socket.io-parser/build/cjs/is-binary.js +54 -0
  243. package/node_modules/socket.io-parser/build/cjs/package.json +3 -0
  244. package/node_modules/socket.io-parser/build/esm/binary.d.ts +20 -0
  245. package/node_modules/socket.io-parser/build/esm/binary.js +86 -0
  246. package/node_modules/socket.io-parser/build/esm/index.d.ts +101 -0
  247. package/node_modules/socket.io-parser/build/esm/index.js +352 -0
  248. package/node_modules/socket.io-parser/build/esm/is-binary.d.ts +7 -0
  249. package/node_modules/socket.io-parser/build/esm/is-binary.js +50 -0
  250. package/node_modules/socket.io-parser/build/esm/package.json +3 -0
  251. package/node_modules/socket.io-parser/build/esm-debug/binary.d.ts +20 -0
  252. package/node_modules/socket.io-parser/build/esm-debug/binary.js +86 -0
  253. package/node_modules/socket.io-parser/build/esm-debug/index.d.ts +101 -0
  254. package/node_modules/socket.io-parser/build/esm-debug/index.js +357 -0
  255. package/node_modules/socket.io-parser/build/esm-debug/is-binary.d.ts +7 -0
  256. package/node_modules/socket.io-parser/build/esm-debug/is-binary.js +50 -0
  257. package/node_modules/socket.io-parser/build/esm-debug/package.json +3 -0
  258. package/node_modules/socket.io-parser/package.json +44 -0
  259. package/node_modules/ws/LICENSE +20 -0
  260. package/node_modules/ws/README.md +548 -0
  261. package/node_modules/ws/browser.js +8 -0
  262. package/node_modules/ws/index.js +22 -0
  263. package/node_modules/ws/lib/buffer-util.js +131 -0
  264. package/node_modules/ws/lib/constants.js +19 -0
  265. package/node_modules/ws/lib/event-target.js +292 -0
  266. package/node_modules/ws/lib/extension.js +203 -0
  267. package/node_modules/ws/lib/limiter.js +55 -0
  268. package/node_modules/ws/lib/permessage-deflate.js +530 -0
  269. package/node_modules/ws/lib/receiver.js +743 -0
  270. package/node_modules/ws/lib/sender.js +607 -0
  271. package/node_modules/ws/lib/stream.js +161 -0
  272. package/node_modules/ws/lib/subprotocol.js +62 -0
  273. package/node_modules/ws/lib/validation.js +152 -0
  274. package/node_modules/ws/lib/websocket-server.js +562 -0
  275. package/node_modules/ws/lib/websocket.js +1407 -0
  276. package/node_modules/ws/package.json +74 -0
  277. package/node_modules/ws/wrapper.mjs +21 -0
  278. package/node_modules/xmlhttprequest-ssl/LICENSE +22 -0
  279. package/node_modules/xmlhttprequest-ssl/README.md +67 -0
  280. package/node_modules/xmlhttprequest-ssl/lib/XMLHttpRequest.js +689 -0
  281. package/node_modules/xmlhttprequest-ssl/package.json +40 -0
  282. package/openclaw.plugin.json +60 -0
  283. package/package.json +48 -0
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Account config — `channels.pllla` normalized into an account map.
3
+ *
4
+ * One pairing = one rt- key = one aiUserId = one socket lane. Attaching N
5
+ * PLLLA agents to a single gateway therefore means N accounts, each with its
6
+ * own lane and its own stored state (docs/agent/EXTERNAL_RUNTIME.md §6.3).
7
+ *
8
+ * `accounts` is the source of truth. The flat Phase 1 fields
9
+ * (`channels.pllla.{serverOrigin,pairingToken,runtimeLabel}`) are read as the
10
+ * `default` account so existing installs keep working unchanged.
11
+ */
12
+ export const DEFAULT_ACCOUNT_ID = "default";
13
+ // accountId 는 상태 파일 이름(`accounts/<accountId>.json`)이 된다 — 경로 구분자·
14
+ // 선행 점을 막아 상태 디렉토리 밖으로 새지 않게 한다. 커넥터 기본값
15
+ // `agent-` + 8 hex 와 `default` 가 통과해야 한다.
16
+ const ACCOUNT_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
17
+ export function isValidAccountId(accountId) {
18
+ return ACCOUNT_ID_PATTERN.test(accountId);
19
+ }
20
+ export function assertValidAccountId(accountId) {
21
+ if (!isValidAccountId(accountId)) {
22
+ throw new Error(`Invalid PLLLA account id "${accountId}" — use letters, digits, ".", "_" or "-" (max 64 chars, must start with a letter or digit).`);
23
+ }
24
+ }
25
+ function normalizeOrigin(value) {
26
+ return typeof value === "string" ? value.trim().replace(/\/+$/, "") : "";
27
+ }
28
+ function normalizeOptional(value) {
29
+ if (typeof value !== "string")
30
+ return undefined;
31
+ const trimmed = value.trim();
32
+ return trimmed ? trimmed : undefined;
33
+ }
34
+ function toAccount(raw) {
35
+ return {
36
+ serverOrigin: normalizeOrigin(raw?.serverOrigin),
37
+ pairingToken: normalizeOptional(raw?.pairingToken),
38
+ runtimeLabel: normalizeOptional(raw?.runtimeLabel),
39
+ };
40
+ }
41
+ /**
42
+ * Every configured account, keyed by accountId. Entries are kept even when
43
+ * `serverOrigin` is missing or the id is unsafe — the caller decides what to
44
+ * report, and silently dropping an account is exactly the defect §6.3 fixes.
45
+ */
46
+ export function parseAccounts(config) {
47
+ const accounts = new Map();
48
+ const rawAccounts = config?.accounts;
49
+ if (rawAccounts && typeof rawAccounts === "object") {
50
+ for (const [accountId, raw] of Object.entries(rawAccounts)) {
51
+ accounts.set(accountId, toAccount(raw));
52
+ }
53
+ }
54
+ // Phase 1 평면 설정 → `default`. 맵에 `default` 가 이미 있으면 맵이 정본이라
55
+ // 평면 필드는 무시한다(둘을 합치면 어느 토큰이 소비될지 예측할 수 없다).
56
+ const flat = toAccount(config);
57
+ if (!accounts.has(DEFAULT_ACCOUNT_ID) &&
58
+ (flat.serverOrigin || flat.pairingToken)) {
59
+ accounts.set(DEFAULT_ACCOUNT_ID, flat);
60
+ }
61
+ return accounts;
62
+ }
63
+ /** `accounts` keys, plus `default` when the flat Phase 1 fields are set. */
64
+ export function listAccountIds(config) {
65
+ return [...parseAccounts(config).keys()];
66
+ }
@@ -0,0 +1,110 @@
1
+ /**
2
+ * PLLLA bridges — one lane per configured account (accounts.ts).
3
+ *
4
+ * Boot sequence (gateway start), per account:
5
+ * 1. Read channel config — `channels.pllla.accounts.<id>`, or the flat
6
+ * Phase 1 fields as the `default` account.
7
+ * 2. Stored bridge state for the account → reuse it. None, but a
8
+ * `pairingToken` → consume it (pairing.ts) and persist (stateStore.ts).
9
+ * Neither → skip that account and say so.
10
+ * 3. Open one PLLLA lane per account (lane.ts) — socket contract straight
11
+ * from the pairing response — and route each task into an OpenClaw
12
+ * agent turn under `pllla:<accountId>:<chatId>`.
13
+ *
14
+ * One account's failure never blocks another's lane: each account is
15
+ * resolved inside its own try/catch and only ever touches its own state
16
+ * file (docs/agent/EXTERNAL_RUNTIME.md §6.3).
17
+ *
18
+ * SDK-free on purpose so it stays unit-testable; index.ts adapts it to the
19
+ * OpenClaw plugin SDK.
20
+ */
21
+ import { consumePairingToken } from "./pairing.js";
22
+ import { PlllaLane } from "./lane.js";
23
+ import { loadBridgeState, saveBridgeState, } from "./stateStore.js";
24
+ import { parseAccounts, } from "./accounts.js";
25
+ export function buildPersonaPrompt(identity) {
26
+ return [
27
+ identity.name ? `You are ${identity.name}, a PLLLA agent.` : "",
28
+ identity.bio,
29
+ identity.systemPrompt,
30
+ ]
31
+ .filter(Boolean)
32
+ .join("\n\n");
33
+ }
34
+ /**
35
+ * Stored state wins; a pairing token is consumed only when there is no usable
36
+ * state for this account. Returns null (after logging why) when the account
37
+ * cannot be opened — never throws for "not configured", only for real errors.
38
+ */
39
+ async function resolveAccountState(accountId, account, log) {
40
+ const stored = loadBridgeState(accountId);
41
+ if (stored && stored.serverOrigin === account.serverOrigin) {
42
+ if (account.pairingToken) {
43
+ log("Stored pairing found — ignoring channels.pllla pairingToken.");
44
+ }
45
+ return stored;
46
+ }
47
+ if (!account.serverOrigin) {
48
+ log("Skipped — serverOrigin is missing.");
49
+ return null;
50
+ }
51
+ if (!account.pairingToken) {
52
+ log(stored
53
+ ? `Skipped — stored pairing is for ${stored.serverOrigin} but config says ${account.serverOrigin}; run \`npx pllla-connect …\` for a fresh token.`
54
+ : "Skipped — no stored pairing and no pairingToken; run `npx pllla-connect …` for a fresh token.");
55
+ return null;
56
+ }
57
+ log("Consuming PLLLA pairing token…");
58
+ const pair = await consumePairingToken({
59
+ serverOrigin: account.serverOrigin,
60
+ pairingToken: account.pairingToken,
61
+ runtimeLabel: account.runtimeLabel || "OpenClaw",
62
+ });
63
+ const state = {
64
+ serverOrigin: account.serverOrigin,
65
+ pair,
66
+ savedAt: new Date().toISOString(),
67
+ };
68
+ saveBridgeState(accountId, state);
69
+ log(`Paired as @${pair.aiUser.username} (${pair.externalRuntime?.label ?? "external runtime"}).`);
70
+ return state;
71
+ }
72
+ export async function createPlllaBridges(params) {
73
+ const { config, runTurn, log } = params;
74
+ const accounts = parseAccounts(config);
75
+ const lanes = new Map();
76
+ if (accounts.size === 0) {
77
+ log("No PLLLA accounts configured — set channels.pllla.accounts.<id>.{serverOrigin,pairingToken} (`npx pllla-connect …` does this).");
78
+ return { lanes, stop: () => undefined };
79
+ }
80
+ await Promise.all([...accounts].map(async ([accountId, account]) => {
81
+ const accountLog = (line) => log(`[${accountId}] ${line}`);
82
+ // 계정별 격리 — 한 계정의 페어링 실패·상태 오류가 다른 계정의 레인을
83
+ // 막지 않는다. 각 계정은 자기 상태 파일만 읽고 쓴다.
84
+ try {
85
+ const state = await resolveAccountState(accountId, account, accountLog);
86
+ if (!state)
87
+ return;
88
+ const lane = new PlllaLane({
89
+ accountId,
90
+ socketContract: state.pair.socket,
91
+ aiUserId: state.pair.aiUser.id,
92
+ systemPrompt: buildPersonaPrompt(state.pair.identity),
93
+ runTurn,
94
+ log: accountLog,
95
+ });
96
+ lane.start();
97
+ lanes.set(accountId, lane);
98
+ }
99
+ catch (error) {
100
+ accountLog(`Failed to open lane: ${error instanceof Error ? error.message : String(error)}`);
101
+ }
102
+ }));
103
+ return {
104
+ lanes,
105
+ stop: () => {
106
+ for (const lane of lanes.values())
107
+ lane.stop();
108
+ },
109
+ };
110
+ }
package/dist/index.js ADDED
@@ -0,0 +1,215 @@
1
+ /**
2
+ * OpenClaw channel plugin entry — PLLLA as an OpenClaw channel.
3
+ *
4
+ * Pinned to the real plugin SDK (OpenClaw 2026.8.2, read from the installed
5
+ * `dist/*.d.ts` on 2026-09-02 — docs/agent/EXTERNAL_RUNTIME.md §6):
6
+ *
7
+ * - `defineChannelPluginEntry` (plugin-sdk/core) is the canonical entry.
8
+ * OpenClaw calls its `register(api)` at gateway start.
9
+ * - The channel itself is `createChatChannelPlugin` around a
10
+ * `createChannelPluginBase` — account listing/resolution comes from
11
+ * accounts.ts, so `openclaw config set channels.pllla.accounts.<id>.…`
12
+ * validates against a registered channel id (the "unknown channel id:
13
+ * pllla" failure seen on a real machine was exactly this registration
14
+ * missing).
15
+ * - The long-lived socket lanes live in a plugin **service**
16
+ * (`api.registerService`) — the SDK's home for background ingress — not
17
+ * in a request handler.
18
+ * - Each PLLLA task becomes one agent turn through
19
+ * `api.runtime.agent.runCommandFromIngress` ("admit a turn for this exact
20
+ * trusted channel plugin"), which returns the reply `payloads` directly —
21
+ * no outbound correlation needed.
22
+ *
23
+ * Persona (from the pairing exchange) is prepended once per OpenClaw session
24
+ * as a bracketed preamble — interim. The proper home is a per-account OpenClaw
25
+ * agent identity; tracked as follow-up.
26
+ */
27
+ import { defineChannelPluginEntry, } from "openclaw/plugin-sdk/core";
28
+ import { createChannelPluginBase, createChatChannelPlugin, } from "openclaw/plugin-sdk/channel-core";
29
+ import { DEFAULT_ACCOUNT_ID, listAccountIds, parseAccounts, } from "./accounts.js";
30
+ import { createPlllaBridges } from "./bridges.js";
31
+ export { listAccountIds, parseAccounts } from "./accounts.js";
32
+ export { createPlllaBridges } from "./bridges.js";
33
+ const CHANNEL_ID = "pllla";
34
+ const SERVICE_ID = "pllla-lanes";
35
+ function readChannelConfig(cfg) {
36
+ const section = cfg.channels?.[CHANNEL_ID];
37
+ return section && typeof section === "object"
38
+ ? section
39
+ : undefined;
40
+ }
41
+ function resolveAccount(cfg, accountId) {
42
+ const id = accountId || DEFAULT_ACCOUNT_ID;
43
+ const account = parseAccounts(readChannelConfig(cfg)).get(id);
44
+ return {
45
+ accountId: id,
46
+ serverOrigin: account?.serverOrigin ?? "",
47
+ pairingToken: account?.pairingToken,
48
+ runtimeLabel: account?.runtimeLabel,
49
+ };
50
+ }
51
+ /** `pllla:<accountId>:<chatId>` → accountId (lane.ts buildSessionKey). */
52
+ export function accountIdFromSessionKey(sessionKey) {
53
+ const parts = sessionKey.split(":");
54
+ return parts.length >= 3 && parts[0] === CHANNEL_ID
55
+ ? parts[1]
56
+ : DEFAULT_ACCOUNT_ID;
57
+ }
58
+ /** The message OpenClaw should see for this task — last user turn. */
59
+ export function pickTurnMessage(input) {
60
+ for (let index = input.messages.length - 1; index >= 0; index -= 1) {
61
+ const message = input.messages[index];
62
+ if (message.role === "user" && message.content.trim()) {
63
+ return message.content;
64
+ }
65
+ }
66
+ return input.messages.map((message) => message.content).join("\n\n");
67
+ }
68
+ /**
69
+ * Persona goes in front of the first turn of each OpenClaw session only —
70
+ * OpenClaw keeps the session transcript itself, so repeating it every turn
71
+ * would just bloat context.
72
+ */
73
+ export function buildTurnMessage(input, seenSessions) {
74
+ const message = pickTurnMessage(input);
75
+ if (seenSessions.has(input.sessionId) || !input.systemPrompt.trim()) {
76
+ return message;
77
+ }
78
+ seenSessions.add(input.sessionId);
79
+ return `[PLLLA agent persona — follow throughout this conversation]\n${input.systemPrompt.trim()}\n[end persona]\n\n${message}`;
80
+ }
81
+ /** Join reply payloads into the single text PLLLA's `agent:response` carries. */
82
+ export function joinPayloadText(payloads) {
83
+ return payloads
84
+ .map((payload) => (payload.text ?? "").trim())
85
+ .filter(Boolean)
86
+ .join("\n\n");
87
+ }
88
+ function createRunTurn(api) {
89
+ const seenSessions = new Set();
90
+ const runtimeEnv = {
91
+ log: (...args) => api.logger.info(...args),
92
+ error: (...args) => api.logger.error(...args),
93
+ // 플러그인 안에서 프로세스를 끝내지 않는다 — 게이트웨이의 것이다.
94
+ exit: () => undefined,
95
+ };
96
+ return async (input) => {
97
+ const result = await api.runtime.agent.runCommandFromIngress({
98
+ message: buildTurnMessage(input, seenSessions),
99
+ sessionKey: input.sessionId,
100
+ channel: CHANNEL_ID,
101
+ messageChannel: CHANNEL_ID,
102
+ accountId: accountIdFromSessionKey(input.sessionId),
103
+ // 응답은 우리가 PLLLA 소켓으로 나른다 — OpenClaw 배달 경로는 끈다.
104
+ deliver: false,
105
+ json: true,
106
+ allowModelOverride: false,
107
+ }, runtimeEnv);
108
+ return joinPayloadText(result.payloads ?? []);
109
+ };
110
+ }
111
+ const channelBase = createChannelPluginBase({
112
+ id: CHANNEL_ID,
113
+ meta: {
114
+ label: "PLLLA",
115
+ selectionLabel: "PLLLA",
116
+ docsPath: "/channels/pllla",
117
+ blurb: "Reply as your PLLLA agent — paired with `npx pllla-connect`.",
118
+ },
119
+ config: {
120
+ listAccountIds: (cfg) => listAccountIds(readChannelConfig(cfg)),
121
+ resolveAccount,
122
+ inspectAccount: (cfg, accountId) => {
123
+ const account = resolveAccount(cfg, accountId);
124
+ return {
125
+ accountId: account.accountId,
126
+ enabled: true,
127
+ configured: Boolean(account.serverOrigin),
128
+ };
129
+ },
130
+ },
131
+ setup: {
132
+ // `openclaw config set channels.pllla.accounts.<id>.<key>` 가 쓰는 병합 —
133
+ // 그 계정의 키만 덮고 다른 계정은 건드리지 않는다 (§6.3).
134
+ applyAccountConfig: ({ cfg, accountId, input }) => {
135
+ const id = accountId || DEFAULT_ACCOUNT_ID;
136
+ const section = readChannelConfig(cfg) ?? {};
137
+ const accounts = { ...(section.accounts ?? {}) };
138
+ accounts[id] = { ...(accounts[id] ?? {}), ...input };
139
+ return {
140
+ ...cfg,
141
+ channels: {
142
+ ...(cfg.channels ?? {}),
143
+ [CHANNEL_ID]: { ...section, accounts },
144
+ },
145
+ };
146
+ },
147
+ },
148
+ });
149
+ let bridges = null;
150
+ /**
151
+ * OpenClaw's own messaging tool ("send this to pllla") lands here. The lane
152
+ * for the account forwards it as `agent:send_message` on the PLLLA socket.
153
+ */
154
+ function sendTextToPllla(ctx) {
155
+ const accountId = ctx.accountId || DEFAULT_ACCOUNT_ID;
156
+ const chatId = ctx.to ?? ctx.target;
157
+ const text = ctx.text ?? "";
158
+ const lane = bridges?.lanes.get(accountId);
159
+ if (!lane || !chatId || !text)
160
+ return {};
161
+ const sent = lane.sendChatMessage(String(chatId), text);
162
+ return sent ? { messageId: `pllla:${chatId}:${Date.now()}` } : {};
163
+ }
164
+ const plllaChannelPlugin = createChatChannelPlugin({
165
+ base: channelBase,
166
+ security: {
167
+ dm: {
168
+ channelKey: CHANNEL_ID,
169
+ // PLLLA 쪽 권한(친구=대화 자격·그랜트=작업 자격)이 정본이다 — 채널 자체는
170
+ // 페어링된 계정 소켓으로만 말이 들어오므로 allowlist 로 잠근다.
171
+ resolvePolicy: () => "allowlist",
172
+ resolveAllowFrom: () => [],
173
+ defaultPolicy: "allowlist",
174
+ },
175
+ },
176
+ threading: { topLevelReplyToMode: "reply" },
177
+ outbound: {
178
+ attachedResults: {
179
+ channel: CHANNEL_ID,
180
+ sendText: sendTextToPllla,
181
+ },
182
+ },
183
+ });
184
+ function registerFull(api) {
185
+ const log = (line) => api.logger.info(`[pllla] ${line}`);
186
+ const runTurn = createRunTurn(api);
187
+ api.registerService({
188
+ id: SERVICE_ID,
189
+ start: async (ctx) => {
190
+ // 상태 파일은 게이트웨이의 state dir 아래에 둔다 (stateStore.ts 는
191
+ // OPENCLAW_STATE_DIR 을 읽는다 — 게이트웨이가 안 넘겨준 경우만 채운다).
192
+ if (!process.env.OPENCLAW_STATE_DIR && ctx.stateDir) {
193
+ process.env.OPENCLAW_STATE_DIR = ctx.stateDir;
194
+ }
195
+ bridges = await createPlllaBridges({
196
+ config: readChannelConfig(ctx.config),
197
+ runTurn,
198
+ log,
199
+ });
200
+ log(`${bridges.lanes.size} PLLLA lane(s) open.`);
201
+ },
202
+ stop: async () => {
203
+ bridges?.stop();
204
+ bridges = null;
205
+ },
206
+ });
207
+ }
208
+ const entry = defineChannelPluginEntry({
209
+ id: CHANNEL_ID,
210
+ name: "PLLLA",
211
+ description: "Reply as a PLLLA agent — one socket lane per paired account, persona from the pairing exchange.",
212
+ plugin: plllaChannelPlugin,
213
+ registerFull,
214
+ });
215
+ export default entry;
package/dist/lane.js ADDED
@@ -0,0 +1,158 @@
1
+ /**
2
+ * PLLLA agent lane — the socket side of the bridge. Follows the
3
+ * self-describing contract from the pairing exchange: connect with the given
4
+ * auth/transports, handle the task event, reply on the response event.
5
+ *
6
+ * One lane per account: the rt- key in `socketContract.auth` and `aiUserId`
7
+ * are that account's identity, and every session key carries the accountId
8
+ * so two agents on one gateway never share a runtime session.
9
+ *
10
+ * The lane knows nothing about OpenClaw: it takes an AgentTurnRunner and
11
+ * reports each task's outcome. The OpenClaw-facing wiring lives in index.ts.
12
+ */
13
+ import { io } from "socket.io-client";
14
+ /**
15
+ * `pllla:<accountId>:<chatId>` — the same chatId under two accounts is two
16
+ * sessions (docs/agent/EXTERNAL_RUNTIME.md §6.3).
17
+ */
18
+ export function buildSessionKey(accountId, chatId) {
19
+ return `pllla:${accountId}:${chatId}`;
20
+ }
21
+ export class PlllaLane {
22
+ options;
23
+ socket = null;
24
+ // sessionId → 마지막 턴의 꼬리. OpenClaw 는 한 세션에 동시 턴을 허용하지 않아
25
+ // ("Session … already has an active turn claim"), 첫 인사 턴과 첫 태스크가 같은
26
+ // 채팅을 물면 충돌한다. 같은 세션의 턴을 직렬화하고, 다른 세션은 병렬로 둔다.
27
+ sessionTails = new Map();
28
+ constructor(options) {
29
+ this.options = options;
30
+ }
31
+ get accountId() {
32
+ return this.options.accountId;
33
+ }
34
+ /**
35
+ * Run one agent turn, serialized per sessionId. Same-session turns queue
36
+ * (greeting → first task → next message); different sessions run in parallel.
37
+ * A failed turn never wedges the queue — the next turn still runs.
38
+ */
39
+ async runTurnSerialized(input) {
40
+ const key = input.sessionId;
41
+ const prior = this.sessionTails.get(key) ?? Promise.resolve();
42
+ let release;
43
+ const gate = new Promise((resolve) => {
44
+ release = resolve;
45
+ });
46
+ const tail = prior.then(() => gate);
47
+ this.sessionTails.set(key, tail);
48
+ await prior.catch(() => undefined);
49
+ try {
50
+ return await this.options.runTurn(input);
51
+ }
52
+ finally {
53
+ release();
54
+ // 우리가 아직 꼬리면 정리한다 — 뒤에 다른 턴이 붙었으면 그대로 둔다.
55
+ if (this.sessionTails.get(key) === tail)
56
+ this.sessionTails.delete(key);
57
+ }
58
+ }
59
+ start() {
60
+ const { socketContract } = this.options;
61
+ const socket = io(socketContract.url, {
62
+ path: socketContract.path,
63
+ transports: socketContract.transports,
64
+ auth: socketContract.auth,
65
+ reconnection: true,
66
+ });
67
+ this.socket = socket;
68
+ socket.on("connect", () => this.options.log("PLLLA lane connected."));
69
+ socket.on("disconnect", (reason) => this.options.log(`PLLLA lane disconnected: ${reason}`));
70
+ const readyEvent = socketContract.events.runtimeReady;
71
+ if (readyEvent) {
72
+ socket.on(readyEvent, (payload) => {
73
+ void this.handleRuntimeReady(payload);
74
+ });
75
+ }
76
+ socket.on(socketContract.events.task, (payload) => {
77
+ void this.handleTask(payload?.task);
78
+ });
79
+ }
80
+ stop() {
81
+ this.socket?.disconnect();
82
+ this.socket = null;
83
+ }
84
+ /**
85
+ * Outbound from OpenClaw's side (its messaging tool) into a PLLLA chat —
86
+ * `agent:send_message` on this account's lane. Returns false when the lane
87
+ * is not connected or the contract has no sendMessage event.
88
+ */
89
+ sendChatMessage(chatId, content) {
90
+ const event = this.options.socketContract.events.sendMessage;
91
+ if (!event || !this.socket?.connected)
92
+ return false;
93
+ this.socket.emit(event, {
94
+ chatId,
95
+ aiUserId: this.options.aiUserId,
96
+ content,
97
+ });
98
+ return true;
99
+ }
100
+ emitResponse(taskId, content, failure) {
101
+ this.socket?.emit(this.options.socketContract.events.response, {
102
+ taskId,
103
+ aiUserId: this.options.aiUserId,
104
+ content,
105
+ ...(failure ? { failure } : {}),
106
+ });
107
+ }
108
+ async handleRuntimeReady(payload) {
109
+ if (!payload?.greeting?.shouldSend)
110
+ return;
111
+ const sendMessageEvent = this.options.socketContract.events.sendMessage;
112
+ const chatId = payload.greeting.chatId;
113
+ if (!sendMessageEvent || !chatId)
114
+ return;
115
+ try {
116
+ const greeting = this.options.firstGreeting ||
117
+ (await this.runTurnSerialized({
118
+ sessionId: buildSessionKey(this.options.accountId, chatId),
119
+ systemPrompt: this.options.systemPrompt,
120
+ messages: [
121
+ {
122
+ role: "user",
123
+ content: "You just came online for your owner for the first time. Send a short first greeting in your own voice.",
124
+ },
125
+ ],
126
+ }));
127
+ this.socket?.emit(sendMessageEvent, {
128
+ chatId,
129
+ aiUserId: this.options.aiUserId,
130
+ content: greeting,
131
+ });
132
+ }
133
+ catch (error) {
134
+ this.options.log(`First greeting failed: ${error instanceof Error ? error.message : String(error)}`);
135
+ }
136
+ }
137
+ async handleTask(task) {
138
+ if (!task?.id)
139
+ return;
140
+ try {
141
+ const content = await this.runTurnSerialized({
142
+ sessionId: buildSessionKey(this.options.accountId, task.chatId),
143
+ // 서버 프롬프트(대화 맥락) 위에 페어링에서 받은 페르소나를 얹는다 —
144
+ // PLLLA 에이전트의 정체성이 런타임까지 그대로 이식되는 지점.
145
+ systemPrompt: [this.options.systemPrompt, task.systemPrompt]
146
+ .filter(Boolean)
147
+ .join("\n\n"),
148
+ messages: Array.isArray(task.messages) ? task.messages : [],
149
+ });
150
+ this.emitResponse(task.id, content);
151
+ }
152
+ catch (error) {
153
+ const message = error instanceof Error ? error.message : "Agent turn failed";
154
+ this.options.log(`Task ${task.id} failed: ${message}`);
155
+ this.emitResponse(task.id, "", message);
156
+ }
157
+ }
158
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * PLLLA pairing exchange — one POST that turns a one-time pair_live_ token
3
+ * into everything the bridge needs: the rt- runtime key, the socket contract
4
+ * (self-describing: url/path/event names come from the server), and the
5
+ * agent's identity for persona injection (docs/agent/EXTERNAL_RUNTIME.md).
6
+ */
7
+ export const SUPPORTED_CONTRACT_VERSION = 1;
8
+ export async function consumePairingToken(params) {
9
+ const response = await fetch(`${params.serverOrigin}/api/user/ai-agent/pair`, {
10
+ method: "POST",
11
+ headers: { "Content-Type": "application/json" },
12
+ body: JSON.stringify({
13
+ pairingToken: params.pairingToken,
14
+ runtimeLabel: params.runtimeLabel,
15
+ }),
16
+ });
17
+ const data = (await response.json().catch(() => null));
18
+ if (!response.ok || !data?.success) {
19
+ throw new Error(data?.error || `PLLLA pairing failed (HTTP ${response.status}).`);
20
+ }
21
+ if (!data.runtimeKey || !data.socket?.url || !data.socket.events?.task) {
22
+ throw new Error("PLLLA pairing response is missing the socket contract.");
23
+ }
24
+ // 계약 세대 검증 — 미지의 세대에서 조용히 오동작하지 않고 정직하게 실패한다.
25
+ if ((data.contractVersion ?? 0) !== SUPPORTED_CONTRACT_VERSION) {
26
+ throw new Error(`This bridge speaks PLLLA contract v${SUPPORTED_CONTRACT_VERSION}, but the server speaks v${data.contractVersion}. Update: openclaw plugins install npm:@pllla/openclaw-plugin@latest`);
27
+ }
28
+ return {
29
+ contractVersion: data.contractVersion ?? 0,
30
+ runtimeKey: data.runtimeKey,
31
+ aiUser: data.aiUser ?? { id: "", username: "", name: "" },
32
+ identity: data.identity ?? { name: "", bio: "", systemPrompt: "" },
33
+ externalRuntime: data.externalRuntime ?? null,
34
+ socket: data.socket,
35
+ };
36
+ }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Bridge state — the consumed pairing result (rt- runtime key + socket
3
+ * contract + identity) persisted across gateway restarts, one file per
4
+ * account:
5
+ *
6
+ * <OPENCLAW_STATE_DIR | ~/.openclaw>/pllla/accounts/<accountId>.json
7
+ *
8
+ * Lives under the OpenClaw state dir so `openclaw uninstall` cleans it up
9
+ * with everything else. The pairing token itself is one-shot; after a
10
+ * successful exchange only this state matters.
11
+ *
12
+ * Phase 1 kept a single `pllla/bridge-state.json`. It is moved to
13
+ * `accounts/default.json` the first time the `default` account is loaded
14
+ * (docs/agent/EXTERNAL_RUNTIME.md §6.3).
15
+ */
16
+ import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync, } from "node:fs";
17
+ import { dirname, join } from "node:path";
18
+ import { homedir } from "node:os";
19
+ import { assertValidAccountId, DEFAULT_ACCOUNT_ID } from "./accounts.js";
20
+ const STATE_FILE_MODE = 0o600;
21
+ export function openclawStateDir() {
22
+ return process.env.OPENCLAW_STATE_DIR || join(homedir(), ".openclaw");
23
+ }
24
+ export function bridgeStatePath(accountId) {
25
+ assertValidAccountId(accountId);
26
+ return join(openclawStateDir(), "pllla", "accounts", `${accountId}.json`);
27
+ }
28
+ /** Phase 1 single-account file — only read for migration. */
29
+ export function legacyBridgeStatePath() {
30
+ return join(openclawStateDir(), "pllla", "bridge-state.json");
31
+ }
32
+ function readState(path) {
33
+ try {
34
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
35
+ return parsed?.pair?.runtimeKey ? parsed : null;
36
+ }
37
+ catch {
38
+ return null;
39
+ }
40
+ }
41
+ function lockDown(path) {
42
+ // writeFileSync 의 mode 는 생성 시에만 적용된다 — 기존 파일·rename 된 파일도
43
+ // rt- 키가 들어 있으니 항상 0600 으로 맞춘다.
44
+ try {
45
+ chmodSync(path, STATE_FILE_MODE);
46
+ }
47
+ catch {
48
+ // Windows 등 chmod 미지원 — 파일 자체는 쓰였다.
49
+ }
50
+ }
51
+ function writeState(path, state) {
52
+ mkdirSync(dirname(path), { recursive: true });
53
+ writeFileSync(path, JSON.stringify(state, null, 2), {
54
+ mode: STATE_FILE_MODE,
55
+ });
56
+ lockDown(path);
57
+ }
58
+ /**
59
+ * Phase 1 `bridge-state.json` → `accounts/default.json`. Never overwrites an
60
+ * existing account file. The legacy file is moved (not copied) so deleting
61
+ * `accounts/default.json` to force a re-pair cannot resurrect the old key.
62
+ */
63
+ function migrateLegacyState(targetPath) {
64
+ const legacyPath = legacyBridgeStatePath();
65
+ if (existsSync(targetPath) || !existsSync(legacyPath))
66
+ return;
67
+ const legacy = readState(legacyPath);
68
+ if (!legacy)
69
+ return;
70
+ mkdirSync(dirname(targetPath), { recursive: true });
71
+ try {
72
+ renameSync(legacyPath, targetPath);
73
+ lockDown(targetPath);
74
+ }
75
+ catch {
76
+ // rename 이 막히면(권한·파일시스템) 내용 복사로 대체 — 원본은 남는다.
77
+ writeState(targetPath, legacy);
78
+ }
79
+ }
80
+ export function loadBridgeState(accountId) {
81
+ const path = bridgeStatePath(accountId);
82
+ if (accountId === DEFAULT_ACCOUNT_ID)
83
+ migrateLegacyState(path);
84
+ return readState(path);
85
+ }
86
+ export function saveBridgeState(accountId, state) {
87
+ writeState(bridgeStatePath(accountId), state);
88
+ }