@readymadeit/mcp 0.0.0-stage → 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 (916) hide show
  1. package/README.md +114 -2
  2. package/bundle/LICENSE +201 -0
  3. package/bundle/boards/aggregator-demo-board.json +54 -0
  4. package/bundle/boards/alpaca-board.json +316 -0
  5. package/bundle/boards/animate-board.json +159 -0
  6. package/bundle/boards/ascii-art-demo-board.json +33 -0
  7. package/bundle/boards/ascii-cam-board.json +184 -0
  8. package/bundle/boards/asset-demo-board.json +54 -0
  9. package/bundle/boards/audio-encode-demo-board.json +153 -0
  10. package/bundle/boards/audiolize-board.json +152 -0
  11. package/bundle/boards/batcher-demo-board.json +43 -0
  12. package/bundle/boards/buffer-demo-board.json +53 -0
  13. package/bundle/boards/cache-demo-board.json +59 -0
  14. package/bundle/boards/camera-demo-board.json +28 -0
  15. package/bundle/boards/canvas-demo-board.json +55 -0
  16. package/bundle/boards/changes-demo-board.json +60 -0
  17. package/bundle/boards/court-booking-browser-demo-board.json +221 -0
  18. package/bundle/boards/court-booking-demo-board.json +218 -0
  19. package/bundle/boards/current-time-board.json +32 -0
  20. package/bundle/boards/debounce-demo-board.json +43 -0
  21. package/bundle/boards/decrypt-demo-board.json +47 -0
  22. package/bundle/boards/delay-demo-board.json +28 -0
  23. package/bundle/boards/detect-demo-board.json +64 -0
  24. package/bundle/boards/document-extract-demo-board.json +55 -0
  25. package/bundle/boards/dropitapp-board.json +138 -0
  26. package/bundle/boards/encode-demo-board.json +39 -0
  27. package/bundle/boards/encrypt-board.json +185 -0
  28. package/bundle/boards/encrypt-demo-board.json +39 -0
  29. package/bundle/boards/face-alert-board.json +181 -0
  30. package/bundle/boards/fetcher-demo-board.json +40 -0
  31. package/bundle/boards/fft-demo-board.json +28 -0
  32. package/bundle/boards/filter-demo-board.json +55 -0
  33. package/bundle/boards/flat-map-demo-board.json +42 -0
  34. package/bundle/boards/frame-limit-test-board.json +130 -0
  35. package/bundle/boards/game-board.json +413 -0
  36. package/bundle/boards/game-of-life-board.json +139 -0
  37. package/bundle/boards/game-of-life-demo-board.json +34 -0
  38. package/bundle/boards/gather-demo-board.json +62 -0
  39. package/bundle/boards/group-by-demo-board.json +62 -0
  40. package/bundle/boards/hacker-demo-board.json +45 -0
  41. package/bundle/boards/hacker-news-board.json +741 -0
  42. package/bundle/boards/hash-demo-board.json +40 -0
  43. package/bundle/boards/health-log-browser-demo-board.json +537 -0
  44. package/bundle/boards/health-log-demo-board.json +498 -0
  45. package/bundle/boards/helloworld-board.json +57 -0
  46. package/bundle/boards/hold-demo-board.json +67 -0
  47. package/bundle/boards/hold-scopes-demo-board.json +141 -0
  48. package/bundle/boards/http-client-browser-demo-board.json +379 -0
  49. package/bundle/boards/http-client-demo-board.json +380 -0
  50. package/bundle/boards/image-picker-demo-board.json +22 -0
  51. package/bundle/boards/imap-email-demo-board.json +35 -0
  52. package/bundle/boards/injector-demo-board.json +55 -0
  53. package/bundle/boards/iterator-demo-board.json +55 -0
  54. package/bundle/boards/limit-demo-board.json +66 -0
  55. package/bundle/boards/link-debugger.json +32 -0
  56. package/bundle/boards/live-location-demo-board.json +93 -0
  57. package/bundle/boards/live-radio-cloud-demo-board.json +216 -0
  58. package/bundle/boards/live-radio-cloud-direct-demo-board.json +311 -0
  59. package/bundle/boards/live-radio-demo-board.json +211 -0
  60. package/bundle/boards/live-radio-members-demo-board.json +314 -0
  61. package/bundle/boards/local-storage-demo-board.json +30 -0
  62. package/bundle/boards/lz-compress-demo-board.json +52 -0
  63. package/bundle/boards/map-demo-board.json +46 -0
  64. package/bundle/boards/match-filter-demo-board.json +43 -0
  65. package/bundle/boards/meeting-poll-browser-demo-board.json +649 -0
  66. package/bundle/boards/meeting-poll-demo-board.json +650 -0
  67. package/bundle/boards/microphone-monitor-demo-board.json +28 -0
  68. package/bundle/boards/microphone-speaker-demo-board.json +86 -0
  69. package/bundle/boards/monitor-demo-board.json +52 -0
  70. package/bundle/boards/mounted-endpoint-demo-board-hkp-rt.json +84 -0
  71. package/bundle/boards/mounted-endpoint-demo-board.json +84 -0
  72. package/bundle/boards/nested-rhythm-demo-board.json +1160 -0
  73. package/bundle/boards/next-bus-board.json +52 -0
  74. package/bundle/boards/next-bus-switch-board.json +79 -0
  75. package/bundle/boards/night-groove-demo-board.json +742 -0
  76. package/bundle/boards/noise-alert-board.json +146 -0
  77. package/bundle/boards/offer-followup-demo-board.json +291 -0
  78. package/bundle/boards/offer-intake-demo-board.json +289 -0
  79. package/bundle/boards/offer-intake-hetzner-demo-board.json +286 -0
  80. package/bundle/boards/offer-intake-imap-demo-board.json +289 -0
  81. package/bundle/boards/offer-intake-imap-hetzner-demo-board.json +354 -0
  82. package/bundle/boards/p2p-receiver-demo-board.json +65 -0
  83. package/bundle/boards/p2p-sender-demo-board.json +94 -0
  84. package/bundle/boards/pad-groove-demo-board.json +644 -0
  85. package/bundle/boards/peer-chat-board.json +109 -0
  86. package/bundle/boards/peer-chat-node-board.json +97 -0
  87. package/bundle/boards/qr-code-demo-board.json +22 -0
  88. package/bundle/boards/queue-demo-board.json +239 -0
  89. package/bundle/boards/reduce-demo-board.json +54 -0
  90. package/bundle/boards/remote-named-demo-board-hkp-rt.json +61 -0
  91. package/bundle/boards/remote-named-demo-board.json +61 -0
  92. package/bundle/boards/rss-demo-board.json +530 -0
  93. package/bundle/boards/rss-radio-board.json +515 -0
  94. package/bundle/boards/rss-radio-brief-one-board.json +237 -0
  95. package/bundle/boards/rss-radio-editor-unit-board.json +555 -0
  96. package/bundle/boards/rss-radio-library-unit-board.json +293 -0
  97. package/bundle/boards/rss-radio-unit-board.json +809 -0
  98. package/bundle/boards/rss-radio-voice-unit-board.json +146 -0
  99. package/bundle/boards/runtime-configs/hold-example.json +41 -0
  100. package/bundle/boards/runtime-configs/injector-example.json +23 -0
  101. package/bundle/boards/runtime-configs/speech-to-text-example.json +35 -0
  102. package/bundle/boards/runtime-configs/text-generation-example.json +31 -0
  103. package/bundle/boards/runtime-configs/text-to-speech-example.json +28 -0
  104. package/bundle/boards/runtime-configs/text-to-speech-inflect-example.json +28 -0
  105. package/bundle/boards/runtime-configs/ws-audio-playback-example.json +23 -0
  106. package/bundle/boards/select-demo-board.json +32 -0
  107. package/bundle/boards/send-ntfy-board.json +52 -0
  108. package/bundle/boards/shared-costs-demo-board.json +361 -0
  109. package/bundle/boards/sign-demo-board.json +41 -0
  110. package/bundle/boards/skill-router-demo-board.json +210 -0
  111. package/bundle/boards/smooth-demo-board.json +91 -0
  112. package/bundle/boards/smtp-email-demo-board.json +34 -0
  113. package/bundle/boards/sort-demo-board.json +55 -0
  114. package/bundle/boards/sound-demo-board.json +65 -0
  115. package/bundle/boards/spectral-modifier-board.json +98 -0
  116. package/bundle/boards/speech-synth-demo-board.json +37 -0
  117. package/bundle/boards/speech-to-text-demo-board.json +146 -0
  118. package/bundle/boards/spotify-board.json +75 -0
  119. package/bundle/boards/spread-demo-board.json +35 -0
  120. package/bundle/boards/sql-demo-board.json +56 -0
  121. package/bundle/boards/sql-explorer-board.json +447 -0
  122. package/bundle/boards/sql-explorer-node-board.json +474 -0
  123. package/bundle/boards/sql-import-board.json +156 -0
  124. package/bundle/boards/state-demo-board.json +65 -0
  125. package/bundle/boards/stopper-demo-board.json +40 -0
  126. package/bundle/boards/storage-demo-board.json +193 -0
  127. package/bundle/boards/store-demo-board.json +63 -0
  128. package/bundle/boards/swimlanes-demo-board.json +197 -0
  129. package/bundle/boards/switch-demo-board.json +51 -0
  130. package/bundle/boards/syn-board.json +19 -0
  131. package/bundle/boards/syn-booking-unit-board.json +998 -0
  132. package/bundle/boards/syn-conversations-anthropic-demo-board.json +925 -0
  133. package/bundle/boards/syn-conversations-demo-board.json +925 -0
  134. package/bundle/boards/syn-hotels-unit-board.json +287 -0
  135. package/bundle/boards/syn-mail-desk-board.json +25 -0
  136. package/bundle/boards/syn-mail-desk-unit-board.json +254 -0
  137. package/bundle/boards/telegram-listener-demo-board.json +31 -0
  138. package/bundle/boards/text-generation-anthropic-demo-board.json +66 -0
  139. package/bundle/boards/text-generation-demo-board.json +51 -0
  140. package/bundle/boards/text-generation-local-demo-board.json +80 -0
  141. package/bundle/boards/text-to-speech-demo-board.json +58 -0
  142. package/bundle/boards/timeline-demo-board.json +477 -0
  143. package/bundle/boards/timer-demo-board.json +35 -0
  144. package/bundle/boards/tracks-demo-board.json +95 -0
  145. package/bundle/boards/trigger-pad-demo-board.json +47 -0
  146. package/bundle/boards/triggerpad-board.json +31 -0
  147. package/bundle/boards/triggerpad-recorder-board.json +56 -0
  148. package/bundle/boards/uuid-generator-demo-board.json +166 -0
  149. package/bundle/boards/voice-assistant-demo-board.json +230 -0
  150. package/bundle/boards/voice-assistant-hkp-rt-demo-board.json +239 -0
  151. package/bundle/boards/voice-assistant-local-demo-board.json +232 -0
  152. package/bundle/boards/voice-assistant-skills-demo-board.json +316 -0
  153. package/bundle/boards/voice-assistant-skills-local-demo-board.json +316 -0
  154. package/bundle/boards/xy-pad-demo-board.json +36 -0
  155. package/bundle/docs/content/board-json.md +277 -0
  156. package/bundle/docs/content/boards/alpaca-board.md +56 -0
  157. package/bundle/docs/content/boards/animate-board.md +40 -0
  158. package/bundle/docs/content/boards/ascii-cam-board.md +37 -0
  159. package/bundle/docs/content/boards/audiolize-board.md +47 -0
  160. package/bundle/docs/content/boards/court-booking-browser-demo-board.md +43 -0
  161. package/bundle/docs/content/boards/court-booking-demo-board.md +120 -0
  162. package/bundle/docs/content/boards/current-time-board.md +32 -0
  163. package/bundle/docs/content/boards/dropitapp-board.md +57 -0
  164. package/bundle/docs/content/boards/encrypt-board.md +36 -0
  165. package/bundle/docs/content/boards/face-alert-board.md +50 -0
  166. package/bundle/docs/content/boards/game-board.md +69 -0
  167. package/bundle/docs/content/boards/game-of-life-board.md +47 -0
  168. package/bundle/docs/content/boards/hacker-news-board.md +76 -0
  169. package/bundle/docs/content/boards/health-log-browser-demo-board.md +56 -0
  170. package/bundle/docs/content/boards/health-log-demo-board.md +138 -0
  171. package/bundle/docs/content/boards/helloworld-board.md +40 -0
  172. package/bundle/docs/content/boards/link-debugger.md +31 -0
  173. package/bundle/docs/content/boards/live-radio-cloud-demo-board.md +70 -0
  174. package/bundle/docs/content/boards/live-radio-cloud-direct-demo-board.md +89 -0
  175. package/bundle/docs/content/boards/live-radio-demo-board.md +102 -0
  176. package/bundle/docs/content/boards/live-radio-members-demo-board.md +91 -0
  177. package/bundle/docs/content/boards/meeting-poll-browser-demo-board.md +45 -0
  178. package/bundle/docs/content/boards/meeting-poll-demo-board.md +121 -0
  179. package/bundle/docs/content/boards/mounted-endpoint-demo-board-hkp-rt.md +50 -0
  180. package/bundle/docs/content/boards/nested-rhythm-demo-board.md +165 -0
  181. package/bundle/docs/content/boards/next-bus-board.md +38 -0
  182. package/bundle/docs/content/boards/next-bus-switch-board.md +44 -0
  183. package/bundle/docs/content/boards/night-groove-demo-board.md +110 -0
  184. package/bundle/docs/content/boards/noise-alert-board.md +44 -0
  185. package/bundle/docs/content/boards/pad-groove-demo-board.md +93 -0
  186. package/bundle/docs/content/boards/peer-chat-board.md +49 -0
  187. package/bundle/docs/content/boards/peer-chat-node-board.md +47 -0
  188. package/bundle/docs/content/boards/rss-radio-board.md +221 -0
  189. package/bundle/docs/content/boards/send-ntfy-board.md +33 -0
  190. package/bundle/docs/content/boards/shared-costs-demo-board.md +65 -0
  191. package/bundle/docs/content/boards/spectral-modifier-board.md +55 -0
  192. package/bundle/docs/content/boards/spotify-board.md +46 -0
  193. package/bundle/docs/content/boards/sql-explorer-board.md +70 -0
  194. package/bundle/docs/content/boards/sql-explorer-node-board.md +76 -0
  195. package/bundle/docs/content/boards/sql-import-board.md +37 -0
  196. package/bundle/docs/content/boards/swimlanes-demo-board.md +148 -0
  197. package/bundle/docs/content/boards/syn-mail-desk-board.md +52 -0
  198. package/bundle/docs/content/boards/timeline-demo-board.md +115 -0
  199. package/bundle/docs/content/boards/triggerpad-board.md +32 -0
  200. package/bundle/docs/content/boards/triggerpad-recorder-board.md +52 -0
  201. package/bundle/docs/content/boards/voice-assistant-demo-board.md +51 -0
  202. package/bundle/docs/content/concepts/assets.md +245 -0
  203. package/bundle/docs/content/concepts/blocks.md +300 -0
  204. package/bundle/docs/content/concepts/board-assistant.md +307 -0
  205. package/bundle/docs/content/concepts/board.md +265 -0
  206. package/bundle/docs/content/concepts/cloud-boards.md +803 -0
  207. package/bundle/docs/content/concepts/coordinator.md +297 -0
  208. package/bundle/docs/content/concepts/logging.md +293 -0
  209. package/bundle/docs/content/concepts/mounts.md +314 -0
  210. package/bundle/docs/content/concepts/presets.md +360 -0
  211. package/bundle/docs/content/concepts/remotes.md +423 -0
  212. package/bundle/docs/content/concepts/runtime.md +664 -0
  213. package/bundle/docs/content/concepts/scopes.md +256 -0
  214. package/bundle/docs/content/concepts/service.md +227 -0
  215. package/bundle/docs/content/concepts/units.md +321 -0
  216. package/bundle/docs/content/introduction.md +163 -0
  217. package/bundle/docs/content/llm/circle-text-board.json +55 -0
  218. package/bundle/docs/content/llm/howto-workflow.md +369 -0
  219. package/bundle/docs/content/repository.md +161 -0
  220. package/bundle/docs/content/services/aggregator.md +76 -0
  221. package/bundle/docs/content/services/analyzer.md +44 -0
  222. package/bundle/docs/content/services/ascii-art.md +58 -0
  223. package/bundle/docs/content/services/asset.md +88 -0
  224. package/bundle/docs/content/services/audio-editor.md +57 -0
  225. package/bundle/docs/content/services/audio-encode.md +115 -0
  226. package/bundle/docs/content/services/audio-io.md +173 -0
  227. package/bundle/docs/content/services/batcher.md +49 -0
  228. package/bundle/docs/content/services/board-service.md +48 -0
  229. package/bundle/docs/content/services/browser-sub-service.md +75 -0
  230. package/bundle/docs/content/services/buffer.md +70 -0
  231. package/bundle/docs/content/services/cache.md +92 -0
  232. package/bundle/docs/content/services/camera.md +85 -0
  233. package/bundle/docs/content/services/canvas.md +196 -0
  234. package/bundle/docs/content/services/changes.md +87 -0
  235. package/bundle/docs/content/services/chunked-file-provider.md +63 -0
  236. package/bundle/docs/content/services/communication-dispatcher.md +190 -0
  237. package/bundle/docs/content/services/configurator.md +66 -0
  238. package/bundle/docs/content/services/conversations.md +244 -0
  239. package/bundle/docs/content/services/debounce.md +45 -0
  240. package/bundle/docs/content/services/decrypt.md +53 -0
  241. package/bundle/docs/content/services/delay.md +55 -0
  242. package/bundle/docs/content/services/detect.md +101 -0
  243. package/bundle/docs/content/services/document-extract.md +174 -0
  244. package/bundle/docs/content/services/download.md +67 -0
  245. package/bundle/docs/content/services/encode.md +63 -0
  246. package/bundle/docs/content/services/encrypt.md +53 -0
  247. package/bundle/docs/content/services/fetcher.md +122 -0
  248. package/bundle/docs/content/services/fft.md +96 -0
  249. package/bundle/docs/content/services/file-source.md +67 -0
  250. package/bundle/docs/content/services/filesystem.md +157 -0
  251. package/bundle/docs/content/services/filter.md +133 -0
  252. package/bundle/docs/content/services/flat-map.md +62 -0
  253. package/bundle/docs/content/services/game-of-life.md +93 -0
  254. package/bundle/docs/content/services/gather.md +135 -0
  255. package/bundle/docs/content/services/gif-encoder.md +56 -0
  256. package/bundle/docs/content/services/github.md +85 -0
  257. package/bundle/docs/content/services/group-by.md +54 -0
  258. package/bundle/docs/content/services/hacker.md +79 -0
  259. package/bundle/docs/content/services/hash.md +59 -0
  260. package/bundle/docs/content/services/hold.md +182 -0
  261. package/bundle/docs/content/services/http-relay-client.md +48 -0
  262. package/bundle/docs/content/services/http-uploader.md +62 -0
  263. package/bundle/docs/content/services/http.md +424 -0
  264. package/bundle/docs/content/services/if.md +79 -0
  265. package/bundle/docs/content/services/ifft.md +46 -0
  266. package/bundle/docs/content/services/image-picker.md +50 -0
  267. package/bundle/docs/content/services/imap-email.md +104 -0
  268. package/bundle/docs/content/services/injector.md +129 -0
  269. package/bundle/docs/content/services/input.md +90 -0
  270. package/bundle/docs/content/services/iterator.md +186 -0
  271. package/bundle/docs/content/services/join.md +153 -0
  272. package/bundle/docs/content/services/limit.md +47 -0
  273. package/bundle/docs/content/services/local-storage.md +47 -0
  274. package/bundle/docs/content/services/looper.md +62 -0
  275. package/bundle/docs/content/services/lz-compress.md +52 -0
  276. package/bundle/docs/content/services/map.md +257 -0
  277. package/bundle/docs/content/services/match-filter.md +38 -0
  278. package/bundle/docs/content/services/microphone-monitor.md +66 -0
  279. package/bundle/docs/content/services/monitor.md +67 -0
  280. package/bundle/docs/content/services/ollama.md +116 -0
  281. package/bundle/docs/content/services/openai-prompt.md +74 -0
  282. package/bundle/docs/content/services/output.md +107 -0
  283. package/bundle/docs/content/services/peer-socket.md +61 -0
  284. package/bundle/docs/content/services/qr-code.md +48 -0
  285. package/bundle/docs/content/services/queue.md +201 -0
  286. package/bundle/docs/content/services/reduce.md +51 -0
  287. package/bundle/docs/content/services/rss.md +232 -0
  288. package/bundle/docs/content/services/select.md +47 -0
  289. package/bundle/docs/content/services/sign.md +53 -0
  290. package/bundle/docs/content/services/skill-router.md +154 -0
  291. package/bundle/docs/content/services/smooth.md +59 -0
  292. package/bundle/docs/content/services/smtp-email.md +129 -0
  293. package/bundle/docs/content/services/sort.md +48 -0
  294. package/bundle/docs/content/services/sound.md +87 -0
  295. package/bundle/docs/content/services/speech-synth.md +46 -0
  296. package/bundle/docs/content/services/speech-to-text.md +172 -0
  297. package/bundle/docs/content/services/spotify.md +77 -0
  298. package/bundle/docs/content/services/spread.md +111 -0
  299. package/bundle/docs/content/services/sql.md +491 -0
  300. package/bundle/docs/content/services/state.md +57 -0
  301. package/bundle/docs/content/services/static.md +47 -0
  302. package/bundle/docs/content/services/stopper.md +55 -0
  303. package/bundle/docs/content/services/storage.md +155 -0
  304. package/bundle/docs/content/services/store.md +255 -0
  305. package/bundle/docs/content/services/sub-service.md +152 -0
  306. package/bundle/docs/content/services/switch.md +67 -0
  307. package/bundle/docs/content/services/telegram-listener.md +76 -0
  308. package/bundle/docs/content/services/text-generation.md +390 -0
  309. package/bundle/docs/content/services/text-to-speech.md +130 -0
  310. package/bundle/docs/content/services/timeline.md +333 -0
  311. package/bundle/docs/content/services/timer.md +113 -0
  312. package/bundle/docs/content/services/tracks.md +207 -0
  313. package/bundle/docs/content/services/trigger-pad.md +68 -0
  314. package/bundle/docs/content/services/uuid-generator.md +48 -0
  315. package/bundle/docs/content/services/wav-reader.md +66 -0
  316. package/bundle/docs/content/services/websocket.md +189 -0
  317. package/bundle/docs/content/services/workflow-board-builder.md +65 -0
  318. package/bundle/docs/content/services/xy-pad.md +60 -0
  319. package/bundle/docs/content/targets.md +434 -0
  320. package/bundle/docs/content/testing.md +263 -0
  321. package/bundle/docs/content/vocabulary.md +171 -0
  322. package/bundle/hkp-frontend/src/runtime/browser/BrowserRegistry.tsx +141 -0
  323. package/bundle/hkp-frontend/src/runtime/browser/BrowserRuntime.tsx +202 -0
  324. package/bundle/hkp-frontend/src/runtime/browser/BrowserRuntimeApi.ts +306 -0
  325. package/bundle/hkp-frontend/src/runtime/browser/BrowserRuntimeApp.ts +310 -0
  326. package/bundle/hkp-frontend/src/runtime/browser/BrowserRuntimeScope.ts +500 -0
  327. package/bundle/hkp-frontend/src/runtime/browser/UIRegistry.ts +150 -0
  328. package/bundle/hkp-frontend/src/runtime/browser/assetResolver.ts +123 -0
  329. package/bundle/hkp-frontend/src/runtime/browser/pipelineNames.ts +65 -0
  330. package/bundle/hkp-frontend/src/runtime/browser/registry/Default.ts +237 -0
  331. package/bundle/hkp-frontend/src/runtime/browser/services/Aggregator.ts +127 -0
  332. package/bundle/hkp-frontend/src/runtime/browser/services/AggregatorUI.tsx +83 -0
  333. package/bundle/hkp-frontend/src/runtime/browser/services/Analyzer.tsx +43 -0
  334. package/bundle/hkp-frontend/src/runtime/browser/services/AnalyzerUI/Bars.tsx +33 -0
  335. package/bundle/hkp-frontend/src/runtime/browser/services/AnalyzerUI/HorizontalSpectrogram.tsx +53 -0
  336. package/bundle/hkp-frontend/src/runtime/browser/services/AnalyzerUI/VerticalSpectogram.tsx +56 -0
  337. package/bundle/hkp-frontend/src/runtime/browser/services/AnalyzerUI/Waveform.tsx +109 -0
  338. package/bundle/hkp-frontend/src/runtime/browser/services/AnalyzerUI/index.tsx +99 -0
  339. package/bundle/hkp-frontend/src/runtime/browser/services/AsciiArt.ts +119 -0
  340. package/bundle/hkp-frontend/src/runtime/browser/services/AsciiArtUI.tsx +131 -0
  341. package/bundle/hkp-frontend/src/runtime/browser/services/Asset.ts +125 -0
  342. package/bundle/hkp-frontend/src/runtime/browser/services/AssetUI.tsx +37 -0
  343. package/bundle/hkp-frontend/src/runtime/browser/services/AudioDeviceSettings.tsx +56 -0
  344. package/bundle/hkp-frontend/src/runtime/browser/services/AudioEditor.ts +129 -0
  345. package/bundle/hkp-frontend/src/runtime/browser/services/AudioEditorUI.tsx +549 -0
  346. package/bundle/hkp-frontend/src/runtime/browser/services/AudioInput.ts +298 -0
  347. package/bundle/hkp-frontend/src/runtime/browser/services/AudioInputUI.tsx +257 -0
  348. package/bundle/hkp-frontend/src/runtime/browser/services/AudioOutput.ts +189 -0
  349. package/bundle/hkp-frontend/src/runtime/browser/services/AudioOutputUI.tsx +63 -0
  350. package/bundle/hkp-frontend/src/runtime/browser/services/Batcher.ts +11 -0
  351. package/bundle/hkp-frontend/src/runtime/browser/services/BoardService.tsx +86 -0
  352. package/bundle/hkp-frontend/src/runtime/browser/services/BoardServiceUI.tsx +410 -0
  353. package/bundle/hkp-frontend/src/runtime/browser/services/BrowserGather.ts +311 -0
  354. package/bundle/hkp-frontend/src/runtime/browser/services/BrowserIterator.ts +167 -0
  355. package/bundle/hkp-frontend/src/runtime/browser/services/BrowserSubService.tsx +583 -0
  356. package/bundle/hkp-frontend/src/runtime/browser/services/BrowserSubServiceUI.tsx +187 -0
  357. package/bundle/hkp-frontend/src/runtime/browser/services/BrowserTracks.tsx +477 -0
  358. package/bundle/hkp-frontend/src/runtime/browser/services/Buffer.ts +11 -0
  359. package/bundle/hkp-frontend/src/runtime/browser/services/Cache.ts +134 -0
  360. package/bundle/hkp-frontend/src/runtime/browser/services/CacheUI.tsx +125 -0
  361. package/bundle/hkp-frontend/src/runtime/browser/services/Camera.ts +90 -0
  362. package/bundle/hkp-frontend/src/runtime/browser/services/CameraUI.tsx +321 -0
  363. package/bundle/hkp-frontend/src/runtime/browser/services/Canvas.ts +139 -0
  364. package/bundle/hkp-frontend/src/runtime/browser/services/CanvasUI/canvasDraw.ts +543 -0
  365. package/bundle/hkp-frontend/src/runtime/browser/services/CanvasUI/canvasUtils.ts +39 -0
  366. package/bundle/hkp-frontend/src/runtime/browser/services/CanvasUI/index.tsx +287 -0
  367. package/bundle/hkp-frontend/src/runtime/browser/services/CanvasUI/tests/canvasDraw.test.ts +355 -0
  368. package/bundle/hkp-frontend/src/runtime/browser/services/CanvasUI/tests/canvasUtils.test.ts +89 -0
  369. package/bundle/hkp-frontend/src/runtime/browser/services/CanvasUI/tests/fullscreen.test.tsx +26 -0
  370. package/bundle/hkp-frontend/src/runtime/browser/services/Changes.ts +201 -0
  371. package/bundle/hkp-frontend/src/runtime/browser/services/ChangesUI.tsx +85 -0
  372. package/bundle/hkp-frontend/src/runtime/browser/services/ChunkedFileProvider.tsx +183 -0
  373. package/bundle/hkp-frontend/src/runtime/browser/services/Configurator.ts +258 -0
  374. package/bundle/hkp-frontend/src/runtime/browser/services/ConfiguratorUI.tsx +83 -0
  375. package/bundle/hkp-frontend/src/runtime/browser/services/ConsideredHacker.tsx +28 -0
  376. package/bundle/hkp-frontend/src/runtime/browser/services/Debounce.ts +75 -0
  377. package/bundle/hkp-frontend/src/runtime/browser/services/DebounceUI.tsx +70 -0
  378. package/bundle/hkp-frontend/src/runtime/browser/services/Decrypt.ts +127 -0
  379. package/bundle/hkp-frontend/src/runtime/browser/services/DecryptUI.tsx +39 -0
  380. package/bundle/hkp-frontend/src/runtime/browser/services/Delay.tsx +110 -0
  381. package/bundle/hkp-frontend/src/runtime/browser/services/Detect.ts +214 -0
  382. package/bundle/hkp-frontend/src/runtime/browser/services/DetectUI.tsx +86 -0
  383. package/bundle/hkp-frontend/src/runtime/browser/services/Download.ts +120 -0
  384. package/bundle/hkp-frontend/src/runtime/browser/services/DownloadUI.tsx +61 -0
  385. package/bundle/hkp-frontend/src/runtime/browser/services/Encode.ts +138 -0
  386. package/bundle/hkp-frontend/src/runtime/browser/services/EncodeUI.tsx +52 -0
  387. package/bundle/hkp-frontend/src/runtime/browser/services/Encrypt.ts +125 -0
  388. package/bundle/hkp-frontend/src/runtime/browser/services/EncryptUI.tsx +39 -0
  389. package/bundle/hkp-frontend/src/runtime/browser/services/FFT.tsx +238 -0
  390. package/bundle/hkp-frontend/src/runtime/browser/services/FFTUI.tsx +112 -0
  391. package/bundle/hkp-frontend/src/runtime/browser/services/FeedbackService.ts +247 -0
  392. package/bundle/hkp-frontend/src/runtime/browser/services/FeedbackServiceUI.tsx +35 -0
  393. package/bundle/hkp-frontend/src/runtime/browser/services/Fetcher.tsx +232 -0
  394. package/bundle/hkp-frontend/src/runtime/browser/services/FetcherUI.tsx +154 -0
  395. package/bundle/hkp-frontend/src/runtime/browser/services/FileSource.tsx +171 -0
  396. package/bundle/hkp-frontend/src/runtime/browser/services/Filter.tsx +18 -0
  397. package/bundle/hkp-frontend/src/runtime/browser/services/FilterUI.tsx +89 -0
  398. package/bundle/hkp-frontend/src/runtime/browser/services/FlatMap.ts +68 -0
  399. package/bundle/hkp-frontend/src/runtime/browser/services/FlatMapUI.tsx +38 -0
  400. package/bundle/hkp-frontend/src/runtime/browser/services/GameOfLife.ts +210 -0
  401. package/bundle/hkp-frontend/src/runtime/browser/services/GameOfLifeRenderer.ts +182 -0
  402. package/bundle/hkp-frontend/src/runtime/browser/services/GameOfLifeRendererUI.tsx +48 -0
  403. package/bundle/hkp-frontend/src/runtime/browser/services/GameOfLifeUI.tsx +58 -0
  404. package/bundle/hkp-frontend/src/runtime/browser/services/GatherUI.tsx +84 -0
  405. package/bundle/hkp-frontend/src/runtime/browser/services/GifEncoder.tsx +651 -0
  406. package/bundle/hkp-frontend/src/runtime/browser/services/GithubAPI.ts +294 -0
  407. package/bundle/hkp-frontend/src/runtime/browser/services/GithubComponents.tsx +389 -0
  408. package/bundle/hkp-frontend/src/runtime/browser/services/GithubSink.tsx +109 -0
  409. package/bundle/hkp-frontend/src/runtime/browser/services/GithubSinkUI.tsx +143 -0
  410. package/bundle/hkp-frontend/src/runtime/browser/services/GithubSource.tsx +86 -0
  411. package/bundle/hkp-frontend/src/runtime/browser/services/GithubSourceUI.tsx +162 -0
  412. package/bundle/hkp-frontend/src/runtime/browser/services/GroupBy.ts +11 -0
  413. package/bundle/hkp-frontend/src/runtime/browser/services/HTTPUploader.ts +102 -0
  414. package/bundle/hkp-frontend/src/runtime/browser/services/Hacker.ts +73 -0
  415. package/bundle/hkp-frontend/src/runtime/browser/services/HackerUI.tsx +134 -0
  416. package/bundle/hkp-frontend/src/runtime/browser/services/Hash.ts +134 -0
  417. package/bundle/hkp-frontend/src/runtime/browser/services/HashUI.tsx +45 -0
  418. package/bundle/hkp-frontend/src/runtime/browser/services/Hold.ts +288 -0
  419. package/bundle/hkp-frontend/src/runtime/browser/services/HoldUI.tsx +43 -0
  420. package/bundle/hkp-frontend/src/runtime/browser/services/HttpClient.ts +558 -0
  421. package/bundle/hkp-frontend/src/runtime/browser/services/HttpClientUI.tsx +212 -0
  422. package/bundle/hkp-frontend/src/runtime/browser/services/HttpUploaderTokenInjector.ts +149 -0
  423. package/bundle/hkp-frontend/src/runtime/browser/services/IfService.ts +231 -0
  424. package/bundle/hkp-frontend/src/runtime/browser/services/IfServiceUI.tsx +50 -0
  425. package/bundle/hkp-frontend/src/runtime/browser/services/ImagePicker.tsx +118 -0
  426. package/bundle/hkp-frontend/src/runtime/browser/services/Injector.ts +61 -0
  427. package/bundle/hkp-frontend/src/runtime/browser/services/InjectorUI.tsx +389 -0
  428. package/bundle/hkp-frontend/src/runtime/browser/services/Input.ts +214 -0
  429. package/bundle/hkp-frontend/src/runtime/browser/services/InputUI.tsx +71 -0
  430. package/bundle/hkp-frontend/src/runtime/browser/services/IteratorUI.tsx +59 -0
  431. package/bundle/hkp-frontend/src/runtime/browser/services/LZCompress.ts +63 -0
  432. package/bundle/hkp-frontend/src/runtime/browser/services/Limit.ts +39 -0
  433. package/bundle/hkp-frontend/src/runtime/browser/services/LimitUI.tsx +41 -0
  434. package/bundle/hkp-frontend/src/runtime/browser/services/LocalStorage.ts +65 -0
  435. package/bundle/hkp-frontend/src/runtime/browser/services/LocalStorageUI.tsx +72 -0
  436. package/bundle/hkp-frontend/src/runtime/browser/services/Map.ts +14 -0
  437. package/bundle/hkp-frontend/src/runtime/browser/services/MapUI.tsx +334 -0
  438. package/bundle/hkp-frontend/src/runtime/browser/services/MapUIModeAssist.tsx +61 -0
  439. package/bundle/hkp-frontend/src/runtime/browser/services/MatchFilter.ts +11 -0
  440. package/bundle/hkp-frontend/src/runtime/browser/services/MicrophoneMonitor.ts +190 -0
  441. package/bundle/hkp-frontend/src/runtime/browser/services/MicrophoneMonitorUI.tsx +94 -0
  442. package/bundle/hkp-frontend/src/runtime/browser/services/Monitor.tsx +15 -0
  443. package/bundle/hkp-frontend/src/runtime/browser/services/MonitorUI.tsx +281 -0
  444. package/bundle/hkp-frontend/src/runtime/browser/services/OllamaPrompt.ts +237 -0
  445. package/bundle/hkp-frontend/src/runtime/browser/services/OllamaPromptUI/BusyIndicator.tsx +18 -0
  446. package/bundle/hkp-frontend/src/runtime/browser/services/OllamaPromptUI/PromptInput.tsx +71 -0
  447. package/bundle/hkp-frontend/src/runtime/browser/services/OllamaPromptUI/StreamingPreview.tsx +24 -0
  448. package/bundle/hkp-frontend/src/runtime/browser/services/OllamaPromptUI/index.tsx +156 -0
  449. package/bundle/hkp-frontend/src/runtime/browser/services/OpenAIPrompt.ts +254 -0
  450. package/bundle/hkp-frontend/src/runtime/browser/services/OpenAIPromptUI.tsx +189 -0
  451. package/bundle/hkp-frontend/src/runtime/browser/services/Output.tsx +145 -0
  452. package/bundle/hkp-frontend/src/runtime/browser/services/OutputUI.tsx +118 -0
  453. package/bundle/hkp-frontend/src/runtime/browser/services/PeerChatTemplate.json +43 -0
  454. package/bundle/hkp-frontend/src/runtime/browser/services/PeerConnection.ts +260 -0
  455. package/bundle/hkp-frontend/src/runtime/browser/services/PeerSocket.tsx +268 -0
  456. package/bundle/hkp-frontend/src/runtime/browser/services/PeerSocketReceiverTemplate.json +29 -0
  457. package/bundle/hkp-frontend/src/runtime/browser/services/PeerSocketUI.tsx +341 -0
  458. package/bundle/hkp-frontend/src/runtime/browser/services/PlaceholderUI.tsx +72 -0
  459. package/bundle/hkp-frontend/src/runtime/browser/services/QrCode.ts +63 -0
  460. package/bundle/hkp-frontend/src/runtime/browser/services/QrCodeUI.tsx +197 -0
  461. package/bundle/hkp-frontend/src/runtime/browser/services/Select.ts +40 -0
  462. package/bundle/hkp-frontend/src/runtime/browser/services/SelectUI.tsx +41 -0
  463. package/bundle/hkp-frontend/src/runtime/browser/services/ServiceBase.ts +50 -0
  464. package/bundle/hkp-frontend/src/runtime/browser/services/SetRuntimeVariable.ts +65 -0
  465. package/bundle/hkp-frontend/src/runtime/browser/services/SetRuntimeVariableUI.tsx +50 -0
  466. package/bundle/hkp-frontend/src/runtime/browser/services/Sign.ts +182 -0
  467. package/bundle/hkp-frontend/src/runtime/browser/services/SignUI.tsx +55 -0
  468. package/bundle/hkp-frontend/src/runtime/browser/services/Smooth.ts +101 -0
  469. package/bundle/hkp-frontend/src/runtime/browser/services/SmoothUI.tsx +50 -0
  470. package/bundle/hkp-frontend/src/runtime/browser/services/Sort.ts +59 -0
  471. package/bundle/hkp-frontend/src/runtime/browser/services/SortUI.tsx +61 -0
  472. package/bundle/hkp-frontend/src/runtime/browser/services/Sound.ts +541 -0
  473. package/bundle/hkp-frontend/src/runtime/browser/services/SoundUI.tsx +183 -0
  474. package/bundle/hkp-frontend/src/runtime/browser/services/SpeechSynth.ts +56 -0
  475. package/bundle/hkp-frontend/src/runtime/browser/services/SpeechSynthUI.tsx +108 -0
  476. package/bundle/hkp-frontend/src/runtime/browser/services/Spotify.tsx +111 -0
  477. package/bundle/hkp-frontend/src/runtime/browser/services/SpotifyAPI.ts +113 -0
  478. package/bundle/hkp-frontend/src/runtime/browser/services/SpotifyComponents.tsx +104 -0
  479. package/bundle/hkp-frontend/src/runtime/browser/services/SpotifyUI.tsx +186 -0
  480. package/bundle/hkp-frontend/src/runtime/browser/services/Spread.ts +117 -0
  481. package/bundle/hkp-frontend/src/runtime/browser/services/SpreadUI.tsx +38 -0
  482. package/bundle/hkp-frontend/src/runtime/browser/services/Sql.ts +448 -0
  483. package/bundle/hkp-frontend/src/runtime/browser/services/SqlInspector.tsx +332 -0
  484. package/bundle/hkp-frontend/src/runtime/browser/services/SqlUI.tsx +226 -0
  485. package/bundle/hkp-frontend/src/runtime/browser/services/State.ts +149 -0
  486. package/bundle/hkp-frontend/src/runtime/browser/services/Stopper.ts +29 -0
  487. package/bundle/hkp-frontend/src/runtime/browser/services/Switch.tsx +419 -0
  488. package/bundle/hkp-frontend/src/runtime/browser/services/SwitchUI.tsx +171 -0
  489. package/bundle/hkp-frontend/src/runtime/browser/services/TickRuleDelta.ts +101 -0
  490. package/bundle/hkp-frontend/src/runtime/browser/services/TickRuleDeltaUI.tsx +56 -0
  491. package/bundle/hkp-frontend/src/runtime/browser/services/Timeline.ts +709 -0
  492. package/bundle/hkp-frontend/src/runtime/browser/services/TimelineUI/TimelinePanel.tsx +79 -0
  493. package/bundle/hkp-frontend/src/runtime/browser/services/TimelineUI/TimelineRows.tsx +574 -0
  494. package/bundle/hkp-frontend/src/runtime/browser/services/TimelineUI/Transport.tsx +141 -0
  495. package/bundle/hkp-frontend/src/runtime/browser/services/TimelineUI/index.tsx +119 -0
  496. package/bundle/hkp-frontend/src/runtime/browser/services/TimelineUI/model.ts +425 -0
  497. package/bundle/hkp-frontend/src/runtime/browser/services/TimelineUI/parts.tsx +708 -0
  498. package/bundle/hkp-frontend/src/runtime/browser/services/Timer.ts +15 -0
  499. package/bundle/hkp-frontend/src/runtime/browser/services/TimerUI.tsx +213 -0
  500. package/bundle/hkp-frontend/src/runtime/browser/services/TracksUI.tsx +81 -0
  501. package/bundle/hkp-frontend/src/runtime/browser/services/TriggerPad.ts +108 -0
  502. package/bundle/hkp-frontend/src/runtime/browser/services/TriggerPadUI.tsx +160 -0
  503. package/bundle/hkp-frontend/src/runtime/browser/services/UuidGenerator.ts +74 -0
  504. package/bundle/hkp-frontend/src/runtime/browser/services/UuidGeneratorUI.tsx +59 -0
  505. package/bundle/hkp-frontend/src/runtime/browser/services/WaveformDisplay.tsx +104 -0
  506. package/bundle/hkp-frontend/src/runtime/browser/services/WebsocketClient.ts +114 -0
  507. package/bundle/hkp-frontend/src/runtime/browser/services/WebsocketClientUI.tsx +42 -0
  508. package/bundle/hkp-frontend/src/runtime/browser/services/WorkflowBoardBuilder.ts +499 -0
  509. package/bundle/hkp-frontend/src/runtime/browser/services/WorkflowBoardBuilderUI.tsx +521 -0
  510. package/bundle/hkp-frontend/src/runtime/browser/services/XYPad.ts +68 -0
  511. package/bundle/hkp-frontend/src/runtime/browser/services/XYPadUI.tsx +274 -0
  512. package/bundle/hkp-frontend/src/runtime/browser/services/audio/audioHelper.ts +36 -0
  513. package/bundle/hkp-frontend/src/runtime/browser/services/base/Batcher.ts +76 -0
  514. package/bundle/hkp-frontend/src/runtime/browser/services/base/Buffer.ts +142 -0
  515. package/bundle/hkp-frontend/src/runtime/browser/services/base/Filter.ts +125 -0
  516. package/bundle/hkp-frontend/src/runtime/browser/services/base/GroupBy.ts +101 -0
  517. package/bundle/hkp-frontend/src/runtime/browser/services/base/Map.ts +543 -0
  518. package/bundle/hkp-frontend/src/runtime/browser/services/base/MatchFilter.ts +49 -0
  519. package/bundle/hkp-frontend/src/runtime/browser/services/base/Monitor.ts +75 -0
  520. package/bundle/hkp-frontend/src/runtime/browser/services/base/Reduce.ts +74 -0
  521. package/bundle/hkp-frontend/src/runtime/browser/services/base/Timer.ts +342 -0
  522. package/bundle/hkp-frontend/src/runtime/browser/services/base/bundle.js +7375 -0
  523. package/bundle/hkp-frontend/src/runtime/browser/services/base/eval.ts +208 -0
  524. package/bundle/hkp-frontend/src/runtime/browser/services/base/expressionEval.ts +223 -0
  525. package/bundle/hkp-frontend/src/runtime/browser/services/base/helpers.ts +10 -0
  526. package/bundle/hkp-frontend/src/runtime/browser/services/base/index.ts +37 -0
  527. package/bundle/hkp-frontend/src/runtime/browser/services/camera/videoCapture.ts +78 -0
  528. package/bundle/hkp-frontend/src/runtime/browser/services/changes-modes.ts +9 -0
  529. package/bundle/hkp-frontend/src/runtime/browser/services/colorpalette.ts +26 -0
  530. package/bundle/hkp-frontend/src/runtime/browser/services/detect/DetectionPreview.tsx +76 -0
  531. package/bundle/hkp-frontend/src/runtime/browser/services/detect/mediapipe.ts +34 -0
  532. package/bundle/hkp-frontend/src/runtime/browser/services/detect/models/README.md +14 -0
  533. package/bundle/hkp-frontend/src/runtime/browser/services/detect/models/blaze_face_short_range.tflite +0 -0
  534. package/bundle/hkp-frontend/src/runtime/browser/services/detect-modes.ts +7 -0
  535. package/bundle/hkp-frontend/src/runtime/browser/services/encode-modes.ts +10 -0
  536. package/bundle/hkp-frontend/src/runtime/browser/services/helpers.ts +300 -0
  537. package/bundle/hkp-frontend/src/runtime/browser/services/http-client-methods.ts +10 -0
  538. package/bundle/hkp-frontend/src/runtime/browser/services/sql-database.ts +594 -0
  539. package/bundle/hkp-frontend/src/runtime/browser/services/sql-dump.ts +192 -0
  540. package/bundle/hkp-frontend/src/runtime/browser/services/sql-inspect.ts +130 -0
  541. package/bundle/hkp-frontend/src/runtime/browser/services/sql-inspector.css +155 -0
  542. package/bundle/hkp-frontend/src/runtime/browser/services/sql-modes.ts +27 -0
  543. package/bundle/hkp-frontend/src/runtime/browser/services/tests/TESTING_APPROACH.md +169 -0
  544. package/bundle/hkp-frontend/src/runtime/browser/services/tests/__snapshots__/service-ui.future-enhancements.test.tsx.snap +5 -0
  545. package/bundle/hkp-frontend/src/runtime/browser/services/tests/audio-input-permissions.test.ts +42 -0
  546. package/bundle/hkp-frontend/src/runtime/browser/services/tests/audiolizeLanes.test.ts +70 -0
  547. package/bundle/hkp-frontend/src/runtime/browser/services/tests/browser-scopes.test.ts +305 -0
  548. package/bundle/hkp-frontend/src/runtime/browser/services/tests/cache-service.test.ts +53 -0
  549. package/bundle/hkp-frontend/src/runtime/browser/services/tests/camera-video-capture.test.ts +24 -0
  550. package/bundle/hkp-frontend/src/runtime/browser/services/tests/changes-service.test.ts +133 -0
  551. package/bundle/hkp-frontend/src/runtime/browser/services/tests/crypto-secrets.test.ts +196 -0
  552. package/bundle/hkp-frontend/src/runtime/browser/services/tests/detect-service.test.ts +119 -0
  553. package/bundle/hkp-frontend/src/runtime/browser/services/tests/download.test.ts +100 -0
  554. package/bundle/hkp-frontend/src/runtime/browser/services/tests/expressionEval.test.ts +117 -0
  555. package/bundle/hkp-frontend/src/runtime/browser/services/tests/fetcher-panel.test.tsx +35 -0
  556. package/bundle/hkp-frontend/src/runtime/browser/services/tests/fetcher-service.test.ts +80 -0
  557. package/bundle/hkp-frontend/src/runtime/browser/services/tests/filter-service.test.ts +37 -0
  558. package/bundle/hkp-frontend/src/runtime/browser/services/tests/github-secrets.test.ts +108 -0
  559. package/bundle/hkp-frontend/src/runtime/browser/services/tests/group-by-board.test.ts +42 -0
  560. package/bundle/hkp-frontend/src/runtime/browser/services/tests/hold-service.test.ts +199 -0
  561. package/bundle/hkp-frontend/src/runtime/browser/services/tests/http-client-service.test.ts +474 -0
  562. package/bundle/hkp-frontend/src/runtime/browser/services/tests/http-uploader-token-injector.test.ts +152 -0
  563. package/bundle/hkp-frontend/src/runtime/browser/services/tests/iterator-gather-service.test.ts +277 -0
  564. package/bundle/hkp-frontend/src/runtime/browser/services/tests/map-service.test.ts +1047 -0
  565. package/bundle/hkp-frontend/src/runtime/browser/services/tests/map-ui.test.tsx +70 -0
  566. package/bundle/hkp-frontend/src/runtime/browser/services/tests/nested-rhythm-board.test.ts +311 -0
  567. package/bundle/hkp-frontend/src/runtime/browser/services/tests/night-groove-board.test.ts +290 -0
  568. package/bundle/hkp-frontend/src/runtime/browser/services/tests/pad-groove-board.test.ts +230 -0
  569. package/bundle/hkp-frontend/src/runtime/browser/services/tests/peer-host-discovery.test.ts +77 -0
  570. package/bundle/hkp-frontend/src/runtime/browser/services/tests/peer-socket-discovery.test.ts +69 -0
  571. package/bundle/hkp-frontend/src/runtime/browser/services/tests/pipeline-names.test.ts +63 -0
  572. package/bundle/hkp-frontend/src/runtime/browser/services/tests/secret-canary.test.ts +121 -0
  573. package/bundle/hkp-frontend/src/runtime/browser/services/tests/service-modules.contract.test.ts +229 -0
  574. package/bundle/hkp-frontend/src/runtime/browser/services/tests/service-ui.advanced.test.tsx +670 -0
  575. package/bundle/hkp-frontend/src/runtime/browser/services/tests/service-ui.future-enhancements.test.tsx +219 -0
  576. package/bundle/hkp-frontend/src/runtime/browser/services/tests/service-ui.integration.test.tsx +432 -0
  577. package/bundle/hkp-frontend/src/runtime/browser/services/tests/service-ui.smoke.test.tsx +140 -0
  578. package/bundle/hkp-frontend/src/runtime/browser/services/tests/sound-service.test.ts +293 -0
  579. package/bundle/hkp-frontend/src/runtime/browser/services/tests/spread-service.test.ts +137 -0
  580. package/bundle/hkp-frontend/src/runtime/browser/services/tests/sql-dump.test.ts +172 -0
  581. package/bundle/hkp-frontend/src/runtime/browser/services/tests/sql-inspect.test.ts +163 -0
  582. package/bundle/hkp-frontend/src/runtime/browser/services/tests/sql-inspector.test.tsx +121 -0
  583. package/bundle/hkp-frontend/src/runtime/browser/services/tests/sql.test.ts +702 -0
  584. package/bundle/hkp-frontend/src/runtime/browser/services/tests/sub-service-in-block.test.tsx +84 -0
  585. package/bundle/hkp-frontend/src/runtime/browser/services/tests/switch-service.test.ts +254 -0
  586. package/bundle/hkp-frontend/src/runtime/browser/services/tests/timeline-board.test.ts +218 -0
  587. package/bundle/hkp-frontend/src/runtime/browser/services/tests/timeline-service.test.ts +1002 -0
  588. package/bundle/hkp-frontend/src/runtime/browser/services/tests/timeline-ui-model.test.ts +230 -0
  589. package/bundle/hkp-frontend/src/runtime/browser/services/tests/timeline-ui.test.tsx +371 -0
  590. package/bundle/hkp-frontend/src/runtime/browser/services/tests/timer-service.test.ts +902 -0
  591. package/bundle/hkp-frontend/src/runtime/browser/services/tests/tracks-service.test.ts +275 -0
  592. package/bundle/hkp-frontend/src/runtime/browser/services/tests/tracks-ui.test.tsx +126 -0
  593. package/bundle/hkp-frontend/src/runtime/browser/services/tests/triggerpad-recorder-board.test.ts +100 -0
  594. package/bundle/hkp-frontend/src/runtime/browser/services/tests/uuid-generator-service.test.ts +214 -0
  595. package/bundle/hkp-frontend/src/runtime/browser/services/timeline-core.ts +410 -0
  596. package/bundle/hkp-frontend/src/runtime/browser/services/workflow-prompt/DefaultWorkflowBoard.ts +90 -0
  597. package/bundle/hkp-frontend/src/runtime/browser/services/workflow-prompt/RefinementSession.ts +121 -0
  598. package/bundle/hkp-frontend/src/runtime/browser/services/workflow-prompt/SystemPromptCatalog.ts +245 -0
  599. package/bundle/hkp-frontend/src/runtime/browser/services/workflow-prompt/catalog/browser-service-specs.json +110 -0
  600. package/bundle/hkp-frontend/src/runtime/browser/services/workflow-prompt/catalog/runtime-capabilities.json +37 -0
  601. package/bundle/hkp-frontend/src/runtime/browser/tests/asset-service.test.ts +73 -0
  602. package/bundle/hkp-frontend/src/runtime/browser/tests/board-persistence.integration.test.tsx +391 -0
  603. package/bundle/hkp-frontend/src/runtime/browser/tests/browser-runtime.identity-binding.test.tsx +81 -0
  604. package/bundle/hkp-frontend/src/runtime/browser/tests/demo-boards.regression.test.tsx +386 -0
  605. package/bundle/hkp-frontend/src/runtime/browser/tests/failed-call-lifecycle.test.ts +56 -0
  606. package/bundle/hkp-frontend/src/runtime/browser/tests/http-client.integration.test.ts +146 -0
  607. package/bundle/hkp-frontend/src/runtime/browser/tests/nested-run.integration.test.ts +415 -0
  608. package/bundle/hkp-frontend/src/runtime/browser/tests/replay-next.test.ts +391 -0
  609. package/bundle/hkp-frontend/src/runtime/browser/tests/rerun.test.ts +71 -0
  610. package/bundle/hkp-frontend/src/runtime/browser/tests/run-no-input.test.ts +72 -0
  611. package/bundle/hkp-frontend/src/runtime/browser/tests/runtime-add.integration.test.tsx +256 -0
  612. package/bundle/hkp-frontend/src/runtime/browser/tests/runtime-operations.integration.test.tsx +310 -0
  613. package/bundle/hkp-frontend/src/runtime/browser/tests/runtime-processing.integration.test.ts +311 -0
  614. package/bundle/hkp-frontend/src/runtime/browser/tests/runtime-removal.integration.test.tsx +454 -0
  615. package/bundle/hkp-frontend/src/runtime/browser/tests/runtime-update.identity.integration.test.tsx +145 -0
  616. package/bundle/hkp-frontend/src/runtime/browser/tests/service-operations.integration.test.tsx +511 -0
  617. package/bundle/hkp-node/src/address.ts +72 -0
  618. package/bundle/hkp-node/src/assets.ts +582 -0
  619. package/bundle/hkp-node/src/auth.ts +369 -0
  620. package/bundle/hkp-node/src/callReport.ts +281 -0
  621. package/bundle/hkp-node/src/coordinator/auth.ts +85 -0
  622. package/bundle/hkp-node/src/coordinator/binaryFrame.ts +189 -0
  623. package/bundle/hkp-node/src/coordinator/boardStore.ts +0 -0
  624. package/bundle/hkp-node/src/coordinator/bridge.ts +242 -0
  625. package/bundle/hkp-node/src/coordinator/bridgeProtocol.ts +200 -0
  626. package/bundle/hkp-node/src/coordinator/coordinator.ts +594 -0
  627. package/bundle/hkp-node/src/coordinator/facadeAccess.ts +426 -0
  628. package/bundle/hkp-node/src/coordinator/fileBoardStore.ts +159 -0
  629. package/bundle/hkp-node/src/coordinator/frameLimits.ts +41 -0
  630. package/bundle/hkp-node/src/coordinator/index.ts +12 -0
  631. package/bundle/hkp-node/src/coordinator/join.ts +62 -0
  632. package/bundle/hkp-node/src/coordinator/logStore.ts +335 -0
  633. package/bundle/hkp-node/src/coordinator/members.ts +133 -0
  634. package/bundle/hkp-node/src/coordinator/mount.ts +124 -0
  635. package/bundle/hkp-node/src/coordinator/participantProtocol.ts +169 -0
  636. package/bundle/hkp-node/src/coordinator/participants.ts +666 -0
  637. package/bundle/hkp-node/src/coordinator/router.ts +416 -0
  638. package/bundle/hkp-node/src/coordinator/session.ts +1610 -0
  639. package/bundle/hkp-node/src/coordinator/types.ts +95 -0
  640. package/bundle/hkp-node/src/coordinatorLinks.ts +712 -0
  641. package/bundle/hkp-node/src/credentialedFetch.ts +122 -0
  642. package/bundle/hkp-node/src/egress.ts +344 -0
  643. package/bundle/hkp-node/src/expressionEval.ts +223 -0
  644. package/bundle/hkp-node/src/index.ts +470 -0
  645. package/bundle/hkp-node/src/instance.ts +172 -0
  646. package/bundle/hkp-node/src/mounts.ts +296 -0
  647. package/bundle/hkp-node/src/origins.ts +201 -0
  648. package/bundle/hkp-node/src/reset.ts +187 -0
  649. package/bundle/hkp-node/src/runtime.ts +1345 -0
  650. package/bundle/hkp-node/src/secrets.ts +292 -0
  651. package/bundle/hkp-node/src/server.ts +2120 -0
  652. package/bundle/hkp-node/src/services/asset.ts +150 -0
  653. package/bundle/hkp-node/src/services/claudeModels.ts +370 -0
  654. package/bundle/hkp-node/src/services/communication-dispatcher.ts +735 -0
  655. package/bundle/hkp-node/src/services/conversations.ts +914 -0
  656. package/bundle/hkp-node/src/services/database.ts +386 -0
  657. package/bundle/hkp-node/src/services/document-extract.ts +575 -0
  658. package/bundle/hkp-node/src/services/expression.ts +417 -0
  659. package/bundle/hkp-node/src/services/fileStore.ts +369 -0
  660. package/bundle/hkp-node/src/services/filesystem.ts +280 -0
  661. package/bundle/hkp-node/src/services/gather.ts +335 -0
  662. package/bundle/hkp-node/src/services/hold.ts +275 -0
  663. package/bundle/hkp-node/src/services/http-client.ts +525 -0
  664. package/bundle/hkp-node/src/services/http-server.ts +1222 -0
  665. package/bundle/hkp-node/src/services/imap-email.ts +627 -0
  666. package/bundle/hkp-node/src/services/injector.ts +135 -0
  667. package/bundle/hkp-node/src/services/iterator.ts +203 -0
  668. package/bundle/hkp-node/src/services/join.ts +226 -0
  669. package/bundle/hkp-node/src/services/map.ts +632 -0
  670. package/bundle/hkp-node/src/services/monitor.ts +128 -0
  671. package/bundle/hkp-node/src/services/nested-pipeline.ts +405 -0
  672. package/bundle/hkp-node/src/services/peer-server.ts +268 -0
  673. package/bundle/hkp-node/src/services/queue-store.ts +301 -0
  674. package/bundle/hkp-node/src/services/queue.ts +316 -0
  675. package/bundle/hkp-node/src/services/recordStore.ts +341 -0
  676. package/bundle/hkp-node/src/services/rss-parse.ts +281 -0
  677. package/bundle/hkp-node/src/services/rss.ts +392 -0
  678. package/bundle/hkp-node/src/services/smtp-email.ts +398 -0
  679. package/bundle/hkp-node/src/services/spread.ts +153 -0
  680. package/bundle/hkp-node/src/services/sql-dump.ts +192 -0
  681. package/bundle/hkp-node/src/services/sql.ts +429 -0
  682. package/bundle/hkp-node/src/services/stopper.ts +69 -0
  683. package/bundle/hkp-node/src/services/storage.ts +332 -0
  684. package/bundle/hkp-node/src/services/store.ts +609 -0
  685. package/bundle/hkp-node/src/services/stream.ts +367 -0
  686. package/bundle/hkp-node/src/services/sub-service.ts +600 -0
  687. package/bundle/hkp-node/src/services/telegram-listener.ts +263 -0
  688. package/bundle/hkp-node/src/services/telegram-sender.ts +136 -0
  689. package/bundle/hkp-node/src/services/text-generation.ts +1249 -0
  690. package/bundle/hkp-node/src/services/timer.ts +273 -0
  691. package/bundle/hkp-node/src/services/tracks.ts +381 -0
  692. package/bundle/hkp-node/src/services/websocket-reader.ts +364 -0
  693. package/bundle/hkp-node/src/types.ts +465 -0
  694. package/bundle/hkp-node/src/wsTickets.ts +77 -0
  695. package/bundle/hkp-node/src/yas.ts +95 -0
  696. package/bundle/hkp-python/src/hkp/__init__.py +1 -0
  697. package/bundle/hkp-python/src/hkp/__main__.py +241 -0
  698. package/bundle/hkp-python/src/hkp/address.py +56 -0
  699. package/bundle/hkp-python/src/hkp/assets.py +439 -0
  700. package/bundle/hkp-python/src/hkp/auth.py +252 -0
  701. package/bundle/hkp-python/src/hkp/binary_frame.py +100 -0
  702. package/bundle/hkp-python/src/hkp/call_report.py +201 -0
  703. package/bundle/hkp-python/src/hkp/coordinator_links.py +667 -0
  704. package/bundle/hkp-python/src/hkp/data.py +118 -0
  705. package/bundle/hkp-python/src/hkp/egress.py +310 -0
  706. package/bundle/hkp-python/src/hkp/mount.py +46 -0
  707. package/bundle/hkp-python/src/hkp/mounts.py +288 -0
  708. package/bundle/hkp-python/src/hkp/origins.py +169 -0
  709. package/bundle/hkp-python/src/hkp/runtime.py +1131 -0
  710. package/bundle/hkp-python/src/hkp/secrets.py +261 -0
  711. package/bundle/hkp-python/src/hkp/server.py +1714 -0
  712. package/bundle/hkp-python/src/hkp/services/__init__.py +0 -0
  713. package/bundle/hkp-python/src/hkp/services/asset.py +123 -0
  714. package/bundle/hkp-python/src/hkp/services/audio_encode.py +190 -0
  715. package/bundle/hkp-python/src/hkp/services/expression.py +942 -0
  716. package/bundle/hkp-python/src/hkp/services/gather.py +266 -0
  717. package/bundle/hkp-python/src/hkp/services/hold.py +246 -0
  718. package/bundle/hkp-python/src/hkp/services/http_client.py +444 -0
  719. package/bundle/hkp-python/src/hkp/services/http_server.py +790 -0
  720. package/bundle/hkp-python/src/hkp/services/iterator.py +141 -0
  721. package/bundle/hkp-python/src/hkp/services/join.py +173 -0
  722. package/bundle/hkp-python/src/hkp/services/map_service.py +452 -0
  723. package/bundle/hkp-python/src/hkp/services/monitor.py +99 -0
  724. package/bundle/hkp-python/src/hkp/services/nested_pipeline.py +278 -0
  725. package/bundle/hkp-python/src/hkp/services/skill_router.py +388 -0
  726. package/bundle/hkp-python/src/hkp/services/speech_to_text.py +186 -0
  727. package/bundle/hkp-python/src/hkp/services/spread.py +114 -0
  728. package/bundle/hkp-python/src/hkp/services/stopper.py +59 -0
  729. package/bundle/hkp-python/src/hkp/services/sub_service.py +417 -0
  730. package/bundle/hkp-python/src/hkp/services/text_generation.py +498 -0
  731. package/bundle/hkp-python/src/hkp/services/text_to_speech.py +237 -0
  732. package/bundle/hkp-python/src/hkp/services/timer.py +267 -0
  733. package/bundle/hkp-python/src/hkp/services/tracks.py +251 -0
  734. package/bundle/hkp-python/src/hkp/types.py +412 -0
  735. package/bundle/hkp-python/src/hkp/ws_tickets.py +54 -0
  736. package/bundle/hkp-python/src/hkp/yas.py +192 -0
  737. package/bundle/hkp-rt/lib/CMakeLists.txt +193 -0
  738. package/bundle/hkp-rt/lib/hkp-bundles/CMakeLists.txt +55 -0
  739. package/bundle/hkp-rt/lib/hkp-bundles/plugin_register.cpp +14 -0
  740. package/bundle/hkp-rt/lib/include/app.h +201 -0
  741. package/bundle/hkp-rt/lib/include/assets.h +239 -0
  742. package/bundle/hkp-rt/lib/include/auth.h +167 -0
  743. package/bundle/hkp-rt/lib/include/call_report.h +197 -0
  744. package/bundle/hkp-rt/lib/include/coordinator_links.h +127 -0
  745. package/bundle/hkp-rt/lib/include/discovery_hooks.h +29 -0
  746. package/bundle/hkp-rt/lib/include/egress.h +379 -0
  747. package/bundle/hkp-rt/lib/include/files.h +275 -0
  748. package/bundle/hkp-rt/lib/include/log_entry.h +131 -0
  749. package/bundle/hkp-rt/lib/include/mounts.h +139 -0
  750. package/bundle/hkp-rt/lib/include/origins.h +363 -0
  751. package/bundle/hkp-rt/lib/include/registry.h +119 -0
  752. package/bundle/hkp-rt/lib/include/secrets.h +414 -0
  753. package/bundle/hkp-rt/lib/include/server.h +122 -0
  754. package/bundle/hkp-rt/lib/include/service.h +190 -0
  755. package/bundle/hkp-rt/lib/include/slot_store.h +41 -0
  756. package/bundle/hkp-rt/lib/include/standalone_config.h +253 -0
  757. package/bundle/hkp-rt/lib/include/types/audio.h +3 -0
  758. package/bundle/hkp-rt/lib/include/types/data.h +152 -0
  759. package/bundle/hkp-rt/lib/include/types/message.h +56 -0
  760. package/bundle/hkp-rt/lib/include/types/models.h +154 -0
  761. package/bundle/hkp-rt/lib/include/types/owns_me.h +19 -0
  762. package/bundle/hkp-rt/lib/include/types/ringbuffer.h +86 -0
  763. package/bundle/hkp-rt/lib/include/types/types.h +33 -0
  764. package/bundle/hkp-rt/lib/include/types/validation.h +205 -0
  765. package/bundle/hkp-rt/lib/src/address.h +88 -0
  766. package/bundle/hkp-rt/lib/src/app.cpp +591 -0
  767. package/bundle/hkp-rt/lib/src/assets.cpp +686 -0
  768. package/bundle/hkp-rt/lib/src/binary_frame.h +154 -0
  769. package/bundle/hkp-rt/lib/src/common/expression.h +1481 -0
  770. package/bundle/hkp-rt/lib/src/common/inja.h +36 -0
  771. package/bundle/hkp-rt/lib/src/common/link_socket.cpp +437 -0
  772. package/bundle/hkp-rt/lib/src/common/link_socket.h +97 -0
  773. package/bundle/hkp-rt/lib/src/common/realtime_wakeup.h +120 -0
  774. package/bundle/hkp-rt/lib/src/common/stream_broadcast.h +250 -0
  775. package/bundle/hkp-rt/lib/src/common/string_util.h +31 -0
  776. package/bundle/hkp-rt/lib/src/common/websocket_client_session.cpp +217 -0
  777. package/bundle/hkp-rt/lib/src/common/websocket_client_session.h +37 -0
  778. package/bundle/hkp-rt/lib/src/common/websocket_config.h +119 -0
  779. package/bundle/hkp-rt/lib/src/common/websocket_protocol.h +32 -0
  780. package/bundle/hkp-rt/lib/src/common/websocket_server.cpp +82 -0
  781. package/bundle/hkp-rt/lib/src/common/websocket_server.h +57 -0
  782. package/bundle/hkp-rt/lib/src/common/websocket_server_listener.cpp +115 -0
  783. package/bundle/hkp-rt/lib/src/common/websocket_server_listener.h +35 -0
  784. package/bundle/hkp-rt/lib/src/common/websocket_server_session.cpp +172 -0
  785. package/bundle/hkp-rt/lib/src/common/websocket_server_session.h +53 -0
  786. package/bundle/hkp-rt/lib/src/common.h +13 -0
  787. package/bundle/hkp-rt/lib/src/coordinator_links.cpp +1142 -0
  788. package/bundle/hkp-rt/lib/src/discovery/discovery.cpp +707 -0
  789. package/bundle/hkp-rt/lib/src/discovery/discovery.h +89 -0
  790. package/bundle/hkp-rt/lib/src/http/auth.cpp +569 -0
  791. package/bundle/hkp-rt/lib/src/http/front_door.cpp +518 -0
  792. package/bundle/hkp-rt/lib/src/http/front_door.h +131 -0
  793. package/bundle/hkp-rt/lib/src/http/server.cpp +1539 -0
  794. package/bundle/hkp-rt/lib/src/http/ws_tickets.h +125 -0
  795. package/bundle/hkp-rt/lib/src/mount.h +63 -0
  796. package/bundle/hkp-rt/lib/src/process_context.h +335 -0
  797. package/bundle/hkp-rt/lib/src/process_depth.h +20 -0
  798. package/bundle/hkp-rt/lib/src/registry.cpp +233 -0
  799. package/bundle/hkp-rt/lib/src/runtime.cpp +986 -0
  800. package/bundle/hkp-rt/lib/src/runtime.h +225 -0
  801. package/bundle/hkp-rt/lib/src/runtime_host.h +180 -0
  802. package/bundle/hkp-rt/lib/src/service.cpp +315 -0
  803. package/bundle/hkp-rt/lib/src/services/asset.h +185 -0
  804. package/bundle/hkp-rt/lib/src/services/audio_encode.cpp +372 -0
  805. package/bundle/hkp-rt/lib/src/services/audio_encode.h +97 -0
  806. package/bundle/hkp-rt/lib/src/services/cache.h +362 -0
  807. package/bundle/hkp-rt/lib/src/services/cache_subservices.h +463 -0
  808. package/bundle/hkp-rt/lib/src/services/core_audio.cpp +384 -0
  809. package/bundle/hkp-rt/lib/src/services/core_audio.h +22 -0
  810. package/bundle/hkp-rt/lib/src/services/core_input.h +337 -0
  811. package/bundle/hkp-rt/lib/src/services/core_output.h +370 -0
  812. package/bundle/hkp-rt/lib/src/services/ffmpeg/detail.cpp +749 -0
  813. package/bundle/hkp-rt/lib/src/services/ffmpeg/detail.h +29 -0
  814. package/bundle/hkp-rt/lib/src/services/ffmpeg/ffmpeg.cpp +193 -0
  815. package/bundle/hkp-rt/lib/src/services/ffmpeg/ffmpeg.h +40 -0
  816. package/bundle/hkp-rt/lib/src/services/ffmpeg/types.h +120 -0
  817. package/bundle/hkp-rt/lib/src/services/fft_service.h +190 -0
  818. package/bundle/hkp-rt/lib/src/services/filesystem.h +309 -0
  819. package/bundle/hkp-rt/lib/src/services/filter.h +208 -0
  820. package/bundle/hkp-rt/lib/src/services/gather.h +272 -0
  821. package/bundle/hkp-rt/lib/src/services/hold.h +304 -0
  822. package/bundle/hkp-rt/lib/src/services/http_client.cpp +347 -0
  823. package/bundle/hkp-rt/lib/src/services/http_client.h +67 -0
  824. package/bundle/hkp-rt/lib/src/services/http_client_impl.h +45 -0
  825. package/bundle/hkp-rt/lib/src/services/http_client_template_examples.md +202 -0
  826. package/bundle/hkp-rt/lib/src/services/http_server/http_listener.cpp +130 -0
  827. package/bundle/hkp-rt/lib/src/services/http_server/http_listener.h +33 -0
  828. package/bundle/hkp-rt/lib/src/services/http_server/http_server.cpp +351 -0
  829. package/bundle/hkp-rt/lib/src/services/http_server/http_server.h +79 -0
  830. package/bundle/hkp-rt/lib/src/services/http_server/http_server_impl.cpp +230 -0
  831. package/bundle/hkp-rt/lib/src/services/http_server/http_server_impl.h +64 -0
  832. package/bundle/hkp-rt/lib/src/services/http_server/http_server_subservices.cpp +950 -0
  833. package/bundle/hkp-rt/lib/src/services/http_server/http_server_subservices.h +253 -0
  834. package/bundle/hkp-rt/lib/src/services/http_server/http_session.cpp +495 -0
  835. package/bundle/hkp-rt/lib/src/services/http_server/http_session.h +169 -0
  836. package/bundle/hkp-rt/lib/src/services/http_server/http_stream_listener.cpp +270 -0
  837. package/bundle/hkp-rt/lib/src/services/http_server/http_stream_listener.h +104 -0
  838. package/bundle/hkp-rt/lib/src/services/http_server/request_decode.h +164 -0
  839. package/bundle/hkp-rt/lib/src/services/if_service.h +210 -0
  840. package/bundle/hkp-rt/lib/src/services/ifft_service.h +198 -0
  841. package/bundle/hkp-rt/lib/src/services/injector.h +162 -0
  842. package/bundle/hkp-rt/lib/src/services/iterator.h +201 -0
  843. package/bundle/hkp-rt/lib/src/services/map.h +641 -0
  844. package/bundle/hkp-rt/lib/src/services/monitor.h +161 -0
  845. package/bundle/hkp-rt/lib/src/services/mp3_frames.h +119 -0
  846. package/bundle/hkp-rt/lib/src/services/mp4_to_wav.cpp +281 -0
  847. package/bundle/hkp-rt/lib/src/services/mp4_to_wav.h +263 -0
  848. package/bundle/hkp-rt/lib/src/services/peer_server/peer_registry.cpp +88 -0
  849. package/bundle/hkp-rt/lib/src/services/peer_server/peer_registry.h +43 -0
  850. package/bundle/hkp-rt/lib/src/services/peer_server/peer_server.h +163 -0
  851. package/bundle/hkp-rt/lib/src/services/peer_server/peer_server_listener.cpp +133 -0
  852. package/bundle/hkp-rt/lib/src/services/peer_server/peer_server_listener.h +46 -0
  853. package/bundle/hkp-rt/lib/src/services/peer_server/peer_server_session.cpp +165 -0
  854. package/bundle/hkp-rt/lib/src/services/peer_server/peer_server_session.h +44 -0
  855. package/bundle/hkp-rt/lib/src/services/peer_server/peer_ws_session.cpp +184 -0
  856. package/bundle/hkp-rt/lib/src/services/peer_server/peer_ws_session.h +55 -0
  857. package/bundle/hkp-rt/lib/src/services/receiver_session.h +172 -0
  858. package/bundle/hkp-rt/lib/src/services/root_certificates.h +3959 -0
  859. package/bundle/hkp-rt/lib/src/services/sha256_openssl.h +49 -0
  860. package/bundle/hkp-rt/lib/src/services/speech_http.h +306 -0
  861. package/bundle/hkp-rt/lib/src/services/speech_to_text.cpp +595 -0
  862. package/bundle/hkp-rt/lib/src/services/speech_to_text.h +69 -0
  863. package/bundle/hkp-rt/lib/src/services/static.cpp +45 -0
  864. package/bundle/hkp-rt/lib/src/services/static.h +41 -0
  865. package/bundle/hkp-rt/lib/src/services/stopper.h +72 -0
  866. package/bundle/hkp-rt/lib/src/services/sub_service.h +286 -0
  867. package/bundle/hkp-rt/lib/src/services/text_generation.cpp +1132 -0
  868. package/bundle/hkp-rt/lib/src/services/text_generation.h +71 -0
  869. package/bundle/hkp-rt/lib/src/services/text_to_speech.cpp +853 -0
  870. package/bundle/hkp-rt/lib/src/services/text_to_speech.h +97 -0
  871. package/bundle/hkp-rt/lib/src/services/timer.h +300 -0
  872. package/bundle/hkp-rt/lib/src/services/tracks.h +397 -0
  873. package/bundle/hkp-rt/lib/src/services/tts_inflect.cpp +149 -0
  874. package/bundle/hkp-rt/lib/src/services/tts_inflect.h +82 -0
  875. package/bundle/hkp-rt/lib/src/services/wav_reader.h +213 -0
  876. package/bundle/hkp-rt/lib/src/services/websocket_client_service.h +182 -0
  877. package/bundle/hkp-rt/lib/src/services/websocket_reader.h +148 -0
  878. package/bundle/hkp-rt/lib/src/services/websocket_server_service.h +196 -0
  879. package/bundle/hkp-rt/lib/src/services/websocket_writer.cpp +641 -0
  880. package/bundle/hkp-rt/lib/src/services/websocket_writer.h +117 -0
  881. package/bundle/hkp-rt/lib/src/sub_runtime.cpp +234 -0
  882. package/bundle/hkp-rt/lib/src/sub_runtime.h +160 -0
  883. package/bundle/hkp-rt/lib/src/types/data.cpp +237 -0
  884. package/bundle/hkp-rt/lib/src/types/message.cpp +148 -0
  885. package/bundle/hkp-rt/lib/src/types/models.cpp +83 -0
  886. package/bundle/hkp-rt/lib/src/types/ringbuffer.cpp +336 -0
  887. package/bundle/hkp-rt/lib/src/uuid.h +12 -0
  888. package/bundle/readymade-mcp-commits.json +5 -0
  889. package/dist/cli.d.ts +3 -0
  890. package/dist/cli.d.ts.map +1 -0
  891. package/dist/cli.js +79 -0
  892. package/dist/git.d.ts +8 -0
  893. package/dist/git.d.ts.map +1 -0
  894. package/dist/git.js +53 -0
  895. package/dist/http.d.ts +17 -0
  896. package/dist/http.d.ts.map +1 -0
  897. package/dist/http.js +130 -0
  898. package/dist/index.d.ts +7 -0
  899. package/dist/index.d.ts.map +1 -0
  900. package/dist/index.js +6 -0
  901. package/dist/repository.d.ts +49 -0
  902. package/dist/repository.d.ts.map +1 -0
  903. package/dist/repository.js +452 -0
  904. package/dist/schemas.d.ts +156 -0
  905. package/dist/schemas.d.ts.map +1 -0
  906. package/dist/schemas.js +43 -0
  907. package/dist/server.d.ts +4 -0
  908. package/dist/server.d.ts.map +1 -0
  909. package/dist/server.js +83 -0
  910. package/dist/types.d.ts +63 -0
  911. package/dist/types.d.ts.map +1 -0
  912. package/dist/types.js +20 -0
  913. package/dist/validation.d.ts +9 -0
  914. package/dist/validation.d.ts.map +1 -0
  915. package/dist/validation.js +101 -0
  916. package/package.json +52 -4
