# Develop and build

> Local dev (`pnpm dev`), typecheck targets, native runtime rebuilds, electron-vite build graph, relay/computer native artifacts, and platform release builds.

- Repository: stablyai/orca
- GitHub: https://github.com/stablyai/orca
- Human docs: https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c
- Complete Markdown: https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/llms-full.txt

## Source Files

- `package.json`
- `.github/CONTRIBUTING.md`
- `config/scripts/run-electron-vite-dev.mjs`
- `config/scripts/run-electron-vite-build.mjs`
- `config/electron-builder.config.cjs`
- `config/scripts/ensure-native-runtime.mjs`
- `vite.web.config.ts`

---

---
title: "Develop and build"
description: "Local dev (`pnpm dev`), typecheck targets, native runtime rebuilds, electron-vite build graph, relay/computer native artifacts, and platform release builds."
---

Orca’s source build is a pnpm workspace on **Node 24** that compiles the Electron app through **electron-vite** (`out/main`, `out/preload`, `out/renderer`), emits a standalone **CLI** with `tsc` (`out/cli`), bundles **SSH relay** artifacts per platform (`out/relay`), and on macOS builds the **computer-use** helper app before **electron-builder** packages installers. `pnpm dev` runs `ensure-native-runtime` for Electron, then `config/scripts/run-electron-vite-dev.mjs`, which prepares dev-only CLI wrappers, optional web pairing assets, and launches `electron-vite dev`.

## Prerequisites

| Requirement | Source |
|-------------|--------|
| Node.js **24** | `package.json` → `engines.node` |
| pnpm **10.24+** | `packageManager` field in `package.json` |

```bash
pnpm install
```

`postinstall` runs `config/scripts/rebuild-native-deps.mjs`, which recompiles `better-sqlite3`, `node-pty`, and (on macOS/Linux) `cpu-features` against Electron’s embedded Node ABI via `@electron/rebuild`.

<Note>
Contributor PR checks mirror `.github/CONTRIBUTING.md`: `pnpm lint`, `pnpm typecheck`, `pnpm test`, and `pnpm build`.
</Note>

## Local development

### Start the desktop app

```bash
pnpm dev
```

Equivalent to:

1. `pnpm run ensure:electron-runtime` → `node config/scripts/ensure-native-runtime.mjs --runtime=electron`
2. `node config/scripts/run-electron-vite-dev.mjs` → spawns `electron-vite dev`

Variants:

| Script | Behavior |
|--------|----------|
| `pnpm dev-stable-name` | Passes `--stable-name` so macOS uses Electron’s stock app name (for tools that key off it) |
| `pnpm start` | `ensure:electron-runtime` then `electron-vite preview` (production-like bundle preview) |
| `pnpm dev:web` | Vite dev server for the pairing web client only (`vite.web.config.ts`, `127.0.0.1`) |

### What `run-electron-vite-dev.mjs` sets up

```mermaid
flowchart TB
  subgraph prep [Dev runner prep]
    A[ensure-native-runtime electron]
    B[prepareDevCliWrapper → out/bin/orca-dev]
    C[seedDevInstanceIdentityEnv]
    D[prepareMacDevElectronApp optional]
    E[prepareDevWebClient optional]
  end
  subgraph run [Launch]
    F[electron-vite dev + remote-debugging-port]
  end
  A --> B --> C --> D --> E --> F
```

**Dev CLI (`orca-dev`).** Unless `ORCA_SKIP_DEV_CLI_PREPARE=1`, the runner writes `out/bin/orca-dev` (or `orca-dev.cmd` on Windows) with:

- `ORCA_USER_DATA_PATH` → isolated dev profile (`orca-dev` under Application Support / AppData / XDG config)
- `ORCA_APP_EXECUTABLE` → local Electron binary
- Prepends `out/bin` to `PATH`

The checked-in wrapper at `config/scripts/orca-dev` is also registered as an npm bin; `pnpm run build:cli` can symlink it to `/usr/local/bin/orca-dev` when permissions allow.

