{ "id": "io.github.stepan.WayVoice", "runtime": "org.gnome.Platform", "runtime-version": "50", "sdk": "org.gnome.Sdk", "command": "wayvoice-settings", "build-options": { "build-args": [ "--share=network" ] }, "finish-args": [ "--share=ipc", "--share=network", "--socket=wayland", "--socket=fallback-x11", "--filesystem=xdg-run/pipewire-0", "--talk-name=org.freedesktop.Flatpak", "--talk-name=org.freedesktop.Notifications" ], "cleanup-commands": [ "find /app -name '__pycache__' -type d -prune -exec rm -rf {} +", "find /app -name '*.pyc' -delete", "rm -rf /app/lib/wayvoice/runtime/lib/python3*/site-packages/pip /app/lib/wayvoice/runtime/lib/python3*/site-packages/pip-*.dist-info /app/lib/wayvoice/runtime/bin/pip*", "rm -rf /app/lib/wayvoice/runtime/share" ], "modules": [ { "name": "wl-clipboard", "buildsystem": "meson", "config-opts": [ "-Dwerror=false", "-Dfishcompletiondir=no", "-Dzshcompletiondir=no" ], "cleanup": [ "/share/doc" ], "sources": [ { "type": "git", "url": "https://github.com/bugaevc/wl-clipboard.git", "tag": "v2.3.0", "commit": "67a7b937895bceec1ae5ccebb10216f63f70ca1b" } ] }, { "name": "wayvoice", "buildsystem": "simple", "sources": [ { "type": "dir", "path": ".", "skip": [ ".git", ".flatpak-builder", "build", "build-dir", "dist", "__pycache__", "*.pyc" ] } ], "build-commands": [ "# wl-clipboard is built from source instead of being pulled in as a shared", "# module: the module registry cannot be resolved from every builder, and", "# without wl-copy the text never even reaches the clipboard. It is a tiny", "# meson project whose only build dependency (wayland-client) is part of the", "# GNOME SDK. The pinned tag v2.3.0 and its commit are both recorded, so the", "# build is reproducible and cannot silently follow a moving branch.", "# fish and zsh completions are disabled: upstream installs them under", "# /usr/share, which is read-only in a build sandbox, and only /app is", "# writable (bash completions install into /app and are kept).", "# Note the cleanup list: \"*\" would delete wl-copy/wl-paste themselves,", "# because it wipes everything the module installed, not just its sources.", "#", "# Runtime/SDK: GNOME 50 is the current GNOME release, so the Flatpak is", "# built against it and gets the newest GTK4/libadwaita; branch 49 is", "# still installed on this host and would work as a fallback, but it is", "# an already superseded GNOME. The runtime also ships everything the", "# engine needs at run time: python3 and the PipeWire tools.", "#", "# Two processes, two entry points (see app/src/wayvoice/cli.py):", "# wayvoice-daemon -> wayvoice.daemon, the background dictation service", "# wayvoice-settings -> wayvoice.ui, the GTK4/libadwaita window", "# `command` starts the settings window, because that is the process a", "# launcher has to open: Flatpak appends the arguments of", "# `flatpak run ...` AFTER the command, and it rewrites the exported", "# desktop entry into `flatpak run wayvoice-settings`. With the", "# window as the command both entry points keep working, whereas a daemon", "# command would swallow that argument and only start a daemon.", "# The daemon is started explicitly, in its own sandbox instance:", "# flatpak run --command=wayvoice-daemon io.github.stepan.WayVoice", "#", "# Layout: same shape as scripts/build-deb.sh, rooted at /app:", "# /app/bin/* the POSIX-sh wrappers (they look the", "# sources up relative to themselves)", "# /app/lib/wayvoice/app copy of app/ (pyproject.toml, src/)", "# /app/lib/wayvoice/runtime prebuilt Faster-Whisper venv (below)", "# /app/share/systemd/user the four user units", "# /app/share/applications the desktop entry", "# /app/share/metainfo the AppStream metainfo", "# /app/share/icons/hicolor/... scalable + symbolic app icons", "# /app/share/polkit-1/actions the manage-deps policy", "# /app/share/doc/wayvoice README, CHANGELOG, LICENSE", "# /app/bin + /app/lib/wayvoice/app/src is exactly the first candidate", "# the wrappers probe (/../lib/wayvoice/app/src), and paths.py", "# derives the same root from parents[2] of the module, so nothing in the", "# project needs to know about /app.", "install -d /app/bin /app/lib/wayvoice", "cp -a app /app/lib/wayvoice/app", "find /app/lib/wayvoice/app -name '__pycache__' -type d -prune -exec rm -rf {} +", "find /app/lib/wayvoice/app -name '*.pyc' -delete", "install -m 0755 scripts/wayvoice scripts/wayvoice-daemon scripts/wayvoice-settings scripts/wayvoice-engine-setup scripts/setup-user /app/bin/", "install -d /app/share/systemd/user", "install -m 0644 systemd/wayvoice.service systemd/wayvoice-setup.service systemd/wayvoice-engine-setup.service systemd/wayvoice-ydotool.service /app/share/systemd/user/", "install -d /app/share/applications /app/share/metainfo", "install -m 0644 data/io.github.stepan.WayVoice.desktop /app/share/applications/io.github.stepan.WayVoice.desktop", "install -m 0644 data/io.github.stepan.WayVoice.metainfo.xml /app/share/metainfo/io.github.stepan.WayVoice.metainfo.xml", "install -d /app/share/icons/hicolor/scalable/apps /app/share/icons/hicolor/symbolic/apps", "install -m 0644 data/icons/hicolor/scalable/apps/io.github.stepan.WayVoice.svg /app/share/icons/hicolor/scalable/apps/io.github.stepan.WayVoice.svg", "install -m 0644 data/icons/hicolor/symbolic/apps/io.github.stepan.WayVoice-symbolic.svg /app/share/icons/hicolor/symbolic/apps/io.github.stepan.WayVoice-symbolic.svg", "# The udev rule (data/80-wayvoice-uinput.rules) is deliberately NOT", "# installed: it hands /dev/uinput to the logged-in user, which is a", "# host concern - a Flatpak cannot add rules to the host udev database.", "#", "# The polkit policy is installed for parity with the Debian package, but", "# it has no effect here: pkexec does not exist inside the sandbox and a", "# Flatpak cannot register a polkit action on the host. `wayvoice deps", "# --install` and the matching button in the settings window are", "# therefore unavailable in the Flatpak build, which the application", "# already handles - pkgsys hides the button when pkexec is missing.", "# Nothing is left to install in the sandbox anyway: wl-copy comes from", "# the wl-clipboard module, and notify-send plus the pw-* tools are part", "# of org.gnome.Platform itself.", "install -d /app/share/polkit-1/actions", "install -m 0644 packaging/io.github.stepan.WayVoice.manage-deps.policy /app/share/polkit-1/actions/io.github.stepan.WayVoice.manage-deps.policy", "install -d /app/share/doc/wayvoice", "install -m 0644 README.md CHANGELOG.md LICENSE /app/share/doc/wayvoice/", "# The user units are shipped so that the file set matches the Debian", "# package, but nothing can start them: org.gnome.Platform ships no", "# systemctl at all, and the host user manager cannot see /app. What", "# still works is service.py, which falls back to spawning the daemon", "# directly when no user manager answers - that is what lets the window", "# work in the sandbox. `wayvoice engine-setup` likewise prepares the", "# runtime in-process when systemctl is missing. setup-user still cannot", "# do its job: it applies the GSettings shortcut and enables the ydotoold", "# unit through systemctl, both of which fail here.", "#", "# ydotool is intentionally neither shipped nor emulated: the client", "# needs /dev/uinput and the ydotoold daemon, and Flatpak exposes", "# neither. Auto-paste degrades to \"the text ends up in the clipboard\"", "# in the Flatpak build. That is the expected sandbox behaviour, not a", "# packaging bug, and it cannot be worked around without asking the", "# user for a privileged host helper.", "#", "# Engine, baked into the image so that the first dictation needs neither", "# pip nor a network connection. The version bounds are copied verbatim", "# from app/src/wayvoice/engine_setup.py:", "# av>=11,<19 faster-whisper <= 1.2.x still calls", "# av.open(..., metadata_errors=...); PyAV 19", "# removed that argument, hence the upper bound", "# faster-whisper>=1.1.1,<2 first release of the supported 1.x line", "# ctranslate2, tokenizers, onnxruntime, numpy and huggingface-hub arrive", "# transitively. The venv uses the SDK interpreter, the same one that runs", "# the app, so engine and application share a single Python.", "# pyvenv.cfg therefore records /usr/bin as its home, which stays correct", "# at run time: the SDK is mounted at /usr during the build and the", "# merged-usr runtime is mounted at /usr inside the app sandbox as well", "# (with /bin symlinked to usr/bin), so bin/python3 keeps resolving.", "# Wheel downloads need network during the build only, which is why the", "# manifest asks for it explicitly through build-options.build-args", "# (--share=network).", "#", "# The venv is placed next to the app sources, i.e. /app/lib/wayvoice/runtime,", "# and engine.py looks there first (paths.app_dir().parent / \"runtime\",", "# only when it really holds bin/python), falling back to the per-user", "# $XDG_DATA_HOME/wayvoice/runtime it creates itself. Inside a Flatpak", "# sandbox XDG_DATA_HOME is ~/.var/app//data, a directory that only", "# exists at run time, so the per-user path could not be used for a", "# prebuilt engine: this is what makes the image work offline.", "# WAYVOICE_RUNTIME overrides both, for unusual layouts.", "python3 -m venv /app/lib/wayvoice/runtime", "/app/lib/wayvoice/runtime/bin/python -m pip install --disable-pip-version-check --no-input --upgrade 'av>=11,<19' 'faster-whisper>=1.1.1,<2'", "/app/lib/wayvoice/runtime/bin/python -c \"import av, faster_whisper; m = int(av.__version__.split('.')[0]); assert m < 19, av.__version__; print('PyAV', av.__version__, 'faster-whisper', faster_whisper.__version__)\"", "touch /app/lib/wayvoice/runtime/.engine-v1-ready", "# cleanup: the __pycache__/.pyc trees pip leaves behind plus pip itself are", "# build leftovers and nothing else is generated in the image. The", "# cleanup section uses cleanup-commands because cleanup takes paths,", "# not shell lines.", "#", "# finish-args, one by one:", "# --share=ipc the settings window and the", "# pasted text reach XWayland", "# clients only with the X11", "# IPC socket pair shared", "# --share=network the only outbound connection", "# WayVoice ever makes: fetching", "# model weights from", "# huggingface.co (model_fetch.", "# py, the download the settings", "# window and `wayvoice model", "# --download` start). Needed at", "# run time, not only at build", "# time: verified that without", "# it resolving huggingface.co", "# fails inside the sandbox with", "# gaierror, so the download", "# button cannot work. The", "# permission is broader than the", "# purpose - recognition itself", "# is entirely local and never", "# touches the network.", "# --socket=wayland native display for the GTK4", "# window", "# --socket=fallback-x11 the window also opens on an", "# X11-only session", "# --filesystem=xdg-run/pipewire-0 the microphone. pw-record", "# (audio.py) speaks the", "# PipeWire protocol and needs", "# $XDG_RUNTIME_DIR/pipewire-0", "# from the host; Flatpak has", "# no --socket=pipewire", "# permission, so the socket is", "# mapped in explicitly. If the", "# session has no PipeWire the", "# mapping is skipped with a", "# warning instead of breaking", "# the launch.", "# --talk-name=org.freedesktop.Flatpak the portal bus name Flatpak", "# itself uses (flatpak-spawn", "# --host, portal fallbacks).", "# Nothing in the code calls it", "# today; it is granted so that", "# a host-side helper keeps", "# working if one is ever needed", "# --talk-name=org.freedesktop.Notifications notify-send (notify.py)", "# talks this name directly", "# and Flatpak blocks it by", "# default", "# NOT requested, on purpose:", "# --socket=pulseaudio nothing in WayVoice speaks", "# PulseAudio; Flatpak would", "# also hand over /dev/snd", "# through it, which is not", "# needed once pipewire-0 is", "# mapped", "# --device=all verified unnecessary: with", "# --filesystem=xdg-run/", "# pipewire-0 pw-record already", "# records without /dev/snd, and", "# it is the only conceivable", "# route to /dev/uinput, which", "# stays useless without", "# ydotoold on the host", "# --filesystem=xdg-data/wayvoice:create XDG_DATA_HOME is", "# ~/.var/app//data inside", "# the sandbox; the host", "# ~/.local/share/wayvoice is", "# never read, so granting it", "# would expose a directory the", "# app does not use", "# --talk-name=org.gnome.Shell the private GNOME Shell bus", "# name; WayVoice ships no Shell", "# extension and never calls it,", "# notifications arrive through", "# org.freedesktop.Notifications", "true" ] } ] }