@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,1222 @@
1
+ /**
2
+ * Service Documentation
3
+ * Service ID: http-server-subservices
4
+ * Service Name: HttpServerSubservices
5
+ * Runtime: hkp-node
6
+ * Modes: none — the entry points a board declares say what it is for
7
+ * Key Config: bypass/onProcess/onRequest (the endpoint is assigned, not configured)
8
+ * IO: in=request envelope -> out=response envelope
9
+ * Arrays: not primary
10
+ * Binary: depends on endpoint + nested services
11
+ * MixedData: not native in runtime
12
+ *
13
+ * **There are two ways in, and a board names the ones it uses.** `onRequest` is
14
+ * a caller arriving; `onProcess` is a pass of the board's own chain. Each is a
15
+ * pipeline of its own, because they are different jobs:
16
+ *
17
+ * { "onRequest": [ … ] } requests; a pass goes through
18
+ * { "onProcess": [ … ] } passes; the board answers
19
+ * { "onProcess": [ … ], "onRequest": [ … ] } both, separately
20
+ * { "pipeline": [ … ] } one pipeline, entered from both
21
+ *
22
+ * **Declaring `onRequest` is what takes the answer away from the chain.** With
23
+ * one, that pipeline is the handler and what it returns is what the caller
24
+ * gets; the services after this one still run — that is where a board acts on
25
+ * having served a request — but after the answer is decided. Without one, the
26
+ * request flows into the services after this one and whatever they return is
27
+ * the answer, which is the inversion of control this service is built around
28
+ * and what makes a board able to answer an endpoint at all.
29
+ *
30
+ * So an endpoint can have something to run on a pass without silently becoming
31
+ * an HTTP handler, which is what a single unnamed pipeline could not express:
32
+ * having one at all decided who answered.
33
+ *
34
+ * **A value does not survive between the two on its own.** They are separate
35
+ * pipelines, and a pass ends where it ends — so an endpoint that publishes what
36
+ * the board last handed it holds that value in a slot (see `hold`), in cells
37
+ * this service owns and lends to both of its pipelines. Legacy boards say the
38
+ * same thing as `mode: "process_on_data"`, which is this arrangement built in
39
+ * and unnamed; `entryFor` is where the older spellings are read.
40
+ *
41
+ * **What the handler answers with is the envelope, read backwards.** A value
42
+ * carrying `meta.status` beside `body` or `binary` sets the status, the content
43
+ * type and the headers — the status being what distinguishes an answer from a
44
+ * request passed through unchanged, which is the same shape. Anything else is
45
+ * answered by its own type, which is JSON for everything a board returned
46
+ * before this existed. That is what lets one board
47
+ * serve a feed as XML and the next serve an audio file as audio — without which
48
+ * an endpoint can only ever say `application/json`, whatever it is holding.
49
+ *
50
+ * A `body` that is an `hkp-asset://<id>` reference is answered with that
51
+ * asset's content, resolved from the runtime's asset store as the answer is
52
+ * written, and with the asset's media type unless `meta` names one. A page an
53
+ * endpoint serves is therefore an asset of the board rather than a string in
54
+ * this service's state, and editing it changes the next response.
55
+ *
56
+ * Byte answers are seekable: `Range` is honoured against the bytes the handler
57
+ * produced, because a player dragging a scrubber asks for one and a server that
58
+ * ignores it re-sends the whole file each time.
59
+ *
60
+ * **A stream is the one answer that keeps coming** (see `./stream`). With
61
+ * `stream: { path, … }` declared, a GET on that path is a listener to keep, and
62
+ * what every pass produces is written to all of them. What a pass is — the
63
+ * previous service's output, a message a `websocket-reader` received, the
64
+ * result of the runtime before — is the board's business, not the endpoint's.
65
+ */
66
+ import { IncomingMessage, ServerResponse } from "node:http";
67
+
68
+ import { newRun } from "../runtime";
69
+ import { MountContext, MountHandle } from "../mounts";
70
+ import { parseAssetRef } from "../assets";
71
+ import {
72
+ HostedService,
73
+ JsonRecord,
74
+ ProcessContext,
75
+ RuntimeHost,
76
+ RuntimeScope,
77
+ ServiceConfiguration,
78
+ ServiceCreator,
79
+ ServiceRegistryEntry,
80
+ SlotStore,
81
+ } from "../types";
82
+ import { NestedPipeline } from "./nested-pipeline";
83
+ import {
84
+ StreamBroadcast,
85
+ StreamConfig,
86
+ StreamListener,
87
+ answerRangeProbe,
88
+ boundedRange,
89
+ parseStreamConfig,
90
+ sameStream,
91
+ streamBytes,
92
+ streamState,
93
+ } from "./stream";
94
+
95
+ export const httpServerSubservicesDescriptor: ServiceRegistryEntry = {
96
+ serviceId: "http-server-subservices",
97
+ serviceName: "HttpServerSubservices",
98
+ capabilities: ["subservices"],
99
+ };
100
+
101
+ /**
102
+ * How a board that predates named entry points says which side enters the one
103
+ * pipeline it declares. Still accepted, and still reported back to a board that
104
+ * arrived carrying it; `entryFor` is the whole of what it means now.
105
+ *
106
+ * - `process_on_session` — requests only; data from the outer chain passes
107
+ * through untouched.
108
+ * - `process_on_data` — data from the outer chain is stored and served back to
109
+ * requests verbatim; the nested pipeline is not used.
110
+ * - `process_on_both` — both entry points run the one pipeline, which is why a
111
+ * service inside it can only tell a request from a pass by looking at what it
112
+ * was given.
113
+ */
114
+ type HttpServerMode =
115
+ | "process_on_session"
116
+ | "process_on_data"
117
+ | "process_on_both";
118
+
119
+ /**
120
+ * The two ways into this service, named.
121
+ *
122
+ * `onProcess` is a pass of the board's own chain arriving; `onRequest` is a
123
+ * caller. They are declared as separate pipelines because they are separate
124
+ * jobs — which is what the `mode` flag above was standing in for, badly: one
125
+ * unnamed list could not say what it was for, so a flag beside it had to.
126
+ */
127
+ type EntryName = "onProcess" | "onRequest";
128
+
129
+ const ENTRY_NAMES: EntryName[] = ["onProcess", "onRequest"];
130
+
131
+ /**
132
+ * An incoming request as MixedData: JSON metadata plus the raw body. Mirrors
133
+ * hkp-rt's body-carrying HTTP service so the same pipeline works on either.
134
+ */
135
+ type MixedRequest = {
136
+ meta: JsonRecord;
137
+ /**
138
+ * The raw body, for content whose type does not say what the bytes mean.
139
+ * Absent once the body has been decoded into `body` — keeping both would
140
+ * double the payload to restate what the decoded value already carries — and
141
+ * absent entirely when the request had no body.
142
+ */
143
+ binary?: Uint8Array;
144
+ /**
145
+ * The body decoded according to its content type. Present for the cases a
146
+ * board can act on directly — a JSON webhook, a form post — instead of
147
+ * needing bytes decoded by hand. Absent when the type is not textual, when
148
+ * there is no body, or when it did not parse.
149
+ */
150
+ body?: unknown;
151
+ };
152
+
153
+ /** Content type with any parameters (`; charset=…`) stripped, lower-cased. */
154
+ function mediaType(contentType: string | undefined): string {
155
+ return (contentType ?? "").split(";")[0].trim().toLowerCase();
156
+ }
157
+
158
+ /**
159
+ * Decode a body for the content types where a board would otherwise be stuck
160
+ * with raw bytes. Returns undefined when there is nothing sensible to produce,
161
+ * which includes malformed input: a public endpoint receives whatever it is
162
+ * given, and a parse failure should leave the raw bytes to inspect rather than
163
+ * fail the request.
164
+ */
165
+ function decodeBody(
166
+ binary: Uint8Array,
167
+ contentType: string | undefined,
168
+ ): unknown {
169
+ if (binary.length === 0) {
170
+ return undefined;
171
+ }
172
+
173
+ const type = mediaType(contentType);
174
+ const asText = () => Buffer.from(binary).toString("utf8");
175
+
176
+ if (type === "application/json" || type.endsWith("+json")) {
177
+ try {
178
+ return JSON.parse(asText());
179
+ } catch {
180
+ return undefined;
181
+ }
182
+ }
183
+
184
+ if (type === "application/x-www-form-urlencoded") {
185
+ const fields: JsonRecord = {};
186
+ for (const [key, value] of new URLSearchParams(asText())) {
187
+ fields[key] = value;
188
+ }
189
+ return fields;
190
+ }
191
+
192
+ if (type.startsWith("text/")) {
193
+ return asText();
194
+ }
195
+
196
+ return undefined;
197
+ }
198
+
199
+ class RequestTooLargeError extends Error {
200
+ constructor() {
201
+ super("Request body too large");
202
+ }
203
+ }
204
+
205
+ function header(req: IncomingMessage, name: string): string | undefined {
206
+ const value = req.headers[name];
207
+ return Array.isArray(value) ? value[0] : value;
208
+ }
209
+
210
+ /** Extracts `filename="…"` from a content-disposition header, if present. */
211
+ function filenameFromDisposition(
212
+ disposition: string | undefined,
213
+ ): string | undefined {
214
+ if (!disposition) {
215
+ return undefined;
216
+ }
217
+ const match = disposition.match(/filename\*?=(?:UTF-8'')?"?([^";]+)"?/i);
218
+ return match ? decodeURIComponent(match[1]) : undefined;
219
+ }
220
+
221
+ /**
222
+ * What a pipeline may answer with, as against what it was given.
223
+ *
224
+ * The same envelope a request arrives in — `meta` beside `body` or `binary` —
225
+ * read in the other direction, which is also the shape `http-client` hands back
226
+ * for a call it made. So a board that proxies one endpoint to another passes
227
+ * the response along untouched, and a board that builds one writes the shape it
228
+ * already reads.
229
+ *
230
+ * Without an envelope the answer is JSON, which is what every board written
231
+ * before this got and still gets. The single exception is raw bytes, which are
232
+ * sent as bytes: JSON encoding them produced an object of numbered keys, and no
233
+ * board wanted that.
234
+ */
235
+ type MixedResponse = {
236
+ meta?: JsonRecord;
237
+ binary?: Uint8Array;
238
+ body?: unknown;
239
+ };
240
+
241
+ /** A response, reduced to the three things writing one needs. */
242
+ type Answer = {
243
+ status: number;
244
+ headers: JsonRecord;
245
+ /** The bytes to send; text and JSON are encoded before they get here. */
246
+ payload: Uint8Array;
247
+ };
248
+
249
+ function isBytes(value: unknown): value is Uint8Array {
250
+ return value instanceof Uint8Array;
251
+ }
252
+
253
+ /**
254
+ * Reads a value as a response envelope, or returns null when it is not one.
255
+ *
256
+ * **A status is what tells the two apart.** A request envelope and a response
257
+ * envelope are the same shape, and a pipeline that passes its input through
258
+ * returns the request — so reading any `meta` as a response would answer the
259
+ * caller with the content type they sent, and silently change what every board
260
+ * written before this did. A request has no status and a response always has
261
+ * one, which makes `meta.status` the one field that cannot be a coincidence.
262
+ *
263
+ * It is also what a proxied answer already carries: `http-client` reports a
264
+ * response as `{meta: {status, headers, contentType}, …}`, so a board that
265
+ * calls one endpoint and answers with what it got does nothing special.
266
+ */
267
+ function asResponseEnvelope(value: unknown): MixedResponse | null {
268
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
269
+ return null;
270
+ }
271
+ const candidate = value as MixedResponse;
272
+ const meta = candidate.meta;
273
+ if (!meta || typeof meta !== "object" || Array.isArray(meta)) {
274
+ return null;
275
+ }
276
+ if (typeof meta.status !== "number") {
277
+ return null;
278
+ }
279
+ const carries = "binary" in candidate || "body" in candidate;
280
+ return carries ? candidate : null;
281
+ }
282
+
283
+ /** Header map from an envelope's `meta.headers`, names lower-cased. */
284
+ function envelopeHeaders(meta: JsonRecord): JsonRecord {
285
+ const declared = meta.headers;
286
+ if (!declared || typeof declared !== "object" || Array.isArray(declared)) {
287
+ return {};
288
+ }
289
+ const headers: JsonRecord = {};
290
+ for (const [name, value] of Object.entries(declared as JsonRecord)) {
291
+ if (value !== null && value !== undefined) {
292
+ headers[name.toLowerCase()] = String(value);
293
+ }
294
+ }
295
+ return headers;
296
+ }
297
+
298
+ /**
299
+ * The answer a value stands for: an envelope's status, headers and payload, or
300
+ * the type-led default for a bare value.
301
+ */
302
+ function toAnswer(value: unknown): Answer {
303
+ const envelope = asResponseEnvelope(value);
304
+ if (envelope) {
305
+ const meta = envelope.meta ?? {};
306
+ const headers = envelopeHeaders(meta);
307
+ const status = typeof meta.status === "number" ? meta.status : 200;
308
+ const declaredType =
309
+ typeof meta.contentType === "string" ? meta.contentType : undefined;
310
+
311
+ if (isBytes(envelope.binary)) {
312
+ headers["content-type"] =
313
+ declaredType ?? headers["content-type"] ?? "application/octet-stream";
314
+ return { status, headers, payload: envelope.binary };
315
+ }
316
+
317
+ const body = envelope.body;
318
+ if (typeof body === "string") {
319
+ headers["content-type"] =
320
+ declaredType ?? headers["content-type"] ?? "text/plain; charset=utf-8";
321
+ return {
322
+ status,
323
+ headers,
324
+ payload: new Uint8Array(Buffer.from(body, "utf8")),
325
+ };
326
+ }
327
+
328
+ headers["content-type"] =
329
+ declaredType ?? headers["content-type"] ?? "application/json";
330
+ return {
331
+ status,
332
+ headers,
333
+ payload: new Uint8Array(
334
+ Buffer.from(JSON.stringify(body ?? null), "utf8"),
335
+ ),
336
+ };
337
+ }
338
+
339
+ // Bytes are the one value whose own type says what to do with it: JSON
340
+ // encoding a Uint8Array produces an object of numbered keys, which is nothing
341
+ // anybody asked for. Everything else stays JSON, including a bare string —
342
+ // that is what a board serving a held value already answers with, and a board
343
+ // that means text says so in an envelope.
344
+ if (isBytes(value)) {
345
+ return {
346
+ status: 200,
347
+ headers: { "content-type": "application/octet-stream" },
348
+ payload: value,
349
+ };
350
+ }
351
+
352
+ return {
353
+ status: 200,
354
+ headers: { "content-type": "application/json" },
355
+ payload: new Uint8Array(Buffer.from(JSON.stringify(value ?? null), "utf8")),
356
+ };
357
+ }
358
+
359
+ /**
360
+ * The range a `Range: bytes=…` header asks for, clamped to what there is, or
361
+ * null when the header asks for nothing this can serve — no header, a unit
362
+ * other than bytes, more than one range, or a start past the end.
363
+ *
364
+ * A player seeking inside an audio file sends one of these, and a server that
365
+ * ignores it answers the whole file every time somebody drags the scrubber.
366
+ * Slicing an answer already in hand is the whole of it: the pipeline produced
367
+ * the bytes, and which of them travel is the server's business.
368
+ */
369
+ function requestedRange(
370
+ value: string | undefined,
371
+ length: number,
372
+ ): { start: number; end: number } | null {
373
+ if (!value) {
374
+ return null;
375
+ }
376
+ const match = value.match(/^bytes=(\d*)-(\d*)$/);
377
+ if (!match) {
378
+ return null;
379
+ }
380
+ const [, rawStart, rawEnd] = match;
381
+ if (rawStart === "" && rawEnd === "") {
382
+ return null;
383
+ }
384
+
385
+ // "bytes=-500" is the last 500 bytes, not a range starting at zero.
386
+ if (rawStart === "") {
387
+ const span = Number(rawEnd);
388
+ if (!span) {
389
+ return null;
390
+ }
391
+ return { start: Math.max(0, length - span), end: length - 1 };
392
+ }
393
+
394
+ const start = Number(rawStart);
395
+ if (start >= length) {
396
+ return null;
397
+ }
398
+ const end = rawEnd === "" ? length - 1 : Math.min(Number(rawEnd), length - 1);
399
+ return end < start ? null : { start, end };
400
+ }
401
+
402
+ type HttpServerSubservicesState = JsonRecord & {
403
+ bypass: boolean;
404
+ mode: HttpServerMode;
405
+ /** Name this endpoint is known by; see the field on the service. */
406
+ mountName: string;
407
+ /** Public endpoint assigned by the runtime; empty while bypassed. Reserved
408
+ * name: generic board machinery reads and rewrites it (see the frontend's
409
+ * runtime/board/mount). */
410
+ __hkpMount: string;
411
+ /** Which request headers reach the pipeline; see the field on the service. */
412
+ forwardHeaders: string[] | null;
413
+ pipeline: Array<{
414
+ serviceId: string;
415
+ instanceId: string;
416
+ state: JsonRecord;
417
+ }>;
418
+ };
419
+
420
+ export class HttpServerSubservicesService implements HostedService {
421
+ readonly serviceId = httpServerSubservicesDescriptor.serviceId;
422
+ readonly serviceName = httpServerSubservicesDescriptor.serviceName;
423
+ readonly capabilities = httpServerSubservicesDescriptor.capabilities;
424
+ readonly uuid: string;
425
+
426
+ private bypass = true;
427
+ private mode: HttpServerMode = "process_on_session";
428
+ /**
429
+ * What this endpoint is called, which is what its public address is derived
430
+ * from. Empty falls back to the service's uuid, which is stable in a board
431
+ * file too — so an address only changes when a board deliberately renames it.
432
+ */
433
+ private mountName = "";
434
+ /**
435
+ * Which of a request's headers the pipeline is shown, or null for all.
436
+ *
437
+ * Headers are where a caller puts a credential, and `meta` goes wherever the
438
+ * pipeline takes it — including into a board, if a service is wired to write
439
+ * it there. Naming the ones a board actually reads is how it stops carrying
440
+ * the ones it does not: an empty list forwards none, and no list at all
441
+ * forwards everything, which is what a board that has not thought about it
442
+ * gets.
443
+ */
444
+ private forwardHeaders: string[] | null = null;
445
+ private latestData: unknown = null;
446
+
447
+ private mount: MountHandle | null = null;
448
+ /**
449
+ * Which way the board declared its pipelines; see the header.
450
+ *
451
+ * Kept because state reports what was declared rather than a canonical form:
452
+ * a board saved after being loaded has to come back out the way it went in,
453
+ * or every board on the older spelling rewrites itself the first time
454
+ * somebody saves it.
455
+ */
456
+ private form: "legacy" | "entries" = "legacy";
457
+ /** The one pipeline a legacy board declares, entered from whichever side its
458
+ * `mode` says. */
459
+ private legacy: NestedPipeline | null = null;
460
+ private readonly entries: Record<EntryName, NestedPipeline | null> = {
461
+ onProcess: null,
462
+ onRequest: null,
463
+ };
464
+ /**
465
+ * The cells this endpoint's pipelines hold values in.
466
+ *
467
+ * Owned here because the two entry points are pipelines that never meet: a
468
+ * value one of them produces has nowhere to live until the other runs. One
469
+ * store per endpoint is also what keeps the names in it private, so two
470
+ * endpoints on a runtime may both call a slot `document`.
471
+ */
472
+ private readonly slotStore: SlotStore = new Map<string, unknown>();
473
+ private readonly createService: ServiceCreator;
474
+ private host: RuntimeHost | null = null;
475
+
476
+ private stream: StreamConfig | null = null;
477
+ private readonly broadcast = new StreamBroadcast((listeners) =>
478
+ this.notify({ listeners }, this.uuid),
479
+ );
480
+
481
+ constructor(
482
+ config: ServiceConfiguration,
483
+ createService: ServiceCreator,
484
+ // Upper bound on a request body, in bytes; 0 disables the limit. Supplied by
485
+ // the server because the endpoint is public and shared.
486
+ private readonly maxBodyBytes = 0,
487
+ ) {
488
+ this.uuid = config.uuid;
489
+ this.createService = createService;
490
+
491
+ if (config.state) {
492
+ this.configure(config.state);
493
+ }
494
+ }
495
+
496
+ configure(config: JsonRecord): JsonRecord {
497
+ // `port` is accepted and ignored: the endpoint is served by the shared
498
+ // runtime server under an assigned path, so a service no longer picks a
499
+ // port. Older boards still carry the field, and rejecting it would fail
500
+ // them on load for a setting that no longer means anything.
501
+
502
+ // An array is a decision, including an empty one. Anything else — absent,
503
+ // null, a string — leaves the default of forwarding all of them.
504
+ if (config.forwardHeaders !== undefined) {
505
+ this.forwardHeaders = Array.isArray(config.forwardHeaders)
506
+ ? config.forwardHeaders
507
+ .filter((name): name is string => typeof name === "string")
508
+ .map((name) => name.toLowerCase())
509
+ : null;
510
+ }
511
+ if (typeof config.mountName === "string") {
512
+ // Renaming rotates this endpoint's address, so an already-claimed mount
513
+ // is released and claimed again under the new name rather than left
514
+ // answering on the old one.
515
+ const renamed = config.mountName !== this.mountName;
516
+ this.mountName = config.mountName;
517
+ if (renamed && this.mount) {
518
+ this.releaseMount();
519
+ }
520
+ }
521
+
522
+ // A different stream, or `null` / anything that names no path, ends the
523
+ // one there was: its listeners were answered under the old path and
524
+ // content type, and what it kept for late joiners and probes belongs to
525
+ // it. The same declaration again — a board re-sending its state — changes
526
+ // nothing.
527
+ if (config.stream !== undefined) {
528
+ const declared = parseStreamConfig(config.stream);
529
+ if (this.stream && !sameStream(this.stream, declared)) {
530
+ this.endStream();
531
+ }
532
+ this.stream = declared;
533
+ this.broadcast.setBurstBytes(declared?.burstBytes ?? 0);
534
+ this.notify(this.streamAnnouncement(), this.uuid);
535
+ }
536
+
537
+ if (
538
+ config.mode === "process_on_session" ||
539
+ config.mode === "process_on_data" ||
540
+ config.mode === "process_on_both"
541
+ ) {
542
+ this.mode = config.mode;
543
+ }
544
+
545
+ // Declaring an entry point by name is what puts this endpoint in the newer
546
+ // form, and from then on its state is reported that way.
547
+ for (const name of ENTRY_NAMES) {
548
+ if (Array.isArray(config[name])) {
549
+ this.form = "entries";
550
+ this.entryPipeline(name).setPipeline(config[name]);
551
+ }
552
+ }
553
+
554
+ // An edit aimed at one named entry. The unscoped verbs below cannot say
555
+ // which pipeline they mean once there is more than one.
556
+ if (isJsonRecord(config.configurePipeline)) {
557
+ const payload = config.configurePipeline;
558
+ const name = payload.entry;
559
+ if (name === "onProcess" || name === "onRequest") {
560
+ this.form = "entries";
561
+ this.editPipeline(this.entryPipeline(name), payload);
562
+ }
563
+ }
564
+
565
+ if (
566
+ Array.isArray(config.pipeline) ||
567
+ isJsonRecord(config.appendService) ||
568
+ typeof config.removeService === "string" ||
569
+ isJsonRecord(config.configureService)
570
+ ) {
571
+ // The unscoped verbs belong to the one pipeline a legacy board declares.
572
+ // Left working rather than redirected at an entry, because which entry
573
+ // they would mean is exactly what the older form cannot say.
574
+ this.editPipeline(this.legacyPipeline(), config);
575
+ }
576
+
577
+ if (typeof config.bypass === "boolean" && config.bypass !== this.bypass) {
578
+ this.bypass = config.bypass;
579
+ if (this.bypass) {
580
+ this.releaseMount();
581
+ } else {
582
+ this.claimMount();
583
+ }
584
+ }
585
+
586
+ // Anything above may have left this without an endpoint it should have —
587
+ // coming out of bypass, or a rename that released the old address. One
588
+ // check covers them rather than one per cause.
589
+ if (!this.bypass && !this.mount) {
590
+ this.claimMount();
591
+ }
592
+
593
+ return this.getState();
594
+ }
595
+
596
+ /**
597
+ * The pipeline behind one entry point, built on first use.
598
+ *
599
+ * Built lazily because an endpoint declaring only `onRequest` should not
600
+ * carry an empty runtime for the side it never uses — and because an empty
601
+ * pipeline and an absent one differ here: only the second leaves the board
602
+ * answering.
603
+ */
604
+ private entryPipeline(name: EntryName): NestedPipeline {
605
+ const existing = this.entries[name];
606
+ if (existing) {
607
+ return existing;
608
+ }
609
+ const created = this.newPipeline(name);
610
+ this.entries[name] = created;
611
+ return created;
612
+ }
613
+
614
+ private legacyPipeline(): NestedPipeline {
615
+ if (!this.legacy) {
616
+ this.legacy = this.newPipeline("pipeline");
617
+ }
618
+ return this.legacy;
619
+ }
620
+
621
+ private newPipeline(label: string): NestedPipeline {
622
+ const pipeline = new NestedPipeline(
623
+ `${this.uuid}:${label}`,
624
+ this.createService,
625
+ this.uuid,
626
+ );
627
+ // Both entries hold in the same cells: that they can is the whole reason
628
+ // for declaring them separately.
629
+ pipeline.shareSlots(this.slotStore);
630
+ if (this.host) {
631
+ pipeline.attach(this.host);
632
+ }
633
+ return pipeline;
634
+ }
635
+
636
+ /** The four edit verbs, applied to whichever pipeline was named. */
637
+ private editPipeline(pipeline: NestedPipeline, payload: JsonRecord): void {
638
+ if (Array.isArray(payload.pipeline)) {
639
+ pipeline.setPipeline(payload.pipeline);
640
+ } else if (isJsonRecord(payload.appendService)) {
641
+ pipeline.append(payload.appendService);
642
+ } else if (typeof payload.removeService === "string") {
643
+ pipeline.remove(payload.removeService);
644
+ } else if (isJsonRecord(payload.configureService)) {
645
+ const edit = payload.configureService;
646
+ if (typeof edit.instanceId === "string" && isJsonRecord(edit.state)) {
647
+ pipeline.configureService(edit.instanceId, edit.state);
648
+ }
649
+ }
650
+ }
651
+
652
+ /**
653
+ * The nested service a scoped address names, searching every pipeline this
654
+ * endpoint owns. The two entries are separate pipelines rather than branches
655
+ * of one, so an instanceId used in both resolves to whichever `pipelines()`
656
+ * lists first.
657
+ */
658
+ findNested(instanceId: string): HostedService | undefined {
659
+ for (const pipeline of this.pipelines()) {
660
+ const found = pipeline.find(instanceId);
661
+ if (found) {
662
+ return found;
663
+ }
664
+ }
665
+ return undefined;
666
+ }
667
+
668
+ getState(): JsonRecord {
669
+ const common: JsonRecord = {
670
+ bypass: this.bypass,
671
+ mountName: this.mountName,
672
+ __hkpMount: this.mount?.url ?? "",
673
+ forwardHeaders: this.forwardHeaders,
674
+ };
675
+ if (this.stream) {
676
+ common.stream = streamState(this.stream);
677
+ Object.assign(common, this.streamAnnouncement());
678
+ common.listenerDetails = this.broadcast.details();
679
+ }
680
+
681
+ // What was declared, not what it was understood as. A board that named its
682
+ // entries gets them back; one that carries a `mode` keeps it, because that
683
+ // is the version an older runtime can still load.
684
+ if (this.form === "entries") {
685
+ const state: JsonRecord = { ...common };
686
+ for (const name of ENTRY_NAMES) {
687
+ const pipeline = this.entries[name];
688
+ if (pipeline) {
689
+ state[name] = pipeline.state();
690
+ }
691
+ }
692
+ return state;
693
+ }
694
+
695
+ const state: HttpServerSubservicesState = {
696
+ ...(common as Pick<
697
+ HttpServerSubservicesState,
698
+ "bypass" | "mountName" | "__hkpMount" | "forwardHeaders"
699
+ >),
700
+ mode: this.mode,
701
+ pipeline: this.legacy?.state() ?? [],
702
+ };
703
+ // The stream fields ride along when declared; see getState's top.
704
+ return { ...common, ...state };
705
+ }
706
+
707
+ /** Passes the scope on to the nested pipelines; see SubService.setScope. */
708
+ setScope(scope: RuntimeScope): void {
709
+ for (const pipeline of this.pipelines()) {
710
+ pipeline.setScope(scope);
711
+ }
712
+ }
713
+
714
+ /** Every pipeline this endpoint owns, each one only once. */
715
+ private pipelines(): NestedPipeline[] {
716
+ const all = [this.legacy, this.entries.onProcess, this.entries.onRequest];
717
+ return [...new Set(all.filter((p): p is NestedPipeline => !!p))];
718
+ }
719
+
720
+ /**
721
+ * The pipeline one side enters through, or null where that side has none.
722
+ *
723
+ * This is where a legacy `mode` is read, and the only place it is: a board
724
+ * that names its entries never reaches the table below.
725
+ *
726
+ * | declared | onProcess | onRequest |
727
+ * |--------------------------------|-----------|-----------|
728
+ * | `process_on_session` | — | the one |
729
+ * | `process_on_both` | the one | the one |
730
+ * | `process_on_data` | — | — |
731
+ *
732
+ * `process_on_both` answers with *the same instance* on both sides, never a
733
+ * second copy of the configuration: a pipeline holding a Hold, a timer or a
734
+ * mount is one running thing, and duplicating it would give a board two of
735
+ * each and a slot that never reaches itself.
736
+ */
737
+ private entryFor(name: EntryName): NestedPipeline | null {
738
+ if (this.form === "entries") {
739
+ return this.entries[name];
740
+ }
741
+ if (this.mode === "process_on_data") {
742
+ return null;
743
+ }
744
+ if (name === "onProcess" && this.mode !== "process_on_both") {
745
+ return null;
746
+ }
747
+ return this.legacy;
748
+ }
749
+
750
+ setHost(host: RuntimeHost): void {
751
+ this.host = host;
752
+ // State is applied in the constructor, before the host exists, so a service
753
+ // configured as already-active has nothing to claim its mount from until
754
+ // now. Claiming here is what makes a board load into a live endpoint.
755
+ if (!this.bypass && !this.mount) {
756
+ this.claimMount();
757
+ }
758
+ // A pipeline built in the constructor was built before there was a host to
759
+ // ask what board it belongs to, or to report through.
760
+ for (const pipeline of this.pipelines()) {
761
+ pipeline.attach(host);
762
+ }
763
+ }
764
+
765
+ process(
766
+ input: unknown,
767
+ _notify: (payload: unknown, instanceId?: string) => void,
768
+ ): unknown {
769
+ // The legacy built-in slot: what the board hands this endpoint is what a
770
+ // caller gets back. Expressible now as an `onProcess` that writes a slot
771
+ // and an `onRequest` that reads it, and kept because boards carry the older
772
+ // spelling and a board is a document people keep.
773
+ if (this.form === "legacy" && this.mode === "process_on_data") {
774
+ this.latestData = input;
775
+ return input;
776
+ }
777
+
778
+ const entry = this.bypass ? null : this.entryFor("onProcess");
779
+ if (!entry) {
780
+ this.publish(input);
781
+ return input;
782
+ }
783
+ // Routing only: what the pass's own pipeline returns carries on down the
784
+ // chain. Whatever has to survive until a request arrives — a value this
785
+ // side produces and the other reads — belongs in a slot, which is a
786
+ // service's job and not this one's. It is also what the stream carries.
787
+ return this.runEntry(entry, input).then((output) => {
788
+ this.publish(output);
789
+ return output;
790
+ });
791
+ }
792
+
793
+ /** What a pass produced, to whoever is listening, where it is bytes. */
794
+ private publish(value: unknown): void {
795
+ if (!this.stream || this.bypass) {
796
+ return;
797
+ }
798
+ const bytes = streamBytes(value);
799
+ if (bytes) {
800
+ this.broadcast.publish(bytes);
801
+ }
802
+ }
803
+
804
+ /** Where a player connects, and how many have: what changes as it runs. */
805
+ private streamAnnouncement(): JsonRecord {
806
+ return {
807
+ streamUrl:
808
+ this.stream && this.mount ? `${this.mount.url}${this.stream.path}` : "",
809
+ listeners: this.broadcast.count,
810
+ };
811
+ }
812
+
813
+ private endStream(): void {
814
+ this.broadcast.closeAll();
815
+ }
816
+
817
+ /** A GET on the stream path: a listener to keep, or a probe to answer. */
818
+ private handleListener(req: IncomingMessage, res: ServerResponse): void {
819
+ const stream = this.stream!;
820
+ const range = boundedRange(header(req, "range"));
821
+ if (range && this.broadcast.last) {
822
+ answerRangeProbe(res, stream.contentType, range, this.broadcast.last);
823
+ return;
824
+ }
825
+ const listener: StreamListener = new StreamListener(
826
+ req,
827
+ res,
828
+ stream.contentType,
829
+ stream.maxQueueBytes,
830
+ stream.stallTimeoutMs,
831
+ (closed) => this.broadcast.leave(closed),
832
+ );
833
+ this.broadcast.join(listener);
834
+ }
835
+
836
+ destroy(): void {
837
+ this.releaseMount();
838
+ // Nested services hold the same things top-level ones do — timers, sockets,
839
+ // mounts — and nothing else will ever reach them once this service is gone.
840
+ for (const pipeline of this.pipelines()) {
841
+ pipeline.destroy();
842
+ }
843
+ this.legacy = null;
844
+ this.entries.onProcess = null;
845
+ this.entries.onRequest = null;
846
+ }
847
+
848
+ /** See HostedService.remount: the runtime can serve one now. */
849
+ remount(): void {
850
+ // Bypassed is the same answer it gives at setHost: an endpoint switched
851
+ // off holds no address, and gaining somewhere to claim one does not switch
852
+ // it back on.
853
+ if (!this.bypass) {
854
+ this.claimMount();
855
+ }
856
+ }
857
+
858
+ private claimMount(): void {
859
+ if (this.mount || !this.host?.mount) {
860
+ return;
861
+ }
862
+
863
+ this.mount = this.host.mount(
864
+ this.uuid,
865
+ {
866
+ request: (req, res, context) => {
867
+ if (
868
+ this.isStreamPath(context) &&
869
+ req.method === "GET" &&
870
+ !this.bypass
871
+ ) {
872
+ this.handleListener(req, res);
873
+ return;
874
+ }
875
+ void this.handleRequest(req, res, context);
876
+ },
877
+ },
878
+ { mountName: this.mountName },
879
+ );
880
+
881
+ // A board reads the assigned endpoint from here (or from state), since it
882
+ // is not knowable at design time.
883
+ this.notify(
884
+ {
885
+ __hkpMount: this.mount?.url ?? "",
886
+ ...(this.stream ? this.streamAnnouncement() : {}),
887
+ },
888
+ this.uuid,
889
+ );
890
+ }
891
+
892
+ private isStreamPath(context: MountContext): boolean {
893
+ if (!this.stream) {
894
+ return false;
895
+ }
896
+ return (
897
+ new URL(context.subPath, "http://localhost").pathname === this.stream.path
898
+ );
899
+ }
900
+
901
+ private releaseMount(): void {
902
+ this.mount?.release();
903
+ this.mount = null;
904
+ this.endStream();
905
+ if (this.stream) {
906
+ this.notify({ __hkpMount: "", ...this.streamAnnouncement() }, this.uuid);
907
+ }
908
+ }
909
+
910
+ /**
911
+ * Build the MixedData an incoming request becomes: JSON `meta` describing it,
912
+ * plus the body in whichever single form is useful — decoded as `body` when
913
+ * the content type says what the bytes mean, raw as `binary` otherwise. The
914
+ * meta/binary pair is the shape hkp-rt's body-carrying HTTP service produces,
915
+ * so pipelines handling uploads can be written once against it.
916
+ *
917
+ * `meta.path` stays the URL path this service has always reported. A filename
918
+ * from content-disposition is surfaced separately as `meta.filename` rather
919
+ * than overloading `path`, which would silently change what existing
920
+ * pipelines match on.
921
+ */
922
+ private async readRequest(
923
+ req: IncomingMessage,
924
+ context: MountContext,
925
+ ): Promise<MixedRequest> {
926
+ // The mount prefix is transport addressing, not part of the route the
927
+ // pipeline matches on, so the pipeline sees the path below the mount.
928
+ const url = new URL(context.subPath, "http://localhost");
929
+ const query: JsonRecord = {};
930
+ for (const [key, value] of url.searchParams) {
931
+ query[key] = value;
932
+ }
933
+
934
+ const contentType = header(req, "content-type");
935
+ const meta: JsonRecord = {
936
+ method: req.method ?? "GET",
937
+ path: url.pathname,
938
+ query,
939
+ headers: this.requestHeaders(req),
940
+ };
941
+ if (contentType) {
942
+ meta.contentType = contentType;
943
+ }
944
+ const filename = filenameFromDisposition(
945
+ header(req, "content-disposition"),
946
+ );
947
+ if (filename) {
948
+ meta.filename = filename;
949
+ }
950
+
951
+ const binary = await this.readBody(req);
952
+
953
+ // Exactly one representation of the body, or neither when there was none.
954
+ const body = decodeBody(binary, contentType);
955
+ if (body !== undefined) {
956
+ return { meta, body };
957
+ }
958
+ return binary.length > 0 ? { meta, binary } : { meta };
959
+ }
960
+
961
+ /**
962
+ * The headers this pipeline is shown, lower-cased as HTTP names compare.
963
+ *
964
+ * A caller that has to prove who it is does so in a header — a shared secret,
965
+ * a signature, a bearer token — so a pipeline that cannot see them cannot
966
+ * check one. What a board does not name, it does not receive.
967
+ */
968
+ private requestHeaders(req: IncomingMessage): JsonRecord {
969
+ const headers: JsonRecord = {};
970
+ for (const [name, value] of Object.entries(req.headers)) {
971
+ if (value === undefined) {
972
+ continue;
973
+ }
974
+ if (this.forwardHeaders && !this.forwardHeaders.includes(name)) {
975
+ continue;
976
+ }
977
+ headers[name] = Array.isArray(value) ? value.join(", ") : value;
978
+ }
979
+ return headers;
980
+ }
981
+
982
+ /**
983
+ * Read the request body, refusing anything past the configured cap.
984
+ *
985
+ * A mount is reachable without a token by design, so an unbounded read is a
986
+ * way for anyone holding the URL to exhaust the host — which on a shared
987
+ * instance is everyone else's problem too. The cap is enforced while reading
988
+ * rather than from content-length, which a client controls.
989
+ */
990
+ private readBody(req: IncomingMessage): Promise<Uint8Array> {
991
+ return new Promise((resolve, reject) => {
992
+ const chunks: Buffer[] = [];
993
+ let total = 0;
994
+ let rejected = false;
995
+
996
+ req.on("data", (chunk: Buffer) => {
997
+ if (rejected) {
998
+ // Keep draining but stop accumulating: memory is what the limit
999
+ // protects, and tearing the socket down here would lose the 413 the
1000
+ // caller is about to write.
1001
+ return;
1002
+ }
1003
+ total += chunk.length;
1004
+ if (this.maxBodyBytes > 0 && total > this.maxBodyBytes) {
1005
+ rejected = true;
1006
+ chunks.length = 0;
1007
+ reject(new RequestTooLargeError());
1008
+ return;
1009
+ }
1010
+ chunks.push(chunk);
1011
+ });
1012
+
1013
+ req.on("end", () => {
1014
+ if (!rejected) {
1015
+ resolve(new Uint8Array(Buffer.concat(chunks)));
1016
+ }
1017
+ });
1018
+
1019
+ req.on("error", (err) => {
1020
+ if (!rejected) {
1021
+ reject(err);
1022
+ }
1023
+ });
1024
+ });
1025
+ }
1026
+
1027
+ private async handleRequest(
1028
+ req: IncomingMessage,
1029
+ res: ServerResponse,
1030
+ context: MountContext,
1031
+ ): Promise<void> {
1032
+ if (this.bypass) {
1033
+ res.statusCode = 503;
1034
+ res.setHeader("content-type", "application/json");
1035
+ res.end(JSON.stringify({ error: "http-server-subservices is bypassed" }));
1036
+ return;
1037
+ }
1038
+
1039
+ // Serving a request is one run, however many pipelines it passes through:
1040
+ // the nested handler below descends from it, and the outer chain afterwards
1041
+ // continues it. Minting one here rather than letting each leg mint its own
1042
+ // is what keeps a request's trace joined up instead of arriving as two
1043
+ // unrelated runs that happen to share a timestamp.
1044
+ const runContext = newRun("mount");
1045
+
1046
+ let output: unknown;
1047
+ let processInput: unknown;
1048
+ // Whether the answer is already decided here, or is whatever the rest of
1049
+ // the outer chain makes of what this service emitted.
1050
+ let answeredHere = false;
1051
+ if (this.form === "legacy" && this.mode === "process_on_data") {
1052
+ processInput = this.latestData;
1053
+ output = processInput;
1054
+ // The mode's whole contract: what the board handed this endpoint is what
1055
+ // a caller gets back, verbatim. The services after it still run — having
1056
+ // served a request is something a board may want to act on — but what
1057
+ // they make of it is theirs, not the answer. Letting the chain's tail
1058
+ // answer instead would mean an endpoint could only ever be the last
1059
+ // service in its runtime, so a runtime could publish only one document.
1060
+ answeredHere = true;
1061
+ } else {
1062
+ let request: MixedRequest;
1063
+ try {
1064
+ request = await this.readRequest(req, context);
1065
+ } catch (error) {
1066
+ if (error instanceof RequestTooLargeError) {
1067
+ res.statusCode = 413;
1068
+ res.setHeader("content-type", "application/json");
1069
+ res.end(JSON.stringify({ error: "Request body too large" }));
1070
+ return;
1071
+ }
1072
+ throw error;
1073
+ }
1074
+ processInput = request;
1075
+ // A declared handler is what takes the answer away from the chain — not
1076
+ // the presence of a pipeline, which says only that this endpoint has
1077
+ // something to run, possibly on the other side. Without one the board
1078
+ // answers, which is the inversion of control this service is built
1079
+ // around.
1080
+ const handler = this.entryFor("onRequest");
1081
+ answeredHere = !!handler && !handler.isEmpty();
1082
+ output = handler
1083
+ ? await this.runEntry(handler, processInput, runContext)
1084
+ : processInput;
1085
+ }
1086
+
1087
+ // What the nested pipeline produced, before the outer runtime sees it.
1088
+ const answer = output;
1089
+
1090
+ // processFrom reports this service's own call-process pair, so there is no
1091
+ // manual pair here — emitting one too would double every request in the UI.
1092
+ // It also reports the right value: what this service emitted, rather than
1093
+ // what the whole downstream chain finally returned.
1094
+ if (this.host) {
1095
+ // No-op: the runtime already fans these out to its notification targets.
1096
+ // Re-notifying through the host would deliver every one twice.
1097
+ output = await this.host.processFrom(
1098
+ this.uuid,
1099
+ output,
1100
+ () => {},
1101
+ runContext,
1102
+ );
1103
+ this.host.emitResult(output);
1104
+ }
1105
+
1106
+ this.sendAnswer(
1107
+ req,
1108
+ res,
1109
+ await this.resolveAssetBody(answeredHere ? answer : output),
1110
+ );
1111
+ }
1112
+
1113
+ /**
1114
+ * An answer whose `body` is an `hkp-asset://` reference, with the asset's
1115
+ * content in its place.
1116
+ *
1117
+ * Resolved as the answer is written, from the runtime's asset store, so the
1118
+ * page an endpoint serves is whatever the asset holds now: editing it changes
1119
+ * the next response and nothing is reconfigured. The content type is the
1120
+ * asset's own unless the envelope named one.
1121
+ *
1122
+ * A reference that does not resolve is answered as a 500 naming the asset and
1123
+ * why, and reported. Sending the reference itself as the page would be a
1124
+ * quieter failure, and a worse one.
1125
+ */
1126
+ private async resolveAssetBody(value: unknown): Promise<unknown> {
1127
+ const envelope = asResponseEnvelope(value);
1128
+ if (!envelope || !parseAssetRef(envelope.body)) {
1129
+ return value;
1130
+ }
1131
+ const meta = envelope.meta ?? {};
1132
+ const store = this.host?.assets?.();
1133
+ const { asset, problem } = store
1134
+ ? await store.resolve(envelope.body as string)
1135
+ : { asset: null, problem: "this runtime has no assets" };
1136
+ if (!asset) {
1137
+ this.notify({ error: problem });
1138
+ return {
1139
+ meta: { status: 500, contentType: "application/json" },
1140
+ body: { error: problem },
1141
+ };
1142
+ }
1143
+ const { body: _reference, ...rest } = envelope;
1144
+ return {
1145
+ ...rest,
1146
+ meta: {
1147
+ ...meta,
1148
+ contentType:
1149
+ typeof meta.contentType === "string" && meta.contentType
1150
+ ? meta.contentType
1151
+ : asset.mediaType,
1152
+ },
1153
+ binary: asset.bytes,
1154
+ };
1155
+ }
1156
+
1157
+ /**
1158
+ * Writes what the handler produced, honouring a range request when the
1159
+ * answer is bytes a caller can seek inside.
1160
+ */
1161
+ private sendAnswer(
1162
+ req: IncomingMessage,
1163
+ res: ServerResponse,
1164
+ value: unknown,
1165
+ ): void {
1166
+ const answer = toAnswer(value);
1167
+ for (const [name, headerValue] of Object.entries(answer.headers)) {
1168
+ res.setHeader(name, String(headerValue));
1169
+ }
1170
+
1171
+ const seekable = answer.status === 200;
1172
+ if (seekable) {
1173
+ res.setHeader("accept-ranges", "bytes");
1174
+ }
1175
+
1176
+ const range = seekable
1177
+ ? requestedRange(header(req, "range"), answer.payload.length)
1178
+ : null;
1179
+ if (range) {
1180
+ const slice = answer.payload.subarray(range.start, range.end + 1);
1181
+ res.statusCode = 206;
1182
+ res.setHeader(
1183
+ "content-range",
1184
+ `bytes ${range.start}-${range.end}/${answer.payload.length}`,
1185
+ );
1186
+ res.setHeader("content-length", String(slice.length));
1187
+ res.end(Buffer.from(slice));
1188
+ return;
1189
+ }
1190
+
1191
+ res.statusCode = answer.status;
1192
+ res.setHeader("content-length", String(answer.payload.length));
1193
+ res.end(Buffer.from(answer.payload));
1194
+ }
1195
+
1196
+ /**
1197
+ * Runs one entry's pipeline as a run descended from `parent`.
1198
+ *
1199
+ * Both entry points land here, and they differ only in what they descend
1200
+ * from: a request brings the run its caller minted for the whole exchange,
1201
+ * while data from the outer chain arrives mid-call and descends from whatever
1202
+ * that call is running as.
1203
+ */
1204
+ private async runEntry(
1205
+ pipeline: NestedPipeline,
1206
+ input: unknown,
1207
+ parent?: ProcessContext | null,
1208
+ ): Promise<unknown> {
1209
+ return await pipeline.process(
1210
+ input,
1211
+ parent ?? this.host?.currentContext() ?? null,
1212
+ );
1213
+ }
1214
+
1215
+ private notify(payload: unknown, instanceId?: string): void {
1216
+ this.host?.notify(payload, instanceId ?? this.uuid);
1217
+ }
1218
+ }
1219
+
1220
+ function isJsonRecord(value: unknown): value is JsonRecord {
1221
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1222
+ }