feat: Visible wizard runs, one-step VM reset and screenshots; skill updates

- scripts/vmreset.sh, vmwatch.sh (wizard in a visible Konsole), vmshot.sh.
- vmstate.sh: DKMS per kernel, DKMS override, headers service, dark LnF, wallpaper, layout backup.
- cmp.sh: also kcminputrc and ksplashrc.
- Skill: pitfalls and checks from testing cachyos-gamescope-boot 0.7.0.
This commit is contained in:
theupriser committed 2026-09-24 10:05:46 +02:00
1 parent d2cf240f66
commit b4114a1732
7 files changed
+164 -6

No files matched your search

+61 -4
View File
@@ -9,16 +9,35 @@ The wizard (`setup-gamescope-boot.sh` + `lib/*.sh`) opens a menu that detects
which components are on and toggles them to match the user's choice. Menu order: which components are on and toggles them to match the user's choice. Menu order:
1. SteamOS conversion (boot into gaming mode via autologin, Return to Gaming Mode shortcut, Steam desktop autostart) 1. SteamOS conversion (boot into gaming mode via autologin, Return to Gaming Mode shortcut, Steam desktop autostart)
2. SteamOS theme (Vapor) 2. SteamOS theme (installs `cachyos-vapor` and applies the Vapor global theme with its desktop and window layout; needs a running Plasma session)
3. Steam Deck/Machine icons (`STEAM_GAMEPADUI_ARGS -steamos3`) 3. Steam Deck/Machine icons (`STEAM_GAMEPADUI_ARGS -steamos3`)
4. Single user mode (SDDM, no lock screen/user switching/log out; enabling it enables 1, disabling 1 disables it) 4. Single user mode (SDDM, no lock screen/user switching/log out; enabling it enables 1, disabling 1 disables it)
5. Steam Machine support (only on DMI Valve/Fremont: leds-valve-dkms-git, udev rule, steamos-manager) 5. Steam Machine support (only on DMI Valve/Fremont: leds-valve-dkms-git built for every kernel via `/etc/dkms/leds-valve-dkms.conf`, `ensure-kernel-headers.service`, udev rule, steamos-manager)
Undo journals live in `~/.local/state/cachyos-gamescope-boot/` in the guest. Undo journals live in `~/.local/state/cachyos-gamescope-boot/` in the guest.
Run everything from the root of this repo on the host. Defaults: user Run everything from the root of this repo on the host. Defaults: user
`theupriser`, SSH port `2222` (the scripts take `VM_USER` / `VM_PORT` / `VM_HOST`). `theupriser`, SSH port `2222` (the scripts take `VM_USER` / `VM_PORT` / `VM_HOST`).
The VM's disk, `vars*.fd` and `run.sh` copy may live outside the repo
(`~/vms/cachyos-test` on the main dev machine). Find it with
`readlink /proc/$(pgrep -f '^qemu-system')/cwd` when the VM runs; the
snapshot commands below run in that directory. `scripts/vmreset.sh` takes
`VM_DIR` and defaults to the repo if it holds `disk.qcow2`, else `~/vms/cachyos-test`.
## Quick start (the usual loop)
```bash
scripts/vmreset.sh --fremont # restore ssh-ready, boot, mount repo, autologin, wait for Plasma
scripts/cmp.sh save # baseline of the KDE configs
scripts/vmwatch.sh '1\n3\n\ny\nn\n' "Theme only" # wizard in a visible Konsole in the VM
scripts/vmstate.sh # component state
scripts/vmshot.sh /path/shot.png # screenshot, then view it
```
Prefer `vmwatch.sh` over `vmrun.sh` when the user is watching the QEMU
window: `vmrun.sh` runs invisibly over SSH, so they see nothing happen.
## VM lifecycle ## VM lifecycle
Start in the background: Start in the background:
@@ -68,6 +87,10 @@ sudoers rule for the test user.
## Mounting the project repo ## Mounting the project repo
The mount is lost on every reboot (unless the fstab line below was added).
Without it the wizard fails with `/mnt/setup-gamescope-boot.sh: No such file
or directory`, which is easy to miss in filtered output. Remount after each reboot.
The repo (`REPO`, default `~/projects/cachyos-gamescope-boot`) is shared The repo (`REPO`, default `~/projects/cachyos-gamescope-boot`) is shared
read-write as 9p tag `repo`. The `ssh-ready` snapshot does not mount it: read-write as 9p tag `repo`. The `ssh-ready` snapshot does not mount it:
@@ -124,9 +147,24 @@ restart question.
| `'2\n\ny\nn\n'` | toggle the theme | | `'2\n\ny\nn\n'` | toggle the theme |
| `'a\ny\nn\n'` | re-apply what is on | | `'a\ny\nn\n'` | re-apply what is on |
| `'q\n'` | just show the menu | | `'q\n'` | just show the menu |
| `'1\n3\n\ny\nn\n'` | from all-off: theme only (Steam Machine too with `--fremont`; add `5\n` to drop it) |
| `'1\n\ny\nn\n'` | from a state where 1 is off: turn the conversion on |
**Know the starting ticks before choosing input.** When *everything* is off
(fresh snapshot, or after turning the last component off), the menu treats it
as a first run and pre-ticks **all** components, so `2` means "theme off,
rest on". Otherwise the ticks show what is on now. Unticking 1 also unticks 4,
and ticking 4 ticks 1 again, so `'1\n3\n4\n...'` ends with the conversion on.
When unsure, run `'q\n'` first and read the ticks.
`a` only re-applies components that are already on: it does not retry one that
failed. Re-applying the conversion also resets the autologin session to
gamescope, so set it back to plasma before the next reboot (below).
## Gamescope does not render in this VM ## Gamescope does not render in this VM
(Also after any re-apply that includes the conversion.)
No suitable Vulkan (venus is unstable with the host NVIDIA driver). After No suitable Vulkan (venus is unstable with the host NVIDIA driver). After
enabling the conversion, before rebooting: enabling the conversion, before rebooting:
@@ -157,6 +195,11 @@ DM, set the session, start it).
`scripts/cmp.sh save` stores a baseline of the KDE/GTK configs in the guest `scripts/cmp.sh save` stores a baseline of the KDE/GTK configs in the guest
(`~/.cache/vm-baseline`, not `/tmp`); `scripts/cmp.sh` diffs against it. (`~/.cache/vm-baseline`, not `/tmp`); `scripts/cmp.sh` diffs against it.
After changing the theme, `cmp.sh` should show only spectacle's
`kglobalshortcutsrc` entries (from screenshots) and keys whose value equals
`~/.config/kdedefaults` (e.g. `ColorScheme=BreezeDark`, `widgetStyle=Breeze`):
KDE drops or writes those on its own.
## Reboot checks ## Reboot checks
- Single user mode on: SDDM autologin without greeter: `pgrep sddm-greeter` empty, `plasmashell` running. - Single user mode on: SDDM autologin without greeter: `pgrep sddm-greeter` empty, `plasmashell` running.
@@ -170,20 +213,27 @@ autologin, then (all passed last run; `scripts/vmstate.sh` after every step):
1. Fresh run turning everything on (`'\ny\nn\n'`), set session to plasma, reboot. 1. Fresh run turning everything on (`'\ny\nn\n'`), set session to plasma, reboot.
2. Rerun: all shown on, "Everything is already the way you want it". 2. Rerun: all shown on, "Everything is already the way you want it".
3. `a` re-apply: no duplicate journal entries. 3. `a` re-apply: no duplicate journal entries.
4. Theme off: look restored (CachyOS wallpaper, floating 30px panel, `org.cachyos.hello` launcher icon). 4. Theme on: Steam Deck wallpaper, full-width 46px panel, `distributor-logo-steamdeck` launcher icon, `dark-lnf=com.valve.vapor.desktop` (Brightness & Color's Dark Mode toggle is on, hint "Switch to Breeze"); with single user on, kickoff keeps `primaryActions=3`. Theme off: CachyOS wallpaper, floating 30px panel, CachyOS launcher icon, BreezeDark colors (not light), `cachyos-vapor` removed, `cmp.sh` clean.
5. Single user off: switches to plasmalogin + sync bridge + sudoers; shortcut Exec uses `sudo -n`. 5. Single user off: switches to plasmalogin + sync bridge + sudoers; shortcut Exec uses `sudo -n`.
6. Single user on: back to SDDM. 6. Single user on: back to SDDM.
7. Icons + Steam Machine support off: driver, udev rule, modules-load, steamos-manager removed; yay kept. 7. Icons + Steam Machine support off: driver, udev rule, modules-load, steamos-manager removed; yay kept.
7b. DKMS: `vmstate.sh` shows `installed` for every kernel. Headers at boot:
`sudo pacman -R --noconfirm linux-cachyos-lts-headers` (DKMS drops the LTS
build), set the session to plasma, reboot, then `journalctl -b -u
ensure-kernel-headers` shows the install and DKMS lists LTS again.
8. Conversion off: single user auto-unticked, plasmalogin `[Autologin]` back to CachyOS's `Session=plasma`, journals empty. 8. Conversion off: single user auto-unticked, plasmalogin `[Autologin]` back to CachyOS's `Session=plasma`, journals empty.
9. Remove the test autologin file, reboot: normal login screen. 9. Remove the test autologin file, reboot: normal login screen.
## Visual checks ## Visual checks
`scripts/vmshot.sh <out.png>` does the below. Starting `spectacle` straight
from SSH core-dumps; it has to run as a user unit (`systemd-run --user --wait`).
```bash ```bash
ssh -p 2222 -o BatchMode=yes theupriser@localhost bash -s << 'EOF' ssh -p 2222 -o BatchMode=yes theupriser@localhost bash -s << 'EOF'
export XDG_RUNTIME_DIR=/run/user/1000 WAYLAND_DISPLAY=wayland-0 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus export XDG_RUNTIME_DIR=/run/user/1000 WAYLAND_DISPLAY=wayland-0 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
qdbus6 org.kde.plasmashell /PlasmaShell org.kde.PlasmaShell.activateLauncherMenu # optional, toggles qdbus6 org.kde.plasmashell /PlasmaShell org.kde.PlasmaShell.activateLauncherMenu # optional, toggles
sleep 1; spectacle -b -n -f -o /tmp/x.png sleep 1; systemd-run --user --wait -q spectacle -b -n -f -o /tmp/x.png
EOF EOF
scp -P 2222 theupriser@localhost:/tmp/x.png /tmp/x.png # then view it scp -P 2222 theupriser@localhost:/tmp/x.png /tmp/x.png # then view it
``` ```
@@ -194,6 +244,13 @@ them with `systemd-run --user <app>`.
## Pitfalls ## Pitfalls
- Broken mirror: `mirror5.krfoss.org` served a bad `.sig` ("Maximum file size
exceeded"), failing the conversion's package install. Comment it out:
`sudo sed -i '/krfoss/s/^Server/#Server/' /etc/pacman.d/*mirrorlist*`.
- Windows open in the snapshot (System Settings, CachyOS Hello) show stale
data (e.g. a theme list from before `cachyos-vapor`) and cover screenshots;
`vmreset.sh` closes them.
- The guest's `/tmp` is cleared on reboot; keep baselines elsewhere. - The guest's `/tmp` is cleared on reboot; keep baselines elsewhere.
- The fake Fremont DMI makes leds-valve load 17 LED nodes, but there is no real hardware. - The fake Fremont DMI makes leds-valve load 17 LED nodes, but there is no real hardware.
- shellcheck: `sudo pacman -S shellcheck`, then in `/mnt`: - shellcheck: `sudo pacman -S shellcheck`, then in `/mnt`:
+3
View File
@@ -106,7 +106,10 @@ read-write as 9p tag `repo`; in the guest:
## Helper scripts ## Helper scripts
- `scripts/vmreset.sh [--fremont]`: restore `ssh-ready`, boot, mount the repo, autologin into Plasma
- `scripts/vmrun.sh '<menu input>'`: run the wizard in the guest's Plasma session with scripted input - `scripts/vmrun.sh '<menu input>'`: run the wizard in the guest's Plasma session with scripted input
- `scripts/vmwatch.sh '<menu input>' [label]`: same, but in a visible Konsole window in the VM
- `scripts/vmshot.sh <out.png>`: screenshot the guest's desktop
- `scripts/vmstate.sh`: print the state of every wizard component - `scripts/vmstate.sh`: print the state of every wizard component
- `scripts/cmp.sh [save]`: save / diff the guest's KDE configs against a baseline - `scripts/cmp.sh [save]`: save / diff the guest's KDE configs against a baseline
+1 -1
View File
@@ -10,7 +10,7 @@ mode="${1:-diff}"
# The guest login shell is fish, so pass values in through the bash script itself. # The guest login shell is fish, so pass values in through the bash script itself.
{ printf 'mode=%q base=%q\n' "$mode" "${BASELINE:-}"; cat << 'REMOTE' { printf 'mode=%q base=%q\n' "$mode" "${BASELINE:-}"; cat << 'REMOTE'
base="${base:-$HOME/.cache/vm-baseline}" base="${base:-$HOME/.cache/vm-baseline}"
files="kdeglobals plasmarc plasmashellrc kwinrc kwinrulesrc konsolerc kscreenlockerrc kglobalshortcutsrc gtk-3.0/settings.ini gtk-4.0/settings.ini plasma-org.kde.plasma.desktop-appletsrc" files="kdeglobals kcminputrc ksplashrc plasmarc plasmashellrc kwinrc kwinrulesrc konsolerc kscreenlockerrc kglobalshortcutsrc gtk-3.0/settings.ini gtk-4.0/settings.ini plasma-org.kde.plasma.desktop-appletsrc"
if [ "$mode" = save ]; then if [ "$mode" = save ]; then
for f in $files; do mkdir -p "$(dirname "$base/config/$f")"; cp -f ~/.config/"$f" "$base/config/$f" 2>/dev/null || true; done for f in $files; do mkdir -p "$(dirname "$base/config/$f")"; cp -f ~/.config/"$f" "$base/config/$f" 2>/dev/null || true; done
echo "baseline saved to $base"; exit 0 echo "baseline saved to $base"; exit 0
+45
View File
@@ -0,0 +1,45 @@
#!/bin/bash
# Reset the test VM to a snapshot and bring it up logged in to Plasma:
# power off, restore the snapshot (disk + vars.fd), start it, mount the repo
# on /mnt, set up the test autologin and wait for plasmashell.
# scripts/vmreset.sh [run.sh flags...] e.g. scripts/vmreset.sh --fremont
# Env: VM_USER / VM_PORT / VM_HOST, SNAPSHOT (ssh-ready),
# VM_DIR (where disk.qcow2 lives: this repo if it has one, else ~/vms/cachyos-test),
# VM_LOG (QEMU output, default $VM_DIR/vm.log).
set -euo pipefail
. "$(dirname "$0")/common.sh"
repo="$(cd "$(dirname "$0")/.." && pwd)"
if [[ -z "${VM_DIR:-}" ]]; then
if [[ -f "$repo/disk.qcow2" ]]; then VM_DIR="$repo"; else VM_DIR="$HOME/vms/cachyos-test"; fi
fi
SNAPSHOT="${SNAPSHOT:-ssh-ready}"
VM_LOG="${VM_LOG:-$VM_DIR/vm.log}"
if pgrep -f '^qemu-system' >/dev/null; then
echo "Powering off the running VM..."
vm_ssh sudo systemctl poweroff 2>/dev/null || true
for _ in $(seq 60); do pgrep -f '^qemu-system' >/dev/null || break; sleep 2; done
pgrep -f '^qemu-system' >/dev/null && { echo "VM didn't shut down; stop it first." >&2; exit 1; }
fi
echo "Restoring snapshot '$SNAPSHOT' in $VM_DIR..."
cd "$VM_DIR"
qemu-img snapshot -a "$SNAPSHOT" disk.qcow2
cp "vars.$SNAPSHOT.fd" vars.fd
echo "Starting the VM ($*)..."
nohup ./run.sh "$@" > "$VM_LOG" 2>&1 &
for _ in $(seq 100); do vm_ssh -o ConnectTimeout=3 true 2>/dev/null && break; sleep 3; done
vm_ssh true || { echo "No SSH after 5 minutes; see $VM_LOG" >&2; exit 1; }
vm_ssh bash -s << 'REMOTE'
sudo mount -t 9p -o trans=virtio,version=9p2000.L repo /mnt
sudo mkdir -p /etc/plasmalogin.conf.d
printf '[Autologin]\nUser=%s\nSession=plasma.desktop\n' "$USER" | sudo tee /etc/plasmalogin.conf.d/00-test-autologin.conf >/dev/null
sudo systemctl restart plasmalogin
for _ in $(seq 40); do pgrep -u "$USER" -x plasmashell >/dev/null && break; sleep 3; done
sleep 10
# Windows the snapshot opens at login would cover the screenshots.
pkill -u "$USER" systemsettings; pkill -u "$USER" cachyos-hello
pgrep -u "$USER" -x plasmashell >/dev/null && echo "Logged in to Plasma." || { echo "Plasma did not start." >&2; exit 1; }
REMOTE
+18
View File
@@ -0,0 +1,18 @@
#!/bin/bash
# Screenshot the guest's Plasma session to a file on the host.
# scripts/vmshot.sh <out.png>
# spectacle started straight from SSH crashes; run it as a user unit so it
# gets the session's environment.
# Env: VM_USER (theupriser), VM_PORT (2222), VM_HOST (localhost), GUEST_UID (1000).
set -euo pipefail
. "$(dirname "$0")/common.sh"
out="${1:?usage: $0 <out.png>}"
{ printf 'uid=%q\n' "${GUEST_UID:-1000}"; cat << 'REMOTE'
export XDG_RUNTIME_DIR=/run/user/$uid DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$uid/bus
rm -f /tmp/vmshot.png
sleep 2
systemd-run --user --wait -q spectacle -b -n -f -o /tmp/vmshot.png
REMOTE
} | vm_ssh bash -s
scp -q -P "$VM_PORT" "$VM_USER@$VM_HOST:/tmp/vmshot.png" "$out"
echo "$out"
+4 -1
View File
@@ -9,11 +9,14 @@ echo "DM=$(systemctl show -p Id --value display-manager) sddm-autologin=$(yn /et
echo "plasmalogin [Autologin]: $(sed -n '/^\[Autologin\]/,/^\[/p' /etc/plasmalogin.conf | grep -v '^\[' | tr '\n' ' ')" echo "plasmalogin [Autologin]: $(sed -n '/^\[Autologin\]/,/^\[/p' /etc/plasmalogin.conf | grep -v '^\[' | tr '\n' ' ')"
echo "shortcut=$(grep -h Exec= ~/Desktop/*Gaming*.desktop 2>/dev/null || echo none)" echo "shortcut=$(grep -h Exec= ~/Desktop/*Gaming*.desktop 2>/dev/null || echo none)"
echo "steam-unit=$([ -f ~/.config/systemd/user/steam-desktop-autostart.service ] && systemctl --user is-enabled steam-desktop-autostart.service || echo none) glyphs=$(yn ~/.config/environment.d/99-gamescope-steam-glyphs.conf)" echo "steam-unit=$([ -f ~/.config/systemd/user/steam-desktop-autostart.service ] && systemctl --user is-enabled steam-desktop-autostart.service || echo none) glyphs=$(yn ~/.config/environment.d/99-gamescope-steam-glyphs.conf)"
echo "lnf=$(kreadconfig6 --file kdeglobals --group KDE --key LookAndFeelPackage) scheme=$(kreadconfig6 --file kdeglobals --group General --key ColorScheme) gtk=$(kreadconfig6 --file ~/.config/gtk-3.0/settings.ini --group Settings --key gtk-theme-name) font=$(kreadconfig6 --file kdeglobals --group General --key font)" echo "lnf=$(kreadconfig6 --file kdeglobals --group KDE --key LookAndFeelPackage) dark-lnf=$(kreadconfig6 --file kdeglobals --group KDE --key DefaultDarkLookAndFeel) scheme=$(kreadconfig6 --file kdeglobals --group General --key ColorScheme) cursor=$(kreadconfig6 --file kcminputrc --group Mouse --key cursorTheme) vapor-pkg=$(pacman -Q cachyos-vapor 2>/dev/null || echo none) layout-backup=$(yn ~/.local/state/cachyos-gamescope-boot/theme-layout)"
echo "wallpaper: $(grep -h '^Image=' ~/.config/plasma-org.kde.plasma.desktop-appletsrc | sort -u | tr '\n' ' ')"
echo "lock_screen=$(kreadconfig6 --file kdeglobals --group 'KDE Action Restrictions' --key action/lock_screen) autolock=$(kreadconfig6 --file kscreenlockerrc --group Daemon --key Autolock) metaL=$(kreadconfig6 --file kglobalshortcutsrc --group ksmserver --key 'Lock Session' | cut -c1-12)" echo "lock_screen=$(kreadconfig6 --file kdeglobals --group 'KDE Action Restrictions' --key action/lock_screen) autolock=$(kreadconfig6 --file kscreenlockerrc --group Daemon --key Autolock) metaL=$(kreadconfig6 --file kglobalshortcutsrc --group ksmserver --key 'Lock Session' | cut -c1-12)"
echo "panel: $(grep -A1 '^\[PlasmaViews\]\[Panel [0-9]*\]$' ~/.config/plasmashellrc | grep floating | tr '\n' ' ') $(grep -h 'thickness' ~/.config/plasmashellrc | tr '\n' ' ')" echo "panel: $(grep -A1 '^\[PlasmaViews\]\[Panel [0-9]*\]$' ~/.config/plasmashellrc | grep floating | tr '\n' ' ') $(grep -h 'thickness' ~/.config/plasmashellrc | tr '\n' ' ')"
echo "kickoff: $(grep -E '^(primaryActions|systemFavorites|icon)=' ~/.config/plasma-org.kde.plasma.desktop-appletsrc | tr '\n' ' ')" echo "kickoff: $(grep -E '^(primaryActions|systemFavorites|icon)=' ~/.config/plasma-org.kde.plasma.desktop-appletsrc | tr '\n' ' ')"
echo "leds-pkg=$(pacman -Q leds-valve-dkms-git 2>/dev/null || echo none) steamos-manager=$(pacman -Q steamos-manager 2>/dev/null || echo none)/$(systemctl is-active steamos-manager 2>/dev/null) udev=$(yn /etc/udev/rules.d/70-valve-leds-user.rules) module=$(lsmod | grep -c leds_valve) leds=$(ls /sys/class/leds | grep -c valve)" echo "leds-pkg=$(pacman -Q leds-valve-dkms-git 2>/dev/null || echo none) steamos-manager=$(pacman -Q steamos-manager 2>/dev/null || echo none)/$(systemctl is-active steamos-manager 2>/dev/null) udev=$(yn /etc/udev/rules.d/70-valve-leds-user.rules) module=$(lsmod | grep -c leds_valve) leds=$(ls /sys/class/leds | grep -c valve)"
echo "dkms: $(dkms status leds-valve-dkms 2>/dev/null | cut -d, -f2- | sed 's/, x86_64//' | tr '\n' ';') kernels: $(ls /usr/lib/modules | tr '\n' ' ')"
echo "dkms-override=$(yn /etc/dkms/leds-valve-dkms.conf) headers-check=$(systemctl is-enabled ensure-kernel-headers.service 2>/dev/null || echo none)"
echo "leds-owner=$(stat -c %U /sys/class/leds/valve-leds*/brightness 2>/dev/null | sort -u | tr '\n' ' ')" echo "leds-owner=$(stat -c %U /sys/class/leds/valve-leds*/brightness 2>/dev/null | sort -u | tr '\n' ' ')"
j=~/.local/state/cachyos-gamescope-boot j=~/.local/state/cachyos-gamescope-boot
echo "journals: $(ls "$j" 2>/dev/null | tr '\n' ' ')" echo "journals: $(ls "$j" 2>/dev/null | tr '\n' ' ')"
+32
View File
@@ -0,0 +1,32 @@
#!/bin/bash
# Like vmrun.sh, but run the wizard in a visible Konsole window inside the
# guest's Plasma session, so someone at the QEMU window can follow along.
# Waits for the wizard to finish, then prints its log. The window stays open.
# scripts/vmwatch.sh '<menu input>' ["label shown in the window"]
# Env: VM_USER (theupriser), VM_PORT (2222), VM_HOST (localhost),
# GUEST_REPO (/mnt), GUEST_UID (1000), WAIT (seconds, default 1800).
set -euo pipefail
. "$(dirname "$0")/common.sh"
input="${1:?usage: $0 '<menu input>' [label]}"
label="${2:-Running the wizard}"
# The guest login shell is fish, so pass values in through the bash script itself.
{ printf 'input=%q label=%q repo=%q uid=%q wait=%q\n' "$input" "$label" "${GUEST_REPO:-/mnt}" \
"${GUEST_UID:-1000}" "${WAIT:-1800}"; cat << 'REMOTE'
export XDG_RUNTIME_DIR=/run/user/$uid DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$uid/bus
[ -x "$repo/setup-gamescope-boot.sh" ] || { echo "$repo/setup-gamescope-boot.sh missing: mount the repo (lost after a reboot)" >&2; exit 1; }
rm -f /tmp/wizard.done
{
echo '#!/bin/bash'
printf 'echo %q; echo %q; sleep 3\n' ">>> $label" ">>> menu input: $input"
# shellcheck disable=SC2016
printf 'printf %q | %q 2>&1 | tee /tmp/wizard.log\n' "$input" "$repo/setup-gamescope-boot.sh"
echo 'touch /tmp/wizard.done'
} > /tmp/vmwatch-run.sh
chmod +x /tmp/vmwatch-run.sh
pkill -u "$USER" -x konsole || true
systemd-run --user -q konsole --hold -e /tmp/vmwatch-run.sh
for _ in $(seq $((wait / 2))); do [ -f /tmp/wizard.done ] && break; sleep 2; done
[ -f /tmp/wizard.done ] || echo "(still running after ${wait}s)"
sed 's/\x1b\[[0-9;]*m//g' /tmp/wizard.log
REMOTE
} | vm_ssh bash -s 2>&1