@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,439 @@
|
|
|
1
|
+
"""Assets a runtime was given, and the one way to their content.
|
|
2
|
+
|
|
3
|
+
A board declares content once — a page, a script, an image, a model — as an
|
|
4
|
+
asset *descriptor*: an id, a media type and exactly one source. Service state
|
|
5
|
+
names one by reference, ``hkp-asset://<id>``, as a whole field, and never holds
|
|
6
|
+
the content itself. ``get_state`` therefore echoes the reference, and saving a
|
|
7
|
+
board writes back what was configured: there is no round trip to undo.
|
|
8
|
+
|
|
9
|
+
The descriptors arrive with the runtime's create payload, or on
|
|
10
|
+
``POST /runtimes/<id>/assets`` — and again whenever an asset is edited, which is
|
|
11
|
+
the point: a service resolves its reference at the moment it uses it, so the
|
|
12
|
+
next use gets the new content without anything being reconfigured. A runtime is
|
|
13
|
+
given every asset of its board that is not kept to other runtimes, named by its
|
|
14
|
+
services or not: which one a service uses can be decided as it runs.
|
|
15
|
+
|
|
16
|
+
Where the content comes from depends on the source:
|
|
17
|
+
|
|
18
|
+
``text``, ``base64``
|
|
19
|
+
already in the descriptor
|
|
20
|
+
``http(s)://``
|
|
21
|
+
fetched by this runtime as anyone would fetch it, cached by ``sha256`` or
|
|
22
|
+
revalidated by ETag
|
|
23
|
+
|
|
24
|
+
Anything else — ``file://`` among them, since this runtime keeps no files a
|
|
25
|
+
board may name — is refused by name rather than guessed at.
|
|
26
|
+
|
|
27
|
+
An asset carries no request headers and names no secret. It is resolved without
|
|
28
|
+
anyone looking, by every runtime holding it, which is no place for a
|
|
29
|
+
credential; content that needs one is fetched by a service that says where it
|
|
30
|
+
sends it.
|
|
31
|
+
|
|
32
|
+
The format matches ``hkp-frontend/src/runtime/board/assets.ts`` and
|
|
33
|
+
``hkp-node/src/assets.ts``: a board written against one runtime has to open
|
|
34
|
+
against another.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
from __future__ import annotations
|
|
38
|
+
|
|
39
|
+
import base64
|
|
40
|
+
import binascii
|
|
41
|
+
import hashlib
|
|
42
|
+
import json
|
|
43
|
+
import re
|
|
44
|
+
import threading
|
|
45
|
+
import urllib.error
|
|
46
|
+
import urllib.request
|
|
47
|
+
from collections import OrderedDict
|
|
48
|
+
from dataclasses import dataclass
|
|
49
|
+
from typing import Any, Callable
|
|
50
|
+
from urllib.parse import urlsplit
|
|
51
|
+
|
|
52
|
+
from .egress import refusal_in
|
|
53
|
+
from .egress import urlopen as egress_urlopen
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
ASSET_SCHEME = "hkp-asset://"
|
|
57
|
+
|
|
58
|
+
_ID = r"[A-Za-z0-9_.\-]+"
|
|
59
|
+
_ID_PATTERN = re.compile(rf"^{_ID}$")
|
|
60
|
+
_WHOLE_REFERENCE = re.compile(rf"^hkp-asset://({_ID})$")
|
|
61
|
+
_ANY_REFERENCE = re.compile(rf"hkp-asset://({_ID})")
|
|
62
|
+
|
|
63
|
+
DEFAULT_MAX_BYTES = 64 * 1024 * 1024
|
|
64
|
+
DEFAULT_MAX_CACHE_BYTES = 128 * 1024 * 1024
|
|
65
|
+
DEFAULT_TIMEOUT_S = 30.0
|
|
66
|
+
|
|
67
|
+
#: A descriptor as it travels: ``id``, ``mediaType``, exactly one of ``text``,
|
|
68
|
+
#: ``base64`` or ``url``, and optional ``name``, ``sha256``, ``size`` and
|
|
69
|
+
#: ``runtimes`` — the runtimes it is for, which whoever provisions them reads.
|
|
70
|
+
AssetDescriptor = dict[str, Any]
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@dataclass
|
|
74
|
+
class ResolvedAsset:
|
|
75
|
+
id: str
|
|
76
|
+
media_type: str
|
|
77
|
+
content: bytes
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@dataclass
|
|
81
|
+
class AssetResolution:
|
|
82
|
+
#: The content, or None when there is none.
|
|
83
|
+
asset: ResolvedAsset | None
|
|
84
|
+
#: Why there is none, or "" when there is.
|
|
85
|
+
problem: str
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def parse_asset_ref(value: Any) -> str | None:
|
|
89
|
+
"""The id a whole-value reference names, or None for anything else.
|
|
90
|
+
|
|
91
|
+
A reference inside a longer string is not one: nothing is spliced into text.
|
|
92
|
+
"""
|
|
93
|
+
if not isinstance(value, str):
|
|
94
|
+
return None
|
|
95
|
+
match = _WHOLE_REFERENCE.match(value)
|
|
96
|
+
return match.group(1) if match else None
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def format_asset_ref(asset_id: str) -> str:
|
|
100
|
+
return f"{ASSET_SCHEME}{asset_id}"
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def referenced_assets(value: Any) -> list[str]:
|
|
104
|
+
"""Every asset id a value mentions, however deeply it is nested.
|
|
105
|
+
|
|
106
|
+
Found anywhere in a string, not only as a whole value, so that a reference an
|
|
107
|
+
expression produces is still one this runtime is told about. Finding is
|
|
108
|
+
generous; resolving is not.
|
|
109
|
+
"""
|
|
110
|
+
found: list[str] = []
|
|
111
|
+
|
|
112
|
+
def walk(node: Any) -> None:
|
|
113
|
+
if isinstance(node, str):
|
|
114
|
+
for asset_id in _ANY_REFERENCE.findall(node):
|
|
115
|
+
if asset_id not in found:
|
|
116
|
+
found.append(asset_id)
|
|
117
|
+
elif isinstance(node, dict):
|
|
118
|
+
for child in node.values():
|
|
119
|
+
walk(child)
|
|
120
|
+
elif isinstance(node, (list, tuple)):
|
|
121
|
+
for child in node:
|
|
122
|
+
walk(child)
|
|
123
|
+
|
|
124
|
+
walk(value)
|
|
125
|
+
return found
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def is_text_media_type(media_type: str) -> bool:
|
|
129
|
+
"""Whether a media type is text a pipeline can carry as a string."""
|
|
130
|
+
kind = media_type.split(";")[0].strip().lower()
|
|
131
|
+
return (
|
|
132
|
+
kind.startswith("text/")
|
|
133
|
+
or kind == "application/json"
|
|
134
|
+
or kind.endswith("+json")
|
|
135
|
+
or kind == "application/javascript"
|
|
136
|
+
or kind == "application/xml"
|
|
137
|
+
or kind.endswith("+xml")
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def read_asset_descriptor(value: Any, fallback_id: str | None = None) -> AssetDescriptor | None:
|
|
142
|
+
"""One descriptor off the wire, or None when it is not one.
|
|
143
|
+
|
|
144
|
+
Exactly one source, or it is not an asset: two would leave a runtime choosing
|
|
145
|
+
between them.
|
|
146
|
+
"""
|
|
147
|
+
if not isinstance(value, dict):
|
|
148
|
+
return None
|
|
149
|
+
asset_id = value.get("id") if isinstance(value.get("id"), str) else fallback_id
|
|
150
|
+
if not asset_id or not _ID_PATTERN.match(asset_id):
|
|
151
|
+
return None
|
|
152
|
+
sources = [key for key in ("text", "base64", "url") if isinstance(value.get(key), str)]
|
|
153
|
+
if len(sources) != 1:
|
|
154
|
+
return None
|
|
155
|
+
|
|
156
|
+
media_type = value.get("mediaType")
|
|
157
|
+
descriptor: AssetDescriptor = {
|
|
158
|
+
"id": asset_id,
|
|
159
|
+
"mediaType": media_type if isinstance(media_type, str) and media_type else "application/octet-stream",
|
|
160
|
+
}
|
|
161
|
+
if isinstance(value.get("name"), str):
|
|
162
|
+
descriptor["name"] = value["name"]
|
|
163
|
+
sha = value.get("sha256")
|
|
164
|
+
if isinstance(sha, str) and re.fullmatch(r"[0-9a-fA-F]{64}", sha):
|
|
165
|
+
descriptor["sha256"] = sha.lower()
|
|
166
|
+
size = value.get("size")
|
|
167
|
+
if isinstance(size, (int, float)) and not isinstance(size, bool):
|
|
168
|
+
descriptor["size"] = size
|
|
169
|
+
runtimes = value.get("runtimes")
|
|
170
|
+
if isinstance(runtimes, list):
|
|
171
|
+
descriptor["runtimes"] = [entry for entry in runtimes if isinstance(entry, str)]
|
|
172
|
+
source = sources[0]
|
|
173
|
+
descriptor[source] = value[source]
|
|
174
|
+
return descriptor
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def read_assets_payload(value: Any) -> dict[str, AssetDescriptor | None]:
|
|
178
|
+
"""Reads an assets payload off the wire.
|
|
179
|
+
|
|
180
|
+
A map of id to descriptor, where ``None`` removes one, or a list of
|
|
181
|
+
descriptors. Anything it cannot read is dropped rather than failing the
|
|
182
|
+
request — a malformed entry costs one asset, and the service referencing it
|
|
183
|
+
says so by name.
|
|
184
|
+
"""
|
|
185
|
+
entries: dict[str, AssetDescriptor | None] = {}
|
|
186
|
+
if isinstance(value, list):
|
|
187
|
+
for item in value:
|
|
188
|
+
descriptor = read_asset_descriptor(item)
|
|
189
|
+
if descriptor:
|
|
190
|
+
entries[descriptor["id"]] = descriptor
|
|
191
|
+
return entries
|
|
192
|
+
if not isinstance(value, dict):
|
|
193
|
+
return entries
|
|
194
|
+
for asset_id, item in value.items():
|
|
195
|
+
if item is None:
|
|
196
|
+
if _ID_PATTERN.match(asset_id):
|
|
197
|
+
entries[asset_id] = None
|
|
198
|
+
continue
|
|
199
|
+
descriptor = read_asset_descriptor(item, asset_id)
|
|
200
|
+
if descriptor:
|
|
201
|
+
# Keyed by what the payload named, so the id inside cannot disagree.
|
|
202
|
+
entries[asset_id] = {**descriptor, "id": asset_id}
|
|
203
|
+
return entries
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def _version(descriptor: AssetDescriptor | None) -> str:
|
|
207
|
+
return json.dumps(descriptor, sort_keys=True)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
@dataclass
|
|
211
|
+
class _CacheEntry:
|
|
212
|
+
version: str
|
|
213
|
+
asset: ResolvedAsset
|
|
214
|
+
etag: str | None = None
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
_BASE64_WHITESPACE = re.compile(r"[\t\n\f\r ]+")
|
|
218
|
+
_BASE64_ALPHABET = re.compile(r"[A-Za-z0-9+/]*")
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def decode_base64(value: str) -> bytes | None:
|
|
222
|
+
"""The bytes base64 text stands for, or ``None`` when it is not base64.
|
|
223
|
+
|
|
224
|
+
``b64decode`` without ``validate`` skips what it does not recognise and
|
|
225
|
+
decodes the rest, so text that is not base64 would come out as some other
|
|
226
|
+
bytes. What is taken here is what a browser's ``atob`` takes — ASCII
|
|
227
|
+
whitespace ignored, the standard alphabet, padding optional — so that a
|
|
228
|
+
descriptor resolves to the same content, or the same refusal, on every
|
|
229
|
+
runtime.
|
|
230
|
+
"""
|
|
231
|
+
text = _BASE64_WHITESPACE.sub("", value)
|
|
232
|
+
if len(text) % 4 == 0:
|
|
233
|
+
text = re.sub(r"={1,2}$", "", text)
|
|
234
|
+
if len(text) % 4 == 1 or not _BASE64_ALPHABET.fullmatch(text):
|
|
235
|
+
return None
|
|
236
|
+
try:
|
|
237
|
+
return base64.b64decode(text + "=" * (-len(text) % 4), validate=True)
|
|
238
|
+
except (binascii.Error, ValueError):
|
|
239
|
+
return None
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
class AssetStore:
|
|
243
|
+
"""The descriptors one runtime was given, and the content they resolve to."""
|
|
244
|
+
|
|
245
|
+
def __init__(
|
|
246
|
+
self,
|
|
247
|
+
*,
|
|
248
|
+
max_bytes: int = DEFAULT_MAX_BYTES,
|
|
249
|
+
max_cache_bytes: int = DEFAULT_MAX_CACHE_BYTES,
|
|
250
|
+
timeout_s: float = DEFAULT_TIMEOUT_S,
|
|
251
|
+
opener: Callable[..., Any] | None = None,
|
|
252
|
+
) -> None:
|
|
253
|
+
self._descriptors: dict[str, AssetDescriptor] = {}
|
|
254
|
+
self._cache: OrderedDict[str, _CacheEntry] = OrderedDict()
|
|
255
|
+
self._cached_bytes = 0
|
|
256
|
+
self._listeners: dict[str, set[Callable[[str], None]]] = {}
|
|
257
|
+
self._max_bytes = max_bytes
|
|
258
|
+
self._max_cache_bytes = max_cache_bytes
|
|
259
|
+
self._timeout_s = timeout_s
|
|
260
|
+
self._open = opener or egress_urlopen
|
|
261
|
+
# Services resolve on worker threads and the server pushes on its loop.
|
|
262
|
+
self._lock = threading.RLock()
|
|
263
|
+
|
|
264
|
+
def replace(self, entries: dict[str, AssetDescriptor]) -> None:
|
|
265
|
+
"""Replaces everything held."""
|
|
266
|
+
with self._lock:
|
|
267
|
+
previous = self._descriptors
|
|
268
|
+
self._descriptors = dict(entries)
|
|
269
|
+
touched = set(previous) | set(self._descriptors)
|
|
270
|
+
for asset_id in touched:
|
|
271
|
+
if _version(previous.get(asset_id)) != _version(self._descriptors.get(asset_id)):
|
|
272
|
+
self._changed(asset_id)
|
|
273
|
+
|
|
274
|
+
def merge(self, entries: dict[str, AssetDescriptor | None]) -> None:
|
|
275
|
+
"""Adds, replaces or removes (``None``) individual descriptors.
|
|
276
|
+
|
|
277
|
+
An asset deleted from the board is gone from every runtime that was told
|
|
278
|
+
about it, rather than served on from a stale copy.
|
|
279
|
+
"""
|
|
280
|
+
for asset_id, entry in entries.items():
|
|
281
|
+
with self._lock:
|
|
282
|
+
previous = self._descriptors.get(asset_id)
|
|
283
|
+
if entry is None:
|
|
284
|
+
self._descriptors.pop(asset_id, None)
|
|
285
|
+
else:
|
|
286
|
+
self._descriptors[asset_id] = entry
|
|
287
|
+
if _version(previous) != _version(entry):
|
|
288
|
+
self._changed(asset_id)
|
|
289
|
+
|
|
290
|
+
def ids(self) -> list[str]:
|
|
291
|
+
"""The ids held, for saying what a runtime knows about."""
|
|
292
|
+
with self._lock:
|
|
293
|
+
return list(self._descriptors)
|
|
294
|
+
|
|
295
|
+
def descriptor(self, asset_id: str) -> AssetDescriptor | None:
|
|
296
|
+
with self._lock:
|
|
297
|
+
return self._descriptors.get(asset_id)
|
|
298
|
+
|
|
299
|
+
def subscribe(self, asset_id: str, listener: Callable[[str], None]) -> Callable[[], None]:
|
|
300
|
+
"""Calls ``listener`` with the id whenever the asset changes or goes away.
|
|
301
|
+
|
|
302
|
+
Only a service that loads something once needs it — one that resolves on
|
|
303
|
+
every use already gets the new content.
|
|
304
|
+
"""
|
|
305
|
+
with self._lock:
|
|
306
|
+
self._listeners.setdefault(asset_id, set()).add(listener)
|
|
307
|
+
|
|
308
|
+
def unsubscribe() -> None:
|
|
309
|
+
with self._lock:
|
|
310
|
+
listeners = self._listeners.get(asset_id)
|
|
311
|
+
if listeners is not None:
|
|
312
|
+
listeners.discard(listener)
|
|
313
|
+
if not listeners:
|
|
314
|
+
self._listeners.pop(asset_id, None)
|
|
315
|
+
|
|
316
|
+
return unsubscribe
|
|
317
|
+
|
|
318
|
+
def resolve(self, reference: str) -> AssetResolution:
|
|
319
|
+
"""An asset's content for one use, or a sentence saying why there is none.
|
|
320
|
+
|
|
321
|
+
Blocking: a URL source is fetched on the calling thread. A caller on the
|
|
322
|
+
server's loop runs this in a thread.
|
|
323
|
+
"""
|
|
324
|
+
asset_id = parse_asset_ref(reference)
|
|
325
|
+
if not asset_id:
|
|
326
|
+
return AssetResolution(None, f"{json.dumps(reference)} is not an asset reference")
|
|
327
|
+
with self._lock:
|
|
328
|
+
descriptor = self._descriptors.get(asset_id)
|
|
329
|
+
cached = self._cache.get(asset_id)
|
|
330
|
+
if descriptor is None:
|
|
331
|
+
return AssetResolution(None, f'asset "{asset_id}" is not known to this runtime')
|
|
332
|
+
|
|
333
|
+
version = _version(descriptor)
|
|
334
|
+
revalidate = "url" in descriptor and not descriptor.get("sha256")
|
|
335
|
+
if cached and cached.version == version and not revalidate:
|
|
336
|
+
with self._lock:
|
|
337
|
+
self._cache.move_to_end(asset_id)
|
|
338
|
+
return AssetResolution(cached.asset, "")
|
|
339
|
+
|
|
340
|
+
try:
|
|
341
|
+
content, etag = self._load(descriptor, cached if cached and cached.version == version else None)
|
|
342
|
+
except _Refused as refused:
|
|
343
|
+
return AssetResolution(None, f'asset "{asset_id}": {refused}')
|
|
344
|
+
except Exception as error: # noqa: BLE001 — reported by name, never raised
|
|
345
|
+
return AssetResolution(None, f'asset "{asset_id}": {error}')
|
|
346
|
+
|
|
347
|
+
problem = self._check(descriptor, content)
|
|
348
|
+
if problem:
|
|
349
|
+
return AssetResolution(None, f'asset "{asset_id}": {problem}')
|
|
350
|
+
|
|
351
|
+
asset = ResolvedAsset(asset_id, descriptor["mediaType"], content)
|
|
352
|
+
with self._lock:
|
|
353
|
+
# Only if the descriptor is still the one this was loaded for: an edit
|
|
354
|
+
# that arrived while a fetch was in flight must not be shadowed by it.
|
|
355
|
+
if _version(self._descriptors.get(asset_id)) == version:
|
|
356
|
+
self._remember(asset_id, _CacheEntry(version, asset, etag))
|
|
357
|
+
return AssetResolution(asset, "")
|
|
358
|
+
|
|
359
|
+
# ── Private ──────────────────────────────────────────────────────────────
|
|
360
|
+
|
|
361
|
+
def _load(self, descriptor: AssetDescriptor, cached: _CacheEntry | None) -> tuple[bytes, str | None]:
|
|
362
|
+
if "text" in descriptor:
|
|
363
|
+
return descriptor["text"].encode("utf-8"), None
|
|
364
|
+
if "base64" in descriptor:
|
|
365
|
+
content = decode_base64(descriptor["base64"])
|
|
366
|
+
if content is None:
|
|
367
|
+
raise _Refused("its content is not base64")
|
|
368
|
+
return content, None
|
|
369
|
+
|
|
370
|
+
url = descriptor["url"]
|
|
371
|
+
scheme = urlsplit(url).scheme.lower()
|
|
372
|
+
if scheme not in ("http", "https"):
|
|
373
|
+
if scheme == "file":
|
|
374
|
+
raise _Refused("file:// sources cannot be read by this runtime")
|
|
375
|
+
raise _Refused(f"{scheme}:// sources are not supported by this runtime")
|
|
376
|
+
|
|
377
|
+
headers = {"If-None-Match": cached.etag} if cached and cached.etag else {}
|
|
378
|
+
|
|
379
|
+
request = urllib.request.Request(url, headers=headers)
|
|
380
|
+
try:
|
|
381
|
+
with self._open(request, timeout=self._timeout_s) as response:
|
|
382
|
+
declared = response.headers.get("Content-Length")
|
|
383
|
+
if declared and declared.isdigit() and int(declared) > self._max_bytes:
|
|
384
|
+
raise _Refused(f"{url} is larger than {self._max_bytes} bytes")
|
|
385
|
+
content = response.read(self._max_bytes + 1)
|
|
386
|
+
if len(content) > self._max_bytes:
|
|
387
|
+
raise _Refused(f"{url} is larger than {self._max_bytes} bytes")
|
|
388
|
+
return content, response.headers.get("ETag")
|
|
389
|
+
except urllib.error.HTTPError as error:
|
|
390
|
+
if error.code == 304 and cached:
|
|
391
|
+
return cached.asset.content, cached.etag
|
|
392
|
+
raise _Refused(f"{url} answered {error.code}") from error
|
|
393
|
+
except urllib.error.URLError as error:
|
|
394
|
+
refused = refusal_in(error)
|
|
395
|
+
if refused:
|
|
396
|
+
raise _Refused(f"{url}: {refused}") from error
|
|
397
|
+
raise _Refused(f"{url} is unreachable: {error.reason}") from error
|
|
398
|
+
except TimeoutError as error:
|
|
399
|
+
raise _Refused(f"{url} did not answer in time") from error
|
|
400
|
+
|
|
401
|
+
def _check(self, descriptor: AssetDescriptor, content: bytes) -> str:
|
|
402
|
+
if len(content) > self._max_bytes:
|
|
403
|
+
return f"larger than {self._max_bytes} bytes"
|
|
404
|
+
expected = descriptor.get("sha256")
|
|
405
|
+
if expected:
|
|
406
|
+
actual = hashlib.sha256(content).hexdigest()
|
|
407
|
+
if actual != expected.lower():
|
|
408
|
+
return f"content does not match its sha256 (got {actual})"
|
|
409
|
+
return ""
|
|
410
|
+
|
|
411
|
+
def _remember(self, asset_id: str, entry: _CacheEntry) -> None:
|
|
412
|
+
self._forget(asset_id)
|
|
413
|
+
size = len(entry.asset.content)
|
|
414
|
+
if size > self._max_cache_bytes:
|
|
415
|
+
return
|
|
416
|
+
while self._cache and self._cached_bytes + size > self._max_cache_bytes:
|
|
417
|
+
oldest = next(iter(self._cache))
|
|
418
|
+
self._forget(oldest)
|
|
419
|
+
self._cache[asset_id] = entry
|
|
420
|
+
self._cached_bytes += size
|
|
421
|
+
|
|
422
|
+
def _forget(self, asset_id: str) -> None:
|
|
423
|
+
entry = self._cache.pop(asset_id, None)
|
|
424
|
+
if entry is not None:
|
|
425
|
+
self._cached_bytes -= len(entry.asset.content)
|
|
426
|
+
|
|
427
|
+
def _changed(self, asset_id: str) -> None:
|
|
428
|
+
with self._lock:
|
|
429
|
+
self._forget(asset_id)
|
|
430
|
+
listeners = list(self._listeners.get(asset_id, ()))
|
|
431
|
+
for listener in listeners:
|
|
432
|
+
try:
|
|
433
|
+
listener(asset_id)
|
|
434
|
+
except Exception as error: # noqa: BLE001
|
|
435
|
+
print(f'[assets] listener for "{asset_id}" failed: {error}')
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
class _Refused(Exception):
|
|
439
|
+
"""A source that cannot be read, said in a sentence."""
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
"""Authentication & authorization — ported from hkp-node/src/auth.ts.
|
|
2
|
+
|
|
3
|
+
The same client (hkp-frontend, the hkp-node coordinator) talks to hkp-node and
|
|
4
|
+
hkp-python over the identical REST/WS protocol, so the auth surface is kept
|
|
5
|
+
byte-compatible:
|
|
6
|
+
|
|
7
|
+
- HTTP routes expect ``Authorization: Bearer <token>``.
|
|
8
|
+
- WebSocket upgrades accept the token either in the Authorization header
|
|
9
|
+
(machine clients such as the coordinator) or as an ``?access_token=`` query
|
|
10
|
+
parameter (browsers cannot set headers on a WS handshake), plus an Origin
|
|
11
|
+
check against the allowed-origins list (CSWSH protection).
|
|
12
|
+
- Opaque session tokens are resolved locally *before* falling back to JWT
|
|
13
|
+
verification.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import asyncio
|
|
19
|
+
from dataclasses import dataclass
|
|
20
|
+
from typing import Any, Callable, Optional, Union
|
|
21
|
+
|
|
22
|
+
from .origins import AllowedOrigins, allows_origin
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class AuthenticatedUser:
|
|
27
|
+
"""Who a verified token speaks for.
|
|
28
|
+
|
|
29
|
+
``email`` is present only when the token carries one **and** says it is
|
|
30
|
+
verified, and is normalised the way the lists it is compared with are. An
|
|
31
|
+
address somebody merely typed while signing up is dropped rather than
|
|
32
|
+
carried: everything that reads it — the server's allowlist, a run's caller
|
|
33
|
+
— treats it as proof of who is asking.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
sub: str
|
|
37
|
+
email: str | None = None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def normalize_email(email: str) -> str:
|
|
41
|
+
"""An email as the lists it is compared with keep it."""
|
|
42
|
+
return email.strip().lower()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
#: Owner key used when authentication is disabled. Every request collapses into
|
|
46
|
+
#: this single tenant, which is exactly the pre-multi-tenancy behaviour.
|
|
47
|
+
ANONYMOUS_SUB = "anonymous"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def owner_key_of(user: AuthenticatedUser | None) -> str:
|
|
51
|
+
"""The tenant a request belongs to.
|
|
52
|
+
|
|
53
|
+
Runtimes are namespaced by this key, so a runtime id is only ever resolved
|
|
54
|
+
within the caller's own namespace. Matches the ``userId`` the coordinator
|
|
55
|
+
uses, so a coordinator-provisioned runtime lands where the browser looks.
|
|
56
|
+
"""
|
|
57
|
+
return user.sub if user else ANONYMOUS_SUB
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass
|
|
61
|
+
class AuthConfig:
|
|
62
|
+
"""How requests are authenticated.
|
|
63
|
+
|
|
64
|
+
- ``jwt`` — verify an Auth0 bearer token against the JWKS for
|
|
65
|
+
``domain``/``audience``. When ``allowed_emails`` is set, owning anything
|
|
66
|
+
here additionally takes a **verified** ``email`` claim that is on the
|
|
67
|
+
list. It gates who may own, not who may be identified.
|
|
68
|
+
|
|
69
|
+
``audience`` may list several accepted values. The frontend sends its
|
|
70
|
+
id_token, whose ``aud`` is the Auth0 *client id* of whichever application
|
|
71
|
+
signed the user in — and the web and native apps must be separate Auth0
|
|
72
|
+
applications (only a SPA-type one can do the browser flows, only a
|
|
73
|
+
Native-type one the RFC 8252 flow). One runtime serves users from both,
|
|
74
|
+
so it accepts both client ids.
|
|
75
|
+
- ``none`` — accept everything (no identity). Only ever resolved for a
|
|
76
|
+
local development checkout that opts in via ALLOW_NO_AUTH, or a loopback
|
|
77
|
+
bind (see resolve_server_auth_config in __main__.py).
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
mode: str # "jwt" | "none"
|
|
81
|
+
domain: str = ""
|
|
82
|
+
audience: str | list[str] = ""
|
|
83
|
+
allowed_emails: list[str] | None = None
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
# Resolves an opaque (non-JWT) bearer token to a principal, or None if unknown.
|
|
87
|
+
# Used for coordinator **session tokens**: short random strings the runtime
|
|
88
|
+
# itself mints (gated by a user JWT) and hands to the coordinator, so the
|
|
89
|
+
# coordinator can make long-lived machine calls on that user's behalf without a
|
|
90
|
+
# user JWT that would expire. The token resolves back to the user it was minted
|
|
91
|
+
# for, so there is no unscoped "service" superuser.
|
|
92
|
+
OpaqueTokenResolver = Callable[[str], Optional[AuthenticatedUser]]
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def is_email_allowed(
|
|
96
|
+
claims: dict[str, Any], allowed_emails: list[str] | None
|
|
97
|
+
) -> bool:
|
|
98
|
+
"""Email-allowlist gate applied after signature verification.
|
|
99
|
+
|
|
100
|
+
Fail closed: when a list is configured, a token without an ``email`` claim
|
|
101
|
+
— or with an unverified one — is rejected, because on tenants that allow
|
|
102
|
+
self-signup an attacker could otherwise register an allowlisted address
|
|
103
|
+
without owning it.
|
|
104
|
+
"""
|
|
105
|
+
if allowed_emails is None:
|
|
106
|
+
return True
|
|
107
|
+
email = claims.get("email")
|
|
108
|
+
if not isinstance(email, str) or claims.get("email_verified") is not True:
|
|
109
|
+
return False
|
|
110
|
+
return normalize_email(email) in allowed_emails
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def may_own(user: AuthenticatedUser, allowed_emails: list[str] | None) -> bool:
|
|
114
|
+
"""The allowlist asked of somebody already identified. An identity carries
|
|
115
|
+
an email only when it was verified, so this is ``is_email_allowed`` without
|
|
116
|
+
the claims."""
|
|
117
|
+
if allowed_emails is None:
|
|
118
|
+
return True
|
|
119
|
+
return bool(user.email) and user.email in allowed_emails
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def identity_from_claims(claims: dict[str, Any]) -> AuthenticatedUser | None:
|
|
123
|
+
"""What a token's claims say about who it speaks for, or None without a
|
|
124
|
+
``sub``."""
|
|
125
|
+
sub = claims.get("sub")
|
|
126
|
+
if not isinstance(sub, str) or not sub:
|
|
127
|
+
return None
|
|
128
|
+
email = claims.get("email")
|
|
129
|
+
# Dropped, not refused: a person whose address is unverified can still sign
|
|
130
|
+
# in wherever no list is asked.
|
|
131
|
+
verified = (
|
|
132
|
+
normalize_email(email)
|
|
133
|
+
if isinstance(email, str) and claims.get("email_verified") is True
|
|
134
|
+
else ""
|
|
135
|
+
)
|
|
136
|
+
return AuthenticatedUser(sub=sub, email=verified or None)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def is_loopback_host(host: str) -> bool:
|
|
140
|
+
"""True when the bind address is reachable only from the local machine.
|
|
141
|
+
|
|
142
|
+
A loopback bind keeps other machines out — nothing off-machine can
|
|
143
|
+
connect — which is why running without authentication is permitted there.
|
|
144
|
+
It does not keep out a page in a browser on this machine; origins.py does.
|
|
145
|
+
"""
|
|
146
|
+
h = host.strip().lower()
|
|
147
|
+
return h in ("localhost", "::1", "[::1]") or h.startswith("127.")
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def is_origin_allowed(origin: str | None, allowed: AllowedOrigins) -> bool:
|
|
151
|
+
"""Cross-Site WebSocket Hijacking protection, for an upgrade that carries
|
|
152
|
+
a credential.
|
|
153
|
+
|
|
154
|
+
Browsers always send an Origin header on the WS handshake, so a mismatched
|
|
155
|
+
one is a cross-site attempt and is rejected. Non-browser clients (e.g. the
|
|
156
|
+
coordinator) send no Origin; they are allowed through here and gated by
|
|
157
|
+
the token check instead.
|
|
158
|
+
|
|
159
|
+
An upgrade that carries no credential is asked more of; see
|
|
160
|
+
``admits_without_credential`` in origins.py.
|
|
161
|
+
"""
|
|
162
|
+
if origin is None:
|
|
163
|
+
return True
|
|
164
|
+
return allows_origin(origin, allowed)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class Authenticator:
|
|
168
|
+
"""Resolved auth surface shared by HTTP and WebSocket entry points so both
|
|
169
|
+
apply the exact same checks."""
|
|
170
|
+
|
|
171
|
+
def __init__(
|
|
172
|
+
self,
|
|
173
|
+
config: AuthConfig,
|
|
174
|
+
resolve_opaque_token: OpaqueTokenResolver | None = None,
|
|
175
|
+
) -> None:
|
|
176
|
+
self._config = config
|
|
177
|
+
self._resolve_opaque_token = resolve_opaque_token
|
|
178
|
+
self._jwks_client: Any = None
|
|
179
|
+
if config.mode == "jwt":
|
|
180
|
+
from jwt import PyJWKClient
|
|
181
|
+
|
|
182
|
+
self._jwks_client = PyJWKClient(
|
|
183
|
+
f"https://{config.domain}/.well-known/jwks.json",
|
|
184
|
+
cache_keys=True,
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
async def authorize_owner(self, token: str | None) -> AuthenticatedUser | None:
|
|
188
|
+
"""``identify_token``, then the server's allowlist: whether this person
|
|
189
|
+
may own things here. What every route uses. The token is a raw string,
|
|
190
|
+
from a WebSocket ``?access_token=`` query param or an Authorization
|
|
191
|
+
bearer value."""
|
|
192
|
+
if self._config.mode == "none":
|
|
193
|
+
return AuthenticatedUser(sub=ANONYMOUS_SUB)
|
|
194
|
+
if token and self._resolve_opaque_token is not None:
|
|
195
|
+
# A session token was minted for somebody who had passed the
|
|
196
|
+
# allowlist, and carries no email to ask it of again.
|
|
197
|
+
opaque = self._resolve_opaque_token(token)
|
|
198
|
+
if opaque is not None:
|
|
199
|
+
return opaque
|
|
200
|
+
user = await self.identify_token(token)
|
|
201
|
+
if user is None or not may_own(user, self._config.allowed_emails):
|
|
202
|
+
return None
|
|
203
|
+
return user
|
|
204
|
+
|
|
205
|
+
async def identify_token(self, token: str | None) -> AuthenticatedUser | None:
|
|
206
|
+
"""Who a raw token speaks for, and nothing about what they may do:
|
|
207
|
+
signature, audience and ``sub``, with the email only when it is
|
|
208
|
+
verified. Returns the principal or None.
|
|
209
|
+
"""
|
|
210
|
+
if self._config.mode == "none":
|
|
211
|
+
# Identity is irrelevant in no-auth mode; hand back a stable
|
|
212
|
+
# principal so downstream code that reads `sub` still works.
|
|
213
|
+
return AuthenticatedUser(sub="anonymous")
|
|
214
|
+
|
|
215
|
+
if not token:
|
|
216
|
+
return None
|
|
217
|
+
|
|
218
|
+
# Session tokens are opaque and resolve locally without a network
|
|
219
|
+
# round-trip, so check them before falling back to JWT verification.
|
|
220
|
+
if self._resolve_opaque_token is not None:
|
|
221
|
+
user = self._resolve_opaque_token(token)
|
|
222
|
+
if user is not None:
|
|
223
|
+
return user
|
|
224
|
+
|
|
225
|
+
# JWKS fetch + signature verification are blocking (urllib + crypto);
|
|
226
|
+
# keep them off the event loop.
|
|
227
|
+
return await asyncio.get_running_loop().run_in_executor(
|
|
228
|
+
None, self._verify_jwt, token
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
def _verify_jwt(self, token: str) -> AuthenticatedUser | None:
|
|
232
|
+
import jwt
|
|
233
|
+
|
|
234
|
+
try:
|
|
235
|
+
signing_key = self._jwks_client.get_signing_key_from_jwt(token)
|
|
236
|
+
# The same checks in every runtime (hkp-node's auth.ts, hkp-rt's
|
|
237
|
+
# http/auth.cpp, the website's auth.php): RS256 only, the issuer
|
|
238
|
+
# the keys came from, one of the accepted audiences, and an expiry
|
|
239
|
+
# and a subject that must be there, not merely valid when present.
|
|
240
|
+
decoded = jwt.decode(
|
|
241
|
+
token,
|
|
242
|
+
signing_key.key,
|
|
243
|
+
algorithms=["RS256"],
|
|
244
|
+
audience=self._config.audience,
|
|
245
|
+
issuer=f"https://{self._config.domain}/",
|
|
246
|
+
leeway=60,
|
|
247
|
+
options={"require": ["exp", "iss", "sub"]},
|
|
248
|
+
)
|
|
249
|
+
except Exception:
|
|
250
|
+
return None
|
|
251
|
+
|
|
252
|
+
return identity_from_claims(decoded)
|