feat(packaging): nix web console + plugin/script runner everywhere; fix tray zbus/tokio crash
ci / web (push) Successful in 58s
apple / swift (push) Successful in 1m24s
ci / docs-site (push) Successful in 59s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 17s
decky / build-publish (push) Successful in 25s
windows-host / package (push) Successful in 19m46s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 17s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 17s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 18s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 17s
android / android (push) Successful in 16m12s
arch / build-publish (push) Successful in 12m14s
ci / bench (push) Successful in 5m27s
apple / screenshots (push) Successful in 6m32s
ci / rust (push) Successful in 19m26s
deb / build-publish (push) Successful in 12m58s
docker / deploy-docs (push) Successful in 24s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 24m51s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 24m51s
ci / web (push) Successful in 58s
apple / swift (push) Successful in 1m24s
ci / docs-site (push) Successful in 59s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 17s
decky / build-publish (push) Successful in 25s
windows-host / package (push) Successful in 19m46s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 17s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 17s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 18s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 17s
android / android (push) Successful in 16m12s
arch / build-publish (push) Successful in 12m14s
ci / bench (push) Successful in 5m27s
apple / screenshots (push) Successful in 6m32s
ci / rust (push) Successful in 19m26s
deb / build-publish (push) Successful in 12m58s
docker / deploy-docs (push) Successful in 24s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 24m51s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 24m51s
Ship the management web console and the plugin/script runner across the packaging
matrix, and fix a Linux tray startup crash.
- nix: add punktfunk-web (Nitro SSR on bun) and punktfunk-scripting (Effect runner),
with services.punktfunk.{web,scripting} modules. Both bun node_modules come from
fixed-output derivations pinned to real deps hashes; patchShebangs the web
node_modules so orval/paraglide/vite run in the sandbox. Validated on x86_64-linux
(nix build .#punktfunk-{web,scripting,tray,host} all green).
- punktfunk-scripting now ships for deb/rpm/arch/nix/windows, mirroring the web-console
mechanics (vendored bun on deb/rpm/arch, pkgs.bun on nix; one self-contained
`bun build --target=bun` bundle). Opt-in: host Recommends it, but the systemd --user
unit ships un-started (Windows registers its scheduled task disabled).
- fix(tray): build punktfunk-tray in its OWN cargo invocation on nix/rpm/arch. Cargo
unifies features across one `cargo build`, so co-building the tray with the host pulls
the host's ashpd->zbus/tokio onto the tray's shared zbus and it panics at startup
("there is no reactor running, must be called from the context of a Tokio 1.x
runtime"). Isolated, the tray's zbus stays on async-io. The .deb already did this;
runtime-verified on nix.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
# Build the punktfunk-host / punktfunk-client / punktfunk-web pacman packages from
|
# Build the punktfunk-host / punktfunk-client / punktfunk-web / punktfunk-scripting pacman packages from
|
||||||
# packaging/arch/PKGBUILD and publish them to Gitea's Arch package registry, so Arch boxes
|
# packaging/arch/PKGBUILD and publish them to Gitea's Arch package registry, so Arch boxes
|
||||||
# get new builds via `pacman -Syu`. Counterpart to deb.yml (apt) and rpm.yml (dnf/rpm-ostree).
|
# get new builds via `pacman -Syu`. Counterpart to deb.yml (apt) and rpm.yml (dnf/rpm-ostree).
|
||||||
# Arch is rolling, so the packages build against whatever the archlinux:base-devel container
|
# Arch is rolling, so the packages build against whatever the archlinux:base-devel container
|
||||||
@@ -45,8 +45,9 @@ jobs:
|
|||||||
git nodejs rust clang cmake nasm pkgconf python vulkan-headers \
|
git nodejs rust clang cmake nasm pkgconf python vulkan-headers \
|
||||||
gtk4 libadwaita sdl3 ffmpeg pipewire wayland libxkbcommon opus libei \
|
gtk4 libadwaita sdl3 ffmpeg pipewire wayland libxkbcommon opus libei \
|
||||||
mesa libglvnd unzip libarchive
|
mesa libglvnd unzip libarchive
|
||||||
# bun builds the punktfunk-web console AND is vendored as its runtime (PF_WITH_WEB=1);
|
# bun builds the punktfunk-web console + the punktfunk-scripting runner AND is vendored as
|
||||||
# it's AUR-only on Arch, so bootstrap the official binary.
|
# their runtime (PF_WITH_WEB=1 / PF_WITH_SCRIPTING=1); it's AUR-only on Arch, so bootstrap
|
||||||
|
# the official binary.
|
||||||
command -v bun >/dev/null || {
|
command -v bun >/dev/null || {
|
||||||
curl -fsSL https://bun.sh/install | bash
|
curl -fsSL https://bun.sh/install | bash
|
||||||
install -m0755 "$HOME/.bun/bin/bun" /usr/local/bin/bun
|
install -m0755 "$HOME/.bun/bin/bun" /usr/local/bin/bun
|
||||||
@@ -105,7 +106,7 @@ jobs:
|
|||||||
sudo -u builder git config --global --add safe.directory "$PWD"
|
sudo -u builder git config --global --add safe.directory "$PWD"
|
||||||
mkdir -p dist && chown builder: dist
|
mkdir -p dist && chown builder: dist
|
||||||
cd packaging/arch
|
cd packaging/arch
|
||||||
sudo -u builder env PF_SRCDIR="$GITHUB_WORKSPACE" PF_WITH_WEB=1 \
|
sudo -u builder env PF_SRCDIR="$GITHUB_WORKSPACE" PF_WITH_WEB=1 PF_WITH_SCRIPTING=1 \
|
||||||
PF_PKGVER="$PF_PKGVER" PF_PKGREL="$PF_PKGREL" \
|
PF_PKGVER="$PF_PKGVER" PF_PKGREL="$PF_PKGREL" \
|
||||||
CARGO_HOME="$CARGO_HOME" PKGDEST="$GITHUB_WORKSPACE/dist" \
|
CARGO_HOME="$CARGO_HOME" PKGDEST="$GITHUB_WORKSPACE/dist" \
|
||||||
makepkg -f -d --holdver
|
makepkg -f -d --holdver
|
||||||
|
|||||||
@@ -132,6 +132,8 @@ jobs:
|
|||||||
VERSION="$VERSION" bash packaging/debian/build-client-deb.sh
|
VERSION="$VERSION" bash packaging/debian/build-client-deb.sh
|
||||||
# Reuse CI's bun for the vendored runtime (matches the amd64 runner) instead of downloading.
|
# Reuse CI's bun for the vendored runtime (matches the amd64 runner) instead of downloading.
|
||||||
VERSION="$VERSION" BUN_BIN="$(command -v bun || true)" bash packaging/debian/build-web-deb.sh
|
VERSION="$VERSION" BUN_BIN="$(command -v bun || true)" bash packaging/debian/build-web-deb.sh
|
||||||
|
# The plugin/script runner (bun-bundled Effect SDK) — same vendored-bun mechanics.
|
||||||
|
VERSION="$VERSION" BUN_BIN="$(command -v bun || true)" bash packaging/debian/build-scripting-deb.sh
|
||||||
|
|
||||||
- name: Publish to the Gitea apt registry
|
- name: Publish to the Gitea apt registry
|
||||||
env:
|
env:
|
||||||
|
|||||||
@@ -92,9 +92,10 @@ jobs:
|
|||||||
echo "rpm $V-$R -> group '$GROUP'"
|
echo "rpm $V-$R -> group '$GROUP'"
|
||||||
|
|
||||||
- name: Build RPM
|
- name: Build RPM
|
||||||
# PF_WITH_WEB=1 → also build the noarch punktfunk-web subpackage (the publish loop below
|
# PF_WITH_WEB=1 / PF_WITH_SCRIPTING=1 → also build the punktfunk-web console + the
|
||||||
# globs it in; the host RPM Recommends it). Needs bun (ensured in Prep).
|
# punktfunk-scripting runner subpackages (the publish loop globs them in; the host RPM
|
||||||
run: PF_VERSION="$PF_VERSION" PF_RELEASE="$PF_RELEASE" PF_WITH_WEB=1 bash packaging/rpm/build-rpm.sh
|
# Recommends both). Both need bun (ensured in Prep).
|
||||||
|
run: PF_VERSION="$PF_VERSION" PF_RELEASE="$PF_RELEASE" PF_WITH_WEB=1 PF_WITH_SCRIPTING=1 bash packaging/rpm/build-rpm.sh
|
||||||
|
|
||||||
- name: Sign RPMs (dormant until RPM_GPG_PRIVATE_KEY is set — see packaging/rpm/README.md)
|
- name: Sign RPMs (dormant until RPM_GPG_PRIVATE_KEY is set — see packaging/rpm/README.md)
|
||||||
env:
|
env:
|
||||||
@@ -131,7 +132,8 @@ jobs:
|
|||||||
bash packaging/bazzite/build-sysext.sh --version-id "${{ matrix.fedver }}" \
|
bash packaging/bazzite/build-sysext.sh --version-id "${{ matrix.fedver }}" \
|
||||||
--out "dist-sysext/punktfunk-${PF_VERSION}-${PF_RELEASE}-x86-64.raw" \
|
--out "dist-sysext/punktfunk-${PF_VERSION}-${PF_RELEASE}-x86-64.raw" \
|
||||||
dist/punktfunk-"${PF_VERSION}-${PF_RELEASE}"*.rpm \
|
dist/punktfunk-"${PF_VERSION}-${PF_RELEASE}"*.rpm \
|
||||||
dist/punktfunk-web-"${PF_VERSION}-${PF_RELEASE}"*.rpm
|
dist/punktfunk-web-"${PF_VERSION}-${PF_RELEASE}"*.rpm \
|
||||||
|
dist/punktfunk-scripting-"${PF_VERSION}-${PF_RELEASE}"*.rpm
|
||||||
|
|
||||||
- name: Publish the sysext feed
|
- name: Publish the sysext feed
|
||||||
env:
|
env:
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
# Build the punktfunk Windows HOST as a signed Inno Setup installer and publish it to Gitea's generic
|
# Build the punktfunk Windows HOST as a signed Inno Setup installer and publish it to Gitea's generic
|
||||||
# package registry, so a Windows GPU box can install the streaming host (SYSTEM service + bundled
|
# package registry, so a Windows GPU box can install the streaming host (SYSTEM service + bundled
|
||||||
# pf-vdisplay virtual-display driver + the web management console, run by a scheduled task on a bundled
|
# pf-vdisplay virtual-display driver + the web management console + the opt-in plugin/script runner,
|
||||||
# bun) from one signed setup.exe. Runs on a self-hosted windows-amd64 runner
|
# run by scheduled tasks on a bundled bun) from one signed setup.exe. Runs on a self-hosted
|
||||||
|
# windows-amd64 runner
|
||||||
# (host mode; same MSVC/Windows-SDK/LLVM env as windows.yml — generic from unom/infra's
|
# (host mode; same MSVC/Windows-SDK/LLVM env as windows.yml — generic from unom/infra's
|
||||||
# windows-runner/, FFmpeg/Inno Setup self-provision via the "Ensure Windows toolchain" step below).
|
# windows-runner/, FFmpeg/Inno Setup self-provision via the "Ensure Windows toolchain" step below).
|
||||||
#
|
#
|
||||||
@@ -52,6 +53,7 @@ on:
|
|||||||
- 'packaging/windows/**'
|
- 'packaging/windows/**'
|
||||||
- 'scripts/windows/**'
|
- 'scripts/windows/**'
|
||||||
- 'web/**'
|
- 'web/**'
|
||||||
|
- 'sdk/**'
|
||||||
- 'Cargo.lock'
|
- 'Cargo.lock'
|
||||||
- 'Cargo.toml'
|
- 'Cargo.toml'
|
||||||
- '.gitea/workflows/windows-host.yml'
|
- '.gitea/workflows/windows-host.yml'
|
||||||
@@ -212,6 +214,24 @@ jobs:
|
|||||||
if ($code -ne 200) { throw "web console failed to boot under bun" }
|
if ($code -ne 200) { throw "web console failed to boot under bun" }
|
||||||
"WEB_OUTPUT_DIR=$((Resolve-Path 'web\.output').Path)" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
|
"WEB_OUTPUT_DIR=$((Resolve-Path 'web\.output').Path)" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
|
||||||
|
|
||||||
|
- name: Build plugin/script runner bundle (bun)
|
||||||
|
shell: pwsh
|
||||||
|
# `bun build --target=bun` bundles the SDK's runner CLI to ONE self-contained JS (effect + the
|
||||||
|
# SDK inlined; the dynamic plugin import stays a runtime import). pack-host-installer.ps1 ships
|
||||||
|
# it (+ the shared bun) and registers its scheduled task DISABLED (opt-in). The SDK's deps are
|
||||||
|
# public npm (effect), so no @unom token is needed here.
|
||||||
|
run: |
|
||||||
|
$bun = $env:BUN_EXE
|
||||||
|
Push-Location sdk
|
||||||
|
& $bun install --frozen-lockfile --ignore-scripts; if ($LASTEXITCODE) { throw "sdk bun install failed ($LASTEXITCODE)" }
|
||||||
|
New-Item -ItemType Directory -Force -Path C:\t\scripting | Out-Null
|
||||||
|
& $bun build src/runner-cli.ts --target=bun --outfile=C:\t\scripting\runner-cli.js; if ($LASTEXITCODE) { throw "runner bundle build failed ($LASTEXITCODE)" }
|
||||||
|
Pop-Location
|
||||||
|
if (-not (Select-String -Path C:\t\scripting\runner-cli.js -Pattern 'attempt=' -Quiet)) {
|
||||||
|
throw "runner bundle missing the dynamic plugin import - wrong build"
|
||||||
|
}
|
||||||
|
"SCRIPTING_BUNDLE=C:\t\scripting\runner-cli.js" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
|
||||||
|
|
||||||
- name: Pack + sign installer
|
- name: Pack + sign installer
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
env:
|
env:
|
||||||
|
|||||||
@@ -52,7 +52,7 @@
|
|||||||
packages = forAllSystems (system:
|
packages = forAllSystems (system:
|
||||||
let pf = packagesFor system;
|
let pf = packagesFor system;
|
||||||
in {
|
in {
|
||||||
inherit (pf) punktfunk-host punktfunk-client;
|
inherit (pf) punktfunk-host punktfunk-client punktfunk-web punktfunk-scripting punktfunk-tray;
|
||||||
default = pf.punktfunk-host;
|
default = pf.punktfunk-host;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -68,14 +68,26 @@
|
|||||||
type = "app";
|
type = "app";
|
||||||
program = "${pf.punktfunk-client}/bin/punktfunk-client";
|
program = "${pf.punktfunk-client}/bin/punktfunk-client";
|
||||||
};
|
};
|
||||||
|
# `nix run .#punktfunk-web` — the console (auto-wire the mgmt token / cert via env or the
|
||||||
|
# NixOS module; see packaging/nix/README.md).
|
||||||
|
punktfunk-web = {
|
||||||
|
type = "app";
|
||||||
|
program = "${pf.punktfunk-web}/bin/punktfunk-web-server";
|
||||||
|
};
|
||||||
|
# `nix run .#punktfunk-scripting -- --list` — the plugin/script runner.
|
||||||
|
punktfunk-scripting = {
|
||||||
|
type = "app";
|
||||||
|
program = "${pf.punktfunk-scripting}/bin/punktfunk-scripting";
|
||||||
|
};
|
||||||
default = self.apps.${system}.punktfunk-host;
|
default = self.apps.${system}.punktfunk-host;
|
||||||
});
|
});
|
||||||
|
|
||||||
# `nix flake check` builds both packages.
|
# `nix flake check` builds every package (web included — needs its deps hash filled in, see
|
||||||
|
# packaging/nix/README.md).
|
||||||
checks = forAllSystems (system:
|
checks = forAllSystems (system:
|
||||||
let pf = packagesFor system;
|
let pf = packagesFor system;
|
||||||
in {
|
in {
|
||||||
inherit (pf) punktfunk-host punktfunk-client;
|
inherit (pf) punktfunk-host punktfunk-client punktfunk-web punktfunk-scripting;
|
||||||
});
|
});
|
||||||
|
|
||||||
# `nix develop` — the pinned toolchain plus every system lib the workspace links, wired so
|
# `nix develop` — the pinned toolchain plus every system lib the workspace links, wired so
|
||||||
|
|||||||
+50
-7
@@ -14,10 +14,11 @@
|
|||||||
# picks per GPU. The CLIENT decodes via VAAPI (AMD/Intel, incl. the Deck) with a software
|
# picks per GPU. The CLIENT decodes via VAAPI (AMD/Intel, incl. the Deck) with a software
|
||||||
# fallback, so it works everywhere. See README.md.
|
# fallback, so it works everywhere. See README.md.
|
||||||
pkgbase=punktfunk
|
pkgbase=punktfunk
|
||||||
# punktfunk-web (the browser console) is OPT-IN: building it needs `bun` (AUR-only as bun-bin on
|
# punktfunk-web (the browser console) and punktfunk-scripting (the plugin/script runner) are OPT-IN:
|
||||||
# stock Arch/SteamOS), so a default makepkg builds only host+client with no JS tooling — mirroring
|
# both need `bun` (AUR-only as bun-bin on stock Arch/SteamOS), so a default makepkg builds only
|
||||||
# the RPM spec's `%bcond_with web` (off by default). Set PF_WITH_WEB=1 to also build punktfunk-web
|
# host+client with no JS tooling — mirroring the RPM spec's `%bcond_with web` / `%bcond_with
|
||||||
# (appended to pkgname + bun to makedepends below).
|
# scripting` (off by default). Set PF_WITH_WEB=1 / PF_WITH_SCRIPTING=1 to also build them (each
|
||||||
|
# appends its pkgname + bun to makedepends below).
|
||||||
pkgname=('punktfunk-host' 'punktfunk-client')
|
pkgname=('punktfunk-host' 'punktfunk-client')
|
||||||
# CI (.gitea/workflows/arch.yml) drives the version: stable tags -> X.Y.Z-1, main pushes ->
|
# CI (.gitea/workflows/arch.yml) drives the version: stable tags -> X.Y.Z-1, main pushes ->
|
||||||
# X.Y.Z-0.<run#> in the separate punktfunk-canary repo (mirrors the RPM's 0.ciN release; pkgrel
|
# X.Y.Z-0.<run#> in the separate punktfunk-canary repo (mirrors the RPM's 0.ciN release; pkgrel
|
||||||
@@ -39,11 +40,16 @@ options=('!lto' '!debug')
|
|||||||
makedepends=('rust' 'cargo' 'clang' 'cmake' 'nasm' 'pkgconf' 'git' 'vulkan-headers'
|
makedepends=('rust' 'cargo' 'clang' 'cmake' 'nasm' 'pkgconf' 'git' 'vulkan-headers'
|
||||||
'gtk4' 'libadwaita' 'sdl3' 'ffmpeg' 'pipewire' 'wayland' 'libxkbcommon' 'opus' 'libei')
|
'gtk4' 'libadwaita' 'sdl3' 'ffmpeg' 'pipewire' 'wayland' 'libxkbcommon' 'opus' 'libei')
|
||||||
|
|
||||||
# Opt-in punktfunk-web: only then is bun (the build tool AND the vendored runtime) required.
|
# Opt-in punktfunk-web / punktfunk-scripting: only then is bun (the build tool AND the vendored
|
||||||
|
# runtime) required. Adding bun twice is harmless (makepkg dedups makedepends).
|
||||||
if [ "${PF_WITH_WEB:-0}" = 1 ]; then
|
if [ "${PF_WITH_WEB:-0}" = 1 ]; then
|
||||||
pkgname+=('punktfunk-web')
|
pkgname+=('punktfunk-web')
|
||||||
makedepends+=('bun') # `bun-bin` from the AUR if bun isn't in your configured repos
|
makedepends+=('bun') # `bun-bin` from the AUR if bun isn't in your configured repos
|
||||||
fi
|
fi
|
||||||
|
if [ "${PF_WITH_SCRIPTING:-0}" = 1 ]; then
|
||||||
|
pkgname+=('punktfunk-scripting')
|
||||||
|
makedepends+=('bun')
|
||||||
|
fi
|
||||||
|
|
||||||
# AUR source (a tagged release). For an in-tree CI build, set PF_SRCDIR to the repo root —
|
# AUR source (a tagged release). For an in-tree CI build, set PF_SRCDIR to the repo root —
|
||||||
# build() uses it instead AND the fetch is skipped entirely (a canary pkgver has no tag to
|
# build() uses it instead AND the fetch is skipped entirely (a canary pkgver has no tag to
|
||||||
@@ -78,12 +84,26 @@ build() {
|
|||||||
# Pure Rust `ash` (no new lib, no link-time deps); default on for HEVC (PUNKTFUNK_VULKAN_ENCODE=0
|
# Pure Rust `ash` (no new lib, no link-time deps); default on for HEVC (PUNKTFUNK_VULKAN_ENCODE=0
|
||||||
# opts back to libav VAAPI), and a failed open falls back to VAAPI so unsupported devices are safe.
|
# opts back to libav VAAPI), and a failed open falls back to VAAPI so unsupported devices are safe.
|
||||||
cargo build --release --locked --features punktfunk-host/nvenc,punktfunk-host/vulkan-encode \
|
cargo build --release --locked --features punktfunk-host/nvenc,punktfunk-host/vulkan-encode \
|
||||||
-p punktfunk-host -p punktfunk-client-linux -p punktfunk-client-session -p punktfunk-tray
|
-p punktfunk-host -p punktfunk-client-linux -p punktfunk-client-session
|
||||||
|
# The status tray in its OWN cargo invocation — load-bearing, not tidiness. Cargo unifies features
|
||||||
|
# across everything in one build, so co-building the tray with the host pulls the host's
|
||||||
|
# ashpd -> zbus/tokio onto the tray's shared zbus; the tray (ksni async-io + blocking, no tokio
|
||||||
|
# runtime by design) then panics at startup ("there is no reactor running, must be called from the
|
||||||
|
# context of a Tokio 1.x runtime"). Built alone, its zbus stays on async-io. (Same split the .deb does.)
|
||||||
|
cargo build --release --locked -p punktfunk-tray
|
||||||
# Management web console (opt-in): the Nitro `bun`-preset .output bundle (Bun.serve TLS),
|
# Management web console (opt-in): the Nitro `bun`-preset .output bundle (Bun.serve TLS),
|
||||||
# built AND run with bun.
|
# built AND run with bun.
|
||||||
if [ "${PF_WITH_WEB:-0}" = 1 ]; then
|
if [ "${PF_WITH_WEB:-0}" = 1 ]; then
|
||||||
( cd web && bun install --frozen-lockfile && bun run build )
|
( cd web && bun install --frozen-lockfile && bun run build )
|
||||||
fi
|
fi
|
||||||
|
# Plugin/script runner (opt-in): one self-contained JS via `bun build --target=bun` (effect + the
|
||||||
|
# SDK inlined; the dynamic plugin import stays a runtime import). bun is also its vendored runtime.
|
||||||
|
if [ "${PF_WITH_SCRIPTING:-0}" = 1 ]; then
|
||||||
|
( cd sdk && bun install --frozen-lockfile --ignore-scripts && \
|
||||||
|
bun build src/runner-cli.ts --target=bun --outfile="$srcdir/runner-cli.js" )
|
||||||
|
grep -q 'attempt=' "$srcdir/runner-cli.js" \
|
||||||
|
|| { echo "ERROR: runner bundle missing the dynamic plugin import — wrong build" >&2; exit 1; }
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
package_punktfunk-host() {
|
package_punktfunk-host() {
|
||||||
@@ -99,7 +119,8 @@ package_punktfunk-host() {
|
|||||||
'sway: stream a wlroots desktop (Sway VirtualDisplay backend)'
|
'sway: stream a wlroots desktop (Sway VirtualDisplay backend)'
|
||||||
'xdg-desktop-portal-kde: portal for the headless KDE session helper'
|
'xdg-desktop-portal-kde: portal for the headless KDE session helper'
|
||||||
'xdg-desktop-portal-wlr: portal for the headless Sway session helper'
|
'xdg-desktop-portal-wlr: portal for the headless Sway session helper'
|
||||||
'punktfunk-web: browser management console (device pairing + status)')
|
'punktfunk-web: browser management console (device pairing + status)'
|
||||||
|
'punktfunk-scripting: plugin/script runner for host automation (bun)')
|
||||||
install=punktfunk-host.install
|
install=punktfunk-host.install
|
||||||
# User-editable config: the headless game-mode drop-in (see below) — don't clobber local edits.
|
# User-editable config: the headless game-mode drop-in (see below) — don't clobber local edits.
|
||||||
backup=('etc/gamescope-session-plus/sessions.d/steam')
|
backup=('etc/gamescope-session-plus/sessions.d/steam')
|
||||||
@@ -227,3 +248,25 @@ package_punktfunk-web() {
|
|||||||
install -Dm0644 "$R/LICENSE-MIT" "$pkgdir/usr/share/licenses/punktfunk-web/LICENSE-MIT"
|
install -Dm0644 "$R/LICENSE-MIT" "$pkgdir/usr/share/licenses/punktfunk-web/LICENSE-MIT"
|
||||||
install -Dm0644 "$R/LICENSE-APACHE" "$pkgdir/usr/share/licenses/punktfunk-web/LICENSE-APACHE"
|
install -Dm0644 "$R/LICENSE-APACHE" "$pkgdir/usr/share/licenses/punktfunk-web/LICENSE-APACHE"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
package_punktfunk-scripting() {
|
||||||
|
pkgdesc="punktfunk plugin/script runner (Effect SDK on bun) — supervises host automation scripts + punktfunk-plugin-* packages"
|
||||||
|
# bun is the runtime (it import()s the operator's .ts plugins), a vendored native binary, so this
|
||||||
|
# package is arch-specific (not 'any'). OPT-IN: the systemd --user unit ships disabled (the runner
|
||||||
|
# is inert until you add scripts/plugins). No nodejs/bun dependency.
|
||||||
|
local R; R="$(_repo)"
|
||||||
|
|
||||||
|
# Pre-built self-contained bundle (from build()) + a PATH-stable launcher (matches the .deb/.rpm).
|
||||||
|
install -Dm0644 "$srcdir/runner-cli.js" "$pkgdir/usr/share/punktfunk-scripting/runner-cli.js"
|
||||||
|
# Vendor the build env's bun into a private dir so it never collides with a system-wide bun.
|
||||||
|
install -Dm0755 "$(command -v bun)" "$pkgdir/usr/lib/punktfunk-scripting/bun"
|
||||||
|
install -d "$pkgdir/usr/bin"
|
||||||
|
printf '%s\n' '#!/bin/sh' 'exec /usr/lib/punktfunk-scripting/bun /usr/share/punktfunk-scripting/runner-cli.js "$@"' \
|
||||||
|
> "$pkgdir/usr/bin/punktfunk-scripting"
|
||||||
|
chmod 0755 "$pkgdir/usr/bin/punktfunk-scripting"
|
||||||
|
# systemd USER unit — installed but NOT auto-enabled (opt-in). Enable once you have automation:
|
||||||
|
# systemctl --user enable --now punktfunk-scripting
|
||||||
|
install -Dm0644 "$R/scripts/punktfunk-scripting.service" "$pkgdir/usr/lib/systemd/user/punktfunk-scripting.service"
|
||||||
|
install -Dm0644 "$R/LICENSE-MIT" "$pkgdir/usr/share/licenses/punktfunk-scripting/LICENSE-MIT"
|
||||||
|
install -Dm0644 "$R/LICENSE-APACHE" "$pkgdir/usr/share/licenses/punktfunk-scripting/LICENSE-APACHE"
|
||||||
|
}
|
||||||
|
|||||||
@@ -156,7 +156,9 @@ DEPENDS="$SHDEPS, libei1, pipewire, wireplumber"
|
|||||||
# punktfunk-web = the management web console (pairing + status) every user needs — a separate
|
# punktfunk-web = the management web console (pairing + status) every user needs — a separate
|
||||||
# Architecture:all .deb; Recommends so `apt install punktfunk-host` pulls it by default, while a
|
# Architecture:all .deb; Recommends so `apt install punktfunk-host` pulls it by default, while a
|
||||||
# headless/encoding-only box can opt out with --no-install-recommends.
|
# headless/encoding-only box can opt out with --no-install-recommends.
|
||||||
RECOMMENDS="ffmpeg, gamescope, pipewire-pulse, mesa-va-drivers, intel-media-va-driver, punktfunk-web"
|
# punktfunk-scripting = the plugin/script runner (host automation on bun). Recommends so it's pulled
|
||||||
|
# by default; its systemd --user unit ships disabled (inert until you add scripts/plugins).
|
||||||
|
RECOMMENDS="ffmpeg, gamescope, pipewire-pulse, mesa-va-drivers, intel-media-va-driver, punktfunk-web, punktfunk-scripting"
|
||||||
SUGGESTS="kwin-wayland, mutter"
|
SUGGESTS="kwin-wayland, mutter"
|
||||||
|
|
||||||
INSTALLED_KB="$(du -k -s "$STAGE" | cut -f1)"
|
INSTALLED_KB="$(du -k -s "$STAGE" | cut -f1)"
|
||||||
|
|||||||
Executable
+140
@@ -0,0 +1,140 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build the punktfunk-scripting .deb — the plugin/script runner (the SDK's `punktfunk-scripting`,
|
||||||
|
# built on Effect, run on bun).
|
||||||
|
#
|
||||||
|
# Runtime is BUN: the runner `import()`s the operator's `.ts` plugin/script files directly, which
|
||||||
|
# only bun can do. Like the web console, we VENDOR a bun binary into the package (bun isn't in apt),
|
||||||
|
# which makes the package per-arch (amd64/arm64), NOT `all`. Unlike the console it is NOT a Nitro
|
||||||
|
# bundle: we `bun build` the runner CLI into ONE self-contained JS (effect + the SDK inlined; the
|
||||||
|
# dynamic plugin import stays a runtime import), so there is no node_modules to ship. The host's
|
||||||
|
# punktfunk-host .deb Recommends this so a default `apt install punktfunk-host` pulls the runner too;
|
||||||
|
# its systemd --user unit is installed but NOT auto-enabled (the runner is inert until you add
|
||||||
|
# scripts/plugins — enable it with `systemctl --user enable --now punktfunk-scripting`).
|
||||||
|
#
|
||||||
|
# Usage: VERSION=0.0.1~ci42.gdeadbee [DEB_ARCH=amd64] [BUN_BIN=/path/to/bun] bash packaging/debian/build-scripting-deb.sh
|
||||||
|
# Output: dist/punktfunk-scripting_<version>_<arch>.deb
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
VERSION="${VERSION:?set VERSION (e.g. 0.0.1 or 0.0.1~ci42.gdeadbee)}"
|
||||||
|
PKG="punktfunk-scripting"
|
||||||
|
ROOTDIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||||
|
cd "$ROOTDIR"
|
||||||
|
|
||||||
|
# Per-arch: vendor bun for the target Debian arch. Map deb arch → bun's release arch tag.
|
||||||
|
DEB_ARCH="${DEB_ARCH:-$(dpkg --print-architecture)}"
|
||||||
|
BUN_VERSION="${BUN_VERSION:-1.3.14}" # pinned bun build vendored into the package (matches build-web-deb.sh)
|
||||||
|
case "$DEB_ARCH" in
|
||||||
|
amd64) BUN_ARCH=x64 ;;
|
||||||
|
arm64) BUN_ARCH=aarch64 ;;
|
||||||
|
*) echo "ERROR: unsupported DEB_ARCH=$DEB_ARCH (want amd64 or arm64)" >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
STAGE="$(mktemp -d)"
|
||||||
|
trap 'rm -rf "$STAGE"' EXIT
|
||||||
|
SHAREDIR="$STAGE/usr/share/$PKG"
|
||||||
|
DOCDIR="$STAGE/usr/share/doc/$PKG"
|
||||||
|
LIBDIR="$STAGE/usr/lib/$PKG"
|
||||||
|
|
||||||
|
# --- build the runner bundle -------------------------------------------------
|
||||||
|
# One self-contained JS: `bun build --target=bun` inlines effect + the @punktfunk/host SDK; the
|
||||||
|
# runner's dynamic `import()` of the operator's plugin files is left as a runtime import (bun keeps
|
||||||
|
# unresolvable dynamic specifiers external). `--ignore-scripts` on install: nothing needs a
|
||||||
|
# postinstall, and we skip the `prepare` codegen (it wants ../api/openapi.json — not needed here).
|
||||||
|
mkdir -p "$SHAREDIR"
|
||||||
|
(
|
||||||
|
cd sdk
|
||||||
|
bun install --frozen-lockfile --ignore-scripts
|
||||||
|
bun build src/runner-cli.ts --target=bun --outfile="$SHAREDIR/runner-cli.js"
|
||||||
|
)
|
||||||
|
grep -q 'attempt=' "$SHAREDIR/runner-cli.js" \
|
||||||
|
|| { echo "ERROR: runner bundle missing the dynamic plugin import — wrong build" >&2; exit 1; }
|
||||||
|
|
||||||
|
# --- vendor the bun runtime --------------------------------------------------
|
||||||
|
# Honor a pre-fetched bun (CI may cache it) via BUN_BIN; else download the pinned release.
|
||||||
|
mkdir -p "$LIBDIR"
|
||||||
|
if [ -n "${BUN_BIN:-}" ]; then
|
||||||
|
echo "==> vendoring bun from BUN_BIN=$BUN_BIN"
|
||||||
|
install -m0755 "$BUN_BIN" "$LIBDIR/bun"
|
||||||
|
else
|
||||||
|
url="https://github.com/oven-sh/bun/releases/download/bun-v${BUN_VERSION}/bun-linux-${BUN_ARCH}.zip"
|
||||||
|
echo "==> downloading bun $BUN_VERSION ($BUN_ARCH) from $url"
|
||||||
|
tmp="$(mktemp -d)"
|
||||||
|
curl -fsSL "$url" -o "$tmp/bun.zip"
|
||||||
|
unzip -q "$tmp/bun.zip" -d "$tmp"
|
||||||
|
install -m0755 "$tmp/bun-linux-${BUN_ARCH}/bun" "$LIBDIR/bun"
|
||||||
|
rm -rf "$tmp"
|
||||||
|
fi
|
||||||
|
"$LIBDIR/bun" --version
|
||||||
|
|
||||||
|
# --- file layout -------------------------------------------------------------
|
||||||
|
# Stable PATH-independent launcher (the systemd unit's ExecStart) — runs the bundle on vendored bun.
|
||||||
|
install -d "$STAGE/usr/bin"
|
||||||
|
cat > "$STAGE/usr/bin/punktfunk-scripting" <<'WRAP'
|
||||||
|
#!/bin/sh
|
||||||
|
# The runner runs on the vendored bun (it import()s the operator's .ts plugins); bun lives privately
|
||||||
|
# under /usr/lib/punktfunk-scripting so it never collides with a system-wide bun on PATH.
|
||||||
|
exec /usr/lib/punktfunk-scripting/bun /usr/share/punktfunk-scripting/runner-cli.js "$@"
|
||||||
|
WRAP
|
||||||
|
chmod 0755 "$STAGE/usr/bin/punktfunk-scripting"
|
||||||
|
install -Dm0644 scripts/punktfunk-scripting.service "$STAGE/usr/lib/systemd/user/punktfunk-scripting.service"
|
||||||
|
install -Dm0644 LICENSE-MIT "$DOCDIR/LICENSE-MIT"
|
||||||
|
install -Dm0644 LICENSE-APACHE "$DOCDIR/LICENSE-APACHE"
|
||||||
|
install -Dm0644 sdk/README.md "$DOCDIR/README.md"
|
||||||
|
|
||||||
|
cat > "$DOCDIR/copyright" <<EOF
|
||||||
|
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||||
|
Upstream-Name: punktfunk
|
||||||
|
Source: https://git.unom.io/unom/punktfunk
|
||||||
|
|
||||||
|
Files: *
|
||||||
|
Copyright: punktfunk contributors
|
||||||
|
License: MIT or Apache-2.0
|
||||||
|
Dual-licensed. Full texts in /usr/share/doc/$PKG/LICENSE-MIT and
|
||||||
|
/usr/share/doc/$PKG/LICENSE-APACHE.
|
||||||
|
EOF
|
||||||
|
printf '%s (%s) stable; urgency=medium\n\n * Automated build %s.\n\n -- unom <noreply@anthropic.com> %s\n' \
|
||||||
|
"$PKG" "$VERSION" "$VERSION" "$(date -uR 2>/dev/null || echo 'Thu, 01 Jan 1970 00:00:00 +0000')" \
|
||||||
|
| gzip -9n > "$DOCDIR/changelog.Debian.gz"
|
||||||
|
|
||||||
|
INSTALLED_KB="$(du -k -s "$STAGE" | cut -f1)"
|
||||||
|
|
||||||
|
install -d "$STAGE/DEBIAN"
|
||||||
|
cat > "$STAGE/DEBIAN/control" <<EOF
|
||||||
|
Package: $PKG
|
||||||
|
Version: $VERSION
|
||||||
|
Architecture: $DEB_ARCH
|
||||||
|
Maintainer: unom <noreply@anthropic.com>
|
||||||
|
Installed-Size: $INSTALLED_KB
|
||||||
|
Section: net
|
||||||
|
Priority: optional
|
||||||
|
Homepage: https://git.unom.io/unom/punktfunk
|
||||||
|
Description: punktfunk plugin/script runner (Effect SDK on bun)
|
||||||
|
Runs a punktfunk host's automation: loose scripts in ~/.config/punktfunk/scripts and installed
|
||||||
|
punktfunk-plugin-* packages under ~/.config/punktfunk/plugins, each supervised (Effect fibers with
|
||||||
|
capped-jittered restart; SIGTERM shuts the whole tree down structurally so plugin finalizers run).
|
||||||
|
Bundles its own bun runtime (no system nodejs/bun dependency).
|
||||||
|
.
|
||||||
|
OPT-IN: the systemd --user unit is installed but not auto-enabled (the runner is inert until you add
|
||||||
|
scripts or plugins). A plugin auto-wires to the host's mgmt token + identity cert on the same box —
|
||||||
|
no env editing. Enable it with: systemctl --user enable --now punktfunk-scripting
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat > "$STAGE/DEBIAN/postinst" <<'EOF'
|
||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
if [ "$1" = "configure" ]; then
|
||||||
|
echo "punktfunk-scripting installed. It runs your automation — add scripts to"
|
||||||
|
echo " ~/.config/punktfunk/scripts/ (loose .ts/.js files)"
|
||||||
|
echo "or install plugins into ~/.config/punktfunk/plugins/ (bun add punktfunk-plugin-<name>),"
|
||||||
|
echo "then enable the runner for your user:"
|
||||||
|
echo " systemctl --user enable --now punktfunk-scripting"
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
EOF
|
||||||
|
chmod 0755 "$STAGE/DEBIAN/postinst"
|
||||||
|
|
||||||
|
mkdir -p dist
|
||||||
|
OUT="dist/${PKG}_${VERSION}_${DEB_ARCH}.deb"
|
||||||
|
dpkg-deb --root-owner-group --build "$STAGE" "$OUT" >/dev/null
|
||||||
|
echo "built $OUT"
|
||||||
|
dpkg-deb -I "$OUT" | sed -n 's/^/ /p' | grep -E 'Version|Installed-Size|Depends' || true
|
||||||
+54
-1
@@ -16,8 +16,10 @@ and the native Linux **client**, a **NixOS module** that wires up everything the
|
|||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `packages.x86_64-linux.punktfunk-host` | `punktfunk-host` + `punktfunk-tray` (built with `nvenc` + `vulkan-encode`, like CI) |
|
| `packages.x86_64-linux.punktfunk-host` | `punktfunk-host` + `punktfunk-tray` (built with `nvenc` + `vulkan-encode`, like CI) |
|
||||||
| `packages.x86_64-linux.punktfunk-client` | `punktfunk-client` (GTK4 shell) + `punktfunk-session` (Vulkan streamer, without the Skia OSD — see caveats) |
|
| `packages.x86_64-linux.punktfunk-client` | `punktfunk-client` (GTK4 shell) + `punktfunk-session` (Vulkan streamer, without the Skia OSD — see caveats) |
|
||||||
|
| `packages.x86_64-linux.punktfunk-web` | the management web console (bun-built Nitro SSR bundle; SPAKE2 pairing + host status) |
|
||||||
|
| `packages.x86_64-linux.punktfunk-scripting` | the plugin/script runner (bun-bundled Effect SDK; supervises host automation) |
|
||||||
| `packages.x86_64-linux.default` | = `punktfunk-host` |
|
| `packages.x86_64-linux.default` | = `punktfunk-host` |
|
||||||
| `nixosModules.default` | `services.punktfunk.host` / `services.punktfunk.client` |
|
| `nixosModules.default` | `services.punktfunk.host` / `.client` / `.web` / `.scripting` |
|
||||||
| `devShells.x86_64-linux.default` | pinned Rust (from `rust-toolchain.toml`) + all build deps |
|
| `devShells.x86_64-linux.default` | pinned Rust (from `rust-toolchain.toml`) + all build deps |
|
||||||
| `apps` / `checks` / `formatter` | `nix run`, `nix flake check`, `nix fmt` |
|
| `apps` / `checks` / `formatter` | `nix run`, `nix flake check`, `nix fmt` |
|
||||||
|
|
||||||
@@ -105,6 +107,39 @@ systemctl --user enable --now punktfunk-host
|
|||||||
|
|
||||||
`services.punktfunk.client`: `enable`, `openFirewall` (UDP 5353), `package`.
|
`services.punktfunk.client`: `enable`, `openFirewall` (UDP 5353), `package`.
|
||||||
|
|
||||||
|
`services.punktfunk.web` (the management console — **on by default whenever the host is enabled**,
|
||||||
|
mirroring the RPM's `Recommends: punktfunk-web`):
|
||||||
|
|
||||||
|
| Option | Default | Meaning |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `enable` | `host.enable` | Run the console as a `systemd --user` service on **TCP 47992 (HTTPS)**. Set `false` for a console-less host. |
|
||||||
|
| `openFirewall` | `host.openFirewall` | Open TCP 47992 so other devices on the LAN can reach it. |
|
||||||
|
| `autoStart` | `host.autoStart` | Add the console user service to `default.target` (appliance mode). |
|
||||||
|
| `package` | flake's | Override the package. |
|
||||||
|
|
||||||
|
The console is **auto-wired** to the host on the same box: it reads the host's per-user
|
||||||
|
`~/.config/punktfunk/{mgmt-token,cert.pem,key.pem}` (written by `serve`), serves HTTPS with the
|
||||||
|
host's own identity cert, and proxies the loopback mgmt API with the bearer token injected
|
||||||
|
server-side (never sent to the browser). A login password is generated on first start — read it
|
||||||
|
with `journalctl --user -u punktfunk-web-init` (or `~/.config/punktfunk/web-password`). Then open
|
||||||
|
`https://<host-ip>:47992` and trust the self-signed host cert once. Enable it (with the host) via
|
||||||
|
`systemctl --user enable --now punktfunk-web`.
|
||||||
|
|
||||||
|
`services.punktfunk.scripting` (the plugin/script runner — installed with the host, but **opt-in to
|
||||||
|
run**):
|
||||||
|
|
||||||
|
| Option | Default | Meaning |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `enable` | `host.enable` | Install the runner + define its `systemd --user` unit `punktfunk-scripting`. |
|
||||||
|
| `autoStart` | `false` | Add the unit to `default.target`. Off even on an auto-start host — running operator scripts/plugins is a deliberate opt-in. |
|
||||||
|
| `package` | flake's | Override the package. |
|
||||||
|
|
||||||
|
The runner discovers loose scripts under `~/.config/punktfunk/scripts` and installed
|
||||||
|
`punktfunk-plugin-*` packages under `~/.config/punktfunk/plugins`, and supervises each as an Effect
|
||||||
|
fiber (SIGTERM shuts the tree down structurally so plugin finalizers run). A plugin auto-wires to
|
||||||
|
the host's mgmt token + identity cert. It's inert until you add automation, so the unit ships
|
||||||
|
un-started; turn it on with `systemctl --user enable --now punktfunk-scripting`.
|
||||||
|
|
||||||
### What the host module configures for you
|
### What the host module configures for you
|
||||||
|
|
||||||
Everything the RPM's `%install` + `%post` do, declaratively:
|
Everything the RPM's `%install` + `%post` do, declaratively:
|
||||||
@@ -175,6 +210,24 @@ The shell exports `PF_FFVK_VULKAN_INCLUDE` (Vulkan headers for pf-ffvk bindgen)
|
|||||||
- **First build compiles from scratch** (no split dep cache — pyrowave-sys builds a CMake tree in
|
- **First build compiles from scratch** (no split dep cache — pyrowave-sys builds a CMake tree in
|
||||||
its build.rs that a crane "dummy" source would drop) and has no public binary cache, so expect a
|
its build.rs that a crane "dummy" source would drop) and has no public binary cache, so expect a
|
||||||
long initial build. `nix develop` gives incremental rebuilds.
|
long initial build. `nix develop` gives incremental rebuilds.
|
||||||
|
- **The status tray is built in its own derivation, on purpose.** `punktfunk-tray` uses `ksni`'s
|
||||||
|
`async-io` zbus executor with no tokio runtime (by design — see `crates/punktfunk-tray/Cargo.toml`).
|
||||||
|
Cargo unifies features across everything in one `cargo build`, so co-building the tray with the
|
||||||
|
host would pull the host's `ashpd → zbus/tokio` onto the tray's shared `zbus`, and the tray then
|
||||||
|
panics at startup (`there is no reactor running, must be called from the context of a Tokio 1.x
|
||||||
|
runtime`). Building it as a separate `-p punktfunk-tray` invocation keeps its `zbus` on async-io;
|
||||||
|
the host package copies the resulting binary into its `$out`. (The deb/rpm/arch builds co-build the
|
||||||
|
two in one `cargo build`, so they share this latent crash on Linux — a separate fix.)
|
||||||
|
- **The bun packages (`punktfunk-web`, `punktfunk-scripting`) — their `bun install` deps hashes.**
|
||||||
|
Both build their `node_modules` in a *fixed-output derivation* (`bun install` needs the network +
|
||||||
|
the read-public `@unom` npm registry). Each `outputHash` (in `packaging/nix/packages.nix`) is
|
||||||
|
pinned to a resolved dependency set and **must be refreshed when its lockfile changes** —
|
||||||
|
`web/bun.lock` for the console, `sdk/bun.lock` for the runner: set that `outputHash = lib.fakeHash`,
|
||||||
|
run `nix build .#punktfunk-web` (or `.#punktfunk-scripting`), and copy the `got: sha256-…` value
|
||||||
|
Nix prints back into the field. Everything downstream is offline (the console's codegen + vite
|
||||||
|
build; the runner's `bun build --target=bun` bundle), so only the deps FODs ever need network.
|
||||||
|
Both launchers exec `pkgs.bun` from the store — unlike the deb/rpm, which vendor a bun binary
|
||||||
|
because apt/dnf have none.
|
||||||
- **Commit `flake.lock`:** it pins the input revisions (nixpkgs / crane / rust-overlay). It is
|
- **Commit `flake.lock`:** it pins the input revisions (nixpkgs / crane / rust-overlay). It is
|
||||||
generated on first eval and checked in.
|
generated on first eval and checked in.
|
||||||
- **Session Skia OSD is off under Nix.** `punktfunk-session`'s default `ui` feature draws its
|
- **Session Skia OSD is off under Nix.** `punktfunk-session`'s default `ui` feature draws its
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
# NixOS integration for punktfunk — the declarative equivalent of everything the RPM/deb do in
|
# NixOS integration for punktfunk — the declarative equivalent of everything the RPM/deb do in
|
||||||
# their %install + %post (packaging/rpm/punktfunk.spec, packaging/debian/build-deb.sh):
|
# their %install + %post (packaging/rpm/punktfunk.spec, packaging/debian/build-deb.sh):
|
||||||
# the systemd *user* service, the uinput/uhid/vhci udev rules, the vhci-hcd autoload, the 32 MB
|
# the systemd *user* service, the uinput/uhid/vhci udev rules, the vhci-hcd autoload, the 32 MB
|
||||||
# UDP socket-buffer sysctls, the firewall openers, and the `input`-group membership for virtual
|
# UDP socket-buffer sysctls, the firewall openers, the `input`-group membership for virtual
|
||||||
# gamepads.
|
# gamepads, the management web console (`services.punktfunk.web`, on by default with the host — the
|
||||||
|
# RPM/deb Recommends), and the opt-in plugin/script runner (`services.punktfunk.scripting`).
|
||||||
#
|
#
|
||||||
# Usage (flake):
|
# Usage (flake):
|
||||||
# { inputs.punktfunk.url = "git+https://git.unom.io/unom/punktfunk";
|
# { inputs.punktfunk.url = "git+https://git.unom.io/unom/punktfunk";
|
||||||
@@ -149,6 +150,84 @@ in
|
|||||||
description = "Open UDP 5353 (mDNS) so the client can auto-discover hosts on the LAN.";
|
description = "Open UDP 5353 (mDNS) so the client can auto-discover hosts on the LAN.";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# The management web console (SPAKE2 PIN pairing + host status) — the browser UI every client
|
||||||
|
# needs. Ships by DEFAULT alongside the host (mirrors the RPM's `Recommends: punktfunk-web` and
|
||||||
|
# the .deb the host package pulls in), auto-wired to the host's mgmt token + identity cert.
|
||||||
|
web = {
|
||||||
|
enable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = cfg.host.enable;
|
||||||
|
defaultText = literalExpression "config.services.punktfunk.host.enable";
|
||||||
|
description = ''
|
||||||
|
Run the management web console as a `systemd --user` service on TCP 47992 (HTTPS). Enabled
|
||||||
|
by default whenever the host is enabled — set to `false` for a console-less host. It
|
||||||
|
auto-wires to `~/.config/punktfunk/{mgmt-token,cert.pem,key.pem}` (written by the host's
|
||||||
|
`serve`) and generates a login password on first start.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
package = mkOption {
|
||||||
|
type = types.package;
|
||||||
|
default = self.packages.${system}.punktfunk-web;
|
||||||
|
defaultText = literalExpression "punktfunk.packages.\${system}.punktfunk-web";
|
||||||
|
description = "The punktfunk-web package (the bun-built Nitro SSR console bundle).";
|
||||||
|
};
|
||||||
|
|
||||||
|
openFirewall = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = cfg.host.openFirewall;
|
||||||
|
defaultText = literalExpression "config.services.punktfunk.host.openFirewall";
|
||||||
|
description = "Open TCP 47992 so the console is reachable from other devices on the LAN.";
|
||||||
|
};
|
||||||
|
|
||||||
|
autoStart = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = cfg.host.autoStart;
|
||||||
|
defaultText = literalExpression "config.services.punktfunk.host.autoStart";
|
||||||
|
description = ''
|
||||||
|
Start the console automatically in every user's graphical session (adds it to the user
|
||||||
|
`default.target`). Follows the host's `autoStart` by default — for a login-less appliance,
|
||||||
|
enable lingering for the user as well.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# The plugin/script runner — host automation on bun. Ships with the host (the RPM/deb Recommends
|
||||||
|
# it), but running it is OPT-IN: the `systemd --user` unit is defined yet NOT added to
|
||||||
|
# `default.target`, because the runner is inert until you add scripts/plugins. Turn it on with
|
||||||
|
# `systemctl --user enable --now punktfunk-scripting`.
|
||||||
|
scripting = {
|
||||||
|
enable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = cfg.host.enable;
|
||||||
|
defaultText = literalExpression "config.services.punktfunk.host.enable";
|
||||||
|
description = ''
|
||||||
|
Install the plugin/script runner and define its `systemd --user` unit
|
||||||
|
(`punktfunk-scripting`). Enabled by default whenever the host is — but the unit is not
|
||||||
|
auto-started (see `autoStart`), since the runner does nothing until you add scripts to
|
||||||
|
`~/.config/punktfunk/scripts` or install `punktfunk-plugin-*` packages under
|
||||||
|
`~/.config/punktfunk/plugins`. A plugin auto-wires to the host's mgmt token + identity cert.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
package = mkOption {
|
||||||
|
type = types.package;
|
||||||
|
default = self.packages.${system}.punktfunk-scripting;
|
||||||
|
defaultText = literalExpression "punktfunk.packages.\${system}.punktfunk-scripting";
|
||||||
|
description = "The punktfunk-scripting package (the bun-bundled Effect SDK runner).";
|
||||||
|
};
|
||||||
|
|
||||||
|
autoStart = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
Start the runner automatically in every user's graphical session (adds it to the user
|
||||||
|
`default.target`). Off by default even when the host auto-starts — running arbitrary
|
||||||
|
operator scripts/plugins is a deliberate opt-in; enable it once you have automation to run.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = mkMerge [
|
config = mkMerge [
|
||||||
@@ -226,5 +305,91 @@ in
|
|||||||
allowedUDPPorts = [ 5353 ];
|
allowedUDPPorts = [ 5353 ];
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|
||||||
|
# --- web console ---------------------------------------------------------------------------
|
||||||
|
# The declarative equivalent of the punktfunk-web .deb / RPM subpackage: the two systemd --user
|
||||||
|
# units (the console + its first-run password generator) plus the firewall opener, all auto-wired
|
||||||
|
# to the host's per-user mgmt token + identity cert (no env editing on a packaged install).
|
||||||
|
(mkIf cfg.web.enable {
|
||||||
|
environment.systemPackages = [ cfg.web.package ];
|
||||||
|
|
||||||
|
networking.firewall = mkIf cfg.web.openFirewall {
|
||||||
|
allowedTCPPorts = [ 47992 ]; # console HTTPS (packaging/linux/punktfunk-web.xml)
|
||||||
|
};
|
||||||
|
|
||||||
|
# First-run setup: generate the console login password once, in the user's config dir, and
|
||||||
|
# surface it to the --user journal. Self-gates via ConditionPathExists (mirrors
|
||||||
|
# scripts/punktfunk-web-init.service).
|
||||||
|
systemd.user.services.punktfunk-web-init = {
|
||||||
|
description = "punktfunk web console first-run setup (login password)";
|
||||||
|
documentation = [ "https://git.unom.io/unom/punktfunk" ];
|
||||||
|
unitConfig.ConditionPathExists = "!%h/.config/punktfunk/web-password";
|
||||||
|
path = [ pkgs.coreutils ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
RemainAfterExit = true;
|
||||||
|
ExecStart = "${cfg.web.package}/share/punktfunk-web/web-init.sh";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# The console itself: Nitro SSR on bun, HTTPS on 47992 with the host's identity cert, proxying
|
||||||
|
# the host's loopback mgmt API with the bearer token injected server-side. mgmt-token is
|
||||||
|
# REQUIRED (the host's `serve` writes it) — if absent the unit fails and Restart retries until
|
||||||
|
# the host has created it; web-password is optional ('-'). Mirrors scripts/punktfunk-web.service.
|
||||||
|
systemd.user.services.punktfunk-web = {
|
||||||
|
description = "punktfunk management web console";
|
||||||
|
documentation = [ "https://git.unom.io/unom/punktfunk" ];
|
||||||
|
after = [ "punktfunk-web-init.service" "punktfunk-host.service" ];
|
||||||
|
wants = [ "punktfunk-web-init.service" ];
|
||||||
|
wantedBy = optional cfg.web.autoStart "default.target";
|
||||||
|
environment = {
|
||||||
|
PUNKTFUNK_MGMT_URL = "https://127.0.0.1:47990";
|
||||||
|
PORT = "47992";
|
||||||
|
HOST = "0.0.0.0";
|
||||||
|
# Serve HTTPS with the host's own identity cert (the anchor native clients already pin) and
|
||||||
|
# mark the session cookie Secure. The host's `serve` writes these PEMs.
|
||||||
|
PUNKTFUNK_UI_TLS_CERT = "%h/.config/punktfunk/cert.pem";
|
||||||
|
PUNKTFUNK_UI_TLS_KEY = "%h/.config/punktfunk/key.pem";
|
||||||
|
PUNKTFUNK_UI_SECURE = "1";
|
||||||
|
};
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "simple";
|
||||||
|
EnvironmentFile = [
|
||||||
|
"%h/.config/punktfunk/mgmt-token"
|
||||||
|
"-%h/.config/punktfunk/web-password"
|
||||||
|
];
|
||||||
|
ExecStart = "${cfg.web.package}/bin/punktfunk-web-server";
|
||||||
|
Restart = "on-failure";
|
||||||
|
RestartSec = 2;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
})
|
||||||
|
|
||||||
|
# --- plugin/script runner ------------------------------------------------------------------
|
||||||
|
# Installs the runner + defines its opt-in `systemd --user` unit (mirrors the deb/rpm
|
||||||
|
# punktfunk-scripting subpackage). NOT auto-started unless `scripting.autoStart` is set.
|
||||||
|
(mkIf cfg.scripting.enable {
|
||||||
|
environment.systemPackages = [ cfg.scripting.package ];
|
||||||
|
|
||||||
|
systemd.user.services.punktfunk-scripting = {
|
||||||
|
description = "punktfunk plugin/script runner";
|
||||||
|
documentation = [ "https://git.unom.io/unom/punktfunk" ];
|
||||||
|
# Plugins talk to the host's loopback mgmt API; order after it (soft — the runner backs off
|
||||||
|
# and retries per unit, so this is ordering only, not a hard requirement).
|
||||||
|
after = [ "punktfunk-host.service" ];
|
||||||
|
wantedBy = optional cfg.scripting.autoStart "default.target";
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "simple";
|
||||||
|
ExecStart = "${cfg.scripting.package}/bin/punktfunk-scripting";
|
||||||
|
Restart = "on-failure";
|
||||||
|
RestartSec = 2;
|
||||||
|
# Deliver SIGTERM to the runner (it orchestrates the structural shutdown of its unit
|
||||||
|
# fibers) and give it room to run their finalizers before the cgroup is reaped.
|
||||||
|
KillMode = "mixed";
|
||||||
|
KillSignal = "SIGTERM";
|
||||||
|
TimeoutStopSec = 30;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
})
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
+221
-2
@@ -56,6 +56,12 @@
|
|||||||
gsettings-desktop-schemas,
|
gsettings-desktop-schemas,
|
||||||
adwaita-icon-theme,
|
adwaita-icon-theme,
|
||||||
vulkan-headers,
|
vulkan-headers,
|
||||||
|
# web console (punktfunk-web): a bun-built Nitro SSR bundle, run on bun.
|
||||||
|
bun,
|
||||||
|
nodejs,
|
||||||
|
makeWrapper,
|
||||||
|
cacert,
|
||||||
|
stdenvNoCC,
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
gbm = if libgbm != null then libgbm else mesa;
|
gbm = if libgbm != null then libgbm else mesa;
|
||||||
@@ -101,12 +107,34 @@ let
|
|||||||
platforms = [ "x86_64-linux" ]; # NVENC desktop host; matches the RPM's ExclusiveArch: x86_64
|
platforms = [ "x86_64-linux" ]; # NVENC desktop host; matches the RPM's ExclusiveArch: x86_64
|
||||||
maintainers = [ ];
|
maintainers = [ ];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# The Linux status tray, built in ISOLATION from the host — this is load-bearing, not tidiness.
|
||||||
|
# Cargo unifies features across every package in a single `cargo build`, so co-building the tray
|
||||||
|
# with the host would pull the host's `ashpd → zbus/tokio` onto the tray's SHARED `zbus`. The tray
|
||||||
|
# deliberately runs zbus on `ksni`'s `async-io` executor with the `blocking` API and no tokio
|
||||||
|
# runtime (see crates/punktfunk-tray/Cargo.toml), so a tokio-flavoured zbus panics at startup:
|
||||||
|
# "there is no reactor running, must be called from the context of a Tokio 1.x runtime". A separate
|
||||||
|
# invocation keeps the tray's zbus on async-io. (The host package copies this binary into its $out.)
|
||||||
|
punktfunk-tray = craneLib.buildPackage (commonArgs // {
|
||||||
|
pname = "punktfunk-tray";
|
||||||
|
cargoExtraArgs = "--locked -p punktfunk-tray";
|
||||||
|
PUNKTFUNK_BUILD_VERSION = buildVersion;
|
||||||
|
# Pure-Rust leaf: ksni/zbus talk to the dbus socket, ureq+rustls(ring) + punktfunk-core `tls` —
|
||||||
|
# nothing to link (no buildInputs) and no GPU driver runpath (it never dlopens libcuda/EGL/vulkan).
|
||||||
|
meta = meta // {
|
||||||
|
description = "punktfunk host status tray (Linux StatusNotifierItem)";
|
||||||
|
mainProgram = "punktfunk-tray";
|
||||||
|
};
|
||||||
|
});
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
inherit punktfunk-tray;
|
||||||
|
|
||||||
punktfunk-host = craneLib.buildPackage (commonArgs // {
|
punktfunk-host = craneLib.buildPackage (commonArgs // {
|
||||||
pname = "punktfunk-host";
|
pname = "punktfunk-host";
|
||||||
|
# HOST ONLY — the tray is a separate derivation (see the note above; co-building crashes it).
|
||||||
cargoExtraArgs =
|
cargoExtraArgs =
|
||||||
"--locked -p punktfunk-host -p punktfunk-tray "
|
"--locked -p punktfunk-host "
|
||||||
+ "--features punktfunk-host/nvenc,punktfunk-host/vulkan-encode";
|
+ "--features punktfunk-host/nvenc,punktfunk-host/vulkan-encode";
|
||||||
|
|
||||||
PUNKTFUNK_BUILD_VERSION = buildVersion;
|
PUNKTFUNK_BUILD_VERSION = buildVersion;
|
||||||
@@ -126,6 +154,10 @@ in
|
|||||||
# the host's authorization .desktop must name the ACTUAL binary path (the store path here). The
|
# the host's authorization .desktop must name the ACTUAL binary path (the store path here). The
|
||||||
# tray/client .desktop Exec lines are rewritten for the same reason (menu launch / autostart).
|
# tray/client .desktop Exec lines are rewritten for the same reason (menu launch / autostart).
|
||||||
postInstall = ''
|
postInstall = ''
|
||||||
|
# The status tray, built in its own derivation (see the punktfunk-tray note above) so its zbus
|
||||||
|
# stays on async-io; ship it in the host's $out so the tray .desktop below resolves it.
|
||||||
|
install -Dm0755 ${punktfunk-tray}/bin/punktfunk-tray "$out/bin/punktfunk-tray"
|
||||||
|
|
||||||
# udev: /dev/uinput + /dev/uhid (virtual gamepads) + the vhci sysfs perms for the virtual Deck.
|
# udev: /dev/uinput + /dev/uhid (virtual gamepads) + the vhci sysfs perms for the virtual Deck.
|
||||||
install -Dm0644 scripts/60-punktfunk.rules "$out/lib/udev/rules.d/60-punktfunk.rules"
|
install -Dm0644 scripts/60-punktfunk.rules "$out/lib/udev/rules.d/60-punktfunk.rules"
|
||||||
|
|
||||||
@@ -161,7 +193,8 @@ in
|
|||||||
# empty at build time): append the driver runpath so the runtime dlopen of libcuda.so.1 /
|
# empty at build time): append the driver runpath so the runtime dlopen of libcuda.so.1 /
|
||||||
# libnvidia-encode.so.1 / libEGL.so.1 / the GPU's libvulkan ICD resolves from the running system.
|
# libnvidia-encode.so.1 / libEGL.so.1 / the GPU's libvulkan ICD resolves from the running system.
|
||||||
postFixup = ''
|
postFixup = ''
|
||||||
addDriverRunpath "$out/bin/punktfunk-host" "$out/bin/punktfunk-tray"
|
# Only the host dlopens the GPU stack; the tray (its own derivation, copied in above) does not.
|
||||||
|
addDriverRunpath "$out/bin/punktfunk-host"
|
||||||
'';
|
'';
|
||||||
|
|
||||||
meta = meta // {
|
meta = meta // {
|
||||||
@@ -232,4 +265,190 @@ in
|
|||||||
mainProgram = "punktfunk-client";
|
mainProgram = "punktfunk-client";
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
# --- management web console (punktfunk-web) ------------------------------------------------------
|
||||||
|
# The browser console every client needs for SPAKE2 PIN pairing + host status: a TanStack Start /
|
||||||
|
# React app that vite builds into a Nitro SSR bundle, run on `bun` (the Nitro `bun` preset + a
|
||||||
|
# custom Bun.serve TLS entry — node can't run it; web/nitro-entry/bun-https.mjs). This mirrors the
|
||||||
|
# Debian punktfunk-web .deb (packaging/debian/build-web-deb.sh) and the RPM's `--with web`
|
||||||
|
# subpackage, which the host package Recommends so a default install pulls the console too.
|
||||||
|
#
|
||||||
|
# Unlike apt/dnf — which have no bun in their repos and so VENDOR a bun binary into the package —
|
||||||
|
# Nix has `pkgs.bun`, so the launcher just execs it from the store (no vendored runtime). The
|
||||||
|
# systemd `--user` units + firewall wiring live in the NixOS module, pointed at this store path.
|
||||||
|
punktfunk-web =
|
||||||
|
let
|
||||||
|
# Offline node_modules for the console. `bun install` needs the network AND the @unom npm
|
||||||
|
# registry (web/.npmrc → https://git.unom.io/api/packages/unom/npm/, read-public: the same
|
||||||
|
# anonymous pull CI's rpm/deb builds do), so it lives in a fixed-output derivation — FODs get
|
||||||
|
# network, and `outputHash` pins the result. `--ignore-scripts` skips the install-time
|
||||||
|
# `prepare` codegen (it wants ../api/openapi.json, outside this web-only src scope); the build
|
||||||
|
# derivation below runs codegen itself where the whole tree is present.
|
||||||
|
#
|
||||||
|
# ⚠ When web/bun.lock changes, this hash must be refreshed: set `outputHash = lib.fakeHash`,
|
||||||
|
# rebuild, and copy the sha256 Nix reports back here (see packaging/nix/README.md).
|
||||||
|
webDeps = stdenvNoCC.mkDerivation {
|
||||||
|
pname = "punktfunk-web-deps";
|
||||||
|
inherit version;
|
||||||
|
src = src + "/web";
|
||||||
|
nativeBuildInputs = [ bun cacert ];
|
||||||
|
dontConfigure = true;
|
||||||
|
buildPhase = ''
|
||||||
|
runHook preBuild
|
||||||
|
export HOME=$TMPDIR
|
||||||
|
export BUN_INSTALL_CACHE_DIR=$TMPDIR/bun-cache
|
||||||
|
export SSL_CERT_FILE=${cacert}/etc/ssl/certs/ca-bundle.crt
|
||||||
|
export NODE_EXTRA_CA_CERTS=$SSL_CERT_FILE
|
||||||
|
# copyfile backend ⇒ node_modules is fully materialised (no links into the ephemeral
|
||||||
|
# cache), so the tree survives the copy into the content-addressed $out.
|
||||||
|
bun install --frozen-lockfile --ignore-scripts --no-progress --backend=copyfile
|
||||||
|
runHook postBuild
|
||||||
|
'';
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
mkdir -p $out
|
||||||
|
cp -R node_modules $out/node_modules
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
dontFixup = true;
|
||||||
|
outputHashMode = "recursive";
|
||||||
|
outputHashAlgo = "sha256";
|
||||||
|
outputHash = "sha256-OA4NjwapsCV/z+0rftDCMAQJGWw63Mi/GARetmuy0QU="; # web/bun.lock deps (refresh on lockfile change; see README).
|
||||||
|
};
|
||||||
|
in
|
||||||
|
stdenvNoCC.mkDerivation {
|
||||||
|
pname = "punktfunk-web";
|
||||||
|
inherit src version;
|
||||||
|
# nodejs: the JS build tools' `.bin` shims are `#!/usr/bin/env node`; patchShebangs (below)
|
||||||
|
# repoints them at this node so they run in the sandbox. bun is still the RUNTIME (the launcher
|
||||||
|
# execs it); node is build-time only, for orval/paraglide/vite.
|
||||||
|
nativeBuildInputs = [ bun nodejs makeWrapper ];
|
||||||
|
|
||||||
|
# No cross-derivation dep cache: codegen + the vite build are fully offline (every input is in
|
||||||
|
# the vendored node_modules, the checked-in api/openapi.json, and web/project.inlang).
|
||||||
|
buildPhase = ''
|
||||||
|
runHook preBuild
|
||||||
|
export HOME=$TMPDIR
|
||||||
|
cp -R ${webDeps}/node_modules web/node_modules
|
||||||
|
chmod -R u+w web/node_modules
|
||||||
|
# The JS CLIs (orval, paraglide-js, vite, …) ship a `#!/usr/bin/env node` shebang, and the
|
||||||
|
# build sandbox has no /usr/bin/env — rewrite them to the store `node` before running any
|
||||||
|
# script (else `bun run codegen` dies with "bad interpreter: /usr/bin/env"). Patch the WHOLE
|
||||||
|
# node_modules, not just .bin: bun's .bin entries are symlinks (skipped by patchShebangs'
|
||||||
|
# `-type f`); the real shebang lives in each package's `dist/bin/*.js` that they point to.
|
||||||
|
patchShebangs web/node_modules
|
||||||
|
cd web
|
||||||
|
# `codegen` = orval (a typed React-Query client from ../api/openapi.json) + paraglide-js i18n
|
||||||
|
# compile; both write into src/ and are prerequisites of the build (normally the install-time
|
||||||
|
# `prepare` hook, which was skipped in the deps FOD).
|
||||||
|
bun run codegen
|
||||||
|
# `build` = vite build ⇒ the Nitro `bun`-preset SSR bundle in .output (our Bun.serve TLS entry).
|
||||||
|
bun run build
|
||||||
|
# Guard: assert we produced the bun bundle, not a node one (same check the deb/rpm builders do).
|
||||||
|
grep -q 'Bun\.serve' .output/server/index.mjs \
|
||||||
|
|| { echo "ERROR: web/.output is not a bun bundle (wrong nitro preset)" >&2; exit 1; }
|
||||||
|
cd ..
|
||||||
|
runHook postBuild
|
||||||
|
'';
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
# The SSR bundle + its static assets, plus the first-run helper and env sample.
|
||||||
|
mkdir -p $out/share/punktfunk-web/.output
|
||||||
|
cp -R web/.output/server $out/share/punktfunk-web/.output/server
|
||||||
|
cp -R web/.output/public $out/share/punktfunk-web/.output/public
|
||||||
|
install -Dm0755 scripts/web-init.sh $out/share/punktfunk-web/web-init.sh
|
||||||
|
install -Dm0644 web/web.env.example $out/share/punktfunk-web/web.env.example
|
||||||
|
|
||||||
|
# PATH-stable launcher: run the SSR bundle on bun from the store (mirrors the deb/rpm
|
||||||
|
# /usr/bin/punktfunk-web-server, minus the vendored-bun indirection).
|
||||||
|
makeWrapper ${bun}/bin/bun $out/bin/punktfunk-web-server \
|
||||||
|
--add-flags "$out/share/punktfunk-web/.output/server/index.mjs"
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
|
||||||
|
dontFixup = true;
|
||||||
|
|
||||||
|
meta = meta // {
|
||||||
|
description = "punktfunk management web console (Nitro SSR on bun + React)";
|
||||||
|
mainProgram = "punktfunk-web-server";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# --- plugin/script runner (punktfunk-scripting) --------------------------------------------------
|
||||||
|
# The host's automation runner: the `@punktfunk/host` SDK's `punktfunk-scripting` CLI (built on
|
||||||
|
# Effect), which discovers ~/.config/punktfunk/{scripts,plugins} and supervises each unit as an
|
||||||
|
# Effect fiber. It runs on `bun` (it import()s the operator's `.ts` plugin files, which only bun
|
||||||
|
# can do). Mirrors the Debian punktfunk-scripting .deb / the RPM's `--with scripting` subpackage,
|
||||||
|
# which the host package Recommends — the NixOS module wires the opt-in systemd --user unit.
|
||||||
|
#
|
||||||
|
# Unlike the deb/rpm we don't `bun build` into a bundle + vendor bun; we still bundle (one
|
||||||
|
# self-contained JS, effect inlined) but the launcher execs `pkgs.bun` from the store.
|
||||||
|
punktfunk-scripting =
|
||||||
|
let
|
||||||
|
# Offline node_modules for the SDK build — same fixed-output pattern as punktfunk-web's webDeps
|
||||||
|
# (`bun install` needs the network). ⚠ Refresh `outputHash` when sdk/bun.lock changes (set
|
||||||
|
# lib.fakeHash, rebuild, copy the printed sha256 — see packaging/nix/README.md).
|
||||||
|
sdkDeps = stdenvNoCC.mkDerivation {
|
||||||
|
pname = "punktfunk-scripting-deps";
|
||||||
|
inherit version;
|
||||||
|
src = src + "/sdk";
|
||||||
|
nativeBuildInputs = [ bun cacert ];
|
||||||
|
dontConfigure = true;
|
||||||
|
buildPhase = ''
|
||||||
|
runHook preBuild
|
||||||
|
export HOME=$TMPDIR
|
||||||
|
export BUN_INSTALL_CACHE_DIR=$TMPDIR/bun-cache
|
||||||
|
export SSL_CERT_FILE=${cacert}/etc/ssl/certs/ca-bundle.crt
|
||||||
|
export NODE_EXTRA_CA_CERTS=$SSL_CERT_FILE
|
||||||
|
bun install --frozen-lockfile --ignore-scripts --no-progress --backend=copyfile
|
||||||
|
runHook postBuild
|
||||||
|
'';
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
mkdir -p $out
|
||||||
|
cp -R node_modules $out/node_modules
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
dontFixup = true;
|
||||||
|
outputHashMode = "recursive";
|
||||||
|
outputHashAlgo = "sha256";
|
||||||
|
outputHash = "sha256-+KCKCA0q0bwTxr7bsA3X4DbT/8nUjJA/JIoJU6BfiZw="; # sdk/bun.lock deps (refresh on lockfile change; see README).
|
||||||
|
};
|
||||||
|
in
|
||||||
|
stdenvNoCC.mkDerivation {
|
||||||
|
pname = "punktfunk-scripting";
|
||||||
|
inherit src version;
|
||||||
|
nativeBuildInputs = [ bun makeWrapper ];
|
||||||
|
|
||||||
|
# `bun build --target=bun` bundles the runner CLI to ONE self-contained JS: effect + the SDK
|
||||||
|
# are inlined, and the runner's dynamic `import()` of the operator's plugin files is left as a
|
||||||
|
# runtime import (bun keeps unresolvable dynamic specifiers external). Fully offline.
|
||||||
|
buildPhase = ''
|
||||||
|
runHook preBuild
|
||||||
|
export HOME=$TMPDIR
|
||||||
|
cp -R ${sdkDeps}/node_modules sdk/node_modules
|
||||||
|
chmod -R u+w sdk/node_modules
|
||||||
|
( cd sdk && bun build src/runner-cli.ts --target=bun --outfile=$TMPDIR/runner-cli.js )
|
||||||
|
grep -q 'attempt=' $TMPDIR/runner-cli.js \
|
||||||
|
|| { echo "ERROR: runner bundle missing the dynamic plugin import — wrong build" >&2; exit 1; }
|
||||||
|
runHook postBuild
|
||||||
|
'';
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
install -Dm0644 $TMPDIR/runner-cli.js $out/share/punktfunk-scripting/runner-cli.js
|
||||||
|
# Launcher: run the bundle on bun from the store (mirrors the deb/rpm /usr/bin/punktfunk-scripting).
|
||||||
|
makeWrapper ${bun}/bin/bun $out/bin/punktfunk-scripting \
|
||||||
|
--add-flags "$out/share/punktfunk-scripting/runner-cli.js"
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
|
||||||
|
dontFixup = true;
|
||||||
|
|
||||||
|
meta = meta // {
|
||||||
|
description = "punktfunk plugin/script runner (Effect SDK on bun)";
|
||||||
|
mainProgram = "punktfunk-scripting";
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ PF_RELEASE="${PF_RELEASE:-1}"
|
|||||||
# builder image, not in a plain mock chroot). Default off so a bare `rpmbuild`/COPR still works.
|
# builder image, not in a plain mock chroot). Default off so a bare `rpmbuild`/COPR still works.
|
||||||
WEB_OPT=()
|
WEB_OPT=()
|
||||||
[ "${PF_WITH_WEB:-0}" = "1" ] && WEB_OPT=(--with web)
|
[ "${PF_WITH_WEB:-0}" = "1" ] && WEB_OPT=(--with web)
|
||||||
|
# PF_WITH_SCRIPTING=1 builds the punktfunk-scripting subpackage (the plugin/script runner). Same bun
|
||||||
|
# requirement as web; default off so a bare `rpmbuild`/COPR still works.
|
||||||
|
SCRIPTING_OPT=()
|
||||||
|
[ "${PF_WITH_SCRIPTING:-0}" = "1" ] && SCRIPTING_OPT=(--with scripting)
|
||||||
ROOTDIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
ROOTDIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||||
cd "$ROOTDIR"
|
cd "$ROOTDIR"
|
||||||
|
|
||||||
@@ -51,7 +55,7 @@ fi
|
|||||||
# resolves them from RPMs. Our builder image provides the toolchain via rustup (so
|
# resolves them from RPMs. Our builder image provides the toolchain via rustup (so
|
||||||
# rust-toolchain.toml's pinned channel works) and the -devel libs via dnf, neither of which
|
# rust-toolchain.toml's pinned channel works) and the -devel libs via dnf, neither of which
|
||||||
# rpmbuild's RPM-level check sees — skip it; a genuinely missing dep fails the compile/link.
|
# rpmbuild's RPM-level check sees — skip it; a genuinely missing dep fails the compile/link.
|
||||||
rpmbuild -bb --nodeps "${WEB_OPT[@]}" \
|
rpmbuild -bb --nodeps "${WEB_OPT[@]}" "${SCRIPTING_OPT[@]}" \
|
||||||
--define "_topdir $TOP" \
|
--define "_topdir $TOP" \
|
||||||
--define "pf_version ${PF_VERSION}" \
|
--define "pf_version ${PF_VERSION}" \
|
||||||
--define "pf_release ${PF_RELEASE}" \
|
--define "pf_release ${PF_RELEASE}" \
|
||||||
|
|||||||
@@ -49,6 +49,12 @@ ExclusiveArch: x86_64
|
|||||||
# the console, or enable bun + `--with web` in the COPR project. Mirrors the Debian punktfunk-web .deb.
|
# the console, or enable bun + `--with web` in the COPR project. Mirrors the Debian punktfunk-web .deb.
|
||||||
%bcond_with web
|
%bcond_with web
|
||||||
|
|
||||||
|
# Plugin/script runner subpackage (punktfunk-scripting). OFF by default for the same reason as web:
|
||||||
|
# building the bun bundle needs `bun`, absent from a plain rpmbuild / COPR mock chroot. CI's builder
|
||||||
|
# image has bun and builds with `--with scripting`, so the Gitea RPM registry carries it. Mirrors the
|
||||||
|
# Debian punktfunk-scripting .deb.
|
||||||
|
%bcond_with scripting
|
||||||
|
|
||||||
# --- Build toolchain ---------------------------------------------------------
|
# --- Build toolchain ---------------------------------------------------------
|
||||||
BuildRequires: cargo
|
BuildRequires: cargo
|
||||||
BuildRequires: rust
|
BuildRequires: rust
|
||||||
@@ -112,6 +118,10 @@ Recommends: intel-media-driver
|
|||||||
# Weak-dep so `dnf install punktfunk` pulls it where it exists (the Gitea registry); harmless where
|
# Weak-dep so `dnf install punktfunk` pulls it where it exists (the Gitea registry); harmless where
|
||||||
# it doesn't (a COPR build without `--with web` simply has no punktfunk-web to satisfy).
|
# it doesn't (a COPR build without `--with web` simply has no punktfunk-web to satisfy).
|
||||||
Recommends: punktfunk-web
|
Recommends: punktfunk-web
|
||||||
|
# The plugin/script runner (host automation on bun). Same weak-dep story: pulled where it exists,
|
||||||
|
# harmless where a `--with scripting`-less build didn't produce it. Its systemd --user unit ships
|
||||||
|
# disabled — the runner is inert until you add scripts/plugins.
|
||||||
|
Recommends: punktfunk-scripting
|
||||||
|
|
||||||
%description
|
%description
|
||||||
punktfunk is a Linux-first, low-latency desktop and game streaming host. It speaks
|
punktfunk is a Linux-first, low-latency desktop and game streaming host. It speaks
|
||||||
@@ -156,6 +166,23 @@ mgmt token, identity cert, and a generated login password, no env editing. Bundl
|
|||||||
runtime. Enable with `systemctl --user enable --now punktfunk-web`.
|
runtime. Enable with `systemctl --user enable --now punktfunk-web`.
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
|
%if %{with scripting}
|
||||||
|
%package scripting
|
||||||
|
Summary: punktfunk plugin/script runner (Effect SDK on bun)
|
||||||
|
# Runtime is BUN — the runner import()s the operator's .ts plugin files, which only bun can do. bun
|
||||||
|
# isn't in Fedora repos, so we VENDOR it into the package (arch-specific, not noarch). The runner
|
||||||
|
# itself is bundled to ONE self-contained JS (effect + SDK inlined), so no node_modules ship.
|
||||||
|
|
||||||
|
%description scripting
|
||||||
|
The plugin/script runner for a punktfunk streaming host: it discovers loose scripts under
|
||||||
|
~/.config/punktfunk/scripts and installed punktfunk-plugin-* packages under ~/.config/punktfunk/
|
||||||
|
plugins, and supervises each as an Effect fiber (capped-jittered restart; SIGTERM shuts the whole
|
||||||
|
tree down structurally so plugin finalizers run). A plugin auto-wires to the host's mgmt token +
|
||||||
|
identity cert on the same box — no env editing. Bundles its own bun runtime. OPT-IN: the systemd
|
||||||
|
--user unit ships disabled (the runner is inert until you add scripts/plugins). Enable with
|
||||||
|
`systemctl --user enable --now punktfunk-scripting`.
|
||||||
|
%endif
|
||||||
|
|
||||||
%prep
|
%prep
|
||||||
%autosetup -n %{name}-%{version}
|
%autosetup -n %{name}-%{version}
|
||||||
|
|
||||||
@@ -185,7 +212,13 @@ export PUNKTFUNK_BUILD_VERSION="%{version}-%{release}"
|
|||||||
# Pure Rust `ash` (no new lib / no link-time dep); default on for HEVC (PUNKTFUNK_VULKAN_ENCODE=0 opts
|
# Pure Rust `ash` (no new lib / no link-time dep); default on for HEVC (PUNKTFUNK_VULKAN_ENCODE=0 opts
|
||||||
# back to libav VAAPI), and a failed open falls back to VAAPI so unsupported devices degrade gracefully.
|
# back to libav VAAPI), and a failed open falls back to VAAPI so unsupported devices degrade gracefully.
|
||||||
cargo build --release --locked --features punktfunk-host/nvenc,punktfunk-host/vulkan-encode \
|
cargo build --release --locked --features punktfunk-host/nvenc,punktfunk-host/vulkan-encode \
|
||||||
-p punktfunk-host -p punktfunk-client-linux -p punktfunk-client-session -p punktfunk-tray
|
-p punktfunk-host -p punktfunk-client-linux -p punktfunk-client-session
|
||||||
|
# The status tray in its OWN cargo invocation — load-bearing, not tidiness. Cargo unifies features
|
||||||
|
# across everything in one build, so co-building the tray with the host pulls the host's
|
||||||
|
# ashpd -> zbus/tokio onto the tray's shared zbus; the tray (ksni async-io + blocking, no tokio
|
||||||
|
# runtime by design) then panics at startup ("there is no reactor running, must be called from the
|
||||||
|
# context of a Tokio 1.x runtime"). Built alone, its zbus stays on async-io. (Same split the .deb does.)
|
||||||
|
cargo build --release --locked -p punktfunk-tray
|
||||||
|
|
||||||
%if %{with web}
|
%if %{with web}
|
||||||
# Management web console: build the Nitro SSR bundle with bun (the `bun` preset + our Bun.serve
|
# Management web console: build the Nitro SSR bundle with bun (the `bun` preset + our Bun.serve
|
||||||
@@ -197,6 +230,18 @@ if ! grep -q 'Bun\.serve' web/.output/server/index.mjs; then
|
|||||||
fi
|
fi
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
|
%if %{with scripting}
|
||||||
|
# Plugin/script runner: bundle the SDK's runner CLI to ONE self-contained JS with bun
|
||||||
|
# (`--target=bun` inlines effect + the SDK; the dynamic plugin import stays a runtime import). bun is
|
||||||
|
# both the build tool AND the vendored runtime (in %%install below).
|
||||||
|
(cd sdk && bun install --frozen-lockfile --ignore-scripts && \
|
||||||
|
bun build src/runner-cli.ts --target=bun --outfile=../runner-cli.js)
|
||||||
|
if ! grep -q 'attempt=' runner-cli.js; then
|
||||||
|
echo "ERROR: runner bundle missing the dynamic plugin import — wrong build" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
%endif
|
||||||
|
|
||||||
%install
|
%install
|
||||||
# Binary
|
# Binary
|
||||||
install -Dm0755 target/release/punktfunk-host %{buildroot}%{_bindir}/punktfunk-host
|
install -Dm0755 target/release/punktfunk-host %{buildroot}%{_bindir}/punktfunk-host
|
||||||
@@ -316,6 +361,22 @@ install -Dm0755 scripts/web-init.sh %{buildroot}%{_datadir}/punkt
|
|||||||
install -Dm0644 web/web.env.example %{buildroot}%{_datadir}/punktfunk-web/web.env.example
|
install -Dm0644 web/web.env.example %{buildroot}%{_datadir}/punktfunk-web/web.env.example
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
|
%if %{with scripting}
|
||||||
|
# --- plugin/script runner subpackage (punktfunk-scripting) ---
|
||||||
|
install -Dm0644 runner-cli.js %{buildroot}%{_datadir}/punktfunk-scripting/runner-cli.js
|
||||||
|
# Vendor the build env's bun (arch-specific, like the web subpackage) into a private libexec dir.
|
||||||
|
install -Dm0755 "$(command -v bun)" %{buildroot}%{_libexecdir}/punktfunk-scripting/bun
|
||||||
|
# PATH-stable launcher (matches the .deb's /usr/bin/punktfunk-scripting) — runs the bundle on bun.
|
||||||
|
cat > %{buildroot}%{_bindir}/punktfunk-scripting <<'WRAP'
|
||||||
|
#!/bin/sh
|
||||||
|
exec /usr/libexec/punktfunk-scripting/bun /usr/share/punktfunk-scripting/runner-cli.js "$@"
|
||||||
|
WRAP
|
||||||
|
chmod 0755 %{buildroot}%{_bindir}/punktfunk-scripting
|
||||||
|
# systemd --user unit — installed but NOT auto-enabled (opt-in; the runner is inert until you add
|
||||||
|
# scripts/plugins). Enable with `systemctl --user enable --now punktfunk-scripting`.
|
||||||
|
install -Dm0644 scripts/punktfunk-scripting.service %{buildroot}%{_userunitdir}/punktfunk-scripting.service
|
||||||
|
%endif
|
||||||
|
|
||||||
%files
|
%files
|
||||||
%license LICENSE-MIT LICENSE-APACHE THIRD-PARTY-NOTICES.txt
|
%license LICENSE-MIT LICENSE-APACHE THIRD-PARTY-NOTICES.txt
|
||||||
%doc README.md packaging/README.md
|
%doc README.md packaging/README.md
|
||||||
@@ -360,6 +421,17 @@ install -Dm0644 web/web.env.example %{buildroot}%{_datadir}/punkt
|
|||||||
%{_userunitdir}/punktfunk-web-init.service
|
%{_userunitdir}/punktfunk-web-init.service
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
|
%if %{with scripting}
|
||||||
|
%files scripting
|
||||||
|
%license LICENSE-MIT LICENSE-APACHE THIRD-PARTY-NOTICES.txt
|
||||||
|
%{_bindir}/punktfunk-scripting
|
||||||
|
%dir %{_libexecdir}/punktfunk-scripting
|
||||||
|
%{_libexecdir}/punktfunk-scripting/bun
|
||||||
|
%dir %{_datadir}/punktfunk-scripting
|
||||||
|
%{_datadir}/punktfunk-scripting/runner-cli.js
|
||||||
|
%{_userunitdir}/punktfunk-scripting.service
|
||||||
|
%endif
|
||||||
|
|
||||||
%post client
|
%post client
|
||||||
# Pick up the DualSense hidraw rule without a reboot (best-effort; on rpm-ostree it
|
# Pick up the DualSense hidraw rule without a reboot (best-effort; on rpm-ostree it
|
||||||
# applies on the next boot into the layered deployment).
|
# applies on the next boot into the layered deployment).
|
||||||
@@ -403,7 +475,17 @@ echo " journalctl --user -u punktfunk-web-init | sed -n 's/.*password generat
|
|||||||
echo "Then open https://<host-ip>:47992"
|
echo "Then open https://<host-ip>:47992"
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
|
%if %{with scripting}
|
||||||
|
%post scripting
|
||||||
|
echo "punktfunk-scripting installed. It runs your automation — add scripts to"
|
||||||
|
echo " ~/.config/punktfunk/scripts/ (loose .ts/.js files)"
|
||||||
|
echo "or install plugins into ~/.config/punktfunk/plugins/ (bun add punktfunk-plugin-<name>),"
|
||||||
|
echo "then enable the runner: systemctl --user enable --now punktfunk-scripting"
|
||||||
|
%endif
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Thu Jul 17 2026 punktfunk <noreply@anthropic.com> - 0.0.1-3
|
||||||
|
- Add punktfunk-scripting subpackage (plugin/script runner, --with scripting; bun-bundled Effect SDK).
|
||||||
* Mon Jun 15 2026 punktfunk <noreply@anthropic.com> - 0.0.1-2
|
* Mon Jun 15 2026 punktfunk <noreply@anthropic.com> - 0.0.1-2
|
||||||
- Add punktfunk-web subpackage (management console, --with web; auto-wired to the host token).
|
- Add punktfunk-web subpackage (management console, --with web; auto-wired to the host token).
|
||||||
* Wed Jun 10 2026 punktfunk <noreply@anthropic.com> - 0.0.1-1
|
* Wed Jun 10 2026 punktfunk <noreply@anthropic.com> - 0.0.1-1
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ param(
|
|||||||
[string]$PfxPassword = $env:MSIX_CERT_PASSWORD,
|
[string]$PfxPassword = $env:MSIX_CERT_PASSWORD,
|
||||||
[string]$FfmpegDir = $env:FFMPEG_DIR, # bundle its bin\*.dll (amf-qsv build)
|
[string]$FfmpegDir = $env:FFMPEG_DIR, # bundle its bin\*.dll (amf-qsv build)
|
||||||
[string]$WebDir = $env:WEB_OUTPUT_DIR, # built web .output tree -> bundle the mgmt console
|
[string]$WebDir = $env:WEB_OUTPUT_DIR, # built web .output tree -> bundle the mgmt console
|
||||||
[string]$BunExe = $env:BUN_EXE, # portable bun.exe runtime for the console
|
[string]$ScriptingBundle = $env:SCRIPTING_BUNDLE, # built runner-cli.js -> bundle the plugin/script runner
|
||||||
|
[string]$BunExe = $env:BUN_EXE, # portable bun.exe runtime for the console + runner
|
||||||
[string]$VbCableDir = $env:VBCABLE_DIR, # official base VB-CABLE package -> bundle the virtual mic
|
[string]$VbCableDir = $env:VBCABLE_DIR, # official base VB-CABLE package -> bundle the virtual mic
|
||||||
[switch]$NoDriver, # build without the bundled pf-vdisplay driver
|
[switch]$NoDriver, # build without the bundled pf-vdisplay driver
|
||||||
[switch]$NoSign # skip signing (local debug)
|
[switch]$NoSign # skip signing (local debug)
|
||||||
@@ -257,28 +258,50 @@ if ($ffmpegBinSrc -and (Test-Path $ffmpegBinSrc)) {
|
|||||||
}
|
}
|
||||||
else { Write-Host "no FFMPEG_DIR\bin -> installer built WITHOUT FFmpeg DLLs (nvenc/software-only host)" }
|
else { Write-Host "no FFMPEG_DIR\bin -> installer built WITHOUT FFmpeg DLLs (nvenc/software-only host)" }
|
||||||
|
|
||||||
# --- stage the web management console (the self-contained .output tree + a portable bun + launcher) -
|
# --- stage the bun runtime + the two bun payloads (web console, plugin/script runner) --------------
|
||||||
# The console runs as the PunktfunkWeb scheduled task (`bun {app}\web\.output\server\index.mjs`),
|
# Both the web console and the runner run on bun. Stage everything ISCC reads into $OutDir (the
|
||||||
# auto-wired to the host's loopback mgmt API. Stage everything ISCC reads into $OutDir (the
|
# non-WOW64-redirected C:\t area, same reason as the .iss/host.env staging above). bun is staged ONCE
|
||||||
# non-WOW64-redirected C:\t area, same reason as the .iss/host.env staging above). The .output is
|
# and shared: the two payloads pass their own defines and the .iss keys WithWeb / WithScripting on
|
||||||
# self-contained (Nitro noExternals - deps bundled + tree-shaken, no node_modules), so bun runs it
|
# (their dir + BunExe). Each payload is omitted when its inputs are unset (e.g. a local debug pack).
|
||||||
# directly; omitted when -WebDir/-BunExe are unset (host-only installer, e.g. a local debug pack).
|
$haveBun = $BunExe -and (Test-Path $BunExe)
|
||||||
if ($WebDir -and (Test-Path $WebDir) -and $BunExe -and (Test-Path $BunExe)) {
|
$wantWeb = $WebDir -and (Test-Path $WebDir) -and $haveBun
|
||||||
|
$wantScripting = $ScriptingBundle -and (Test-Path $ScriptingBundle) -and $haveBun
|
||||||
|
if ($wantWeb -or $wantScripting) {
|
||||||
|
$bunStage = Join-Path $OutDir 'bun.exe'
|
||||||
|
Copy-Item -LiteralPath $BunExe -Destination $bunStage -Force
|
||||||
|
$defines += "/DBunExe=$bunStage"
|
||||||
|
}
|
||||||
|
# The web console: the self-contained .output tree (Nitro noExternals - deps bundled + tree-shaken,
|
||||||
|
# no node_modules), run by the PunktfunkWeb scheduled task, auto-wired to the host's loopback mgmt API.
|
||||||
|
if ($wantWeb) {
|
||||||
$webStage = Join-Path $OutDir 'web'
|
$webStage = Join-Path $OutDir 'web'
|
||||||
if (Test-Path $webStage) { Remove-Item $webStage -Recurse -Force }
|
if (Test-Path $webStage) { Remove-Item $webStage -Recurse -Force }
|
||||||
New-Item -ItemType Directory -Force -Path $webStage | Out-Null
|
New-Item -ItemType Directory -Force -Path $webStage | Out-Null
|
||||||
Copy-Item (Join-Path $WebDir '*') -Destination $webStage -Recurse -Force
|
Copy-Item (Join-Path $WebDir '*') -Destination $webStage -Recurse -Force
|
||||||
$bunStage = Join-Path $OutDir 'bun.exe'
|
|
||||||
Copy-Item -LiteralPath $BunExe -Destination $bunStage -Force
|
|
||||||
$webRun = Join-Path $OutDir 'web-run.cmd'
|
$webRun = Join-Path $OutDir 'web-run.cmd'
|
||||||
Copy-Item (Join-Path $repoRoot 'scripts\windows\web-run.cmd') -Destination $webRun -Force
|
Copy-Item (Join-Path $repoRoot 'scripts\windows\web-run.cmd') -Destination $webRun -Force
|
||||||
# The console is provisioned by `punktfunk-host.exe web setup` (not a staged web-setup.ps1).
|
# The console is provisioned by `punktfunk-host.exe web setup` (not a staged web-setup.ps1).
|
||||||
$defines += "/DWebDir=$webStage"
|
$defines += "/DWebDir=$webStage"
|
||||||
$defines += "/DBunExe=$bunStage"
|
|
||||||
$defines += "/DWebRunCmd=$webRun"
|
$defines += "/DWebRunCmd=$webRun"
|
||||||
Write-Host "bundling the web console from $WebDir (+ bun $BunExe)"
|
Write-Host "bundling the web console from $WebDir (+ bun $BunExe)"
|
||||||
}
|
}
|
||||||
else { Write-Host "no -WebDir/-BunExe -> installer built WITHOUT the web console" }
|
else { Write-Host "no -WebDir/-BunExe -> installer built WITHOUT the web console" }
|
||||||
|
# The plugin/script runner: one self-contained bundle (effect + the SDK inlined). Its scheduled task
|
||||||
|
# is registered DISABLED (opt-in) by the installer. Built by CI (SCRIPTING_BUNDLE) alongside the web
|
||||||
|
# console; omitted when -ScriptingBundle/-BunExe are unset.
|
||||||
|
if ($wantScripting) {
|
||||||
|
$scrStage = Join-Path $OutDir 'scripting'
|
||||||
|
if (Test-Path $scrStage) { Remove-Item $scrStage -Recurse -Force }
|
||||||
|
New-Item -ItemType Directory -Force -Path $scrStage | Out-Null
|
||||||
|
$scrBundle = Join-Path $scrStage 'runner-cli.js'
|
||||||
|
Copy-Item -LiteralPath $ScriptingBundle -Destination $scrBundle -Force
|
||||||
|
$scrRun = Join-Path $scrStage 'scripting-run.cmd'
|
||||||
|
Copy-Item (Join-Path $repoRoot 'scripts\windows\scripting-run.cmd') -Destination $scrRun -Force
|
||||||
|
$defines += "/DScriptingBundle=$scrBundle"
|
||||||
|
$defines += "/DScriptingRunCmd=$scrRun"
|
||||||
|
Write-Host "bundling the plugin/script runner from $ScriptingBundle (+ bun $BunExe)"
|
||||||
|
}
|
||||||
|
else { Write-Host "no -ScriptingBundle/-BunExe -> installer built WITHOUT the plugin/script runner" }
|
||||||
|
|
||||||
# --- build + stage the HDR Vulkan layer (pf-vkhdr-layer) --------------------------------------
|
# --- build + stage the HDR Vulkan layer (pf-vkhdr-layer) --------------------------------------
|
||||||
# A tiny always-on Vulkan implicit layer (cdylib) that advertises HDR10/scRGB surface formats on the
|
# A tiny always-on Vulkan implicit layer (cdylib) that advertises HDR10/scRGB surface formats on the
|
||||||
|
|||||||
@@ -40,6 +40,11 @@
|
|||||||
#ifndef WebRunCmd
|
#ifndef WebRunCmd
|
||||||
#define WebRunCmd "..\..\scripts\windows\web-run.cmd"
|
#define WebRunCmd "..\..\scripts\windows\web-run.cmd"
|
||||||
#endif
|
#endif
|
||||||
|
; The plugin/script runner launcher (the action the opt-in PunktfunkScripting task runs) - staged
|
||||||
|
; next to the .iss by pack-host-installer.ps1 (absolute path passed in).
|
||||||
|
#ifndef ScriptingRunCmd
|
||||||
|
#define ScriptingRunCmd "..\..\scripts\windows\scripting-run.cmd"
|
||||||
|
#endif
|
||||||
; StageDir (the staged pf-vdisplay payload + nefconc.exe + install-pf-vdisplay.ps1) is optional.
|
; StageDir (the staged pf-vdisplay payload + nefconc.exe + install-pf-vdisplay.ps1) is optional.
|
||||||
#ifdef StageDir
|
#ifdef StageDir
|
||||||
#define WithDriver
|
#define WithDriver
|
||||||
@@ -66,6 +71,13 @@
|
|||||||
#define WithWeb
|
#define WithWeb
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
; ScriptingBundle (the built runner-cli.js) + BunExe are passed together by pack-host-installer.ps1
|
||||||
|
; to bundle the plugin/script runner. Both required -> WithScripting.
|
||||||
|
#ifdef ScriptingBundle
|
||||||
|
#ifdef BunExe
|
||||||
|
#define WithScripting
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
; VkLayerDir (the staged pf-vkhdr-layer: pf_vkhdr_layer.dll + .json) is optional - present when the
|
; VkLayerDir (the staged pf-vkhdr-layer: pf_vkhdr_layer.dll + .json) is optional - present when the
|
||||||
; HDR Vulkan layer was built. It lets Vulkan games (Doom: The Dark Ages, etc.) enable HDR over the
|
; HDR Vulkan layer was built. It lets Vulkan games (Doom: The Dark Ages, etc.) enable HDR over the
|
||||||
; virtual display (the ICD won't advertise HDR there; the layer injects the surface formats, self-
|
; virtual display (the ICD won't advertise HDR there; the layer injects the surface formats, self-
|
||||||
@@ -179,15 +191,26 @@ Source: "{#LicensesDir}\*"; DestDir: "{app}\licenses"; Flags: ignoreversion
|
|||||||
; dynamically (replaceable DLLs) - FFmpeg is used under the LGPL v2.1+; see {app}\licenses.
|
; dynamically (replaceable DLLs) - FFmpeg is used under the LGPL v2.1+; see {app}\licenses.
|
||||||
Source: "{#FfmpegBin}\*.dll"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "{#FfmpegBin}\*.dll"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
#endif
|
#endif
|
||||||
|
; The portable bun runtime -> {app}\bun\bun.exe. Shared by the web console AND the plugin/script
|
||||||
|
; runner (both run on bun), so stage it once when EITHER is bundled.
|
||||||
|
#if defined(WithWeb) || defined(WithScripting)
|
||||||
|
Source: "{#BunExe}"; DestDir: "{app}\bun"; DestName: "bun.exe"; Flags: ignoreversion
|
||||||
|
#endif
|
||||||
#ifdef WithWeb
|
#ifdef WithWeb
|
||||||
; The web management console: the self-contained Nitro SSR bundle (.output = server + public; deps
|
; The web management console: the self-contained Nitro SSR bundle (.output = server + public; deps
|
||||||
; bundled in, no node_modules) -> {app}\web\.output, a portable bun runtime -> {app}\bun\bun.exe, and
|
; bundled in, no node_modules) -> {app}\web\.output, and the launcher the PunktfunkWeb task runs ->
|
||||||
; the launcher the PunktfunkWeb task runs -> {app}\web\web-run.cmd. (`punktfunk-host.exe web setup`
|
; {app}\web\web-run.cmd. (`punktfunk-host.exe web setup` provisions the console at install time - no
|
||||||
; provisions the console at install time - no staged provisioner script.)
|
; staged provisioner script.)
|
||||||
Source: "{#WebDir}\*"; DestDir: "{app}\web\.output"; Flags: ignoreversion recursesubdirs createallsubdirs
|
Source: "{#WebDir}\*"; DestDir: "{app}\web\.output"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||||
Source: "{#BunExe}"; DestDir: "{app}\bun"; DestName: "bun.exe"; Flags: ignoreversion
|
|
||||||
Source: "{#WebRunCmd}"; DestDir: "{app}\web"; DestName: "web-run.cmd"; Flags: ignoreversion
|
Source: "{#WebRunCmd}"; DestDir: "{app}\web"; DestName: "web-run.cmd"; Flags: ignoreversion
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef WithScripting
|
||||||
|
; The plugin/script runner: one self-contained bundle (effect + the SDK inlined) -> {app}\scripting\
|
||||||
|
; runner-cli.js, and the launcher the (opt-in) PunktfunkScripting task runs -> {app}\scripting\
|
||||||
|
; scripting-run.cmd. Runs on the shared bun above.
|
||||||
|
Source: "{#ScriptingBundle}"; DestDir: "{app}\scripting"; DestName: "runner-cli.js"; Flags: ignoreversion
|
||||||
|
Source: "{#ScriptingRunCmd}"; DestDir: "{app}\scripting"; DestName: "scripting-run.cmd"; Flags: ignoreversion
|
||||||
|
#endif
|
||||||
#ifdef WithDriver
|
#ifdef WithDriver
|
||||||
; The driver payload + nefconc.exe + install-pf-vdisplay.ps1, extracted to {tmp} and removed after install.
|
; The driver payload + nefconc.exe + install-pf-vdisplay.ps1, extracted to {tmp} and removed after install.
|
||||||
Source: "{#StageDir}\*"; DestDir: "{tmp}\pfvdisplay"; Flags: deleteafterinstall recursesubdirs createallsubdirs; Tasks: installdriver
|
Source: "{#StageDir}\*"; DestDir: "{tmp}\pfvdisplay"; Flags: deleteafterinstall recursesubdirs createallsubdirs; Tasks: installdriver
|
||||||
@@ -255,6 +278,16 @@ Filename: "{app}\punktfunk-host.exe"; Parameters: "service start"; WorkingDir: "
|
|||||||
Filename: "{app}\punktfunk-host.exe"; Parameters: "web setup {code:WebSetupParams}{code:PublicFwParam}"; WorkingDir: "{app}"; \
|
Filename: "{app}\punktfunk-host.exe"; Parameters: "web setup {code:WebSetupParams}{code:PublicFwParam}"; WorkingDir: "{app}"; \
|
||||||
StatusMsg: "Setting up the punktfunk web console..."; Flags: runhidden waituntilterminated
|
StatusMsg: "Setting up the punktfunk web console..."; Flags: runhidden waituntilterminated
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef WithScripting
|
||||||
|
; Register the plugin/script runner's scheduled task (boot, SYSTEM, restart-on-failure) but leave it
|
||||||
|
; DISABLED - the runner is OPT-IN (inert until you add scripts/plugins). Enable it when ready:
|
||||||
|
; Enable-ScheduledTask -TaskName PunktfunkScripting
|
||||||
|
; Best-effort (-ErrorAction SilentlyContinue): a task hiccup never fails the whole install. No braces
|
||||||
|
; in the command, so no Inno {{ }} escaping needed.
|
||||||
|
Filename: "powershell.exe"; \
|
||||||
|
Parameters: "-NoProfile -ExecutionPolicy Bypass -Command ""$a=New-ScheduledTaskAction -Execute '{app}\scripting\scripting-run.cmd'; $t=New-ScheduledTaskTrigger -AtStartup; $p=New-ScheduledTaskPrincipal -UserId 'SYSTEM' -LogonType ServiceAccount -RunLevel Highest; $s=New-ScheduledTaskSettingsSet -RestartCount 999 -RestartInterval (New-TimeSpan -Minutes 1) -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries; Register-ScheduledTask -TaskName PunktfunkScripting -Action $a -Trigger $t -Principal $p -Settings $s -Force -ErrorAction SilentlyContinue | Out-Null; Disable-ScheduledTask -TaskName PunktfunkScripting -ErrorAction SilentlyContinue | Out-Null"""; \
|
||||||
|
StatusMsg: "Registering the punktfunk script runner (disabled; opt-in)..."; Flags: runhidden waituntilterminated
|
||||||
|
#endif
|
||||||
; Launch the status tray as the SIGNED-IN user (not the elevated install user) right away, so the
|
; Launch the status tray as the SIGNED-IN user (not the elevated install user) right away, so the
|
||||||
; icon appears without waiting for the next sign-in.
|
; icon appears without waiting for the next sign-in.
|
||||||
Filename: "{app}\punktfunk-tray.exe"; Flags: runasoriginaluser nowait skipifsilent; Tasks: trayicon
|
Filename: "{app}\punktfunk-tray.exe"; Flags: runasoriginaluser nowait skipifsilent; Tasks: trayicon
|
||||||
@@ -283,6 +316,13 @@ Filename: "powershell.exe"; \
|
|||||||
Parameters: "-NoProfile -ExecutionPolicy Bypass -Command ""Stop-ScheduledTask -TaskName PunktfunkWeb -ErrorAction SilentlyContinue; Get-NetTCPConnection -LocalPort 47992,3000 -State Listen -ErrorAction SilentlyContinue | ForEach-Object {{ Stop-Process -Id $_.OwningProcess -Force -ErrorAction SilentlyContinue }; Unregister-ScheduledTask -TaskName PunktfunkWeb -Confirm:$false -ErrorAction SilentlyContinue; Get-NetFirewallRule -DisplayName 'punktfunk web console (*' -ErrorAction SilentlyContinue | Remove-NetFirewallRule"""; \
|
Parameters: "-NoProfile -ExecutionPolicy Bypass -Command ""Stop-ScheduledTask -TaskName PunktfunkWeb -ErrorAction SilentlyContinue; Get-NetTCPConnection -LocalPort 47992,3000 -State Listen -ErrorAction SilentlyContinue | ForEach-Object {{ Stop-Process -Id $_.OwningProcess -Force -ErrorAction SilentlyContinue }; Unregister-ScheduledTask -TaskName PunktfunkWeb -Confirm:$false -ErrorAction SilentlyContinue; Get-NetFirewallRule -DisplayName 'punktfunk web console (*' -ErrorAction SilentlyContinue | Remove-NetFirewallRule"""; \
|
||||||
Flags: runhidden waituntilterminated; RunOnceId: "PunktfunkWebCleanup"
|
Flags: runhidden waituntilterminated; RunOnceId: "PunktfunkWebCleanup"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef WithScripting
|
||||||
|
; Stop + remove the PunktfunkScripting task (leaves %ProgramData%\punktfunk config + the operator's
|
||||||
|
; scripts/plugins, like the rest of the uninstall does). Unconditional cleanup of the task name.
|
||||||
|
Filename: "powershell.exe"; \
|
||||||
|
Parameters: "-NoProfile -ExecutionPolicy Bypass -Command ""Stop-ScheduledTask -TaskName PunktfunkScripting -ErrorAction SilentlyContinue; Unregister-ScheduledTask -TaskName PunktfunkScripting -Confirm:$false -ErrorAction SilentlyContinue"""; \
|
||||||
|
Flags: runhidden waituntilterminated; RunOnceId: "PunktfunkScriptingCleanup"
|
||||||
|
#endif
|
||||||
|
|
||||||
[Code]
|
[Code]
|
||||||
{ True if another Moonlight-compatible streaming host is installed - by its SCM service key or its
|
{ True if another Moonlight-compatible streaming host is installed - by its SCM service key or its
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# punktfunk plugin/script runner — systemd USER unit (bun runtime, OPT-IN).
|
||||||
|
#
|
||||||
|
# Runs the operator's automation under supervision: loose files in ~/.config/punktfunk/scripts/ and
|
||||||
|
# installed `punktfunk-plugin-*` packages under ~/.config/punktfunk/plugins/. Each unit is an Effect
|
||||||
|
# fiber (a plugin restarts on failure with capped-jittered backoff; a bare script is one-shot).
|
||||||
|
# SIGTERM interrupts the whole tree STRUCTURALLY, so every plugin's scoped finalizers run before
|
||||||
|
# exit (clean deregister / preset release) — hence the generous stop timeout below.
|
||||||
|
#
|
||||||
|
# OPT-IN — unlike punktfunk-web, the package does NOT auto-enable this: the runner does nothing until
|
||||||
|
# you add scripts or install plugins. Turn it on once you have automation to run:
|
||||||
|
# systemctl --user enable --now punktfunk-scripting
|
||||||
|
#
|
||||||
|
# Auto-wired like the console: a plugin's connect() reads the host's mgmt token + identity cert from
|
||||||
|
# ~/.config/punktfunk/{mgmt-token,cert.pem} (written by the host's `serve`) — no env editing.
|
||||||
|
[Unit]
|
||||||
|
Description=punktfunk plugin/script runner
|
||||||
|
Documentation=https://git.unom.io/unom/punktfunk
|
||||||
|
# Plugins talk to the host's loopback mgmt API; order after it. Soft (ordering only, no Requires):
|
||||||
|
# the runner supervises each unit with backoff, so a plugin started before the host simply retries.
|
||||||
|
After=punktfunk-host.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=/usr/bin/punktfunk-scripting
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=2
|
||||||
|
# Deliver the stop signal to the runner process itself (it orchestrates the structural shutdown of
|
||||||
|
# its unit fibers), and give it room to run their finalizers before the cgroup is reaped.
|
||||||
|
KillMode=mixed
|
||||||
|
KillSignal=SIGTERM
|
||||||
|
TimeoutStopSec=30
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
@echo off
|
||||||
|
rem punktfunk plugin/script runner launcher - the action the PunktfunkScripting scheduled task runs.
|
||||||
|
rem
|
||||||
|
rem OPT-IN: the installer registers that task DISABLED (the runner is inert until you add automation).
|
||||||
|
rem Enable it once you have scripts/plugins: Enable-ScheduledTask -TaskName PunktfunkScripting
|
||||||
|
rem
|
||||||
|
rem Lays out next to the installed payload: {app}\scripting\scripting-run.cmd + runner-cli.js and
|
||||||
|
rem {app}\bun\bun.exe (so %~dp0 = {app}\scripting\). The runner discovers the operator's units under
|
||||||
|
rem %ProgramData%\punktfunk\{scripts,plugins}; a plugin's connect() auto-wires to the host's mgmt
|
||||||
|
rem token + identity cert in %ProgramData%\punktfunk\ (written by the host's `serve`). No env editing.
|
||||||
|
setlocal EnableExtensions
|
||||||
|
|
||||||
|
set "BUN=%~dp0..\bun\bun.exe"
|
||||||
|
set "RUNNER=%~dp0runner-cli.js"
|
||||||
|
|
||||||
|
if not exist "%RUNNER%" (
|
||||||
|
echo [punktfunk-scripting] runner bundle missing at "%RUNNER%".
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%BUN%" (
|
||||||
|
echo [punktfunk-scripting] bundled bun missing at "%BUN%".
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
rem The runner import()s the operator's .ts plugin files, so it runs on the bundled bun. SIGTERM (task
|
||||||
|
rem End) interrupts the whole unit tree structurally so plugin finalizers run before exit.
|
||||||
|
"%BUN%" "%RUNNER%"
|
||||||
Reference in New Issue
Block a user