- build a lean onefile Launcher.exe from src/launcher.py (stdlib-only, no
PySide6/Pillow) alongside the onedir app build
- release dir is now the portable layout: Launcher.exe + app\ (CMBot.exe +
config + version.txt)
- emit two zips: CMBot-<ver>.zip (self-update payload = app\ contents, what
manifest.url points to) and CMBot-<ver>-portable.zip (initial install)
- launcher: harden logging setup for --windowed builds / read-only roots
Actual PyInstaller build must run on Windows; script is syntax-verified.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Launcher core that reuses services/update_service: seed default config into
~/.cmbot on first run, check the manifest, download the release zip, verify
SHA-256, extract, validate version, swap into app/ (keeping app.old/ for
rollback), then launch app/CMBot.exe. Degrades to the local version on any
failure; checks install-root writability before updating.
- update_service: UpdateInfo gains sha256/size/min_supported; release URL
resolved to absolute; extract make_auth_header + add download() helper
- tests/test_launcher.py: 11 tests (seed, find-root, swap, sha mismatch,
no-update); also verified end-to-end against a local HTTP server
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>