@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,914 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service Documentation
|
|
3
|
+
* Service ID: conversations
|
|
4
|
+
* Service Name: Conversations
|
|
5
|
+
* Runtime: hkp-node
|
|
6
|
+
* Modes: ingest | thread | transition | actionable
|
|
7
|
+
* put-artifact | list-artifacts | set-artifact-status
|
|
8
|
+
* Key Config: mode, states, initialState, direction, state, stateFrom,
|
|
9
|
+
* conversationFrom, idFrom, inState, idleSeconds, limit,
|
|
10
|
+
* kind, status, payloadFrom, database
|
|
11
|
+
* IO: in=an email envelope (ingest) or a conversation id (everything else)
|
|
12
|
+
* ingest -> { conversationId, state, isNew, email }, or
|
|
13
|
+
* nothing at all when the message is already known
|
|
14
|
+
* thread -> { conversationId, emails, count, lastInbound }
|
|
15
|
+
* transition -> { conversationId, state, previous, updatedAt }
|
|
16
|
+
* actionable -> { conversations, count } — pair with `iterator` to
|
|
17
|
+
* act on them one at a time
|
|
18
|
+
* put-artifact -> the artifact
|
|
19
|
+
* list-artifacts -> { artifacts, count }
|
|
20
|
+
* set-artifact-status -> the artifact
|
|
21
|
+
*
|
|
22
|
+
* The domain half of the pair. `sql` knows SQL and nothing else; this knows
|
|
23
|
+
* what a conversation is — that mail arrives in threads, that a thread is in
|
|
24
|
+
* some state, and that work produced along the way has to be found again — and
|
|
25
|
+
* owns the three tables that say so.
|
|
26
|
+
*
|
|
27
|
+
* Which file it keeps them in is the board's to say, through `database`, and a
|
|
28
|
+
* board that leaves it empty gets one derived from its title — which is what
|
|
29
|
+
* every board did before the field existed, and the reason two boards sharing
|
|
30
|
+
* a title also share their conversations. The tenant is never the board's to
|
|
31
|
+
* choose: that comes from the runtime's scope.
|
|
32
|
+
*
|
|
33
|
+
* It reaches the database through `database.ts` directly, not through the `sql`
|
|
34
|
+
* service. Two services, one module: a service instance wrapping another
|
|
35
|
+
* service instance would be a service with a host no runtime gave it and a uuid
|
|
36
|
+
* nobody registered. The module is the shared part because the sharing happens
|
|
37
|
+
* at the level of a call.
|
|
38
|
+
*
|
|
39
|
+
* **Threading is by header, and therefore deterministic.** A conversation's id
|
|
40
|
+
* is the message id that began it: the id of any message already known from
|
|
41
|
+
* this one's `References`/`In-Reply-To` chain, else the oldest entry in that
|
|
42
|
+
* chain, else this message's own id. Two replies to a root nobody ever saw
|
|
43
|
+
* still land together, because they name the same root. The cost of choosing
|
|
44
|
+
* headers over a subject-and-participants heuristic is that a correspondent
|
|
45
|
+
* whose client drops `References` starts a new conversation — wrong, but
|
|
46
|
+
* wrong in the direction that leaves two threads to merge rather than two
|
|
47
|
+
* customers' bookings in one thread.
|
|
48
|
+
*
|
|
49
|
+
* **Ingesting a message twice is not an event.** A mailbox poll re-delivers,
|
|
50
|
+
* and the second delivery stops the pipeline exactly as a `store` miss does —
|
|
51
|
+
* there is nothing to pass on. That is what makes the mail loop safe to run on
|
|
52
|
+
* a timer without a board having to remember what it has seen.
|
|
53
|
+
*
|
|
54
|
+
* **States are declared by the board, not by this service.** A workflow's
|
|
55
|
+
* states are the workflow's, and hard-coding them here would mean a code change
|
|
56
|
+
* to add one. Declaring them buys something specific in return: whatever writes
|
|
57
|
+
* a transition — a model deciding the next step, most of all — can be refused
|
|
58
|
+
* when it invents a state, instead of stranding the conversation in a state no
|
|
59
|
+
* poll will ever select again.
|
|
60
|
+
*/
|
|
61
|
+
import {
|
|
62
|
+
HostedService,
|
|
63
|
+
JsonRecord,
|
|
64
|
+
RuntimeHost,
|
|
65
|
+
ServiceConfiguration,
|
|
66
|
+
ServiceRegistryEntry,
|
|
67
|
+
} from "../types";
|
|
68
|
+
import { Database, DatabaseStore, SqlValue } from "./database";
|
|
69
|
+
import { normalizeMessageId, normalizeReferences } from "./imap-email";
|
|
70
|
+
|
|
71
|
+
export const conversationsDescriptor: ServiceRegistryEntry = {
|
|
72
|
+
serviceId: "conversations",
|
|
73
|
+
serviceName: "Conversations",
|
|
74
|
+
version: "v1",
|
|
75
|
+
capabilities: [],
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
type Mode =
|
|
79
|
+
| "ingest"
|
|
80
|
+
| "thread"
|
|
81
|
+
| "transition"
|
|
82
|
+
| "actionable"
|
|
83
|
+
| "put-artifact"
|
|
84
|
+
| "list-artifacts"
|
|
85
|
+
| "set-artifact-status";
|
|
86
|
+
|
|
87
|
+
const MODES: Mode[] = [
|
|
88
|
+
"ingest",
|
|
89
|
+
"thread",
|
|
90
|
+
"transition",
|
|
91
|
+
"actionable",
|
|
92
|
+
"put-artifact",
|
|
93
|
+
"list-artifacts",
|
|
94
|
+
"set-artifact-status",
|
|
95
|
+
];
|
|
96
|
+
|
|
97
|
+
type Direction = "inbound" | "outbound";
|
|
98
|
+
|
|
99
|
+
const DEFAULT_STATE = "init";
|
|
100
|
+
const DEFAULT_ARTIFACT_STATUS = "pending";
|
|
101
|
+
const DEFAULT_LIMIT = 100;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* The tables, created on first use.
|
|
105
|
+
*
|
|
106
|
+
* `sender`/`recipient` rather than `from`/`to` because both of those are SQL
|
|
107
|
+
* keywords, and a column that has to be quoted everywhere it appears is a
|
|
108
|
+
* column that will one day not be. The board still sees `from` and `to`: the
|
|
109
|
+
* envelope vocabulary is `imap-email`'s, and this is the only place the two
|
|
110
|
+
* names differ.
|
|
111
|
+
*
|
|
112
|
+
* Every index here answers a query this service actually makes — `actionable`
|
|
113
|
+
* selects on state and age, `thread` reads one conversation in order, the
|
|
114
|
+
* facade lists artifacts by status. An index for anything else would be a
|
|
115
|
+
* guess.
|
|
116
|
+
*/
|
|
117
|
+
const SCHEMA = `
|
|
118
|
+
CREATE TABLE IF NOT EXISTS conversation (
|
|
119
|
+
id TEXT PRIMARY KEY,
|
|
120
|
+
state TEXT NOT NULL,
|
|
121
|
+
subject TEXT NOT NULL DEFAULT '',
|
|
122
|
+
participants TEXT NOT NULL DEFAULT '[]',
|
|
123
|
+
createdAt TEXT NOT NULL,
|
|
124
|
+
updatedAt TEXT NOT NULL
|
|
125
|
+
);
|
|
126
|
+
CREATE INDEX IF NOT EXISTS conversation_by_state
|
|
127
|
+
ON conversation (state, updatedAt);
|
|
128
|
+
|
|
129
|
+
CREATE TABLE IF NOT EXISTS email (
|
|
130
|
+
messageId TEXT PRIMARY KEY,
|
|
131
|
+
conversationId TEXT NOT NULL
|
|
132
|
+
REFERENCES conversation (id) ON DELETE CASCADE,
|
|
133
|
+
direction TEXT NOT NULL,
|
|
134
|
+
sentAt TEXT NOT NULL,
|
|
135
|
+
receivedAt TEXT NOT NULL,
|
|
136
|
+
sender TEXT NOT NULL DEFAULT '',
|
|
137
|
+
recipient TEXT NOT NULL DEFAULT '',
|
|
138
|
+
subject TEXT NOT NULL DEFAULT '',
|
|
139
|
+
body TEXT NOT NULL DEFAULT ''
|
|
140
|
+
);
|
|
141
|
+
CREATE INDEX IF NOT EXISTS email_by_conversation
|
|
142
|
+
ON email (conversationId, sentAt, receivedAt);
|
|
143
|
+
|
|
144
|
+
CREATE TABLE IF NOT EXISTS artifact (
|
|
145
|
+
id TEXT PRIMARY KEY,
|
|
146
|
+
conversationId TEXT NOT NULL
|
|
147
|
+
REFERENCES conversation (id) ON DELETE CASCADE,
|
|
148
|
+
kind TEXT NOT NULL,
|
|
149
|
+
payload TEXT NOT NULL,
|
|
150
|
+
status TEXT NOT NULL,
|
|
151
|
+
createdAt TEXT NOT NULL,
|
|
152
|
+
updatedAt TEXT NOT NULL
|
|
153
|
+
);
|
|
154
|
+
CREATE INDEX IF NOT EXISTS artifact_by_conversation
|
|
155
|
+
ON artifact (conversationId, createdAt);
|
|
156
|
+
CREATE INDEX IF NOT EXISTS artifact_by_status
|
|
157
|
+
ON artifact (status, updatedAt);
|
|
158
|
+
`;
|
|
159
|
+
|
|
160
|
+
type Notify = (payload: unknown, instanceId?: string) => void;
|
|
161
|
+
|
|
162
|
+
/** The value at a dotted path, or undefined where the path does not lead. */
|
|
163
|
+
function valueAt(input: unknown, path: string): unknown {
|
|
164
|
+
if (!path) {
|
|
165
|
+
return undefined;
|
|
166
|
+
}
|
|
167
|
+
let current: unknown = input;
|
|
168
|
+
for (const step of path.split(".")) {
|
|
169
|
+
if (current === null || typeof current !== "object") {
|
|
170
|
+
return undefined;
|
|
171
|
+
}
|
|
172
|
+
current = (current as JsonRecord)[step];
|
|
173
|
+
}
|
|
174
|
+
return current;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function asRecord(value: unknown): JsonRecord {
|
|
178
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
179
|
+
? (value as JsonRecord)
|
|
180
|
+
: {};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function text(value: unknown): string {
|
|
184
|
+
if (typeof value === "string") {
|
|
185
|
+
return value;
|
|
186
|
+
}
|
|
187
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
188
|
+
return String(value);
|
|
189
|
+
}
|
|
190
|
+
return "";
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* An address list as one string.
|
|
195
|
+
*
|
|
196
|
+
* A `to` header holds several addresses and arrives as an array as often as a
|
|
197
|
+
* string; keeping the shape would make every reader handle both.
|
|
198
|
+
*/
|
|
199
|
+
function addresses(value: unknown): string {
|
|
200
|
+
if (Array.isArray(value)) {
|
|
201
|
+
return value.map(text).filter(Boolean).join(", ");
|
|
202
|
+
}
|
|
203
|
+
return text(value);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** A list of names, however the board chose to write it. */
|
|
207
|
+
function names(value: unknown): string[] {
|
|
208
|
+
if (Array.isArray(value)) {
|
|
209
|
+
return value.map(text).filter(Boolean);
|
|
210
|
+
}
|
|
211
|
+
if (typeof value === "string") {
|
|
212
|
+
return value
|
|
213
|
+
.split(",")
|
|
214
|
+
.map((entry) => entry.trim())
|
|
215
|
+
.filter(Boolean);
|
|
216
|
+
}
|
|
217
|
+
return [];
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** An ISO timestamp, whatever the header offered. */
|
|
221
|
+
function timestamp(value: unknown, fallback: string): string {
|
|
222
|
+
if (typeof value === "string" && value) {
|
|
223
|
+
const parsed = new Date(value);
|
|
224
|
+
if (!Number.isNaN(parsed.getTime())) {
|
|
225
|
+
return parsed.toISOString();
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
if (value instanceof Date && !Number.isNaN(value.getTime())) {
|
|
229
|
+
return value.toISOString();
|
|
230
|
+
}
|
|
231
|
+
return fallback;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/** Reads a JSON column, tolerating one that was never written by us. */
|
|
235
|
+
function parsed(value: unknown, fallback: unknown): unknown {
|
|
236
|
+
if (typeof value !== "string") {
|
|
237
|
+
return fallback;
|
|
238
|
+
}
|
|
239
|
+
try {
|
|
240
|
+
return JSON.parse(value);
|
|
241
|
+
} catch {
|
|
242
|
+
return fallback;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/** A row as a board sees it: the envelope's names, and JSON columns opened. */
|
|
247
|
+
function describedEmail(row: JsonRecord): JsonRecord {
|
|
248
|
+
return {
|
|
249
|
+
messageId: row.messageId,
|
|
250
|
+
conversationId: row.conversationId,
|
|
251
|
+
direction: row.direction,
|
|
252
|
+
sentAt: row.sentAt,
|
|
253
|
+
receivedAt: row.receivedAt,
|
|
254
|
+
from: row.sender,
|
|
255
|
+
to: row.recipient,
|
|
256
|
+
subject: row.subject,
|
|
257
|
+
body: row.body,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function describedConversation(row: JsonRecord): JsonRecord {
|
|
262
|
+
return {
|
|
263
|
+
conversationId: row.id,
|
|
264
|
+
state: row.state,
|
|
265
|
+
subject: row.subject,
|
|
266
|
+
participants: parsed(row.participants, []),
|
|
267
|
+
createdAt: row.createdAt,
|
|
268
|
+
updatedAt: row.updatedAt,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function describedArtifact(row: JsonRecord): JsonRecord {
|
|
273
|
+
return {
|
|
274
|
+
id: row.id,
|
|
275
|
+
conversationId: row.conversationId,
|
|
276
|
+
kind: row.kind,
|
|
277
|
+
payload: parsed(row.payload, row.payload),
|
|
278
|
+
status: row.status,
|
|
279
|
+
createdAt: row.createdAt,
|
|
280
|
+
updatedAt: row.updatedAt,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export class ConversationsService implements HostedService {
|
|
285
|
+
readonly serviceId = conversationsDescriptor.serviceId;
|
|
286
|
+
readonly serviceName = conversationsDescriptor.serviceName;
|
|
287
|
+
readonly version = conversationsDescriptor.version;
|
|
288
|
+
readonly capabilities = conversationsDescriptor.capabilities;
|
|
289
|
+
readonly uuid: string;
|
|
290
|
+
|
|
291
|
+
private host: RuntimeHost | null = null;
|
|
292
|
+
private mode: Mode = "ingest";
|
|
293
|
+
/** The declared state machine. Empty accepts anything. */
|
|
294
|
+
private states: string[] = [];
|
|
295
|
+
private initialState = DEFAULT_STATE;
|
|
296
|
+
private direction: Direction = "inbound";
|
|
297
|
+
private state = "";
|
|
298
|
+
private stateFrom = "state";
|
|
299
|
+
private conversationFrom = "";
|
|
300
|
+
private idFrom = "";
|
|
301
|
+
private inState: string[] = [];
|
|
302
|
+
private idleSeconds = 0;
|
|
303
|
+
private limit = DEFAULT_LIMIT;
|
|
304
|
+
private kind = "";
|
|
305
|
+
private status = "";
|
|
306
|
+
private payloadFrom = "";
|
|
307
|
+
/** The file this board keeps its conversations in. Empty derives one. */
|
|
308
|
+
private database = "";
|
|
309
|
+
private lastCount = 0;
|
|
310
|
+
private lastError = "";
|
|
311
|
+
/** Boards whose tables this instance has already created. */
|
|
312
|
+
private prepared = new Set<string>();
|
|
313
|
+
|
|
314
|
+
constructor(
|
|
315
|
+
config: ServiceConfiguration,
|
|
316
|
+
private readonly databases: DatabaseStore,
|
|
317
|
+
) {
|
|
318
|
+
this.uuid = config.uuid;
|
|
319
|
+
if (config.state) {
|
|
320
|
+
this.configure(config.state);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
setHost(host: RuntimeHost): void {
|
|
325
|
+
this.host = host;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
getState(): JsonRecord {
|
|
329
|
+
return {
|
|
330
|
+
mode: this.mode,
|
|
331
|
+
states: [...this.states],
|
|
332
|
+
initialState: this.initialState,
|
|
333
|
+
direction: this.direction,
|
|
334
|
+
state: this.state,
|
|
335
|
+
stateFrom: this.stateFrom,
|
|
336
|
+
conversationFrom: this.conversationFrom,
|
|
337
|
+
idFrom: this.idFrom,
|
|
338
|
+
inState: [...this.inState],
|
|
339
|
+
idleSeconds: this.idleSeconds,
|
|
340
|
+
limit: this.limit,
|
|
341
|
+
kind: this.kind,
|
|
342
|
+
status: this.status,
|
|
343
|
+
payloadFrom: this.payloadFrom,
|
|
344
|
+
database: this.database,
|
|
345
|
+
lastCount: this.lastCount,
|
|
346
|
+
error: this.lastError,
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
configure(config: JsonRecord): JsonRecord {
|
|
351
|
+
if (typeof config.mode === "string" && MODES.includes(config.mode as Mode)) {
|
|
352
|
+
this.mode = config.mode as Mode;
|
|
353
|
+
}
|
|
354
|
+
if (config.states !== undefined) {
|
|
355
|
+
this.states = names(config.states);
|
|
356
|
+
}
|
|
357
|
+
if (typeof config.initialState === "string" && config.initialState) {
|
|
358
|
+
this.initialState = config.initialState;
|
|
359
|
+
}
|
|
360
|
+
if (config.direction === "inbound" || config.direction === "outbound") {
|
|
361
|
+
this.direction = config.direction;
|
|
362
|
+
}
|
|
363
|
+
if (typeof config.state === "string") {
|
|
364
|
+
this.state = config.state;
|
|
365
|
+
}
|
|
366
|
+
if (typeof config.stateFrom === "string") {
|
|
367
|
+
this.stateFrom = config.stateFrom;
|
|
368
|
+
}
|
|
369
|
+
if (typeof config.idFrom === "string") {
|
|
370
|
+
this.idFrom = config.idFrom;
|
|
371
|
+
}
|
|
372
|
+
if (typeof config.conversationFrom === "string") {
|
|
373
|
+
this.conversationFrom = config.conversationFrom;
|
|
374
|
+
}
|
|
375
|
+
if (config.inState !== undefined) {
|
|
376
|
+
this.inState = names(config.inState);
|
|
377
|
+
}
|
|
378
|
+
if (typeof config.idleSeconds === "number" && config.idleSeconds >= 0) {
|
|
379
|
+
this.idleSeconds = config.idleSeconds;
|
|
380
|
+
}
|
|
381
|
+
if (typeof config.limit === "number" && config.limit > 0) {
|
|
382
|
+
this.limit = Math.floor(config.limit);
|
|
383
|
+
}
|
|
384
|
+
if (typeof config.kind === "string") {
|
|
385
|
+
this.kind = config.kind;
|
|
386
|
+
}
|
|
387
|
+
if (typeof config.status === "string") {
|
|
388
|
+
this.status = config.status;
|
|
389
|
+
}
|
|
390
|
+
if (typeof config.payloadFrom === "string") {
|
|
391
|
+
this.payloadFrom = config.payloadFrom;
|
|
392
|
+
}
|
|
393
|
+
if (typeof config.database === "string" && config.database !== this.database) {
|
|
394
|
+
this.database = config.database.trim();
|
|
395
|
+
// Another file is another set of tables, whatever this one already made.
|
|
396
|
+
this.prepared.clear();
|
|
397
|
+
}
|
|
398
|
+
return this.getState();
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Runs the mode and returns what it produced.
|
|
403
|
+
*
|
|
404
|
+
* Synchronous like `sql` and unlike `store`: SQLite answers inside the call,
|
|
405
|
+
* so there is nothing to wait for and no reason to stop the pipeline and
|
|
406
|
+
* re-enter it. `actionable` is the exception, and for the opposite reason —
|
|
407
|
+
* it has several things to say, not one.
|
|
408
|
+
*/
|
|
409
|
+
process(input: unknown, notify: Notify): unknown {
|
|
410
|
+
const scope = this.host?.scope();
|
|
411
|
+
if (!scope) {
|
|
412
|
+
return this.fail(notify, "conversations has no runtime to scope its database to");
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
let db: Database;
|
|
416
|
+
try {
|
|
417
|
+
db = this.database
|
|
418
|
+
? this.databases.openNamed(scope.owner, this.database)
|
|
419
|
+
: this.databases.open(scope);
|
|
420
|
+
const key = `${scope.owner} ${this.database || scope.boardName}`;
|
|
421
|
+
if (!this.prepared.has(key)) {
|
|
422
|
+
db.exec(SCHEMA);
|
|
423
|
+
this.prepared.add(key);
|
|
424
|
+
}
|
|
425
|
+
} catch (err) {
|
|
426
|
+
return this.fail(notify, `could not open the board's database: ${reason(err)}`);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
try {
|
|
430
|
+
return this.run(db, input, notify);
|
|
431
|
+
} catch (err) {
|
|
432
|
+
return this.fail(notify, `${this.mode} failed: ${reason(err)}`);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
destroy(): void {
|
|
437
|
+
this.host = null;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// ── Private ───────────────────────────────────────────────────────────────
|
|
441
|
+
|
|
442
|
+
private run(db: Database, input: unknown, notify: Notify): unknown {
|
|
443
|
+
switch (this.mode) {
|
|
444
|
+
case "ingest":
|
|
445
|
+
return this.ingest(db, input, notify);
|
|
446
|
+
case "thread":
|
|
447
|
+
return this.thread(db, input, notify);
|
|
448
|
+
case "transition":
|
|
449
|
+
return this.transition(db, input, notify);
|
|
450
|
+
case "actionable":
|
|
451
|
+
return this.actionable(db, notify);
|
|
452
|
+
case "put-artifact":
|
|
453
|
+
return this.putArtifact(db, input, notify);
|
|
454
|
+
case "list-artifacts":
|
|
455
|
+
return this.listArtifacts(db, input, notify);
|
|
456
|
+
case "set-artifact-status":
|
|
457
|
+
return this.setArtifactStatus(db, input, notify);
|
|
458
|
+
default:
|
|
459
|
+
return this.fail(notify, `unknown mode '${this.mode}'`);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Files a message under the conversation it belongs to.
|
|
465
|
+
*
|
|
466
|
+
* The conversation is created if this is the first message of a thread, and
|
|
467
|
+
* left alone otherwise — its state belongs to whatever has been deciding it,
|
|
468
|
+
* and an arriving reply is not a decision. What does change is `updatedAt`,
|
|
469
|
+
* because a thread with a new message in it is a thread something may now
|
|
470
|
+
* need to do, and that is what `actionable` sorts on.
|
|
471
|
+
*/
|
|
472
|
+
private ingest(db: Database, input: unknown, notify: Notify): unknown {
|
|
473
|
+
const envelope = asRecord(input);
|
|
474
|
+
const messageId = normalizeMessageId(envelope.messageId);
|
|
475
|
+
if (!messageId) {
|
|
476
|
+
return this.fail(notify, "ingest needs a messageId to file the message under");
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
const now = new Date().toISOString();
|
|
480
|
+
const conversationId = this.threadOf(db, messageId, envelope);
|
|
481
|
+
const subject = text(envelope.subject);
|
|
482
|
+
const sender = text(envelope.from);
|
|
483
|
+
const recipient = addresses(envelope.to);
|
|
484
|
+
const direction =
|
|
485
|
+
envelope.direction === "inbound" || envelope.direction === "outbound"
|
|
486
|
+
? (envelope.direction as Direction)
|
|
487
|
+
: this.direction;
|
|
488
|
+
|
|
489
|
+
const existing = db.query("SELECT * FROM conversation WHERE id = $id", {
|
|
490
|
+
$id: conversationId,
|
|
491
|
+
})[0];
|
|
492
|
+
const isNew = existing === undefined;
|
|
493
|
+
|
|
494
|
+
if (isNew) {
|
|
495
|
+
db.run(
|
|
496
|
+
`INSERT INTO conversation (id, state, subject, participants, createdAt, updatedAt)
|
|
497
|
+
VALUES ($id, $state, $subject, $participants, $now, $now)`,
|
|
498
|
+
{
|
|
499
|
+
$id: conversationId,
|
|
500
|
+
$state: this.initialState,
|
|
501
|
+
$subject: subject,
|
|
502
|
+
$participants: JSON.stringify(
|
|
503
|
+
[sender, recipient].filter(Boolean),
|
|
504
|
+
),
|
|
505
|
+
$now: now,
|
|
506
|
+
},
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
const stored = db.run(
|
|
511
|
+
`INSERT INTO email
|
|
512
|
+
(messageId, conversationId, direction, sentAt, receivedAt,
|
|
513
|
+
sender, recipient, subject, body)
|
|
514
|
+
VALUES ($messageId, $conversationId, $direction, $sentAt, $receivedAt,
|
|
515
|
+
$sender, $recipient, $subject, $body)
|
|
516
|
+
ON CONFLICT (messageId) DO NOTHING`,
|
|
517
|
+
{
|
|
518
|
+
$messageId: messageId,
|
|
519
|
+
$conversationId: conversationId,
|
|
520
|
+
$direction: direction,
|
|
521
|
+
$sentAt: timestamp(envelope.date ?? envelope.sentAt, now),
|
|
522
|
+
$receivedAt: now,
|
|
523
|
+
$sender: sender,
|
|
524
|
+
$recipient: recipient,
|
|
525
|
+
$subject: subject,
|
|
526
|
+
$body: text(envelope.text ?? envelope.body),
|
|
527
|
+
},
|
|
528
|
+
);
|
|
529
|
+
|
|
530
|
+
if (stored.changes === 0) {
|
|
531
|
+
// Already filed. A poll re-delivering is the normal case, so this is not
|
|
532
|
+
// a failure — it is simply nothing to pass on, and the pipeline stops
|
|
533
|
+
// here rather than answering the same message twice.
|
|
534
|
+
this.lastCount = 0;
|
|
535
|
+
this.lastError = "";
|
|
536
|
+
notify({ conversationId, messageId, stored: false });
|
|
537
|
+
return null;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
if (!isNew) {
|
|
541
|
+
this.touch(db, conversationId, now, sender, recipient);
|
|
542
|
+
}
|
|
543
|
+
this.lastCount = 1;
|
|
544
|
+
this.lastError = "";
|
|
545
|
+
|
|
546
|
+
const conversation = describedConversation(
|
|
547
|
+
db.query("SELECT * FROM conversation WHERE id = $id", {
|
|
548
|
+
$id: conversationId,
|
|
549
|
+
})[0],
|
|
550
|
+
);
|
|
551
|
+
const email = describedEmail(
|
|
552
|
+
db.query("SELECT * FROM email WHERE messageId = $id", {
|
|
553
|
+
$id: messageId,
|
|
554
|
+
})[0],
|
|
555
|
+
);
|
|
556
|
+
const result = { ...conversation, isNew, email };
|
|
557
|
+
notify(result);
|
|
558
|
+
return result;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
/**
|
|
562
|
+
* The conversation a message belongs to.
|
|
563
|
+
*
|
|
564
|
+
* The chain is read newest-intent-first — the message it directly answers,
|
|
565
|
+
* then the references from newest to oldest — so a reply joins the most
|
|
566
|
+
* specific thread we already know about. Only when none of them is known does
|
|
567
|
+
* the root of the chain name a conversation of its own.
|
|
568
|
+
*/
|
|
569
|
+
private threadOf(
|
|
570
|
+
db: Database,
|
|
571
|
+
messageId: string,
|
|
572
|
+
envelope: JsonRecord,
|
|
573
|
+
): string {
|
|
574
|
+
const references = normalizeReferences(envelope.references);
|
|
575
|
+
const inReplyTo = normalizeMessageId(envelope.inReplyTo);
|
|
576
|
+
const chain = [inReplyTo, ...[...references].reverse(), messageId].filter(
|
|
577
|
+
Boolean,
|
|
578
|
+
);
|
|
579
|
+
|
|
580
|
+
for (const id of chain) {
|
|
581
|
+
const found = db.query(
|
|
582
|
+
"SELECT conversationId FROM email WHERE messageId = $id",
|
|
583
|
+
{ $id: id },
|
|
584
|
+
)[0];
|
|
585
|
+
if (found) {
|
|
586
|
+
return String(found.conversationId);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// Nothing known: the thread is named by whatever began it, so two replies
|
|
591
|
+
// to a root we never saw still meet.
|
|
592
|
+
return references[0] || inReplyTo || messageId;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/** Marks a conversation as having moved, and widens who is known to be in it. */
|
|
596
|
+
private touch(
|
|
597
|
+
db: Database,
|
|
598
|
+
conversationId: string,
|
|
599
|
+
now: string,
|
|
600
|
+
...seen: string[]
|
|
601
|
+
): void {
|
|
602
|
+
const row = db.query(
|
|
603
|
+
"SELECT participants FROM conversation WHERE id = $id",
|
|
604
|
+
{ $id: conversationId },
|
|
605
|
+
)[0];
|
|
606
|
+
const known = parsed(row?.participants, []) as unknown[];
|
|
607
|
+
const merged = [...new Set([...known.map(text), ...seen].filter(Boolean))];
|
|
608
|
+
db.run(
|
|
609
|
+
`UPDATE conversation SET updatedAt = $now, participants = $participants
|
|
610
|
+
WHERE id = $id`,
|
|
611
|
+
{
|
|
612
|
+
$id: conversationId,
|
|
613
|
+
$now: now,
|
|
614
|
+
$participants: JSON.stringify(merged),
|
|
615
|
+
},
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
private thread(db: Database, input: unknown, notify: Notify): unknown {
|
|
620
|
+
const conversationId = this.conversationId(input);
|
|
621
|
+
if (!conversationId) {
|
|
622
|
+
return this.fail(notify, "thread needs a conversation id");
|
|
623
|
+
}
|
|
624
|
+
const emails = db
|
|
625
|
+
.query(
|
|
626
|
+
`SELECT * FROM email WHERE conversationId = $id
|
|
627
|
+
ORDER BY sentAt ASC, receivedAt ASC LIMIT $limit`,
|
|
628
|
+
{ $id: conversationId, $limit: this.limit },
|
|
629
|
+
)
|
|
630
|
+
.map(describedEmail);
|
|
631
|
+
this.lastCount = emails.length;
|
|
632
|
+
this.lastError = "";
|
|
633
|
+
// Who wrote in last, separately from the list.
|
|
634
|
+
//
|
|
635
|
+
// A reply goes to the last person who wrote *to us*, and the restricted
|
|
636
|
+
// expression dialect a board composes with has no way to search a list for
|
|
637
|
+
// that. Taking the last email instead is right only until we have sent one
|
|
638
|
+
// ourselves — at which point a board would quietly address its reply to
|
|
639
|
+
// its own sending address.
|
|
640
|
+
const lastInbound =
|
|
641
|
+
[...emails].reverse().find((email) => email.direction === "inbound") ??
|
|
642
|
+
null;
|
|
643
|
+
|
|
644
|
+
const result = {
|
|
645
|
+
conversationId,
|
|
646
|
+
emails,
|
|
647
|
+
count: emails.length,
|
|
648
|
+
lastInbound,
|
|
649
|
+
};
|
|
650
|
+
notify(result);
|
|
651
|
+
return result;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* Moves a conversation to another state.
|
|
656
|
+
*
|
|
657
|
+
* A state the board never declared is refused rather than written. Whatever
|
|
658
|
+
* decides a transition is often a model, and a state nothing selects on is
|
|
659
|
+
* indistinguishable from a conversation that has quietly stopped being
|
|
660
|
+
* worked on — the failure would show up as silence, days later.
|
|
661
|
+
*/
|
|
662
|
+
private transition(db: Database, input: unknown, notify: Notify): unknown {
|
|
663
|
+
const conversationId = this.conversationId(input);
|
|
664
|
+
if (!conversationId) {
|
|
665
|
+
return this.fail(notify, "transition needs a conversation id");
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
const target =
|
|
669
|
+
this.state || text(valueAt(input, this.stateFrom) ?? asRecord(input).state);
|
|
670
|
+
if (!target) {
|
|
671
|
+
return this.fail(notify, "transition needs a state to move to");
|
|
672
|
+
}
|
|
673
|
+
if (this.states.length > 0 && !this.states.includes(target)) {
|
|
674
|
+
return this.fail(
|
|
675
|
+
notify,
|
|
676
|
+
`'${target}' is not one of this board's states (${this.states.join(", ")})`,
|
|
677
|
+
);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
const before = db.query("SELECT state FROM conversation WHERE id = $id", {
|
|
681
|
+
$id: conversationId,
|
|
682
|
+
})[0];
|
|
683
|
+
if (!before) {
|
|
684
|
+
return this.fail(notify, `no conversation '${conversationId}'`);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
const now = new Date().toISOString();
|
|
688
|
+
db.run(
|
|
689
|
+
"UPDATE conversation SET state = $state, updatedAt = $now WHERE id = $id",
|
|
690
|
+
{ $id: conversationId, $state: target, $now: now },
|
|
691
|
+
);
|
|
692
|
+
this.lastCount = 1;
|
|
693
|
+
this.lastError = "";
|
|
694
|
+
const result = {
|
|
695
|
+
conversationId,
|
|
696
|
+
state: target,
|
|
697
|
+
previous: before.state,
|
|
698
|
+
updatedAt: now,
|
|
699
|
+
};
|
|
700
|
+
notify(result);
|
|
701
|
+
return result;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/**
|
|
705
|
+
* The conversations something should now be doing something about.
|
|
706
|
+
*
|
|
707
|
+
* Says what it found and nothing more. Acting on them one at a time is
|
|
708
|
+
* iteration, which is `iterator`'s job — a service that looped over its own
|
|
709
|
+
* results would be building that into whichever service needed it first, and
|
|
710
|
+
* every other service producing a list would then need it too.
|
|
711
|
+
*
|
|
712
|
+
* `idleSeconds` is what stops a poll from picking the same conversation up
|
|
713
|
+
* every tick while the last decision is still being carried out.
|
|
714
|
+
*/
|
|
715
|
+
private actionable(db: Database, notify: Notify): unknown {
|
|
716
|
+
if (this.inState.length === 0) {
|
|
717
|
+
return this.fail(notify, "actionable needs inState to say which states to poll");
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
const placeholders = this.inState.map((_, index) => `$s${index}`);
|
|
721
|
+
const params: Record<string, SqlValue> = { $limit: this.limit };
|
|
722
|
+
this.inState.forEach((state, index) => {
|
|
723
|
+
params[`$s${index}`] = state;
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
let where = `state IN (${placeholders.join(", ")})`;
|
|
727
|
+
if (this.idleSeconds > 0) {
|
|
728
|
+
where += " AND updatedAt <= $before";
|
|
729
|
+
params.$before = new Date(
|
|
730
|
+
Date.now() - this.idleSeconds * 1000,
|
|
731
|
+
).toISOString();
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
const rows = db.query(
|
|
735
|
+
`SELECT * FROM conversation WHERE ${where}
|
|
736
|
+
ORDER BY updatedAt ASC LIMIT $limit`,
|
|
737
|
+
params,
|
|
738
|
+
);
|
|
739
|
+
this.lastCount = rows.length;
|
|
740
|
+
this.lastError = "";
|
|
741
|
+
|
|
742
|
+
const conversations = rows.map(describedConversation);
|
|
743
|
+
const result = { conversations, count: conversations.length };
|
|
744
|
+
notify(result);
|
|
745
|
+
return result;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
/**
|
|
749
|
+
* Keeps something the workflow produced, against the conversation it is for.
|
|
750
|
+
*
|
|
751
|
+
* A draft reply, an extraction, a booking — the service does not care which,
|
|
752
|
+
* because `kind` is the board's word and the payload is whatever it was. What
|
|
753
|
+
* this owns is that the thing is findable later: by conversation, and by
|
|
754
|
+
* whether anyone has dealt with it yet.
|
|
755
|
+
*/
|
|
756
|
+
private putArtifact(db: Database, input: unknown, notify: Notify): unknown {
|
|
757
|
+
const conversationId = this.conversationId(input);
|
|
758
|
+
if (!conversationId) {
|
|
759
|
+
return this.fail(notify, "put-artifact needs a conversation id");
|
|
760
|
+
}
|
|
761
|
+
if (!this.kind) {
|
|
762
|
+
return this.fail(notify, "put-artifact needs a kind");
|
|
763
|
+
}
|
|
764
|
+
const known = db.query("SELECT 1 FROM conversation WHERE id = $id", {
|
|
765
|
+
$id: conversationId,
|
|
766
|
+
})[0];
|
|
767
|
+
if (!known) {
|
|
768
|
+
return this.fail(notify, `no conversation '${conversationId}'`);
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
const payload = this.payloadFrom ? valueAt(input, this.payloadFrom) : input;
|
|
772
|
+
const now = new Date().toISOString();
|
|
773
|
+
const id = `${now}-${Math.random().toString(36).slice(2, 8)}`;
|
|
774
|
+
db.run(
|
|
775
|
+
`INSERT INTO artifact
|
|
776
|
+
(id, conversationId, kind, payload, status, createdAt, updatedAt)
|
|
777
|
+
VALUES ($id, $conversationId, $kind, $payload, $status, $now, $now)`,
|
|
778
|
+
{
|
|
779
|
+
$id: id,
|
|
780
|
+
$conversationId: conversationId,
|
|
781
|
+
$kind: this.kind,
|
|
782
|
+
$payload: JSON.stringify(payload ?? null),
|
|
783
|
+
$status: this.status || DEFAULT_ARTIFACT_STATUS,
|
|
784
|
+
$now: now,
|
|
785
|
+
},
|
|
786
|
+
);
|
|
787
|
+
this.lastCount = 1;
|
|
788
|
+
this.lastError = "";
|
|
789
|
+
const result = describedArtifact(
|
|
790
|
+
db.query("SELECT * FROM artifact WHERE id = $id", { $id: id })[0],
|
|
791
|
+
);
|
|
792
|
+
notify(result);
|
|
793
|
+
return result;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/**
|
|
797
|
+
* The artifacts, newest last, narrowed by whatever was configured.
|
|
798
|
+
*
|
|
799
|
+
* Returns a list rather than a pass each: this is what a facade table reads,
|
|
800
|
+
* and a table wants the rows together.
|
|
801
|
+
*/
|
|
802
|
+
private listArtifacts(db: Database, input: unknown, notify: Notify): unknown {
|
|
803
|
+
const conversationId = this.conversationId(input);
|
|
804
|
+
const clauses: string[] = [];
|
|
805
|
+
const params: Record<string, SqlValue> = { $limit: this.limit };
|
|
806
|
+
if (conversationId) {
|
|
807
|
+
clauses.push("conversationId = $conversationId");
|
|
808
|
+
params.$conversationId = conversationId;
|
|
809
|
+
}
|
|
810
|
+
if (this.kind) {
|
|
811
|
+
clauses.push("kind = $kind");
|
|
812
|
+
params.$kind = this.kind;
|
|
813
|
+
}
|
|
814
|
+
if (this.status) {
|
|
815
|
+
clauses.push("status = $status");
|
|
816
|
+
params.$status = this.status;
|
|
817
|
+
}
|
|
818
|
+
const where = clauses.length ? `WHERE ${clauses.join(" AND ")}` : "";
|
|
819
|
+
|
|
820
|
+
const artifacts = db
|
|
821
|
+
.query(
|
|
822
|
+
`SELECT * FROM artifact ${where} ORDER BY createdAt ASC LIMIT $limit`,
|
|
823
|
+
params,
|
|
824
|
+
)
|
|
825
|
+
.map(describedArtifact);
|
|
826
|
+
this.lastCount = artifacts.length;
|
|
827
|
+
this.lastError = "";
|
|
828
|
+
const result = { artifacts, count: artifacts.length };
|
|
829
|
+
notify(result);
|
|
830
|
+
return result;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
/**
|
|
834
|
+
* Records what was decided about one artifact.
|
|
835
|
+
*
|
|
836
|
+
* Unlike a conversation's state, the status is not checked against a declared
|
|
837
|
+
* set. A transition is written by whatever is deciding — a model, most of all
|
|
838
|
+
* — where an invented value goes unnoticed; a status is written by a person
|
|
839
|
+
* pressing a button whose payload the board fixed, where an invented value is
|
|
840
|
+
* a board bug that shows up the first time anyone presses it.
|
|
841
|
+
*/
|
|
842
|
+
private setArtifactStatus(
|
|
843
|
+
db: Database,
|
|
844
|
+
input: unknown,
|
|
845
|
+
notify: Notify,
|
|
846
|
+
): unknown {
|
|
847
|
+
const record = asRecord(input);
|
|
848
|
+
// A pass that has just done something to a conversation carries what it
|
|
849
|
+
// did, not the artifact it did it about, so the board says where to look.
|
|
850
|
+
const found = this.idFrom ? valueAt(input, this.idFrom) : undefined;
|
|
851
|
+
const id =
|
|
852
|
+
(typeof found === "string" ? found : "") ||
|
|
853
|
+
text(record.id) ||
|
|
854
|
+
(typeof input === "string" ? input : "");
|
|
855
|
+
if (!id) {
|
|
856
|
+
return this.fail(notify, "set-artifact-status needs an artifact id");
|
|
857
|
+
}
|
|
858
|
+
const target = this.status || text(record.status);
|
|
859
|
+
if (!target) {
|
|
860
|
+
return this.fail(notify, "set-artifact-status needs a status");
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
const now = new Date().toISOString();
|
|
864
|
+
const changed = db.run(
|
|
865
|
+
"UPDATE artifact SET status = $status, updatedAt = $now WHERE id = $id",
|
|
866
|
+
{ $id: id, $status: target, $now: now },
|
|
867
|
+
);
|
|
868
|
+
if (changed.changes === 0) {
|
|
869
|
+
return this.fail(notify, `no artifact '${id}'`);
|
|
870
|
+
}
|
|
871
|
+
this.lastCount = 1;
|
|
872
|
+
this.lastError = "";
|
|
873
|
+
const result = describedArtifact(
|
|
874
|
+
db.query("SELECT * FROM artifact WHERE id = $id", { $id: id })[0],
|
|
875
|
+
);
|
|
876
|
+
notify(result);
|
|
877
|
+
return result;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
/**
|
|
881
|
+
* The conversation this pass acts on.
|
|
882
|
+
*
|
|
883
|
+
* The input decides before the configuration does, the way `store` resolves a
|
|
884
|
+
* key: a pass carrying a conversation is being specific about which one.
|
|
885
|
+
*/
|
|
886
|
+
private conversationId(input: unknown): string {
|
|
887
|
+
if (this.conversationFrom) {
|
|
888
|
+
const found = valueAt(input, this.conversationFrom);
|
|
889
|
+
if (typeof found === "string" && found) {
|
|
890
|
+
return found;
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
const record = asRecord(input);
|
|
894
|
+
if (typeof record.conversationId === "string" && record.conversationId) {
|
|
895
|
+
return record.conversationId;
|
|
896
|
+
}
|
|
897
|
+
if (typeof input === "string" && input) {
|
|
898
|
+
return input;
|
|
899
|
+
}
|
|
900
|
+
return "";
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
/** Reports a failure and produces nothing, so the pipeline stops here. */
|
|
904
|
+
private fail(notify: Notify, error: string): null {
|
|
905
|
+
this.lastError = error;
|
|
906
|
+
this.host?.log("error", "service.failed", { message: error });
|
|
907
|
+
notify({ error });
|
|
908
|
+
return null;
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
function reason(err: unknown): string {
|
|
913
|
+
return err instanceof Error ? err.message : String(err);
|
|
914
|
+
}
|