Development and builds
Run the desktop app in development against your local backend, then package per-OS installers with electron-builder.
The desktop app runs in development against whatever backend the monorepo root .env points at, and packages per-OS installers with electron-builder. Both are single commands from apps/desktop.
Running in development
With the web app running, start the app with no extra env:
pnpm --filter desktop develectron-vite dev starts the renderer dev server with HMR, builds the main and preload bundles, and opens the native window.
- Backend target: the root
.env- the same file the web app uses.BASE_URL/PUBLIC_API_URLset in your shell override it (to point dev at staging, for example). - Renderer: loads from the dev server with hot reload; main points the window at it via
ELECTRON_RENDERER_URL.
Dev data folder
A dev app keeps its data (dev sign-in token, window state, the agent runtime's local chats and automations) in a folder named for your app's appId, so two projects' dev apps never share a sign-in or the single-instance lock.
| Build | Data folder |
|---|---|
Dev (pnpm --filter desktop dev) | <appData>/<appId>.dev, apart from an installed copy of the same app |
Launched with --user-data-dir=<path> | <path> (what the desktop e2e suite passes) |
| Packaged installer | Electron's own <appData>/<productName>, unchanged |
<appData> is ~/Library/Application Support on macOS, %APPDATA% on Windows and ~/.config on Linux.
Dev builds before this folder moved kept their data in <appData>/desktop, so the first dev launch after updating starts signed out with no local chats. To carry them over, quit the dev app and copy dev-secrets.json and the agent-runtime/ folder from <appData>/desktop into <appData>/<appId>.dev.
Building installers
Each platform has its own script, running electron-vite build then electron-builder:
pnpm --filter desktop build:mac # or build:win / build:linux| Platform | Bundles |
|---|---|
| macOS | .dmg (+ a .zip the updater consumes) |
| Windows | nsis (.exe) |
| Linux | .AppImage + .deb |
electron-builder targets the host OS, so build each platform's installer on that platform (or in CI). Output lands in apps/desktop/dist/.
- Packaging config:
apps/desktop/electron-builder.mjs(targets,asarUnpack,files, and the per-OS blocks). App identity -appId,productName, deep-link scheme, update feed - is read straight from@repo/config/desktop, so the installer and the running app cannot disagree. - Signing: macOS signing and notarization are secret-gated, Windows uses Azure Trusted Signing when configured, and both build unsigned only when you opt in - see Releasing.
- App icon: replace
apps/desktop/build/icon.pngwith your own 512x512+ PNG; the per-OS.icns/.icoare generated from it at build time.
With AI on, the agent runtime is a second entry in the SAME electron-vite pass (out/main/daemon-entry.js, left asar-unpacked so the app can fork it) - no extra build step and no runtime to vendor. The in-app terminal's native node-pty binding is a Node-API addon, so no ABI rebuild is needed - electron-builder's beforePack hook downloads the prebuilt binding for the platform and arch that pack targets.
Projects
How the desktop app keeps its on-device data per project, and how a connected folder lets that project's chats and automations run in one of the user's own real folders.
End-to-end tests
Run the desktop Playwright harness that drives the built Electron app against a local backend, and the services and system libraries it needs.