GenerateSaaS

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 dev

electron-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_URL set 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.

BuildData 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 installerElectron'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
PlatformBundles
macOS.dmg (+ a .zip the updater consumes)
Windowsnsis (.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.png with your own 512x512+ PNG; the per-OS .icns / .ico are 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.

On this page