# -*- mode: python ; coding: utf-8 -*- # # clawdmeter.spec — build the standalone Windows daemon + tray executable. # # Produces a single, self-contained dist\Clawdmeter.exe that bundles its own # Python, bleak (WinRT BLE) and the winrt media projection — so it runs on ANY # Windows 11 machine with no Python and no pip install. The exe is the tray app # (daemon thread + notification-area icon + login-autostart toggle); it is built # windowed, so there is no console window and it logs to # %LOCALAPPDATA%\Clawdmeter\daemon.log. # # Build (from the repo root, inside the venv) — or just run build-exe.ps1: # .venv\Scripts\python.exe -m PyInstaller --noconfirm clawdmeter.spec # # winrt + bleak ship C-extension projections (.pyd) that PyInstaller's static # analysis misses: bleak imports its WinRT backend dynamically, and the # winrt.windows.* namespaces are split across separate distributions. collect_all # pulls in their submodules, binaries and metadata so both BLE (bluetooth) and the # now-playing media session work inside the frozen exe. Verified on hardware: # connects, reads the media session, and pushes Cyrillic now-playing payloads. from PyInstaller.utils.hooks import collect_all datas = [('firmware/src/logo.h', 'firmware/src')] # tray icon parsed at runtime binaries = [] hiddenimports = [ # Imported lazily inside tray_windows.main(), so name them explicitly. 'daemon.claude_usage_daemon_windows', 'daemon.autostart_windows', 'daemon.icon_assets', # The exact winrt media modules read_now_playing() pulls in. 'winrt.windows.media', 'winrt.windows.media.control', ] for _pkg in ('winrt', 'bleak', 'pystray', 'PIL'): _d, _b, _h = collect_all(_pkg) datas += _d binaries += _b hiddenimports += _h a = Analysis( ['daemon\\tray_windows.py'], pathex=['.'], binaries=binaries, datas=datas, hiddenimports=hiddenimports, hookspath=[], hooksconfig={}, runtime_hooks=[], excludes=['tkinter', 'pytest'], noarchive=False, optimize=0, ) pyz = PYZ(a.pure) exe = EXE( pyz, a.scripts, a.binaries, a.datas, [], name='Clawdmeter', debug=False, bootloader_ignore_signals=False, strip=False, upx=False, # UPX compression raises AV false-positive rates — leave it off upx_exclude=[], runtime_tmpdir=None, console=False, # windowed tray app: no console window; logs to daemon.log disable_windowed_traceback=False, argv_emulation=False, target_arch=None, codesign_identity=None, entitlements_file=None, )