@@ -0,0 +1,1345 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ import { randomUUID } from "node:crypto";
3
+
4
+ import {
5
+ Caller,
6
+ HostedService,
7
+ HostedServiceFactory,
8
+ JsonRecord,
9
+ LogEntry,
10
+ LogLevel,
11
+ ProcessContext,
12
+ RuntimeConfiguration,
13
+ RuntimeDescriptor,
14
+ RuntimeHost,
15
+ RuntimeNotification,
16
+ RunActor,
17
+ RuntimeScope,
18
+ ServiceCreator,
19
+ ServiceConfiguration,
20
+ ServiceDescriptor,
21
+ SlotStore,
22
+ } from "./types";
23
+ import { ADDRESS_SEPARATOR, descend, splitAddress } from "./address";
24
+ import { SecretVault } from "./secrets";
25
+ import { AssetDescriptor, AssetStore, AssetStoreOptions } from "./assets";
26
+ import { ANONYMOUS_SUB, AuthenticatedUser } from "./auth";
27
+ import { MountHandle, MountHandlers } from "./mounts";
28
+
29
+ /** Severity order, so a runtime can drop anything below what it records. */
30
+ export const LOG_LEVELS: Record<LogLevel, number> = {
31
+ debug: 0,
32
+ info: 1,
33
+ warn: 2,
34
+ error: 3,
35
+ };
36
+
37
+ /** A run with no parent: something outside the board asked for this. */
38
+ export const DEFAULT_PERSON_RUN_TTL_MS = 15 * 60 * 1000;
39
+
40
+ export function newRun(
41
+ kind: Exclude<RunActor["kind"], "person"> = "board",
42
+ ): ProcessContext {
43
+ return { runId: randomUUID(), actor: { kind } };
44
+ }
45
+
46
+ /**
47
+ * Reads a context a peer sent, filling in what it left out.
48
+ *
49
+ * A caller that names no run is not continuing one, so a run is begun rather
50
+ * than left unidentified — work that cannot be attributed to anything is worse
51
+ * than work attributed to a run of its own. Returns undefined only when there
52
+ * was no context at all, which lets the caller decide.
53
+ */
54
+ export function contextFromWire(value: unknown): ProcessContext | undefined {
55
+ if (typeof value !== "object" || value === null) {
56
+ return undefined;
57
+ }
58
+ const wire = value as Record<string, unknown>;
59
+ const str = (key: string): string | undefined =>
60
+ typeof wire[key] === "string" && wire[key] ? (wire[key] as string) : undefined;
61
+
62
+ return {
63
+ runId: str("runId") ?? randomUUID(),
64
+ parentRunId: str("parentRunId"),
65
+ requestId: str("requestId"),
66
+ actor: { kind: "local" },
67
+ };
68
+ }
69
+
70
+ /**
71
+ * Who a signed-in user is to a run they begin, or nothing where there is no
72
+ * identity to state.
73
+ *
74
+ * A client holding a token reaches only the runtimes of its own tenant, so it
75
+ * is their owner. A member reaches somebody else's board only through its
76
+ * coordinator, which says so itself.
77
+ *
78
+ * A server running without authentication resolves everybody to the one
79
+ * anonymous tenant. That is *no caller*, not a caller called anonymous —
80
+ * otherwise everybody on a development machine would be the same person.
81
+ */
82
+ export function callerOf(user: AuthenticatedUser | undefined): Caller | undefined {
83
+ if (!user || user.sub === ANONYMOUS_SUB) {
84
+ return undefined;
85
+ }
86
+ return {
87
+ sub: user.sub,
88
+ ...(user.email ? { email: user.email } : {}),
89
+ role: "owner",
90
+ };
91
+ }
92
+
93
+ /**
94
+ * The context of a run a client holding a token begins: a REST process call,
95
+ * or a `processRuntime` on a runtime's socket.
96
+ *
97
+ * The run metadata the client sent is kept, as `contextFromWire` reads it.
98
+ * Whatever it said about a caller is not read at all: who is calling is what
99
+ * the server verified, so a client cannot act in another person's name by
100
+ * saying so.
101
+ */
102
+ export function contextForClient(
103
+ wire: unknown,
104
+ user: AuthenticatedUser | undefined,
105
+ ): ProcessContext {
106
+ const context =
107
+ contextFromWire(wire) ?? newRun("local");
108
+ const caller = callerOf(user);
109
+ return {
110
+ ...context,
111
+ actor: caller
112
+ ? {
113
+ kind: "person",
114
+ ...caller,
115
+ expiresAt: Date.now() + DEFAULT_PERSON_RUN_TTL_MS,
116
+ }
117
+ : { kind: "local" },
118
+ };
119
+ }
120
+
121
+ /** A caller as a participant link states one, or nothing when the shape is off. */
122
+ export function callerFromWire(value: unknown): Caller | undefined {
123
+ if (typeof value !== "object" || value === null) {
124
+ return undefined;
125
+ }
126
+ const wire = value as Record<string, unknown>;
127
+ if (typeof wire.sub !== "string" || !wire.sub) {
128
+ return undefined;
129
+ }
130
+ return {
131
+ sub: wire.sub,
132
+ ...(typeof wire.email === "string" && wire.email
133
+ ? { email: wire.email }
134
+ : {}),
135
+ ...(typeof wire.name === "string" && wire.name ? { name: wire.name } : {}),
136
+ ...(wire.role === "owner" || wire.role === "member"
137
+ ? { role: wire.role }
138
+ : {}),
139
+ };
140
+ }
141
+
142
+ /** An actor as a trusted participant link states it. */
143
+ export function actorFromWire(value: unknown): RunActor | undefined {
144
+ if (typeof value !== "object" || value === null) {
145
+ return undefined;
146
+ }
147
+ const wire = value as Record<string, unknown>;
148
+ if (wire.kind === "person") {
149
+ const caller = callerFromWire(wire);
150
+ return caller
151
+ ? {
152
+ kind: "person",
153
+ ...caller,
154
+ expiresAt:
155
+ typeof wire.expiresAt === "number" &&
156
+ Number.isFinite(wire.expiresAt)
157
+ ? wire.expiresAt
158
+ : 0,
159
+ }
160
+ : { kind: "person", sub: "", expiresAt: 0 };
161
+ }
162
+ if (
163
+ wire.kind === "board" ||
164
+ wire.kind === "mount" ||
165
+ wire.kind === "local"
166
+ ) {
167
+ return { kind: wire.kind };
168
+ }
169
+ return undefined;
170
+ }
171
+
172
+ /**
173
+ * The context of a run as it is said over a participant link, by either end:
174
+ * a coordinator continuing a run on a runtime, or a runtime handing back what
175
+ * it produced.
176
+ *
177
+ * The one path on which a caller is taken as stated. The link is the board's
178
+ * own — opened by a runtime server with the board's ticket — and the
179
+ * coordinator on it is what verified the person. Kept apart from
180
+ * `contextFromWire` so that no other entry point can come to trust a caller by
181
+ * sharing a parser.
182
+ */
183
+ export function contextFromLink(value: unknown): ProcessContext | undefined {
184
+ const context = contextFromWire(value);
185
+ if (!context) {
186
+ return undefined;
187
+ }
188
+ const wire = value as Record<string, unknown>;
189
+ // The reply address belongs to whoever was waiting at the other end.
190
+ const { requestId: _requestId, ...run } = context;
191
+ return {
192
+ ...run,
193
+ actor: actorFromWire(wire.actor) ?? { kind: "board" },
194
+ };
195
+ }
196
+
197
+ /**
198
+ * A run as it is said to another runtime: which run, and who began it. The
199
+ * reply address is left out — it means something only to whoever is waiting.
200
+ */
201
+ export function contextToWire(
202
+ context: ProcessContext | null | undefined,
203
+ ): ProcessContext | undefined {
204
+ if (!context) {
205
+ return undefined;
206
+ }
207
+ return {
208
+ runId: context.runId,
209
+ ...(context.parentRunId ? { parentRunId: context.parentRunId } : {}),
210
+ actor: context.actor,
211
+ };
212
+ }
213
+
214
+ /**
215
+ * A run invoked from inside another one, as a nested pipeline is.
216
+ *
217
+ * The child gets an identity of its own rather than borrowing its parent's, so
218
+ * that work done inside a sub-pipeline stays distinguishable from work done
219
+ * around it — which is the whole difference between a trace that shows nesting
220
+ * and one that shows a flat list in timestamp order. The actor is inherited
221
+ * exactly, whether it is a person, the board, a mount, or local work.
222
+ */
223
+ export function childRun(parent: ProcessContext | null): ProcessContext {
224
+ return parent
225
+ ? {
226
+ runId: randomUUID(),
227
+ parentRunId: parent.runId,
228
+ actor: parent.actor,
229
+ }
230
+ : newRun();
231
+ }
232
+
233
+ /** Whether a run's delegated person authority is no longer valid. */
234
+ export function runExpired(
235
+ context: ProcessContext | null | undefined,
236
+ now = Date.now(),
237
+ ): boolean {
238
+ return (
239
+ context?.actor.kind === "person" && context.actor.expiresAt <= now
240
+ );
241
+ }
242
+
243
+ /**
244
+ * Where work that belongs to no call begins, across every runtime in the
245
+ * process. A run's frame remembers which of these it was begun under, and is
246
+ * only the current one while that still holds.
247
+ */
248
+ const detachments = new AsyncLocalStorage<object>();
249
+
250
+ /**
251
+ * Runs `fn` outside whatever run it was scheduled from.
252
+ *
253
+ * A callback keeps the async context of the code that scheduled it, so a tick
254
+ * armed from inside a call would otherwise go on reading that call's run —
255
+ * and its caller, and its expiry — for as long as it fires. A service that
256
+ * emits by itself wraps the emission in this, and what it emits begins a run
257
+ * of its own. Nested runtimes are covered by the one call: each keeps its own
258
+ * frame, and none of them outlives the detachment.
259
+ */
260
+ export function detached<T>(fn: () => T): T {
261
+ return detachments.run({}, fn);
262
+ }
263
+
264
+ /** One call in progress on a runtime; see `HostedRuntime.runState`. */
265
+ type RunFrame = {
266
+ context: ProcessContext;
267
+ service: string | null;
268
+ /** The detachment this frame was begun under; see `detached`. */
269
+ detachment: object | undefined;
270
+ /** Set once the call it stood for has returned; see `configureService`. */
271
+ ended?: boolean;
272
+ };
273
+
274
+ /**
275
+ * Grants a runtime's services public endpoints. Supplied by the server, which
276
+ * owns the listening socket; absent for inner sub-service pipelines, which are
277
+ * not addressable from outside.
278
+ */
279
+ export type RuntimeMounts = {
280
+ mount(
281
+ serviceUuid: string,
282
+ handlers: MountHandlers,
283
+ options?: { boardName?: string; mountName?: string },
284
+ ): MountHandle | null;
285
+ };
286
+
287
+ /**
288
+ * How a runtime reads a `file://` asset source, supplied by the server that owns
289
+ * the file store. Given the runtime's scope, because a path only means
290
+ * something inside the tenant it belongs to. Absent: no `file://` source
291
+ * resolves.
292
+ */
293
+ export type AssetFileReader = (
294
+ scope: RuntimeScope,
295
+ url: URL,
296
+ ) => Promise<Uint8Array | null>;
297
+
298
+ export class HostedRuntime implements RuntimeHost {
299
+ readonly id: string;
300
+ readonly name: string;
301
+ boardName: string;
302
+ /** The tenant this runtime answers to; see RuntimeScope. */
303
+ private owner: string;
304
+ /** See RuntimeConfiguration.garbageCollected. Absent means persist. */
305
+ readonly garbageCollected: boolean;
306
+
307
+ private readonly services = new Map<string, HostedService>();
308
+ private serviceOrder: string[] = [];
309
+ private readonly notificationTargets = new Set<
310
+ (notification: RuntimeNotification) => void
311
+ >();
312
+ private readonly resultTargets = new Set<
313
+ (result: unknown, context: ProcessContext | null) => void
314
+ >();
315
+ private readonly createService: ServiceCreator;
316
+ private readonly mounts?: RuntimeMounts;
317
+ /**
318
+ * Where this runtime's mounts are claimed, when it has no server of its own;
319
+ * see `delegateMounts`.
320
+ */
321
+ private mountsFrom:
322
+ | ((
323
+ serviceUuid: string,
324
+ handlers: MountHandlers,
325
+ options: { mountName?: string },
326
+ ) => MountHandle | null)
327
+ | null = null;
328
+ /**
329
+ * The call being processed right now, and which service it is inside.
330
+ *
331
+ * Async-local rather than a field: a pass awaits the services it calls, so
332
+ * two runs started independently — a timer tick and an arriving message —
333
+ * interleave freely. A plain field would let the second overwrite the first's
334
+ * context mid-await, and every log entry and captured run id after that point
335
+ * would name the wrong run. `AsyncLocalStorage` gives each run its own view
336
+ * and restores the outer one on the way out, which is what a nested pull
337
+ * needs.
338
+ */
339
+ private readonly runState = new AsyncLocalStorage<RunFrame>();
340
+ private readonly logTargets = new Set<(entry: LogEntry) => void>();
341
+ /** See RuntimeConfiguration.logData. A board-wide override, not the gate. */
342
+ private logData = true;
343
+ private capture = false;
344
+ /** Whether anything is recorded at all; see RuntimeConfiguration.logging. */
345
+ private logging = false;
346
+ /** The least severe level recorded; see RuntimeConfiguration.logLevel. */
347
+ private logLevel: LogLevel = "info";
348
+ /**
349
+ * Values for the references this runtime's services carry. Held apart from
350
+ * every service's state, and reachable only through `secrets()`.
351
+ */
352
+ private readonly vault = new SecretVault();
353
+ /**
354
+ * Where this runtime's secrets actually come from, when they are not its
355
+ * own; see `delegateSecrets`.
356
+ */
357
+ private secretsFrom: (() => SecretVault | null) | null = null;
358
+ /**
359
+ * The descriptors of the assets this runtime's services reference, and the
360
+ * content they resolve to. Reachable through `assets()`.
361
+ */
362
+ private readonly assetStore: AssetStore;
363
+ /**
364
+ * Where this runtime's assets actually come from, when they are not its own;
365
+ * see `delegateAssets`.
366
+ */
367
+ private assetsFrom: (() => AssetStore | null) | null = null;
368
+ /**
369
+ * The cells services in this runtime hold values in between passes.
370
+ *
371
+ * Owned rather than delegated by default: a runtime is the outermost thing a
372
+ * slot name can mean, so two services that name the same slot and are given
373
+ * nothing more specific share this one.
374
+ */
375
+ private readonly ownSlots: SlotStore = new Map<string, unknown>();
376
+ /**
377
+ * Where this runtime's slots actually come from, when they are not its own;
378
+ * see `delegateSlots`.
379
+ */
380
+ private slotsFrom: (() => SlotStore | null) | null = null;
381
+
382
+ constructor(
383
+ config: RuntimeConfiguration,
384
+ createService: (config: ServiceConfiguration) => HostedService,
385
+ mounts?: RuntimeMounts,
386
+ // Known only to whoever resolved the caller, so it is passed in rather than
387
+ // read from the config a client sent. Absent collapses to the single tenant
388
+ // an unauthenticated server already uses.
389
+ owner: string = ANONYMOUS_SUB,
390
+ readAssetFile?: AssetFileReader,
391
+ assetLimits?: AssetStoreOptions,
392
+ ) {
393
+ this.id = config.id;
394
+ this.name = config.name;
395
+ this.owner = owner;
396
+ this.boardName = config.boardName ?? "";
397
+ this.garbageCollected = config.garbageCollected === true;
398
+ this.logData = config.logData !== false;
399
+ this.logging = config.logging === true;
400
+ if (config.logLevel && config.logLevel in LOG_LEVELS) {
401
+ this.logLevel = config.logLevel;
402
+ }
403
+ this.createService = createService;
404
+ this.mounts = mounts;
405
+
406
+ // Before any service is built, because a service that opens a connection
407
+ // while being configured asks for its credential during `addService`.
408
+ if (config.secrets) {
409
+ this.vault.replace(config.secrets);
410
+ }
411
+ // Likewise assets: a service that loads its content while being configured
412
+ // asks for it during `addService`.
413
+ this.assetStore = new AssetStore(
414
+ {
415
+ readFile: readAssetFile
416
+ ? (url) => readAssetFile(this.scope(), url)
417
+ : undefined,
418
+ },
419
+ assetLimits,
420
+ );
421
+ if (config.assets) {
422
+ this.assetStore.replace(config.assets);
423
+ }
424
+
425
+ for (const serviceConfig of config.services) {
426
+ this.addService(serviceConfig);
427
+ }
428
+ }
429
+
430
+ serialize(outputUrl?: string): RuntimeDescriptor {
431
+ const descriptor: RuntimeDescriptor = {
432
+ id: this.id,
433
+ name: this.name,
434
+ garbageCollected: this.garbageCollected,
435
+ boardName: this.boardName,
436
+ services: this.listServices(),
437
+ inputs: [],
438
+ };
439
+
440
+ if (outputUrl) {
441
+ descriptor.outputUrl = outputUrl;
442
+ }
443
+
444
+ return descriptor;
445
+ }
446
+
447
+ listServices(): ServiceDescriptor[] {
448
+ return this.serviceOrder
449
+ .map((serviceId) => this.services.get(serviceId))
450
+ .filter((service): service is HostedService => Boolean(service))
451
+ .map((service) => ({
452
+ serviceId: service.serviceId,
453
+ serviceName: service.serviceName,
454
+ version: service.version,
455
+ capabilities: service.capabilities,
456
+ uuid: service.uuid,
457
+ state: service.getState(),
458
+ }));
459
+ }
460
+
461
+ /**
462
+ * The service an address names, flat or scoped.
463
+ *
464
+ * The flat list is asked first, so a uuid that happens to contain a dot is
465
+ * still that service rather than a path into something else. Only when no
466
+ * service carries the whole address is it read as one — see `address.ts`.
467
+ */
468
+ getService(address: string): HostedService | undefined {
469
+ const direct = this.services.get(address);
470
+ if (direct) {
471
+ return direct;
472
+ }
473
+ const segments = splitAddress(address);
474
+ if (segments.length < 2) {
475
+ return undefined;
476
+ }
477
+ const head = this.services.get(segments[0]);
478
+ return head ? descend(head, segments.slice(1)) : undefined;
479
+ }
480
+
481
+ addService(config: ServiceConfiguration): JsonRecord {
482
+ if (this.services.has(config.uuid)) {
483
+ throw new Error(`Service already exists: ${config.uuid}`);
484
+ }
485
+
486
+ const service = this.createService(config);
487
+
488
+ service.setHost?.(this);
489
+
490
+ this.services.set(service.uuid, service);
491
+ this.serviceOrder.push(service.uuid);
492
+ return service.getState();
493
+ }
494
+
495
+ configureService(
496
+ address: string,
497
+ config: JsonRecord,
498
+ context?: ProcessContext | null,
499
+ ): JsonRecord | null {
500
+ const service = this.getService(address);
501
+ if (!service) {
502
+ return null;
503
+ }
504
+ const configure = () =>
505
+ this.inService(service.uuid, () => service.configure(config));
506
+ if (!context) {
507
+ return configure();
508
+ }
509
+ // Configuring is over when `configure` returns, and the run ends with it.
510
+ // What a service arms while being configured — an interval, a socket, a
511
+ // poll — keeps this frame as its async context, and without an end it
512
+ // would go on emitting as whoever configured it, until their authority
513
+ // expired and the board stopped with it.
514
+ const frame: RunFrame = {
515
+ context,
516
+ service: this.currentService,
517
+ detachment: detachments.getStore(),
518
+ };
519
+ try {
520
+ return this.runState.run(frame, configure);
521
+ } finally {
522
+ frame.ended = true;
523
+ }
524
+ }
525
+
526
+ removeService(uuid: string): boolean {
527
+ const service = this.services.get(uuid);
528
+ service?.destroy?.();
529
+
530
+ const deleted = this.services.delete(uuid);
531
+ if (deleted) {
532
+ this.serviceOrder = this.serviceOrder.filter(
533
+ (serviceUuid) => serviceUuid !== uuid,
534
+ );
535
+ }
536
+ return deleted;
537
+ }
538
+
539
+ destroy(): void {
540
+ for (const service of this.services.values()) {
541
+ service.destroy?.();
542
+ }
543
+ this.services.clear();
544
+ this.serviceOrder = [];
545
+ this.notificationTargets.clear();
546
+ this.resultTargets.clear();
547
+ this.logTargets.clear();
548
+ }
549
+
550
+ registerNotificationTarget(
551
+ target: (notification: RuntimeNotification) => void,
552
+ ): () => void {
553
+ this.notificationTargets.add(target);
554
+ return () => {
555
+ this.notificationTargets.delete(target);
556
+ };
557
+ }
558
+
559
+ /**
560
+ * Where what this runtime emits goes. A target is told the run the value was
561
+ * emitted in, or null when it was emitted outside one, so that whoever
562
+ * carries it on can say which run it continues.
563
+ */
564
+ registerResultTarget(
565
+ target: (result: unknown, context: ProcessContext | null) => void,
566
+ ): () => void {
567
+ this.resultTargets.add(target);
568
+ return () => {
569
+ this.resultTargets.delete(target);
570
+ };
571
+ }
572
+
573
+ emitResult(output: unknown, context?: ProcessContext | null): void {
574
+ context = context ?? this.currentContext();
575
+ for (const target of this.resultTargets) {
576
+ target(output, context);
577
+ }
578
+ }
579
+
580
+ rearrangeServices(newOrder: string[]): boolean {
581
+ if (newOrder.length !== this.serviceOrder.length) {
582
+ return false;
583
+ }
584
+
585
+ const known = new Set(this.serviceOrder);
586
+ for (const uuid of newOrder) {
587
+ if (!known.has(uuid)) {
588
+ return false;
589
+ }
590
+ }
591
+
592
+ this.serviceOrder = [...newOrder];
593
+ return true;
594
+ }
595
+
596
+ process(
597
+ input: unknown,
598
+ onNotification: (notification: RuntimeNotification) => void,
599
+ context?: ProcessContext,
600
+ ): Promise<unknown> {
601
+ return this.withContext(context ?? newRun(), () =>
602
+ this.processFromIndex(0, input, onNotification),
603
+ );
604
+ }
605
+
606
+ // ── RuntimeHost ────────────────────────────────────────────────────────────
607
+
608
+ currentContext(): ProcessContext | null {
609
+ return this.frame?.context ?? null;
610
+ }
611
+
612
+ /** Which service the current pass is inside, for a log entry to name. */
613
+ private get currentService(): string | null {
614
+ return this.frame?.service ?? null;
615
+ }
616
+
617
+ /**
618
+ * The call in progress, or nothing when the frame this code inherited is no
619
+ * longer one: its call has returned, or the work has been detached from it.
620
+ */
621
+ private get frame(): RunFrame | undefined {
622
+ const frame = this.runState.getStore();
623
+ return frame &&
624
+ !frame.ended &&
625
+ frame.detachment === detachments.getStore()
626
+ ? frame
627
+ : undefined;
628
+ }
629
+
630
+ processFrom(
631
+ startAfterUuid: string,
632
+ input: unknown,
633
+ onNotification: (notification: RuntimeNotification) => void,
634
+ context?: ProcessContext,
635
+ ): Promise<unknown> {
636
+ const startIndex = this.serviceOrder.indexOf(startAfterUuid) + 1;
637
+ // Three ways to arrive here, and each wants a different run:
638
+ //
639
+ // - Named explicitly: a service that left its call and came back — an HTTP
640
+ // response, an awaited write — handing back what it captured.
641
+ // - Called from inside a call: a service pulling the services after it
642
+ // rather than returning to them. Still the same run, and the current
643
+ // context already says which, so nothing has to be threaded by hand.
644
+ // - Neither: a timer tick, an arriving message. Nothing to continue, so
645
+ // this begins a run.
646
+ //
647
+ // A service that leaves its call and forgets to capture lands in the third
648
+ // case, which splits its trace in two rather than attributing its work to
649
+ // whichever run happened to be in flight. Fragmentation is visible in a
650
+ // trace; misattribution reads as fact.
651
+ const runContext = context ?? this.currentContext() ?? newRun();
652
+
653
+ // A service pushing from itself (a Timer tick, an inbound message, a peer
654
+ // event) was never called by the loop below, so the loop never reported it.
655
+ // Report it here, or the UI shows a service producing nothing while the
656
+ // service after it plainly receives data.
657
+ this.emitNotification(
658
+ {
659
+ instanceId: startAfterUuid,
660
+ payload: { __internal: { state: "call-process", data: null } },
661
+ },
662
+ onNotification,
663
+ );
664
+ this.emitNotification(
665
+ {
666
+ instanceId: startAfterUuid,
667
+ payload: { __internal: { state: "call-process-finished", data: input } },
668
+ },
669
+ onNotification,
670
+ );
671
+
672
+ return this.withContext(runContext, () =>
673
+ this.processFromIndex(startIndex, input, onNotification),
674
+ );
675
+ }
676
+
677
+ /**
678
+ * Runs the pipeline starting **at** a service rather than after it.
679
+ *
680
+ * `processFrom` exists for a service handing work onward — it means "carry on
681
+ * behind me", so it advances past the caller. This is the other question:
682
+ * something outside the pipeline wants a particular service to do its job
683
+ * with a given payload, and that service must actually run.
684
+ *
685
+ * hkp-rt spells the same distinction as `processFrom(service, data,
686
+ * advanceBefore)`; kept as a separate entry point here so the advancing call,
687
+ * which every service uses, cannot change shape by accident.
688
+ */
689
+ processAt(
690
+ startAtUuid: string,
691
+ input: unknown,
692
+ onNotification: (notification: RuntimeNotification) => void,
693
+ context?: ProcessContext,
694
+ ): Promise<unknown> {
695
+ const startIndex = this.serviceOrder.indexOf(startAtUuid);
696
+ if (startIndex < 0) {
697
+ // Not one of this runtime's own: a scoped address enters the pipeline
698
+ // that holds it instead, and runs to the end of *that* list. What it
699
+ // produces is the nested pipeline's answer, and reaches the board the
700
+ // way that pipeline's answers always do.
701
+ const segments = splitAddress(startAtUuid);
702
+ const head = segments.length > 1 && this.services.get(segments[0]);
703
+ const enter = head ? head.processNested : undefined;
704
+ if (!head || !enter) {
705
+ throw new Error(`No such service: ${startAtUuid}`);
706
+ }
707
+ return enter.call(
708
+ head,
709
+ segments.slice(1).join(ADDRESS_SEPARATOR),
710
+ input,
711
+ context,
712
+ );
713
+ }
714
+ // Nothing to continue: whoever asked for this is outside the board, so it
715
+ // begins a run rather than joining one.
716
+ return this.withContext(context ?? newRun(), () =>
717
+ this.processFromIndex(startIndex, input, onNotification),
718
+ );
719
+ }
720
+
721
+ notify(
722
+ payload: unknown,
723
+ instanceId: string,
724
+ context?: ProcessContext | null,
725
+ ): void {
726
+ this.emitNotification(
727
+ { instanceId, payload, ...(context ? { context } : {}) },
728
+ () => {},
729
+ );
730
+ }
731
+
732
+ log(level: LogLevel, event: string, data?: unknown): void {
733
+ // Nothing to attribute an entry to means nothing worth recording: a service
734
+ // logging outside a call has no run, and an entry that names no run cannot
735
+ // be found again.
736
+ // Off means off: no entry is built, so nothing is spent deciding what it
737
+ // would have said.
738
+ if (
739
+ !this.logging ||
740
+ LOG_LEVELS[level] < LOG_LEVELS[this.logLevel] ||
741
+ this.logTargets.size === 0
742
+ ) {
743
+ return;
744
+ }
745
+
746
+ const run = this.currentContext();
747
+ if (!run) {
748
+ return;
749
+ }
750
+
751
+ const entry: LogEntry = {
752
+ runId: run.runId,
753
+ ts: new Date().toISOString(),
754
+ runtimeId: this.id,
755
+ serviceUuid: this.currentService ?? "",
756
+ level,
757
+ event,
758
+ };
759
+ if (run.parentRunId) {
760
+ entry.parentRunId = run.parentRunId;
761
+ }
762
+ if (run.actor.kind === "person") {
763
+ entry.caller = run.actor.sub;
764
+ }
765
+ if (this.logData && data !== undefined) {
766
+ entry.data = data;
767
+ }
768
+
769
+ for (const target of this.logTargets) {
770
+ target(entry);
771
+ }
772
+ }
773
+
774
+ /** service.processed, carrying how long the call took. */
775
+ private logProcessed(result: unknown, durationMs: number): void {
776
+ if (!this.logging || LOG_LEVELS.debug < LOG_LEVELS[this.logLevel]) {
777
+ return;
778
+ }
779
+ const before = this.logTargets.size;
780
+ const run = this.currentContext();
781
+ if (before === 0 || !run) {
782
+ return;
783
+ }
784
+ for (const target of this.logTargets) {
785
+ target({
786
+ runId: run.runId,
787
+ ...(run.parentRunId ? { parentRunId: run.parentRunId } : {}),
788
+ ...(run.actor.kind === "person" ? { caller: run.actor.sub } : {}),
789
+ ts: new Date().toISOString(),
790
+ runtimeId: this.id,
791
+ serviceUuid: this.currentService ?? "",
792
+ level: "debug",
793
+ event: "service.processed",
794
+ durationMs,
795
+ });
796
+ }
797
+ }
798
+
799
+ /**
800
+ * Where this runtime's entries go. The server registers one to carry them to
801
+ * the board's coordinator; a nested pipeline's host registers one to carry
802
+ * them out to the runtime around it.
803
+ */
804
+ registerLogTarget(target: (entry: LogEntry) => void): () => void {
805
+ this.logTargets.add(target);
806
+ return () => {
807
+ this.logTargets.delete(target);
808
+ };
809
+ }
810
+
811
+ /** Forwards an entry produced by a nested pipeline, unchanged. */
812
+ forwardLog(entry: LogEntry): void {
813
+ for (const target of this.logTargets) {
814
+ target(entry);
815
+ }
816
+ }
817
+
818
+ setLogData(enabled: boolean): void {
819
+ this.logData = enabled;
820
+ }
821
+
822
+ /**
823
+ * Capture mode: whether this runtime's call reports leave it with their
824
+ * values. Switched while the board runs and never part of its configuration,
825
+ * so a runtime always starts without it. Read where a report leaves the
826
+ * server (`outgoingPayload`), so a pipeline nested at any depth — whose
827
+ * reports reach the server only through this runtime — follows it too.
828
+ */
829
+ setCapture(enabled: boolean): void {
830
+ this.capture = enabled;
831
+ }
832
+
833
+ capturesCalls(): boolean {
834
+ return this.capture;
835
+ }
836
+
837
+ getLogData(): boolean {
838
+ return this.logData;
839
+ }
840
+
841
+ logSettings(): { logging: boolean; logData: boolean; logLevel: LogLevel } {
842
+ return {
843
+ logging: this.logging,
844
+ logData: this.logData,
845
+ logLevel: this.logLevel,
846
+ };
847
+ }
848
+
849
+ scope(): RuntimeScope {
850
+ return { owner: this.owner, boardName: this.boardName };
851
+ }
852
+
853
+ secrets(): SecretVault {
854
+ return this.secretsFrom?.() ?? this.vault;
855
+ }
856
+
857
+ /**
858
+ * Take secrets from somewhere else rather than from this runtime's own vault.
859
+ *
860
+ * A nested pipeline is a runtime nobody provisions: no create payload reaches
861
+ * it, so its own vault stays empty and a service inside it could never
862
+ * resolve a reference. What it does have is the runtime around it, which was
863
+ * provisioned — so it asks that one instead.
864
+ *
865
+ * Asked for each time rather than copied, so that a value pushed after a
866
+ * board is running reaches a nested service as immediately as a top-level
867
+ * one, and so that nesting composes: each level delegates outward until it
868
+ * reaches the runtime that was actually given something.
869
+ */
870
+ delegateSecrets(source: () => SecretVault | null): void {
871
+ this.secretsFrom = source;
872
+ }
873
+
874
+ assets(): AssetStore {
875
+ return this.assetsFrom?.() ?? this.assetStore;
876
+ }
877
+
878
+ /**
879
+ * Take assets from somewhere else rather than from this runtime's own store.
880
+ *
881
+ * The same arrangement as secrets: a nested pipeline is provisioned by nobody,
882
+ * so it asks the runtime around it — on each lookup, so an asset edited while
883
+ * the board runs is what a nested service resolves next, and so nesting
884
+ * composes outward to the runtime that was actually given the descriptors.
885
+ */
886
+ delegateAssets(source: () => AssetStore | null): void {
887
+ this.assetsFrom = source;
888
+ }
889
+
890
+ /**
891
+ * Takes in descriptors for assets this runtime's services reference, or
892
+ * `null` for one that was deleted. Merges, like secrets, because a client
893
+ * edits one asset at a time.
894
+ */
895
+ setAssets(entries: Record<string, AssetDescriptor | null>): void {
896
+ this.assetStore.merge(entries);
897
+ }
898
+
899
+ slots(): SlotStore {
900
+ return this.slotsFrom?.() ?? this.ownSlots;
901
+ }
902
+
903
+ /**
904
+ * Hold values somewhere other than this runtime's own cells.
905
+ *
906
+ * What a nested pipeline shares with is decided by the service hosting it: an
907
+ * endpoint that wants its two entry points to hold between them gives both of
908
+ * them one store, and a service with nothing to share passes its parent's
909
+ * along — so a slot named deep inside reaches the nearest owner that declared
910
+ * one, and nesting never isolates what is inside it by accident.
911
+ */
912
+ delegateSlots(source: () => SlotStore | null): void {
913
+ this.slotsFrom = source;
914
+ }
915
+
916
+ /**
917
+ * Takes in values for references this runtime's services already hold.
918
+ *
919
+ * Merges rather than replaces, because this is what a client editing one
920
+ * entry sends, and what a client re-pushes after a restart. Replacing on a
921
+ * partial push would strip credentials from services nobody touched.
922
+ */
923
+ setSecrets(entries: Record<string, { value: string; audience?: string[] }>): void {
924
+ this.vault.merge(entries);
925
+ }
926
+
927
+ /**
928
+ * Adopt the scope of the runtime around this one.
929
+ *
930
+ * A nested pipeline is built by the service hosting it, which knows neither
931
+ * tenant nor board — so a runtime created that way starts anonymous, and
932
+ * anything it stores would land beside every other anonymous board's. The
933
+ * host hands it the scope once there is one to hand.
934
+ */
935
+ setScope(scope: RuntimeScope): void {
936
+ this.owner = scope.owner;
937
+ this.boardName = scope.boardName;
938
+ // Down, however deep it goes. A service holding a pipeline passes this to
939
+ // its own runtime, whose services pass it on again — which is what makes a
940
+ // `conversations` inside a `join` inside an `iterator` answer to the board
941
+ // it is actually part of rather than to the anonymous default.
942
+ for (const service of this.services.values()) {
943
+ service.setScope?.(scope);
944
+ }
945
+ }
946
+
947
+ setLogLevel(level: LogLevel): void {
948
+ this.logLevel = level;
949
+ }
950
+
951
+ getLogLevel(): LogLevel {
952
+ return this.logLevel;
953
+ }
954
+
955
+ setLogging(enabled: boolean): void {
956
+ this.logging = enabled;
957
+ }
958
+
959
+ getLogging(): boolean {
960
+ return this.logging;
961
+ }
962
+
963
+ mount(
964
+ serviceUuid: string,
965
+ handlers: MountHandlers,
966
+ options: { mountName?: string } = {},
967
+ ): MountHandle | null {
968
+ // A nested runtime has no server of its own, so it asks the one around it
969
+ // — the same arrangement as secrets and slots. Without this an endpoint
970
+ // inside a scope had no address at all, which made a scope something a
971
+ // board could not put an endpoint in.
972
+ if (this.mountsFrom) {
973
+ return this.mountsFrom(serviceUuid, handlers, options);
974
+ }
975
+ // The board comes from the runtime, not from the service: a mount's address
976
+ // is derived from where it sits, and a service does not know that.
977
+ return (
978
+ this.mounts?.mount(serviceUuid, handlers, {
979
+ boardName: this.boardName,
980
+ mountName: options.mountName,
981
+ }) ?? null
982
+ );
983
+ }
984
+
985
+ /**
986
+ * Claim mounts somewhere else rather than on a server of this runtime's own.
987
+ *
988
+ * Read on each call rather than copied, so a pipeline attached before its
989
+ * host could answer still reaches one, and so nesting composes outward to
990
+ * whichever runtime is actually listening.
991
+ */
992
+ delegateMounts(
993
+ source: (
994
+ serviceUuid: string,
995
+ handlers: MountHandlers,
996
+ options: { mountName?: string },
997
+ ) => MountHandle | null,
998
+ ): void {
999
+ this.mountsFrom = source;
1000
+ // The services here were built before this runtime could serve a mount, so
1001
+ // any that wanted one gave up on it. Now that there is somewhere to claim
1002
+ // from, they are told to ask again; a service with nothing to claim has
1003
+ // nothing to do here and does not answer.
1004
+ for (const service of this.services.values()) {
1005
+ service.remount?.();
1006
+ }
1007
+ }
1008
+
1009
+ // ──────────────────────────────────────────────────────────────────────────
1010
+
1011
+ /**
1012
+ * Runs `fn` with `context` as the current one, restoring what was there
1013
+ * before.
1014
+ *
1015
+ * Restoring rather than clearing is what makes this survive a service that
1016
+ * calls back into this runtime from inside its own `process` — the pull that
1017
+ * a cache miss or a router performs. That inner call is still part of the
1018
+ * outer run, and when it returns the outer loop has more services to visit,
1019
+ * so the context it was running under has to come back.
1020
+ *
1021
+ * Safe as ambient state only because a pass is synchronous: the loop never
1022
+ * yields, so no second call can interleave with this one and observe a
1023
+ * context that is not its own. A pass that awaited would need the context
1024
+ * threaded through the call instead.
1025
+ */
1026
+ private withContext<T>(context: ProcessContext, fn: () => T): T {
1027
+ // The service is carried alongside the context so both are restored
1028
+ // together on the way out of a nested pull.
1029
+ return this.runState.run(
1030
+ {
1031
+ context,
1032
+ service: this.currentService,
1033
+ detachment: detachments.getStore(),
1034
+ },
1035
+ fn,
1036
+ );
1037
+ }
1038
+
1039
+ /** Runs `fn` with the pass recorded as being inside `uuid`. */
1040
+ private inService<T>(uuid: string | null, fn: () => T): T {
1041
+ const store = this.frame;
1042
+ if (!store) {
1043
+ return fn();
1044
+ }
1045
+ const outer = store.service;
1046
+ store.service = uuid;
1047
+ try {
1048
+ return fn();
1049
+ } finally {
1050
+ store.service = outer;
1051
+ }
1052
+ }
1053
+
1054
+ private async processFromIndex(
1055
+ startIndex: number,
1056
+ input: unknown,
1057
+ onNotification: (notification: RuntimeNotification) => void,
1058
+ ): Promise<unknown> {
1059
+ let result: unknown = input;
1060
+
1061
+ for (const uuid of this.serviceOrder.slice(startIndex)) {
1062
+ if (runExpired(this.currentContext())) {
1063
+ this.log("warn", "run.expired");
1064
+ return null;
1065
+ }
1066
+ const service = this.services.get(uuid);
1067
+ if (!service) {
1068
+ continue;
1069
+ }
1070
+
1071
+ this.emitNotification(
1072
+ {
1073
+ instanceId: uuid,
1074
+ payload: {
1075
+ __internal: {
1076
+ state: "call-process",
1077
+ data: result,
1078
+ },
1079
+ },
1080
+ },
1081
+ onNotification,
1082
+ );
1083
+
1084
+ const startedAt = Date.now();
1085
+ // Restored rather than cleared, for the same reason the context is: a
1086
+ // service that pulls the ones after it re-enters this loop, and when it
1087
+ // returns the entries that follow still belong to the service that
1088
+ // pulled.
1089
+ const pending = this.inService(uuid, () => {
1090
+ // The flow itself, at debug: which service the runtime called, and
1091
+ // below, what it returned and how long it took.
1092
+ //
1093
+ // Deliberately without the value flowing through. The level says how
1094
+ // much of the shape of a run to keep, and turning it up must not also
1095
+ // start recording the data — a board author reaching for more detail
1096
+ // about *what ran* is not asking to write payloads to disk. What flows
1097
+ // through is recorded only where a service was configured to record it.
1098
+ this.log("debug", "service.process");
1099
+ const notificationContext = this.currentContext() ?? undefined;
1100
+ return service.process(result, (payload, instanceId) => {
1101
+ this.emitNotification(
1102
+ {
1103
+ instanceId: instanceId ?? uuid,
1104
+ payload,
1105
+ context: notificationContext,
1106
+ },
1107
+ onNotification,
1108
+ );
1109
+ });
1110
+ });
1111
+
1112
+ // A service may answer within the call or after it. Awaiting either is
1113
+ // what lets the one that answers late still be a service the pipeline
1114
+ // reads a result from, rather than one that has to call the rest of the
1115
+ // pipeline itself. Awaiting a plain value costs a microtask.
1116
+ result = isThenable(pending) ? await pending : pending;
1117
+ this.inService(uuid, () =>
1118
+ this.logProcessed(result, Date.now() - startedAt),
1119
+ );
1120
+
1121
+ this.emitNotification(
1122
+ {
1123
+ instanceId: uuid,
1124
+ payload: {
1125
+ __internal: {
1126
+ state: "call-process-finished",
1127
+ data: result,
1128
+ },
1129
+ },
1130
+ },
1131
+ onNotification,
1132
+ );
1133
+
1134
+ if (result === null || result === undefined) {
1135
+ // Where the run ended, named. Recorded above debug because it is the
1136
+ // outcome of the run rather than a step in it: a board that keeps only
1137
+ // what matters still wants to know its flow stopped, and where.
1138
+ this.inService(uuid, () => this.log("info", "pipeline.stopped"));
1139
+ break;
1140
+ }
1141
+ }
1142
+
1143
+ return result;
1144
+ }
1145
+
1146
+ private emitNotification(
1147
+ notification: RuntimeNotification,
1148
+ onNotification: (notification: RuntimeNotification) => void,
1149
+ ): void {
1150
+ const contextual = notification.context
1151
+ ? notification
1152
+ : {
1153
+ ...notification,
1154
+ context: this.currentContext() ?? newRun("board"),
1155
+ };
1156
+ onNotification(contextual);
1157
+ for (const target of this.notificationTargets) {
1158
+ target(contextual);
1159
+ }
1160
+ }
1161
+ }
1162
+
1163
+ /**
1164
+ * A single tenant's view of the runtime app. Runtime ids are only unique within
1165
+ * an owner — boards ship stable, human-readable ids (`node`, `chat-node`), so
1166
+ * two users loading the same board must each get their own runtime rather than
1167
+ * sharing one. Every route resolves runtimes through one of these views, so a
1168
+ * handler cannot reach another tenant's runtime even by id.
1169
+ */
1170
+ export class TenantRuntimes {
1171
+ constructor(
1172
+ readonly owner: string,
1173
+ private readonly app: RuntimeApp,
1174
+ ) {}
1175
+
1176
+ createRuntime(config: RuntimeConfiguration): HostedRuntime {
1177
+ return this.app.createRuntime(this.owner, config);
1178
+ }
1179
+
1180
+ getRuntime(runtimeId: string): HostedRuntime | undefined {
1181
+ return this.app.getRuntime(this.owner, runtimeId);
1182
+ }
1183
+
1184
+ getRuntimes(): HostedRuntime[] {
1185
+ return this.app.getRuntimes(this.owner);
1186
+ }
1187
+
1188
+ removeRuntime(runtimeId: string): boolean {
1189
+ return this.app.removeRuntime(this.owner, runtimeId);
1190
+ }
1191
+
1192
+ removeAllRuntimes(): void {
1193
+ this.app.removeAllRuntimes(this.owner);
1194
+ }
1195
+ }
1196
+
1197
+ /**
1198
+ * The space the runtimes of one deployed board live in.
1199
+ *
1200
+ * A runtime id is unique within a space, and there are two kinds. What a
1201
+ * tenant's clients create over the api shares one space, named by the tenant
1202
+ * alone. What a coordinator builds for a board gets a space per board, so two
1203
+ * boards that both call a runtime `node` each keep their own, and neither is
1204
+ * replaced by a client creating a runtime of that id.
1205
+ *
1206
+ * NUL occurs in neither part, so a board's space is never a tenant's.
1207
+ */
1208
+ export function boardSpace(owner: string, boardName: string): string {
1209
+ return `${owner}\u0000${boardName}`;
1210
+ }
1211
+
1212
+ export class RuntimeApp {
1213
+ // space → runtimeId → runtime. A tenant's own space is its owner key: the
1214
+ // authenticated `sub` (or "anonymous" when auth is off, collapsing to a
1215
+ // single bucket). A deployed board's is `boardSpace`.
1216
+ private readonly runtimes = new Map<string, Map<string, HostedRuntime>>();
1217
+
1218
+ constructor(
1219
+ private readonly registry: Map<string, HostedServiceFactory>,
1220
+ // Supplied by the server, which owns the listening socket. Absent in tests
1221
+ // and anywhere runtimes need no public endpoints.
1222
+ private readonly mountsFor?: (
1223
+ owner: string,
1224
+ runtimeId: string,
1225
+ space: string,
1226
+ ) => RuntimeMounts,
1227
+ // Supplied by the server, which owns the file store. Absent: no runtime
1228
+ // resolves a `file://` asset.
1229
+ private readonly readAssetFile?: AssetFileReader,
1230
+ // What an asset may weigh, which the server also sizes its requests by.
1231
+ private readonly assetLimits?: AssetStoreOptions,
1232
+ ) {}
1233
+
1234
+ /** A tenant-scoped view; the only way route handlers reach runtimes. */
1235
+ forOwner(owner: string): TenantRuntimes {
1236
+ return new TenantRuntimes(owner, this);
1237
+ }
1238
+
1239
+ /**
1240
+ * Builds a runtime for `owner`, replacing anything under its id in `space`:
1241
+ * the tenant's own unless a board's is named.
1242
+ */
1243
+ createRuntime(
1244
+ owner: string,
1245
+ config: RuntimeConfiguration,
1246
+ space: string = owner,
1247
+ ): HostedRuntime {
1248
+ const owned = this.ownerRuntimes(space);
1249
+ const existing = owned.get(config.id);
1250
+ existing?.destroy();
1251
+
1252
+ const runtime = new HostedRuntime(
1253
+ config,
1254
+ (serviceConfig) => this.createService(serviceConfig),
1255
+ this.mountsFor?.(owner, config.id, space),
1256
+ owner,
1257
+ this.readAssetFile,
1258
+ this.assetLimits,
1259
+ );
1260
+ owned.set(runtime.id, runtime);
1261
+ return runtime;
1262
+ }
1263
+
1264
+ getRuntime(space: string, runtimeId: string): HostedRuntime | undefined {
1265
+ return this.runtimes.get(space)?.get(runtimeId);
1266
+ }
1267
+
1268
+ getRuntimes(space: string): HostedRuntime[] {
1269
+ const owned = this.runtimes.get(space);
1270
+ return owned ? [...owned.values()] : [];
1271
+ }
1272
+
1273
+ /** The runtimes a tenant's deployed boards have here, whichever board. */
1274
+ getBoardRuntimes(owner: string): HostedRuntime[] {
1275
+ const boards = boardSpace(owner, "");
1276
+ return [...this.runtimes]
1277
+ .filter(([space]) => space.startsWith(boards))
1278
+ .flatMap(([, owned]) => [...owned.values()]);
1279
+ }
1280
+
1281
+ /** How many runtimes a tenant has, in its own space and its boards'. */
1282
+ countRuntimes(owner: string): number {
1283
+ return this.getRuntimes(owner).length + this.getBoardRuntimes(owner).length;
1284
+ }
1285
+
1286
+ removeRuntime(space: string, runtimeId: string): boolean {
1287
+ const owned = this.runtimes.get(space);
1288
+ if (!owned) {
1289
+ return false;
1290
+ }
1291
+ const runtime = owned.get(runtimeId);
1292
+ runtime?.destroy();
1293
+ const deleted = owned.delete(runtimeId);
1294
+ if (owned.size === 0) {
1295
+ this.runtimes.delete(space);
1296
+ }
1297
+ return deleted;
1298
+ }
1299
+
1300
+ /** Removes what is in a tenant's own space; a board's runtimes are its
1301
+ * coordinator's to remove. */
1302
+ removeAllRuntimes(owner: string): void {
1303
+ const owned = this.runtimes.get(owner);
1304
+ if (!owned) {
1305
+ return;
1306
+ }
1307
+ for (const runtime of owned.values()) {
1308
+ runtime.destroy();
1309
+ }
1310
+ this.runtimes.delete(owner);
1311
+ }
1312
+
1313
+ getRegistry() {
1314
+ return [...this.registry.values()].map((entry) => entry.descriptor);
1315
+ }
1316
+
1317
+ private ownerRuntimes(owner: string): Map<string, HostedRuntime> {
1318
+ const existing = this.runtimes.get(owner);
1319
+ if (existing) {
1320
+ return existing;
1321
+ }
1322
+ const created = new Map<string, HostedRuntime>();
1323
+ this.runtimes.set(owner, created);
1324
+ return created;
1325
+ }
1326
+
1327
+ createService(config: ServiceConfiguration): HostedService {
1328
+ const factory = this.registry.get(config.serviceId);
1329
+ if (!factory) {
1330
+ throw new Error(`Unknown serviceId: ${config.serviceId}`);
1331
+ }
1332
+ return factory.create(config, (serviceConfig) =>
1333
+ this.createService(serviceConfig),
1334
+ );
1335
+ }
1336
+ }
1337
+
1338
+ /** Whether a value is worth awaiting — a promise, or anything promise-like. */
1339
+ function isThenable(value: unknown): value is PromiseLike<unknown> {
1340
+ return (
1341
+ typeof value === "object" &&
1342
+ value !== null &&
1343
+ typeof (value as PromiseLike<unknown>).then === "function"
1344
+ );
1345
+ }