**Parallel worktrees.** Git branch and worktree name seed `ORCA_DEV_INSTANCE_LABEL`, `ORCA_DEV_DOCK_TITLE`, and a per-repo remote-debugging port (SHA1 of repo root → base port 9333–9532, with a 64-port sweep). Set `REMOTE_DEBUGGING_PORT` to override.

**macOS Dock identity.** On Darwin, the runner may copy `Electron.app` to `out/electron-dev/<hash>/` with a unique bundle ID and Dock title so multiple `pnpm dev` instances are distinguishable. Skip with `ORCA_SKIP_DEV_ELECTRON_APP_PREPARE=1` or use `dev-stable-name` / `ORCA_DEV_STABLE_NAME=1`.

**Web pairing bundle.** If `out/web/web-index.html` is missing or stale, dev logs a skip unless `ORCA_DEV_WEB_PREPARE=1` or sources changed; run `pnpm run build:web` when browser pairing is required.

**Host environment cleanup.** The runner deletes `ELECTRON_RUN_AS_NODE` before spawning electron-vite so `require('electron')` resolves to the real Electron API, not the npm stub.

### Renderer-only web client

`vite.web.config.ts` builds `src/renderer/web-index.html` to `out/web/` with `base: './'` for reverse-proxy path prefixes. Feature wall is enabled at compile time via `ORCA_FEATURE_WALL_ENABLED: 'true'`.

## Typecheck

Orca uses **`tsgo`** (TypeScript native preview) for day-to-day checks and keeps **`tsc`** aliases for compatibility.

| Script | Project | Scope |
|--------|---------|-------|
| `pnpm typecheck:node` / `pnpm tc:node` | `config/tsconfig.node.json` | `src/main`, `src/preload`, `src/shared`, `src/relay`, `electron.vite.config.*` |
| `pnpm typecheck:cli` / `pnpm tc:cli` | `config/tsconfig.tc.cli.json` | CLI + shared + selected main hook modules |
| `pnpm typecheck:web` / `pnpm tc:web` | `config/tsconfig.tc.web.json` | Renderer, preload API types, shared, select main IPC |
| `pnpm typecheck` / `pnpm tc` | All three sequentially | Full gate used in CI |

`tsc` equivalents: `typecheck:tsc`, `typecheck:tsc:node`, etc., pointing at `tsconfig.cli.json` / `tsconfig.web.json` without the `tc.*` path tweaks.

<Warning>
Project-owned types must live in `.ts` files under `src/preload` and `src/shared`, not `.d.ts` — CI rejects new `.d.ts` there because `skipLibCheck` can hide broken IPC signatures.
</Warning>

## Native module runtimes

Two ABIs matter: **system Node** (Vitest, relay esbuild target) and **Electron’s Node** (app, dev, packaged CLI via `ELECTRON_RUN_AS_NODE`).

### Ensure scripts

```bash
# Before dev / electron-vite preview / e2e — Electron ABI
pnpm run ensure:electron-runtime

# Before unit tests — Node ABI (also invoked by pnpm test)
node config/scripts/ensure-native-runtime.mjs --runtime=node
```

`ensure-native-runtime.mjs` loads `better-sqlite3` (in-memory DB) and `node-pty` (including Windows `conpty` vs `pty` by build number). On failure it runs `pnpm rebuild <modules>` (Node) or `rebuild-native-deps.mjs` (Electron).

### Manual rebuilds

| Command | When |
|---------|------|
| `pnpm run rebuild:electron` | Re-run postinstall Electron rebuild (`rebuild-native-deps.mjs`) |
| `pnpm run rebuild:node` | `pnpm rebuild better-sqlite3 node-pty` for Vitest |
| `ORCA_FORCE_NATIVE_REBUILD=1 pnpm run rebuild:electron` | Skip probe, force `@electron/rebuild` |

On Windows, postinstall may continue if a `.node` file is locked (`ORCA_STRICT_NATIVE_REBUILD` disables leniency). Close Orca/Electron processes before reinstalling.

## Build pipeline

### Full contributor build

```bash
pnpm build
```

