3.8 KiB
electron/CLAUDE.md
Electron 40.x (CastLabs DRM build) desktop shell + auto-updater via GitHub Releases. Entry: main.js. Version is in package.json. See root CLAUDE.md for repo-wide constraints.
Coding precedences
Full precedences live in root CLAUDE.md. Always: understand the end goal before coding (what does the user actually need?); reuse before you write (grep existing IPC handlers / helpers in main.js, most needs already have one); ~300 LOC/file ceiling; downward-tree imports; comments only when necessary (the non-obvious WHY), one line each; no em-dashes or en-dashes anywhere (—, –); say IDK to the user when you don't know, then go find out; test the packaged build path after meaningful changes (not just dev); weigh speed (startup time), efficiency (memory), robustness (auto-updater, OAuth windows), UX, and security (signed binaries, no plaintext secrets) on every change.
Build / release
- Local build:
npm run buildproducesbuild-staging/containing the frontend dist, backend bundle, standalone Python 3.13, and the 9router binary. Build artifacts are ephemeral; not git-tracked. - macOS release: requires Apple ID, app-specific password, and team ID env vars. App is signed + notarized.
- Windows release: signed via Azure code signing in CI (
.github/workflows/release-windows.yml); triggers onv*tags.
Bundling
- Python 3.13 is bundled via python-build-standalone, so users do not need a system Python.
- 9router binary is pulled at build time by
scripts/fetch-router.sh/fetch-router.ps1. The version pin (0.3.60) is load-bearing for cross-provider WebSearch; see rootCLAUDE.md.
Versioning
- Source of truth:
electron/package.jsonversion. Bump alongside any user-facing release; CI tags off it. - Bump only when cutting a release; coordinate with the publish flow rather than landing version bumps speculatively.
Dev vs production
The packaged DMG/EXE behaves differently from bash run.sh in ways that silently break code:
- Paths:
__dirnameandapp.getAppPath()resolve inside anasararchive in production. Useapp.getPath('userData')for writable storage;process.resourcesPathpoints to unpacked resources. - Python: bundled standalone Python 3.13 lives under
process.resourcesPath/python/, not the system Python. Spawn it explicitly; don't assumepython3is onPATH. - 9router: the binary lives under
process.resourcesPath/9router/, not downloaded at runtime. Spawn from the bundled path. - Backend startup: in dev,
run.shlaunches uvicorn directly. In prod,main.jsspawns the bundled Python + backend. Bearer token must be on disk before the HTTP bind so the shell can read it. - Auto-updater: only fires in signed production builds. Staging/test builds must use a separate channel (via
electron-builder--config) to avoid clobbering the stable feed. - Deep links (
openswarm://): registered viaapp.setAsDefaultProtocolClient. OAuth and Stripe return flows depend on this; test on a packaged build, not the dev shell. - Code signing: macOS unsigned/un-notarized builds get Gatekeeper-blocked; Windows unsigned builds trigger SmartScreen. CI signs on
v*tags only. - Platform splits:
process.platform, path separators, line endings, and macOS-only flows (notarization, dock icon, menu bar) require explicit handling for both targets.
For any change touching paths, subprocess spawning, IPC, deep links, or the auto-updater: build with npm run build and run the produced DMG or EXE before reporting done.
Pitfalls
build-staging/is regenerated on every build; never commit it.- Auto-updater reads the latest release feed from GitHub; staging/test builds should use a separate channel to avoid pushing unsigned bits to users.