Close unauthenticated remote mutation paths and keep filesystem destinations behind one-shot Tauri capabilities. CodeRabbit and Greptile findings were fixed on-branch; all review threads are resolved. Full CI, Security, Rust, and cross-platform smoke checks are green.
- validate managed binaries before exec; 0-byte GGUF placeholders fail
actionably instead of "Exec format error" (#1172)
- KittenTTS: tokenizer-measured chunking to the ONNX 512-token cap;
clear 400 for unspeakable input (#1173)
- clean SIGTERM during weight load: shutdown-aware loader, benign
cancelled-load classification, lifespan hardening, scoped log
silencers (transformers load + alembic fileConfig) (#1174)
- broken ASR deep-imports (lightning_fabric) mark the engine
unavailable with a repair hint and fall through (#1185)
- uv cache + managed Python follow the chosen install drive on
Windows (cherry-picked cross-drive class fix + spaces/D: tests) (#1186)
- adaptive silence-removal ladder for quiet clone references; localized
actionable error for truly silent clips, all 21 locales (#1188)
- CHANGELOG: consolidated Unreleased into the quiet one-liner style
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(settings): configurable models directory (#64)
Let users pick where model weights download (the HuggingFace / Torch
cache) instead of being pinned to ~/.cache/huggingface — useful when the
system drive is small or slow.
Backend:
- core/user_env.py: durable per-user env file (~/.config/omnivoice/env)
helper with upsert/unset that preserves other keys and writes 0600.
main.py already loads this at startup before importing torch/HF, so the
value takes effect on the next launch. Path resolves at call time via an
OMNIVOICE_ENV_FILE override so it's robust to module re-import in tests.
- settings.py: GET/PUT /api/settings/storage/models-dir — validates the
dir is writable (mkdir + write-probe → 400 if not), persists the choice,
and writes OMNIVOICE_CACHE_DIR to the durable env. Empty path clears →
reverts to default. Returns restart_required since an in-use cache can't
be safely moved mid-process. Loopback-gated like the other settings.
Frontend:
- StoragePanel: Models tab panel to view/set/reset the directory, shows
effective vs configured vs default + a restart note.
Cross-platform default parity preserved (default cache path is the HF
default on every OS); local-first (no network); backward-compatible
(absent setting → existing behavior). No version bump.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(#64): harden models-dir input + clear CodeQL hygiene flags
- settings.py: reject control/NUL chars in the path with a 400 before any
filesystem call (an embedded NUL otherwise raised ValueError → 500). Also
serves as the explicit input-validation barrier for the user-chosen path
(loopback-gated same-user local file picker — no cross-privilege boundary).
- test_user_env.py: use `with open(...)` so the file is closed and the assert
has no side effects.
- user_env.py: comment the best-effort chmod except clause.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(#64): single source of truth for models dir + review fixes
Address CodeRabbit + Greptile review on PR #149:
- P1 (both bots): the settings_store copy of the models dir was only ever
read by this GET endpoint, so it was a redundant cache that could diverge
from the durable env file (the value main.py actually reads). Drop it —
the per-user env file (OMNIVOICE_CACHE_DIR) is now the single source of
truth: PUT writes it, GET reads it back. No divergence possible.
- XDG-aware default (CodeRabbit): _default_models_dir now honors
XDG_CACHE_HOME, matching huggingface_hub's real default on Linux.
- Atomic 0600 write (Greptile, security): user_env writes via an os.open
opener that creates the file 0600 from the start — no world-readable
window before chmod for a file that can hold HF_TOKEN.
- _read_lines only swallows FileNotFoundError; other OSErrors propagate so
an upsert can't silently drop existing keys on a transient read failure.
- Guard makedirs("") when the env path is a bare filename (no parent).
- Best-effort write-probe cleanup in a finally; raise ... from e.
- a11y: label the models-dir input via aria-labelledby/aria-describedby.
- OS-neutral unwritable-dir test (mock makedirs) instead of Unix-only
/dev/null path semantics.
12 tests green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>