Order (from `package.json`):

1. `pnpm typecheck`
2. `pnpm run build:relay`
3. `pnpm run build:computer-macos`
4. `pnpm run build:cli`
5. `pnpm run build:electron-vite`
6. `pnpm run build:web`

### Release build (CI / maintainers)

```bash
pnpm build:release
```

Same as `build` except **skips typecheck** and runs `pnpm run verify:computer-native` after the macOS computer helper build. Release workflows set `ORCA_BUILD_IDENTITY`, `ORCA_POSTHOG_WRITE_KEY`, and related vars so `electron.vite.config.ts` injects telemetry constants; local builds substitute literal `null`.

### electron-vite graph

`electron.vite.config.ts` defines three targets:

| Target | Entry highlights | Output role |
|--------|------------------|-------------|
| **main** | `index`, `daemon-entry`, `computer-sidecar`, `stt-worker`, `agent-hooks/managed-agent-hook-controls` | Main process; daemon entry must stay unpackable for `fork()` |
| **preload** | Preload bridge | IPC surface to renderer |
| **renderer** | React + Tailwind | Desktop UI |

`build:electron-vite` runs `config/scripts/run-electron-vite-build.mjs`, which invokes `electron-vite build` with `NODE_OPTIONS` including `--max-old-space-size=4096` to avoid renderer OOM on CI macOS runners.

Main-process `externalizeDeps` keeps most deps external except bundled xterm headless/serialize paths used from the unpacked daemon.

### CLI compile

```bash
pnpm run build:cli
```

Runs `tsc -p config/tsconfig.cli.json --outDir out` then `install-dev-cli.mjs` for the optional global `orca-dev` symlink. Production `orca` resolves from `out/cli/index.js` (`package.json` `bin.orca`).

### Relay artifacts

`pnpm run build:relay` esbuild-bundles `src/relay/relay.ts` into self-contained CommonJS per platform:

- `out/relay/linux-x64/relay.js`
- `out/relay/linux-arm64/relay.js`
- `out/relay/darwin-x64/relay.js`
- `out/relay/darwin-arm64/relay.js`

Native addons (`node-pty`, `@parcel/watcher`, `better-sqlite3`) stay **external**; remote hosts install or degrade gracefully. Each folder gets a `.version` file with content hash.

Packaged apps copy `out/relay` to `process.resourcesPath/relay` via electron-builder `extraResources`.

### Computer-use native artifacts

| Script | Platform | Output |
|--------|----------|--------|
| `build:computer-macos` | darwin only (no-op elsewhere) | Universal Swift binary + `Orca Computer Use.app` under `native/computer-use-macos/.build/release/` |
| (packaged) | win | `computer-use-windows/runtime.ps1` in resources |
| (packaged) | linux | `computer-use-linux/runtime.py` in resources |

`verify:computer-native` runs Swift tests, Python syntax/import checks, and PowerShell parse/handshake validation as appropriate for the host OS.

`build:mac:release` requires signing env vars verified by `verify-macos-release-env.mjs` (`APPLE_ID`, `APPLE_APP_SPECIFIC_PASSWORD`, `APPLE_TEAM_ID`, `CSC_LINK`, `CSC_KEY_PASSWORD`). Local ad-hoc macOS builds use `pnpm build:mac` without `ORCA_MAC_RELEASE=1`.

## Platform installers (electron-builder)

Config: `config/electron-builder.config.cjs`. All platform targets run `pnpm build` (or release variant), `ensure:electron-runtime`, then electron-builder.

| Script | Artifact |
|--------|----------|
| `pnpm build:unpack` | `--dir` unpacked app for inspection |
| `pnpm build:mac` | DMG + ZIP (x64, arm64); ad-hoc signing unless `ORCA_MAC_RELEASE=1` |
| `pnpm build:mac:release` | Hardened runtime + notarization + `forceCodeSigning` |
| `pnpm build:win` | NSIS installer (`orca-windows-setup`) |
| `pnpm build:linux` | AppImage + deb (`orca-ide` package name; executable `orca-ide`) |

