Tags: streamloop/streamkit
Tags
ci: isolate Docker image builders (streamer45#683) Signed-off-by: streamkit-devin <[email protected]> Co-authored-by: streamkit-devin <[email protected]>
fix(plugins): servo host logging, bounded reply waits, first-frame in… …strumentation (streamer45#668) * fix(plugins): route servo shared-thread logs to host, bound reply waits, add first-frame instrumentation Signed-off-by: streamkit-devin <[email protected]> * fix(plugins): reset first-render stopwatch on viewport resize and gate first-frame log on page_painted Signed-off-by: streamkit-devin <[email protected]> * fix(plugins): gate frame readback on page_painted so deferred navigations emit transparent frames Signed-off-by: streamkit-devin <[email protected]> * chore(marketplace): regenerate official plugins metadata for servo 0.2.4 Signed-off-by: streamkit-devin <[email protected]> --------- Signed-off-by: streamkit-devin <[email protected]> Co-authored-by: streamkit-devin <[email protected]>
fix(plugins): hold servo frame emission until initial page load (stre… …amer45#658) * fix(plugins): hold servo frame emission until initial page load Captures previously started encoding immediately while the page was still loading, producing several seconds of white/blank lead-in frames (duration depending on site load time). The servo thread now reports load+paint state with each frame and the node holds the first emitted frame until the page is ready, capped by load_timeout_secs (previously a documented no-op). Signed-off-by: streamkit-devin <[email protected]> * chore(plugins): bump servo plugin to 0.2.2 Signed-off-by: streamkit-devin <[email protected]> * fix(plugins): re-arm paint gate on deferred auth navigation Also report poisoned instances as loaded so the first-frame gate does not wait out the full load timeout on a cached fallback, and document the load_timeout_secs / native_call_timeout_secs interaction. Signed-off-by: streamkit-devin <[email protected]> --------- Signed-off-by: streamkit-devin <[email protected]> Co-authored-by: streamkit-devin <[email protected]>
feat(examples): add web-capture gateway (streamer45#631) * feat(examples): add web-capture gateway (clip/cast.streamkit.dev) A thin Go gateway, mirroring examples/speech-gateway, that renders any web page to video via the Servo plugin: clip.* returns a finite MP4 (oneshot), cast.* serves a live WebM stream (dynamic session). The target URL is the verbatim path suffix, with an optional comma-separated options segment (dur=, vp=) and strong 720p30 defaults. Worth a reviewer's attention: - cast owns session lifetime (the engine does not auto-stop a pipeline when its MSE viewers disconnect): one shared session per URL+viewport, viewer refcount, idle + max-lifetime reaper, and graceful-shutdown teardown. The MSE proxy closes the upstream body on client disconnect so a read parked between frames can't keep the viewer count pinned. - SSRF guard (public/URL-only): blocks loopback/private/link-local/CGNAT/ cloud-metadata targets. - Encoders are swappable profiles, software by default (runs GPU-free), hardware opt-in via --clip-encoder/--cast-encoder. - Prometheus metrics incl. active sessions/viewers and reap counters. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Signed-off-by: streamer45 <[email protected]> * fix(web-capture): harden SSRF, session lifetime, and clip concurrency Addresses code-review findings on the gateway: - SSRF guard: also block 0.0.0.0/8, 198.18.0.0/15, and the IPv4 embedded in NAT64 (64:ff9b::/96) and 6to4 (2002::/16) IPv6 addresses. - cast sessions: create on a background context so a creator disconnecting mid-create no longer fails the other deduped viewers (and the create error can't masquerade as a viewer "client gave up"); cap viewers up front with a clean 503 instead of failing downstream at max_clients; drop the reconcile step that could abandon a live session on a transient list miss. - clip: acquireClip now observes client disconnect (no unbounded queueing on a full slot channel), and clip length rounds to whole frames instead of truncating sub-second durations. - proxyMSE reuses a single retry timer; collapse the two near-identical HTTP clients into one; document the upstream-status guard (it prevents a WriteHeader panic). Fix README idle-TTL default (30, not 45). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Signed-off-by: streamer45 <[email protected]> * refactor(web-capture): embed pipeline YAML from template files Move the clip/cast pipeline skeletons out of Go string constants into cmd/gateway/pipelines/*.yml.tmpl, embedded via go:embed — they now read as standalone (commented) YAML that can be diffed and reviewed on their own. Rendering is unchanged: the same placeholder substitution (URL still strconv.Quote'd) and per-profile encoder-block injection. The small per-profile encoder snippets stay in encoders.go since they're tied to the profile metadata. Also tighten the cast tests to assert on the `frame_count:` param rather than the bare word, so a comment mentioning it can't trip them. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Signed-off-by: streamer45 <[email protected]> * fix(web-capture): close shutdown/create race, proxy + log correctness Second review pass on the gateway: - shutdownAll no longer leaks a session whose creation is in flight at SIGTERM: it marks the manager closed and waits each session's ready channel so it has an id to DELETE; acquire rejects new requests once closed. Previously an id-less in-flight session was skipped and re-registered after the maps were cleared, so its backend pipeline was never torn down. - streamCopy only logs a copy error when the client is still connected — a normal cast disconnect (ctx canceled / closed body) is no longer logged as an error, which otherwise floods the log on a busy instance. - copyHeaders strips the full hop-by-hop set (Connection, Upgrade, TE, Trailer, Keep-Alive, Proxy-*), not just Content-Length/Transfer-Encoding. - serveCast renders the cast YAML lazily, so deduped viewers of an already- running stream don't do throwaway render work. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Signed-off-by: streamer45 <[email protected]> * feat(web-capture): H.264/fMP4 cast for Safari/iOS; user-role token Leverage two just-merged backend PRs (auth streamer45#634, fMP4 MSE streamer45#637): - cast can now serve H.264 in fragmented MP4 via new h264-sw/h264-hw cast profiles — http::mse auto-detects fMP4 (streamer45#637), so a plain <video> plays the live stream in Safari/iOS. The cast muxer is chosen per profile now (WebM for VP9/AV1, fMP4 for H.264); VP9/WebM stays the default for crisp screen text on Chromium/Firefox. - Docs: the gateway no longer needs an admin token — the built-in user role now allows transport::http::mse (streamer45#634), so a user-role token covers the whole pipeline. (The demo's token minting is updated separately.) Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Signed-off-by: streamer45 <[email protected]> * refactor(web-capture): single host, output as the first path segment Serve both outputs from one host (web.streamkit.dev): the path is /{clip|cast}/[options/]{url} — output first, then optional config, then the target URL. detectMode now reads the mode from the path only (the subdomain branch is dropped), so any host works. Usage page, README, and tests updated. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Signed-off-by: streamer45 <[email protected]> * regression test * fix(servo,web-capture): stop cross-session pixel leak; land clip first fragment fast Servo's SoftwareRenderingContext shares a surfman surface pool across instances, and spin_event_loop leaves the last-painted instance's context bound — so a freshly-opened clip/cast could read another, unrelated session's pixels (a privacy leak on the public demo). - Gate surface reads on first paint: until notify_new_frame_ready has fired for the current page, emit a transparent frame instead of reading a possibly-stale surface; re-arm the gate on URL change. - Re-bind the instance's rendering context immediately before read_to_image (after spin_event_loop) so the read always captures this instance's surface, never a concurrent node's. - Add an isolated integration test that drives two concurrent solid- colour data: pages and asserts neither leaks the other's pixels. - web-capture: tune the OpenH264 GOP to ~1s (one IDR per fps frames) so the fragmented-MP4 clip flushes its first playable fragment fast. Signed-off-by: streamkit-devin <[email protected]> * fix(servo): re-arm first-paint gate on viewport resize A resize via UpdateConfig reallocates the surfman surface, which is pooled across instances and may hold another instance's pixels until the resized page repaints. Re-arm the painted gate (as on URL change) so handle_render emits transparent frames until the post-resize paint, closing the same cross-session leak on the resize path. Signed-off-by: streamkit-devin <[email protected]> * feat(examples/web-capture): serve autoplay clip player page for browser parity Browser visits to /clip/* (Accept: text/html) now get the same autoplay HTML player page already served for /cast/*, instead of the browser's bare native media viewer. The page's <video> re-requests the same URL with Accept: */* and receives the raw progressive MP4, so CLI clients (curl/ffmpeg) are unaffected. The HTML branch runs before clip-slot acquire, so a browser visit does not consume render concurrency. Generalizes castPageTemplate/writeCastPage into playerPageTemplate/ writePlayerPage(kind) shared by both handlers. Signed-off-by: streamkit-devin <[email protected]> * fix(plugins/servo): clear cached frame on URL change The URL-change branch of handle_update_config re-armed the first-paint gate but left state.last_good_frame holding the previous URL's frame. If read_to_image returned None on a tick after the new page painted, handle_render fell back to that stale frame. Null it on URL change, mirroring the viewport-resize branch. Signed-off-by: streamkit-devin <[email protected]> * fix(examples/web-capture): clamp max-concurrency to >=1, fix option example --max-concurrency 0 sized clipSem as an unbuffered channel, deadlocking every clip render; clamp to a floor of 1 via clampConcurrency. Also fix the optionsSegmentRe comment to use the real 'res=' key instead of the non-existent 'vp='. Signed-off-by: streamkit-devin <[email protected]> * feat(transport::http::mse): add fMP4 support for H.264 casting The node only detected WebM Cluster IDs to delimit the init segment and GOPs, so an H.264 cast stream (fragmented MP4: ftyp+moov init, repeating moof+mdat fragments) never flipped init_complete and zero bytes reached clients. Browsers showed nothing for the h264-sw cast encoder the demo pins. Detect the container framing from the first packet (ftyp/styp box at offset 4 => fMP4, else WebM) and drive a parallel fMP4 state machine that mirrors the WebM init+GOP replay: buffer ftyp+moov as the init segment, split off the first moof-led fragment, and replay init + latest fragment to late-joiners. The WebM path is unchanged. Adds unit tests for framing detection, MP4 box scanning (incl. 64-bit largesize), init/fragment splitting, cross-packet init buffering, and GOP buffer capping. Signed-off-by: streamkit-devin <[email protected]> * fix(web-capture): close pipe reader if clip request build fails If http.NewRequestWithContext fails in proxyOneshot, the multipart writer goroutine parks forever on the io.Pipe whose reader is never consumed. Close the reader on the error path so the worker unwinds. Addresses Devin Review. Signed-off-by: streamkit-devin <[email protected]> * chore(plugins/servo): bump version to 0.1.1 Release-ready bump covering the cross-session pixel-leak privacy fix and the first-paint transparent-frame gate. Regenerates marketplace/official-plugins.json via scripts/marketplace/generate_official_plugins.py. Signed-off-by: streamkit-devin <[email protected]> * fix(web-capture): fail fast in proxyMSE when session reaped mid-retry A post-teardown 404 from the MSE upstream is indistinguishable by status from a pre-ready 404, so the readiness loop spun the full mseReadyTimeout and returned a misleading 502 when a session was reaped (idle/max-lifetime) just as a deduped late viewer connected. Check session liveness in the loop and return 503 immediately when the session is gone. Signed-off-by: streamkit-devin <[email protected]> * fix(web-capture): block IPv4-compatible IPv6 and local-use NAT64 in SSRF guard ::a.b.c.d (IPv4-compatible) bypassed blockedV4 because To4() returns nil, and the RFC 8215 local-use NAT64 prefix (64:ff9b:1::/48) was not matched by embeddedV4, so ::169.254.169.254 and 64:ff9b:1::a9fe:a9fe reached the metadata IP. Extend embeddedV4 to extract the embedded v4 from both forms and re-screen it; add regression cases for the two bypasses. Signed-off-by: streamkit-devin <[email protected]> * perf(web-capture): defer target DNS lookup off the player-page path handleCapture ran the full validateTarget (up to a 5s DNS lookup) before the acceptsHTML short-circuit, so a browser address-bar visit paid the lookup just to render the static autoplay page. Split out parseTargetURL (scheme + literal-IP screen, no DNS) for the cheap fail-fast path and defer the hostname resolution into serveClip/serveCast after the player page is served. Also drop the dead dur<=0 re-defaulting in serveClip (opts.dur is always >0). Signed-off-by: streamkit-devin <[email protected]> * refactor(servo): dedupe transparent-frame paths and surface make_current errors Extract a transparent_frame(w,h) helper and route the unpainted, panic-fallback, and read_to_image-miss paths through it, collapsing three copies of the buffer build. Move the painted-surface read into read_painted_frame and stop discarding the make_current() Result before read_to_image — a bind failure means the frame may not be this instance's surface, so log it instead of silently risking the cross-session leak the bind is meant to prevent. Signed-off-by: streamkit-devin <[email protected]> * refactor(web-capture): dedupe gateway auth/maxViewers state and strip Connection-listed headers - Drop gateway.authToken/authReq; route auth through the single skitClient.auth. - Drop gateway.maxViewers; read from the session manager so the two can't drift. - copyHeaders now also strips headers named in the Connection header (RFC 7230 §6.1). Signed-off-by: streamkit-devin <[email protected]> * chore(deps): bump anyhow + wasmtime-wasi and ignore ttf-parser unmaintained advisory Resolves the cargo-deny advisory failures from newly published RUSTSEC notices: - anyhow 1.0.102 -> 1.0.103 (RUSTSEC-2026-0190, downcast_mut unsoundness) - wasmtime-wasi 46.0.0 -> 46.0.1 (RUSTSEC-2026-0188, WASI FilePerms bypass) - ignore RUSTSEC-2026-0192 (ttf-parser unmaintained via fontdue; no safe upgrade, tracked in streamer45#642), matching the existing unmaintained-advisory ignores. Signed-off-by: streamkit-devin <[email protected]> * fix(web-capture): reject sessions whose create finishes after shutdown A create in flight when shutdownAll ran would re-register its id in the fresh byID map and hand the caller a session that shutdownAll was about to destroy. Now acquire re-checks closed after the create resolves and returns errShuttingDown; shutdownAll still tears the pipeline down. Signed-off-by: streamkit-devin <[email protected]> * fix(web-capture): derive MSE Content-Type fallback from the cast encoder profile Store encoder content types as plain MIME strings (YAML-quoted at template render time via strconv.Quote) so proxyMSE can reuse the active cast profile's type instead of a hardcoded video/webm that would mislabel H.264/fMP4 streams if the upstream header were ever missing. Also bound the stream client's wait for upstream response headers so a hung backend can't pin a viewer request indefinitely. Signed-off-by: streamkit-devin <[email protected]> * fix(web-capture): clamp max-sessions/max-viewers and drop unused authToken field A max-sessions or max-viewers value < 1 would silently reject every cast; floor them at 1 with a log line, like max-concurrency. Signed-off-by: streamkit-devin <[email protected]> * chore(servo): drop redundant uuid dev-dependency Integration tests already see the identical uuid entry from [dependencies]. Signed-off-by: streamkit-devin <[email protected]> * chore(deps): ignore quick-xml RUSTSEC-2026-0194/0195 advisories via opendal No upgrade path exists: opendal 0.57 (latest) pins quick-xml 0.39, while the fixes land in quick-xml >= 0.41. Exposure is limited to XML responses from the operator-configured S3 endpoint. Removal tracked in streamer45#647. Signed-off-by: streamkit-devin <[email protected]> * chore(deps): update crossbeam-epoch to 0.9.20 for RUSTSEC-2026-0204 Signed-off-by: streamkit-devin <[email protected]> * clarify --------- Signed-off-by: streamer45 <[email protected]> Signed-off-by: streamkit-devin <[email protected]> Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]> Co-authored-by: streamkit-devin <[email protected]>
feat(plugins): bump servo to 0.4.0 and add auth config for private pa… …ges (streamer45#630) * feat(plugins): bump servo to 0.3.0 and add auth config for private pages Upgrade the native Servo web-renderer plugin from servo 0.1 to 0.3.0 and wire a new optional `auth` config through ServoConfig to WebView creation. - Custom request headers / bearer token attached to the initial navigation via the 0.3.0 UrlRequest/load_request API. - HTTP Basic/Digest answered non-interactively via the WebViewDelegate request_authentication hook. - Custom User-Agent applied to the process-global servo Preferences (affects all servo nodes). Auth is init-time only (not hot-swapped on UpdateConfig). Credentials are never logged and userinfo is stripped from logged URLs. Signed-off-by: streamkit-devin <[email protected]> * chore(plugins): regenerate official-plugins.json for servo 0.2.0 Signed-off-by: streamkit-devin <[email protected]> * fix(plugins): re-apply servo auth headers on runtime URL changes handle_update_config navigated via webview.load(), which dropped the configured custom/bearer request headers when the tunable url parameter changed at runtime, silently breaking header auth on subsequent private pages (basic auth, bound to the delegate, kept working). Route both initial and runtime navigations through a shared navigate_with_auth helper so header/bearer auth stays consistent with basic auth. Signed-off-by: streamkit-devin <[email protected]> * fix(plugins): redact URL credentials in servo validation/creation errors Signed-off-by: streamkit-devin <[email protected]> * ci: retrigger servo lint after runner out-of-disk failure Signed-off-by: streamkit-devin <[email protected]> * feat(plugins): bump servo to 0.4.0 servo-script 0.4.0 pins p256/p384/p521 =0.14.0-rc.14, which do not build against the latest primeorder 0.14.0; the lockfile pins primeorder 0.14.0-rc.14 to keep the graph consistent. Signed-off-by: streamkit-devin <[email protected]> * fix(plugins): load initial servo URL via WebViewBuilder to avoid dropped navigation Signed-off-by: streamkit-devin <[email protected]> * fix(plugins): defer header-carrying navigation to the render loop Signed-off-by: streamkit-devin <[email protected]> --------- Signed-off-by: streamkit-devin <[email protected]> Co-authored-by: streamkit-devin <[email protected]>
feat(marketplace): GPU (CUDA) plugin bundle variants + bundle-based d… …emo image (streamer45#638) * feat(marketplace): GPU (CUDA) plugin bundle variants + bundle-based demo image Add optional per-plugin CUDA bundle variants alongside the canonical CPU bundle, end-to-end across schema, install client, release pipeline, and the CPU demo image. - marketplace.rs: add backward-compatible variants[] to manifests (empty => byte-identical to pre-variant CPU manifests). - marketplace_installer.rs: resolve bundle by explicit accelerator or CUDA auto-detect (libcuda probe), falling back to the CPU bundle. - build_registry.py / verify_bundles.py: --accelerator passes layer a cuda variant onto the published CPU manifest (append-only, immutable); CUDA NEEDED/RUNPATH deps allowlisted for cuda bundles only. - CI: build-marketplace-cuda job on the self-hosted Ada GPU runner builds + publishes <id>-<ver>-cuda-bundle.tar.zst to the per-plugin release. - Dockerfile.demo: fetch sha256-pinned prebuilt CPU marketplace bundles instead of rebuilding 11 plugins from source; drop the from-source sherpa-onnx copy (bundles vendor their own libs) and the torch-based Helsinki conversion (pull pre-converted model tarballs). Signed-off-by: streamkit-devin <[email protected]> * ci(marketplace): don't block registry publish on skipped CUDA build publish-registry depends on build-marketplace-cuda, which is skipped when build_cuda=false. A skipped dependency fails the implicit success() gate, so CPU registry publishing was silently skipped too. Gate publish-registry on the CPU build succeeding and the CUDA build not failing. Signed-off-by: streamkit-devin <[email protected]> * ci(marketplace): pass signing key to CUDA registry build build_registry.py requires --signing-key, but the CUDA registry variant step omitted it, so argparse would abort the GPU registry build (and, via the publish-registry guard, block CPU publishing too). Signed-off-by: streamkit-devin <[email protected]> * fix(marketplace): harden GPU variant pipeline and detect accelerator switch Addresses review findings on the marketplace GPU bundle work: - Make build-marketplace-cuda best-effort (continue-on-error) so a failed GPU job no longer blocks CPU per-plugin releases or registry publishing; simplify the publish-registry guard to gate solely on the CPU build. - Guard the empty Bash array expansion in build_official_plugins_cuda.sh so feature-less plugins build under set -u on Bash < 4.4. - Embed manifest.json in CUDA variant bundles for parity with CPU bundles. - Record the activated accelerator in an installed bundle and return a clear 'already installed as <variant>' error instead of a silent no-op when a request would switch the variant for an already-installed version. Signed-off-by: streamkit-devin <[email protected]> * fix(marketplace): carry plugin asset types into bundles + harden CUDA variant build Asset types (e.g. Slint's) declared in plugin.yml were dropped by build_manifest, so manifest.json never carried them. The server only learns asset types from a plugin.yml beside the .so, so both raw-extraction consumers (the demo image) and real marketplace installs lost the type. - build_manifest now includes a non-empty assets block (omitted when empty, keeping asset-less manifests byte-identical for the append-only check). - build_bundle embeds a plugin.yml beside the entrypoint so bundles are self-describing, matching what the installer writes post-download. - Bump slint 0.4.0 -> 0.5.0 so the corrected, assets-bearing manifest can be published under the append-only registry; inject the source plugin.yml into the demo's pinned (pre-self-describing) slint bundle in the interim. - build_official_plugins_cuda.sh fails loudly when a cuda-declared plugin has neither a cuda Cargo feature nor sherpa linkage (was silently packaging a CPU .so as the cuda variant). - CUDA variant pass verifies the existing manifest signature before re-signing, matching the CPU reuse path. - Mirror the assets field in check_registry_versions.py. Signed-off-by: streamkit-devin <[email protected]> * fix(marketplace): re-key bundle installs by accelerator and address review findings Key install dirs by id+version+accelerator so CPU and CUDA variants of the same version coexist and switching never silently no-ops; removes the .accelerator marker and its fail-open path. resolve_accelerator is probed once and threaded through download_bundle. ActivePluginRecord now tracks the activated accelerator. Build/registry: extract shared manifest_builder (build_manifest + SHERPA libs) so the builder and append-only guard cannot drift; normalize accelerator case in reuse/dedup; drop the dead variants fallback. Whisper bundle build pins SOURCE_DATE_EPOCH + -march=x86-64 for portable ggml. Demo pins ggml-tiny-q5_1.bin sha256 and injects a version-matched slint plugin.yml. Signed-off-by: streamkit-devin <[email protected]> * chore(ci): retrigger checks after apt mirror flake in servo lint Signed-off-by: streamkit-devin <[email protected]> * fix(marketplace): apply whisper portability flags to CUDA build and guard variant manifests Signed-off-by: streamkit-devin <[email protected]> * test(server): exercise accelerator-keyed bundle removal on uninstall Signed-off-by: streamkit-devin <[email protected]> * feat(ui): surface accelerator variants in marketplace and installed plugins Expose the active accelerator on PluginSummary (from active records on restart and from the installer on fresh installs), add an accelerator selector (auto-detect/cpu/cuda) to the marketplace details pane wired to InstallPluginRequest.accelerator, and show the active variant in the installed plugins list. Signed-off-by: streamkit-devin <[email protected]> --------- Signed-off-by: streamkit-devin <[email protected]> Co-authored-by: streamkit-devin <[email protected]>
feat(marketplace): GPU (CUDA) plugin bundle variants + bundle-based d… …emo image (streamer45#638) * feat(marketplace): GPU (CUDA) plugin bundle variants + bundle-based demo image Add optional per-plugin CUDA bundle variants alongside the canonical CPU bundle, end-to-end across schema, install client, release pipeline, and the CPU demo image. - marketplace.rs: add backward-compatible variants[] to manifests (empty => byte-identical to pre-variant CPU manifests). - marketplace_installer.rs: resolve bundle by explicit accelerator or CUDA auto-detect (libcuda probe), falling back to the CPU bundle. - build_registry.py / verify_bundles.py: --accelerator passes layer a cuda variant onto the published CPU manifest (append-only, immutable); CUDA NEEDED/RUNPATH deps allowlisted for cuda bundles only. - CI: build-marketplace-cuda job on the self-hosted Ada GPU runner builds + publishes <id>-<ver>-cuda-bundle.tar.zst to the per-plugin release. - Dockerfile.demo: fetch sha256-pinned prebuilt CPU marketplace bundles instead of rebuilding 11 plugins from source; drop the from-source sherpa-onnx copy (bundles vendor their own libs) and the torch-based Helsinki conversion (pull pre-converted model tarballs). Signed-off-by: streamkit-devin <[email protected]> * ci(marketplace): don't block registry publish on skipped CUDA build publish-registry depends on build-marketplace-cuda, which is skipped when build_cuda=false. A skipped dependency fails the implicit success() gate, so CPU registry publishing was silently skipped too. Gate publish-registry on the CPU build succeeding and the CUDA build not failing. Signed-off-by: streamkit-devin <[email protected]> * ci(marketplace): pass signing key to CUDA registry build build_registry.py requires --signing-key, but the CUDA registry variant step omitted it, so argparse would abort the GPU registry build (and, via the publish-registry guard, block CPU publishing too). Signed-off-by: streamkit-devin <[email protected]> * fix(marketplace): harden GPU variant pipeline and detect accelerator switch Addresses review findings on the marketplace GPU bundle work: - Make build-marketplace-cuda best-effort (continue-on-error) so a failed GPU job no longer blocks CPU per-plugin releases or registry publishing; simplify the publish-registry guard to gate solely on the CPU build. - Guard the empty Bash array expansion in build_official_plugins_cuda.sh so feature-less plugins build under set -u on Bash < 4.4. - Embed manifest.json in CUDA variant bundles for parity with CPU bundles. - Record the activated accelerator in an installed bundle and return a clear 'already installed as <variant>' error instead of a silent no-op when a request would switch the variant for an already-installed version. Signed-off-by: streamkit-devin <[email protected]> * fix(marketplace): carry plugin asset types into bundles + harden CUDA variant build Asset types (e.g. Slint's) declared in plugin.yml were dropped by build_manifest, so manifest.json never carried them. The server only learns asset types from a plugin.yml beside the .so, so both raw-extraction consumers (the demo image) and real marketplace installs lost the type. - build_manifest now includes a non-empty assets block (omitted when empty, keeping asset-less manifests byte-identical for the append-only check). - build_bundle embeds a plugin.yml beside the entrypoint so bundles are self-describing, matching what the installer writes post-download. - Bump slint 0.4.0 -> 0.5.0 so the corrected, assets-bearing manifest can be published under the append-only registry; inject the source plugin.yml into the demo's pinned (pre-self-describing) slint bundle in the interim. - build_official_plugins_cuda.sh fails loudly when a cuda-declared plugin has neither a cuda Cargo feature nor sherpa linkage (was silently packaging a CPU .so as the cuda variant). - CUDA variant pass verifies the existing manifest signature before re-signing, matching the CPU reuse path. - Mirror the assets field in check_registry_versions.py. Signed-off-by: streamkit-devin <[email protected]> * fix(marketplace): re-key bundle installs by accelerator and address review findings Key install dirs by id+version+accelerator so CPU and CUDA variants of the same version coexist and switching never silently no-ops; removes the .accelerator marker and its fail-open path. resolve_accelerator is probed once and threaded through download_bundle. ActivePluginRecord now tracks the activated accelerator. Build/registry: extract shared manifest_builder (build_manifest + SHERPA libs) so the builder and append-only guard cannot drift; normalize accelerator case in reuse/dedup; drop the dead variants fallback. Whisper bundle build pins SOURCE_DATE_EPOCH + -march=x86-64 for portable ggml. Demo pins ggml-tiny-q5_1.bin sha256 and injects a version-matched slint plugin.yml. Signed-off-by: streamkit-devin <[email protected]> * chore(ci): retrigger checks after apt mirror flake in servo lint Signed-off-by: streamkit-devin <[email protected]> * fix(marketplace): apply whisper portability flags to CUDA build and guard variant manifests Signed-off-by: streamkit-devin <[email protected]> * test(server): exercise accelerator-keyed bundle removal on uninstall Signed-off-by: streamkit-devin <[email protected]> * feat(ui): surface accelerator variants in marketplace and installed plugins Expose the active accelerator on PluginSummary (from active records on restart and from the installer on fresh installs), add an accelerator selector (auto-detect/cpu/cuda) to the marketplace details pane wired to InstallPluginRequest.accelerator, and show the active variant in the installed plugins list. Signed-off-by: streamkit-devin <[email protected]> --------- Signed-off-by: streamkit-devin <[email protected]> Co-authored-by: streamkit-devin <[email protected]>
feat(marketplace): GPU (CUDA) plugin bundle variants + bundle-based d… …emo image (streamer45#638) * feat(marketplace): GPU (CUDA) plugin bundle variants + bundle-based demo image Add optional per-plugin CUDA bundle variants alongside the canonical CPU bundle, end-to-end across schema, install client, release pipeline, and the CPU demo image. - marketplace.rs: add backward-compatible variants[] to manifests (empty => byte-identical to pre-variant CPU manifests). - marketplace_installer.rs: resolve bundle by explicit accelerator or CUDA auto-detect (libcuda probe), falling back to the CPU bundle. - build_registry.py / verify_bundles.py: --accelerator passes layer a cuda variant onto the published CPU manifest (append-only, immutable); CUDA NEEDED/RUNPATH deps allowlisted for cuda bundles only. - CI: build-marketplace-cuda job on the self-hosted Ada GPU runner builds + publishes <id>-<ver>-cuda-bundle.tar.zst to the per-plugin release. - Dockerfile.demo: fetch sha256-pinned prebuilt CPU marketplace bundles instead of rebuilding 11 plugins from source; drop the from-source sherpa-onnx copy (bundles vendor their own libs) and the torch-based Helsinki conversion (pull pre-converted model tarballs). Signed-off-by: streamkit-devin <[email protected]> * ci(marketplace): don't block registry publish on skipped CUDA build publish-registry depends on build-marketplace-cuda, which is skipped when build_cuda=false. A skipped dependency fails the implicit success() gate, so CPU registry publishing was silently skipped too. Gate publish-registry on the CPU build succeeding and the CUDA build not failing. Signed-off-by: streamkit-devin <[email protected]> * ci(marketplace): pass signing key to CUDA registry build build_registry.py requires --signing-key, but the CUDA registry variant step omitted it, so argparse would abort the GPU registry build (and, via the publish-registry guard, block CPU publishing too). Signed-off-by: streamkit-devin <[email protected]> * fix(marketplace): harden GPU variant pipeline and detect accelerator switch Addresses review findings on the marketplace GPU bundle work: - Make build-marketplace-cuda best-effort (continue-on-error) so a failed GPU job no longer blocks CPU per-plugin releases or registry publishing; simplify the publish-registry guard to gate solely on the CPU build. - Guard the empty Bash array expansion in build_official_plugins_cuda.sh so feature-less plugins build under set -u on Bash < 4.4. - Embed manifest.json in CUDA variant bundles for parity with CPU bundles. - Record the activated accelerator in an installed bundle and return a clear 'already installed as <variant>' error instead of a silent no-op when a request would switch the variant for an already-installed version. Signed-off-by: streamkit-devin <[email protected]> * fix(marketplace): carry plugin asset types into bundles + harden CUDA variant build Asset types (e.g. Slint's) declared in plugin.yml were dropped by build_manifest, so manifest.json never carried them. The server only learns asset types from a plugin.yml beside the .so, so both raw-extraction consumers (the demo image) and real marketplace installs lost the type. - build_manifest now includes a non-empty assets block (omitted when empty, keeping asset-less manifests byte-identical for the append-only check). - build_bundle embeds a plugin.yml beside the entrypoint so bundles are self-describing, matching what the installer writes post-download. - Bump slint 0.4.0 -> 0.5.0 so the corrected, assets-bearing manifest can be published under the append-only registry; inject the source plugin.yml into the demo's pinned (pre-self-describing) slint bundle in the interim. - build_official_plugins_cuda.sh fails loudly when a cuda-declared plugin has neither a cuda Cargo feature nor sherpa linkage (was silently packaging a CPU .so as the cuda variant). - CUDA variant pass verifies the existing manifest signature before re-signing, matching the CPU reuse path. - Mirror the assets field in check_registry_versions.py. Signed-off-by: streamkit-devin <[email protected]> * fix(marketplace): re-key bundle installs by accelerator and address review findings Key install dirs by id+version+accelerator so CPU and CUDA variants of the same version coexist and switching never silently no-ops; removes the .accelerator marker and its fail-open path. resolve_accelerator is probed once and threaded through download_bundle. ActivePluginRecord now tracks the activated accelerator. Build/registry: extract shared manifest_builder (build_manifest + SHERPA libs) so the builder and append-only guard cannot drift; normalize accelerator case in reuse/dedup; drop the dead variants fallback. Whisper bundle build pins SOURCE_DATE_EPOCH + -march=x86-64 for portable ggml. Demo pins ggml-tiny-q5_1.bin sha256 and injects a version-matched slint plugin.yml. Signed-off-by: streamkit-devin <[email protected]> * chore(ci): retrigger checks after apt mirror flake in servo lint Signed-off-by: streamkit-devin <[email protected]> * fix(marketplace): apply whisper portability flags to CUDA build and guard variant manifests Signed-off-by: streamkit-devin <[email protected]> * test(server): exercise accelerator-keyed bundle removal on uninstall Signed-off-by: streamkit-devin <[email protected]> * feat(ui): surface accelerator variants in marketplace and installed plugins Expose the active accelerator on PluginSummary (from active records on restart and from the installer on fresh installs), add an accelerator selector (auto-detect/cpu/cuda) to the marketplace details pane wired to InstallPluginRequest.accelerator, and show the active variant in the installed plugins list. Signed-off-by: streamkit-devin <[email protected]> --------- Signed-off-by: streamkit-devin <[email protected]> Co-authored-by: streamkit-devin <[email protected]>
fix: revert Parakeet 0.3.0 manifest to match signed content (streamer… …45#422) PR streamer45#378 manually added a `repository` field to the Parakeet 0.3.0 manifest in docs/public/registry without re-signing it. The minisig was generated for the original manifest (without `repository`), so signature verification fails with 'Minisign signature verification failed'. - Revert docs/public manifest to match dist/registry (signed) content - Add verify_existing_signature() guard in build_registry.py to catch manifest-signature mismatches when reusing existing registry entries - Add unit tests for the new guard function Signed-off-by: StreamKit Devin <[email protected]> Co-authored-by: StreamKit Devin <[email protected]> Co-authored-by: Claudio Costa <[email protected]>
fix(engine): replace fixed sleeps with polling in backpressure tests (s… …treamer45#370) The test_dynamic_connection_under_backpressure test used a fixed 100ms sleep before asserting the pacer node reached Running state. On slow CI runners the node lifecycle (Creating → Initializing → Running) takes longer than 100ms, causing sporadic assertion failures. Replace all fixed sleep + assert patterns across the three backpressure tests with a wait_for_states polling helper (same pattern used in the async_node_creation test suite). Each call polls get_node_states() every 20ms with a 5-second timeout, eliminating timing-dependent flakiness. Signed-off-by: StreamKit Devin <[email protected]> Co-authored-by: StreamKit Devin <[email protected]> Co-authored-by: Claudio Costa <[email protected]>
PreviousNext