Electron wrapper around the jaw manager dashboard. In development it attaches to
or spawns jaw dashboard serve; in packaged builds it prefers the bundled
Node.js sidecar server shipped under extraResources/server.
Default manager lanes:
- Web/CLI dashboard:
24576 - Electron implicit spawn:
24577-24590
The Electron main process must not import server-native modules such as
better-sqlite3. Native/server dependencies live in the manager or bundled
sidecar process.
# from the repo root
npm install
npm --prefix electron installFrom the repo root, run the dashboard server and Electron with hot reload:
npm run electron:devIf an Electron manager server is already running on 24577:
npm --prefix electron run devnpm run electron:build
# outputs:
# electron/out/main/index.js
# electron/out/preload/index.jsRun the built shell without packaging:
npm --prefix electron run startPackaged apps include a self-contained server sidecar:
npm run sidecar:bundlescripts/bundle-sidecar.sh currently:
- downloads Node.js
24.17.0for the target platform/arch, - runs the root backend and frontend builds,
- copies
dist/,public/,package.json, and lockfile intoelectron/sidecar/server, - installs production dependencies with scripts disabled from the copied lockfile,
- excludes retired JWC,
jawcode,@jawcode-dev,@oven,bun,claude-e,claude-exec,@bitkyc08/ai-e, andjaw-claude-ipayloads; saved retired selections require an explicit supported runtime choice, and Code sessions use the native Codex, Claude, Cursor or Grok adapter, - prunes frontend-only packages,
- rebuilds
better-sqlite3, - creates
bin/jaworbin/jaw.cmdto launchdist/bin/cli-jaw.js, - verifies the staging sidecar contains no bundled retired runtime payload.
electron/src/main/lib/jaw-spawn.ts searches the bundled sidecar first in
packaged apps, then falls back to JAW_BIN and the global jaw binary.
npm run electron:dist:macThe root electron:dist:mac script runs:
npm run build:frontend
npm run sidecar:bundle
npm --prefix electron run build
CSC_IDENTITY_AUTO_DISCOVERY=false npm --prefix electron run dist:mac
npm run check:electron-dist-mac-no-jwc
npm run check:app-iconsThe dist checks validate the packaged .app under electron/dist/mac-arm64.
They do not replace /Applications/cli-jaw.app; app replacement remains a
manual step after build verification.
Current electron-builder.yml targets:
| Platform | Arch | Artifacts |
|---|---|---|
| macOS | arm64 | DMG, ZIP |
| Windows | x64 | NSIS installer, ZIP |
| Linux | x64 | AppImage |
The ordinary local electron:dist:mac command is ad-hoc signed. The canonical
GitHub release workflow requires a Team U9ATA49N28 Developer ID signature,
Apple notarization and a stapled ticket on both the app and the DMG that
carries it. Windows artifacts remain unsigned.
.github/workflows/desktop-release.yml is the canonical desktop release path.
It runs on GitHub Release publish and manual workflow_dispatch.
For each platform matrix entry it:
- checks out the release tag or current ref,
- installs Node.js 24 and Python 3.11 for
node-gyp, - runs root
npm ci --ignore-scripts, - bundles the platform sidecar,
- installs Electron dependencies,
- typechecks and builds Electron,
- signs, notarizes and staples the macOS app and signs the DMG, then
notarizes and staples the DMG with
scripts/notarize-mac-dmg.mjs(Windows/Linux remain unsigned). Stapling changes the DMG bytes electron-builder already hashed, so the script rebuilds the DMG blockmap and the DMG entry oflatest-mac.ymlfrom the stapled file and leaves the ZIP entry the updater downloads as built, - verifies the macOS identity, team, hardened runtime, timestamp, nested
signature, Gatekeeper verdict and stapled ticket of the app, and the
Developer ID signature, team,
openGatekeeper verdict (must reportNotarized Developer ID) and stapled ticket of the DMG, - verifies the selected release's macOS
latest-mac.yml, ZIP size/SHA-512, blockmap, every other listed artifact (the DMG), and packaged GitHub provider; the Windows leg likewise verifieslatest.yml, its exact NSIS installer name, size/SHA-512, blockmap, legacy fields, and provider, - verifies the final sidecar and packaged app icon inputs,
- uploads artifacts to the release or 7-day manual-run artifact storage.
The Windows update feed (latest.yml, the NSIS installer, and its blockmap) is
published and checked by the release workflow, but the updater client remains
macOS-only. Enable Windows in shouldEnableAppUpdater only after the installer
is Authenticode-signed and electron-builder has a matching publisherName, so
NsisUpdater can enforce publisher verification.
The macOS updater runs only in an installed, packaged app. It performs a
delayed silent check on startup, then an automatic check every 24 hours while
the app keeps running, and exposes CLI-JAW → Check for Updates….
It never auto-downloads or auto-installs: both actions require native-dialog
consent, and coordinated manager cleanup completes before restart/install.
Stable builds use GitHub's latest stable release. Preview builds select a
matching preview tag from the GitHub release feed and use that release's own
latest-mac.yml (electron-updater's documented GitHub fallback). The first
signed release is a manual-DMG bootstrap for users of older unsigned builds.
Recent desktop surfaces include:
- sidecar-first
jawdetection, - first-launch and tray menu CLI install flow,
- background tray mode,
- browser webview panel and URL controls,
- diff repo picker and dashboard git diff API bridge,
- widened/resizable right sidebar panels,
- visible quit cleanup/progress,
- macOS Automation permission prompt for Computer Use-related flows.
| Var | Default | Description |
|---|---|---|
JAW_MANAGER_URL |
http://127.0.0.1:24577/ |
Manager URL to attach to |
JAW_MANAGER_PORT |
24577 |
Port used when URL is not set; implicit spawn falls back through 24590 |
JAW_BIN |
auto-detected | Path to a jaw CLI binary when not using the bundled sidecar |
JAW_ELECTRON_DEVTOOLS |
unset | Set to 1 to open DevTools |
NODE_ENV |
unset | development enables DevTools |
--port <n> Override manager port (implicit spawn uses 24577-24590)
--manager-url <url> Override full manager URL
--attach-only Never spawn jaw dashboard serve; only attach
--spawn Force spawn even if no health probe is required
- Health-check
${MANAGER_URL}api/dashboard/healthwith backoff200/400/800/1600/3000/5000msfor up to 60 seconds. - Healthy manager loads immediately.
- Unhealthy and not
--attach-only: discover sidecar/globaljaw, spawnjaw dashboard serve --port <port> --no-open, then re-check. - Binary missing: show native install/pick-path/quit dialog.
- Crash loop guard: more than 3 manager exits within 60 seconds stops auto-restart and shows a dialog.
- Quit:
SIGTERM, 5 second grace, thenSIGKILL.