Notable packaging rules:

- **`asarUnpack`**: `out/cli`, `out/shared`, agent hook dirs, `daemon-entry.js`, `computer-sidecar.js`, `ws`, `tweetnacl`, `zod`, `yaml`, `sherpa-onnx*`, etc., so forked Node and `ELECTRON_RUN_AS_NODE` CLI can `require` across directories.
- **`extraResources`**: relay bundles, platform `orca` CLI shims, `agent-browser` binaries, computer-use runtimes, onboarding feature-wall media.
- **`npmRebuild: true`**: Rebuilds `node-pty` per target arch (avoids arm64 binaries inside x64 macOS DMGs).
- **macOS computer helper**: `afterPack` signs `Orca Computer Use.app` before the outer app is sealed.

Maintainer releases are cut from GitHub Actions **Cut Release** — not a local `pnpm release:*` script. See `.github/CONTRIBUTING.md` for version semantics and safety guarantees.

## Output layout

:::files
orca/
├── out/
│   ├── main/           # electron-vite main (+ daemon, sidecar, hooks)
│   ├── preload/
│   ├── renderer/
│   ├── cli/            # tsc-compiled CLI
│   ├── shared/         # shared modules used by CLI
│   ├── web/            # vite.web pairing client
│   ├── relay/          # per-platform relay.js bundles
│   ├── bin/            # dev orca-dev wrapper (created at dev time)
│   └── electron-dev/   # macOS per-instance Electron.app copies
├── native/
│   └── computer-use-macos/.build/release/
│       └── Orca Computer Use.app
└── dist/               # electron-builder installers (after platform build)
:::

Production entry: `package.json` `"main": "./out/main/index.js"`, `"bin": { "orca": "./out/cli/index.js" }`.

## Useful environment variables

| Variable | Effect |
|----------|--------|
| `ORCA_SKIP_DEV_CLI_PREPARE` | Skip `out/bin/orca-dev` generation |
| `ORCA_SKIP_DEV_ELECTRON_APP_PREPARE` | Skip macOS per-instance Electron.app copy |
| `ORCA_SKIP_DEV_WEB_PREPARE` | Skip web bundle freshness check/build in dev |
| `ORCA_DEV_WEB_PREPARE=1` | Force web client build when pairing HTML missing |
| `ORCA_DEV_USER_DATA_PATH` | Override dev profile directory |
| `ORCA_MAC_RELEASE=1` | Enable hardened runtime, notarization, strict signing |
| `ORCA_FORCE_NATIVE_REBUILD=1` | Force electron native rebuild |
| `ORCA_BUILD_IDENTITY` / `ORCA_POSTHOG_WRITE_KEY` | Official telemetry compile-time injection (CI only) |
| `ELECTRON_RUN_AS_NODE` | Stripped by dev runner; do not rely on it leaking into electron-vite |

## Verification commands

| Command | Purpose |
|---------|---------|
| `pnpm test` | Vitest unit tests (ensures Node native runtime first) |
| `pnpm test:e2e` | Playwright Electron (headless) |
| `pnpm smoke:computer` | Computer-use smoke script |
| `pnpm lint` | oxlint + styled-scrollbar check |
| `pnpm verify:macos-entitlements` | Entitlements plist consistency |

<Tip>
If `pnpm dev` fails immediately after switching Node versions or branches, run `pnpm run rebuild:electron` then retry. If tests fail on `better-sqlite3`, run `pnpm run rebuild:node`.
</Tip>

## Related pages

<CardGroup>
  <Card title="Installation" href="/installation">
    Node 24 and pnpm prerequisites for source builds, plus packaged installs when you do not need to compile locally.
  </Card>
  <Card title="Testing" href="/testing">
    Vitest, Playwright Electron E2E, computer-use smoke paths, and SSH-localhost test flags.
  </Card>
  <Card title="Contributing" href="/contributing">
    PR checklist, cross-platform constraints, and maintainer release workflow via GitHub Actions.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    Native dependency failures, runtime reachability, and platform-specific build errors.
  </Card>
</CardGroup>
