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