This file describes the repository structure, architecture, and working model for OpenCode agents operating on this codebase.
README.md must always be an exact copy of the repository-root AGENTS.md.
When one changes, update the other in the same edit.
This is the source tree for ARCgentic — an Electron + React desktop application that is the primary UI for ARC (Annotated Research Context) workflows.
The app no longer includes or spawns the OpenCode runtime directly. Instead it integrates with the external ARC-Opencode-Runtime Docker image, which runs opencode serve against a mounted ARC with a baked-in OpenCode profile. The app is the orchestrator the runtime contract expects: it selects an ARC, builds/pulls the runtime image, creates and manages a Docker container, mounts the ARC, and connects the renderer to the container's OpenCode HTTP API. The mapped API is bound to 127.0.0.1 and protected by fresh, per-app-process OpenCode Basic-auth credentials.
The app replaces a now-deleted VS Code extension.
Develop the GUI inside the devcontainer (Linux + Xvfb/VNC + Docker-in-Docker); ship native desktop artifacts through GitHub Releases; the ARC runtime always runs in Docker.
Devcontainer (develop) Native CI packaging GitHub Release
┌─────────────────────┐ ┌────────────────────────┐ ┌────────────────────────┐
│ electron-vite dev │ │ Windows x64 NSIS │ │ Setup.exe │
│ Xvfb :99 + noVNC │ ──▶ │ Linux x64/arm64 │ ──▶ │ x86_64/arm64 AppImages │
│ Docker-in-Docker │ │ macOS x64/arm64 DMGs │ │ Intel/Apple Silicon DMG│
└─────────────────────┘ └────────────────────────┘ └────────────────────────┘
- GUI dev:
npm run dev:vncinelectron/— electron-vite dev server + hot reload under Xvfb:99. View the app in a browser athttp://localhost:6080(noVNC) or via a VNC client on port5900. - Runtime builds: the devcontainer's
docker-in-dockerfeature buildsarc-opencode-runtime:latestfrom the clonedARC-Opencode-Runtime/repo. SetARC_RUNTIME_MODE=build(forced by devcontainer env). - Testing:
npm run build && npm run test:ci— 60+ Playwright-Electron tests are organized into explicit projects;@dockertests drive the real container lifecycle separately.
cd electron
npm run build # electron-vite build → out/
npm run dist:win # electron-builder → electron/dist/ARCgentic-Setup-x.y.z.exe
npm run dist:linux:x64 # electron-builder → x86_64 AppImage
npm run dist:linux:arm64 # electron-builder → arm64 AppImage
npm run dist:mac:x64 # electron-builder → unsigned Intel DMG (run on macOS)
npm run dist:mac:arm64 # electron-builder → unsigned Apple Silicon DMG (run on macOS)electron-builder bundles a Linux-compatible NSIS compiler — no Windows machine or Wine needed for unsigned builds.
- Install Docker Desktop at https://www.docker.com/products/docker-desktop, or a compatible Docker Engine on Linux (the app shows onboarding guidance if Docker is missing).
- Download the native artifact from GitHub Releases: Windows x64 NSIS, Linux x86_64/arm64 AppImage, or macOS Intel/Apple Silicon DMG.
- macOS builds are currently unsigned and unnotarized. Use Finder's right-click → Open flow to approve a trusted download; these are direct releases and are not App Store packages.
- Pick an ARC directory → the app obtains the configured runtime image and starts a managed container.
- The app starts the container, mounts the ARC at
/workspace/arc, and connects to the OpenCode API.
The production OpenCode runtime never runs on the host machine. It always runs inside a Docker container managed by the app. There is no production "Connect to running" or arbitrary-endpoint fallback. The shipped configuration defaults to pull mode from the public docker.io/zimmerd/arc-opencode-runtime:latest image. Startup prefers a verified local copy and pulls only when it is missing or the user explicitly requests Pull latest. An immutable versioned-image cutover remains pending; the devcontainer continues to force ARC_RUNTIME_MODE=build and uses the ARC-Opencode-Runtime/ source path.
The current public runtime tag contains only linux/amd64. ARM desktop artifacts
are built and smoke-tested natively, but managed runtime execution on Apple
Silicon or ARM Linux currently depends on Docker's amd64 emulation. Native ARM
runtime support remains pending publication of a validated linux/arm64 image.
Every new feature or bug fix must be accompanied by tests. No phase is complete until its tests pass.
- Playwright e2e only — no unit test framework. All tests live in
electron/e2e/. - Tests run against a production build (
npm run build→out/). Always build before running tests.
| Tag | Meaning | Server required | AI required |
|---|---|---|---|
@smoke |
Pure UI; no OpenCode server needed, no Docker needed | No | No |
@integration |
Deterministic app/backend flows against a per-test, in-memory fake OpenCode server | In-process fake | No |
@docker |
Container-backed runtime lifecycle against a live Docker daemon + the arc-opencode-runtime image |
Yes (container) | No |
@llm |
Real local OpenCode + live AI provider; run manually | Yes | Yes |
@llm-tools |
Best-effort real tool-call tests; may fail due to LLM non-determinism | Yes | Yes |
# Run from electron/
npm run build # must build first
npm run test:smoke # @smoke suite
npm run test:int # @integration suite
npm run test:docker # @docker suite (requires Docker + built image)
npm run test:llm # @llm suite (manual; requires MISTRAL_API_KEY)
npm run test:llm-tools # @llm-tools suite (manual; requires MISTRAL_API_KEY)global-setup.tsmaterializes a pinned revision of the public ArcPrototype repository under the gitignoredelectron/e2e/test-data/cache, then owns Xvfb when the host has no display;global-teardown.tsowns only that Xvfb process andpids.tshas been removed.- Each test gets a disposable temporary copy of that clean downloaded ARC, plus an isolated home directory and Electron
userDatadirectory. Integration tests also get an isolated in-memory fake OpenCode server on an ephemeral loopback port. PLAYWRIGHT_TEST=1suppresses DevTools and enables the explicitly gatedARC_TEST_RUNTIME_ENDPOINTfixture path. This endpoint is unavailable in normal development and production processes.- Tests no longer require an
ARC-Opencode-Runtime/checkout or external profile path. Docker tests still require a live daemon and built runtime image; LLM projects spawn the pinned local OpenCode CLI without injecting the external profile.
sendToRendererpattern — simulate menu/tray IPC pushes in tests:await electronApp.evaluate(({ BrowserWindow }, args) => { BrowserWindow.getAllWindows()[0].webContents.send(args.channel, args.payload) }, { channel: 'menu:newSession', payload: null })
- Shared app helpers —
e2e/support/app-helpers.tsowns the select/start/connect flow used by integration specs; production UI exposes onlyStart runtime, not "Connect to running". - Smoke tests are backend-free and Docker-free. Integration tests are deterministic and isolated; Docker tests validate the real image and Docker resource contract.
- Current spec files:
smoke.spec.ts,launcher.spec.ts,assistant.spec.ts,native.spec.ts,rich-ui.spec.ts,prompt-ux.spec.ts,session-lifecycle.spec.ts,subtask-navigation.spec.ts,model-provider.spec.ts,file-tree.spec.ts,leave-arc.spec.ts,security.spec.ts,docker-cli.spec.ts,docker-required.spec.ts,runtime-image.spec.ts,docker-runtime.spec.ts,gui-debug-mcp.spec.ts, andllm.spec.ts.
- Linux
qualityruns type checking, the production build, and smoke + integration projects. - Linux
windows-installercross-builds the NSIS installer. - Windows
windows-packaged-smokesilently installs that artifact and runs Playwright against the installed executable. - Native Ubuntu x64/arm64 jobs build and smoke-test AppImages; native macOS Intel/Apple Silicon jobs build and smoke-test unsigned DMGs. These packaging jobs run on
main, tags, and manual dispatch, but not pull requests. - A pushed
v*tag runs the complete pipeline, requires the tag to equalv${package.json version}, generates and verifies one checksum manifest for all five artifacts, and creates or refreshes the corresponding GitHub Release. Tags containing-are published as prereleases. - Docker CI remains pending the immutable versioned runtime image cutover.
- Never put OpenCode config, AGENTS.md, or scaffolding inside an ARC. The ARC (for example
arcs/my-arc/) must stay clean and spec-compliant. - The OpenCode profile is owned by the external ARC-Opencode-Runtime, not this repo. It is baked into the runtime Docker image at
/opt/arc-opencode-profileand injected viaOPENCODE_CONFIG_DIR. Do not duplicate it here. - Do not add git-related tool permissions to
opencode.json. Git operations go through thebashtool; to prompt before them, setbashpermission to"ask". changelog.mdis the historical source of truth for completed phases and major milestones. Keep it focused on what has shipped, not speculative future work.contextIsolation: true,nodeIntegration: false,sandbox: trueon the Electron renderer. No Node.js in the renderer.- All native capabilities must be wrapped in
electron/src/main/native/. The renderer must not access Node or Electron APIs directly. - The renderer communicates with the main process only via the preload IPC bridge. Add new capabilities by extending the bridge, not by relaxing sandbox settings.
- Docker is driven by shelling out to the
dockerCLI from the Electron main process viachild_process(matches the runtime repo's own approach; no Docker SDK dependency). - The user-visible product name is ARCgentic. Backend names and compatibility identifiers remain internal; do not expose or rebrand their operational values in Docker/API contracts.
- User-selected files are staged outside the ARC. Dragged, pasted, and picker-selected regular files are copied into a generation-scoped temporary directory and mounted read-only at
/workspace/attachments. Supported bounded images, text, and PDFs become model context only when the selected model advertises that capability; every staged file remains tool-visible. The Files panel keeps a runtime-scoped temporary inventory between the ARC tree and Session changes, using the existing preview pane and explicit reuse/delete actions. Removing a prompt card only detaches it from that draft. Build mode may copy a file into/workspace/arconly when the current prompt explicitly requests it, while Plan mode never mutates the ARC. Directories and symlinks are rejected, and staged scopes are cleaned on explicit deletion, runtime stop/exit, ARC change, and app shutdown. - Runtime endpoints are managed-only in production. Do not add an attach-to-arbitrary-server fallback; the only non-container endpoint is gated by
PLAYWRIGHT_TEST=1for test fixtures. - Rendered Markdown is untrusted input. Raw HTML and images are not allowed in Markdown output; preserve sanitization, HTTPS-only external links, navigation guards, trusted IPC sender checks, and ARC path containment.
.
├── .devcontainer/ # Devcontainer config (Dockerfile, devcontainer.json, postCreate.sh)
│ Includes the docker-in-docker feature so the app can run `docker`.
├── ARC-Opencode-Runtime/ # External runtime (cloned for dev/inspection; NOT part of this
│ repo's source). Provides the Docker image + OpenCode profile.
├── arcs/
│ └── my-arc/ # Sample ARC fixture — do not put profile config inside it
├── electron/ # Electron + React application
│ ├── package.json
│ ├── tsconfig.json # Renderer TypeScript config
│ ├── tsconfig.node.json # Main + preload TypeScript config
│ ├── electron.vite.config.ts
│ ├── electron-builder.yml
│ ├── resources/ # Build resources: app icons (.ico, .png)
│ ├── gui-debug-mcp/ # Dev-only MCP server giving text-only agents GUI
│ │ ├── core.ts # debugging eyes (snapshot/see/act). Registered in
│ │ └── index.ts # repo-root opencode.json. NOT part of the shipped app.
│ └── src/
│ ├── main/ # Electron main process
│ │ ├── index.ts # App entry, BrowserWindow, IPC handlers
│ │ ├── runtime/ # Container-backed runtime controller
│ │ │ ├── config.ts # Per-process auth, gated test endpoint + image config
│ │ │ ├── container.ts # ContainerController: docker run/stop/rm/logs/events
│ │ │ ├── image.ts # Image build/pull/inspect
│ │ │ ├── ports.ts # Host port allocation for container mapping
│ │ │ ├── arcs.ts # ARC discovery + recent list + validation
│ │ │ ├── health.ts # Endpoint readiness (GET /session) + container inspect
│ │ │ ├── logs.ts # Ring buffer attached to container logs stream
│ │ │ ├── dockerCli.ts # Cross-platform Docker CLI executable resolution
│ │ │ ├── docker.ts # Docker Desktop detection + status probe
│ │ │ └── index.ts # Public API surface
│ │ └── native/ # Native OS integrations
│ │ └── attachments.ts # Bounded temporary file staging outside the active ARC
│ ├── preload/
│ │ └── index.ts # contextBridge — exposes window.electron, window.runtime, window.native
│ └── renderer/
│ ├── index.html
│ ├── env.d.ts # Global window type declarations
│ ├── main.tsx # React 19 root mount
│ ├── App.tsx # Root reducer: docker-required/selecting/launching/connected
│ ├── App.css
│ ├── global.css # Design token CSS variables
│ └── components/
│ ├── Brand.tsx # ARCgentic wordmark and orbit mark
│ ├── Icon.tsx # Local dependency-free interface icon set
│ ├── ArcSelectionView.tsx # First screen: pick an ARC
│ ├── ArcRuntimeView.tsx # Runtime view: image + managed container lifecycle
│ ├── DockerRequiredView.tsx # Onboarding: Docker Desktop missing
│ ├── ChatView.tsx # Assistant chat
│ ├── SessionList.tsx # Sidebar session list
│ ├── SessionStartScreen.tsx # New-session prompt starters and blank-chat entry
│ ├── TutorialCoachmark.tsx # Anchored cross-view guided tutorial
│ ├── TutorialToggle.tsx # Persisted tutorial-mode control
│ ├── PromptInput.tsx # Prompt bar + @-file picker
│ ├── StagedFilesList.tsx # Runtime-scoped temporary file inventory
│ └── ...
├── runtime/
│ ├── scripts/
│ │ ├── opencode-dev.sh # Start opencode WITHOUT the runtime profile (raw dev)
│ │ ├── start-vnc.sh # Start/stop Xvfb + x11vnc + noVNC for visual debugging
│ │ └── saves.sh # Historical helper script; not part of the supported workflow
│ └── state/ # Gitignored — runtime PID/log state
├── AGENTS.md # This file
├── opencode.json # Repo-root OpenCode config (tool permissions)
├── README.md # Must stay identical to AGENTS.md
├── architecture-electron.md # Detailed Electron app design and test strategy
├── architecture-runtime.md # Detailed runtime integration design
├── test-inventory.md # Test surface inventory
└── changelog.md # Completed phases and shipped milestones
Read these first when you need implementation-level design context:
architecture-electron.md— Electron main/preload/renderer boundaries, UI patterns, native integrations, and E2E test strategyarchitecture-runtime.md— external runtime integration, Docker image model, container lifecycle, and operational ruleschangelog.md— historical record of completed phases and major shipped milestones
Host machine Docker (docker-in-docker in dev, or Docker Desktop in prod)
┌─────────────────────┐ ┌──────────────────────────────────────┐
│ Electron app │ │ ARC-Opencode-Runtime container │
│ ├── main/ │ │ (one container per ARC) │
│ │ ├── runtime/ │──docker CLI──▶ │ - opencode serve :4096 │
│ │ │ ├──container│ run/stop/rm │ - baked profile /opt/arc-opencode-… │
│ │ │ ├──image │ │ - ARC bind-mounted /workspace/arc │
│ │ │ ├──arcs │ │ │
│ │ │ └──ports │ HTTP/SSE │ OpenCode HTTP API + Basic auth │
│ │ └── native/ │◀─127.0.0.1:*──│ GET /session (readiness) │
│ ├── preload/ │ loopback only │ │
│ └── renderer/ │ └──────────────────────────────────────┘
│ └── React app │ ▲
│ SDK client ─┘ │ bind mount
└─────────────────────┘ /workspace/arcs/my-arc (host ARC)
- Electron main process — orchestrates the external runtime: canonical ARC identity, image build/pull, serialized and cancellable container start/stop, loopback port allocation, log streaming,
docker waitdeath monitoring, and verified shutdown. Drives Docker viachild_process. Handles validated IPC from the renderer. - Preload script — secure bridge between renderer and main; uses
contextBridgeonly. - Renderer — React 19 + TypeScript, no Node.js access. A root reducer owns
docker-required,selecting,launching, andconnected; runtime-scoped client/SSE/file/provider caches reset together when identity changes. The SDK client (@opencode-ai/sdk/client) targets the allocated endpoint and sends the derived Basic authorization header. - ARC-Opencode-Runtime container — the external runtime. Headless, runs
opencode serve --hostname 0.0.0.0 --port 4096internally, while Docker publishes the API and optional browser-OAuth callback only on host127.0.0.1. The ARC is bind-mounted at/workspace/arc; the profile is baked at/opt/arc-opencode-profile. One managed container is active at a time.
| Package | Purpose |
|---|---|
electron v41 |
Desktop shell |
electron-vite v5 |
Build tooling (Vite 7) |
react v19 |
Renderer framework |
@opencode-ai/sdk v1.18.2 |
Typed OpenCode server client (renderer imports @opencode-ai/sdk/client sub-path) |
typescript v5 |
Type checking |
No Docker SDK dependency — the main process shells out to the docker CLI.
The app integrates with ARC-Opencode-Runtime as an external Docker-image dependency (not vendored into this repo's source). The integration contract is defined by ARC-Opencode-Runtime/docs/runtime-contract.md:
- ARC bind mount:
<host-arc-path> → /workspace/arc - In-container server:
opencode serve --hostname 0.0.0.0 --port 4096 --cors null; host publication:127.0.0.1:<allocated-port>:4096 - OpenAI browser OAuth callback: conditional
127.0.0.1:1455:1455; when host port 1455 is occupied, browser auth is disabled while headless OAuth remains available - Per-process random
OPENCODE_SERVER_USERNAME/OPENCODE_SERVER_PASSWORD; all readiness, SDK, direct-fetch, and provider-auth calls send Basic auth - Provider OAuth is two-step: authorize, then await callback completion; successful auth disposes instance-scoped provider state before refreshing the catalog
- Readiness: authenticated
GET /session→ 200, followed by profile verification throughGET /mcp - Profile baked at
/opt/arc-opencode-profile(the app must NOT overrideOPENCODE_CONFIG_DIR/ARC_ROOT) - Canonical ARC path SHA-256 identities and ownership labels on containers and per-ARC auth/state/cache volumes
- No migration of legacy slug-named volumes; unlabelled or mismatched resources are never adopted or deleted
- Image source configurable: source build in the devcontainer or local-first registry pull in shipped builds
Env vars (set in .devcontainer/devcontainer.json remoteEnv):
| Var | Default | Purpose |
|---|---|---|
ARC_RUNTIME_MODE |
build |
build or pull (devcontainer forces build; shipped app defaults to pull) |
ARC_RUNTIME_REPO |
/workspace/ARC-Opencode-Runtime |
Path to the cloned runtime repo (build mode) |
ARC_RUNTIME_IMAGE |
arc-opencode-runtime:latest |
Local image tag; pull mode verifies this before contacting the registry |
ARC_RUNTIME_REGISTRY |
docker.io/zimmerd/arc-opencode-runtime:latest |
Registry ref (pull mode) |
ARC_ROOT |
/workspace/arcs/my-arc |
Sample ARC fixture (tests + default) |
OPENCODE_CONFIG_DIR |
/workspace/ARC-Opencode-Runtime/profile |
Temporary dev/profile tooling default; automated tests remove it |
MISTRAL_API_KEY |
from host | Forwarded into containers as AI provider credential |
cd electron
npm run dev:vnc # electron-vite dev server + hot reload under Xvfb/noVNC
npm run typecheck # type check all targets
npm run build # production build# Build the external runtime image (heavy: dotnet + scientific Python + chromium)
docker build -t arc-opencode-runtime:latest -f ARC-Opencode-Runtime/docker/Dockerfile ARC-Opencode-Runtime
# Or validate it independently with the runtime repo's own contract test
cd ARC-Opencode-Runtime && ARC_HOST_PATH=/workspace/arcs/my-arc bash ./scripts/validate-image.sh arc-opencode-runtime:latest 14097The devcontainer has no monitor, but it runs a virtual display (Xvfb) that you can view from your host machine via a browser or VNC client. This requires two separate terminals inside the devcontainer — Electron runs until the window is closed and must not be launched from a tool call that waits for completion.
Terminal 1 — start the virtual display and VNC server (run once):
./runtime/scripts/start-vnc.shThis starts Xvfb on :99, x11vnc on port 5900, and noVNC on port 6080.
Terminal 2 — launch the Electron app:
# Option A: dev mode with hot reload
cd electron
npm run dev:vnc
# Option B: launch the pre-built app directly
cd electron
npm run build # skip if out/ is already up to date
DISPLAY=:99 node_modules/.bin/electron out/main/index.js --no-sandbox --disable-gpuView the app from your host machine:
| Method | Address | Notes |
|---|---|---|
| Browser (noVNC) | http://localhost:6080 |
Click "Connect", no password |
| VNC client | localhost:5900 |
No password; use RealVNC, TightVNC, etc. |
Both ports are already forwarded by the devcontainer.
Stop everything:
# Stop VNC services:
./runtime/scripts/start-vnc.sh --stop
# Stop the Electron app: Ctrl+C in Terminal 2Agent constraint: agents must never launch the Electron binary from a tool call. Electron runs until the user closes the window, so launching it inside a tool call blocks that call until timeout.
See changelog.md for the historical record of completed phases. Pending work should live in normal issue tracking or direct task requests, not in a speculative phase plan document.
- Phase 0 — Cleanup: complete
- Phase 1 — Electron scaffold: complete
- Phase 2 — TypeScript runtime controller: complete
- Phase 3 — OpenCode SDK client layer: complete
- Phase 4 — Launcher MVP: complete
- Phase 5 — Assistant experience: complete
- Phase 6 — Theme and skin system: complete
- Phase 7 — Electron-native integrations: complete
- Phase 8 — Permission approval UI: complete
- Phase 9 — Rich message rendering: complete
- Phase 10 — Prompt UX enhancements: complete
- Phase 11 — Session lifecycle commands: complete
- Phase 12 — Model and provider management: complete
- Runtime Overhaul Phase A — Docker image plumbing: complete
- Runtime Overhaul Phase B — Container controller & ARC selection: complete
- Runtime Overhaul Phase C — ARC selection + 3-state frontend shell: complete
- Runtime Overhaul Phase D — Cut over & remove internal runtime: complete
- Runtime Overhaul Phase E — Tests rework: complete
- Runtime Overhaul Phase F — Hardening: complete
- Runtime Overhaul Phase G — Documentation: complete
- Production hardening Phases 1-4 — runtime ownership/security, renderer reliability, isolated tests, and installer CI: complete
- Production hardening Phase 5 — public versioned runtime image publication/cutover: pending
- Phase 13 — Share feature: pending
- Phase 14 — Settings panel: pending
- Phase 15 — Packaging and distribution: in progress
- OpenCode is pinned to
1.18.2across the devcontainer CLI, the runtime image, the global SDK install, and the Electron app dependency. Upgrade them in lockstep after validating the server and SDK contract. electron-vite@5requires Vite<=7. Use@vitejs/plugin-react@4.x.npmcache should be set explicitly inside the devcontainer:NPM_CONFIG_CACHE=/workspace/.npm-cache npm ...- The devcontainer uses the
docker-in-dockerfeature so the Electron main process can shell out todockerto build/run the runtime image. A devcontainer rebuild is required to activate Docker after adding/changing the feature. ARC_ROOTdefaults to/workspace/arcs/my-arcin the devcontainer env for app development. Playwright independently downloads a pinned public ArcPrototype revision intoelectron/e2e/test-data/, removes Git metadata, and copies that clean fixture to a disposable per-test ARC.OPENCODE_CONFIG_DIRstill defaults to/workspace/ARC-Opencode-Runtime/profilein the devcontainer for temporary dev/profile workflows. Automated tests explicitly remove it; integration uses an in-memory fake and LLM tests use an isolated local OpenCode process.runtime/state/is gitignored and holds runtime PID/log state only.- The current devcontainer forwards
MISTRAL_API_KEYfrom the host environment. It does not forwardGITHUB_TOKENorOPENAI_API_KEYanymore. - The renderer imports
@opencode-ai/sdk/client(sub-path) — the root export pulls inserver.js/node:child_processwhich breaks the sandboxed renderer bundle. Do not change to a root import. ARC-Opencode-Runtime/is a private external checkout used temporarily by devcontainer build mode and inspection. Tests no longer require it. It is not part of this repo's source tree and must not be committed; remove the build-mode/source-path dependency only when the public versioned image cutover is complete.- GUI debugging for text-only models — the
gui-debugMCP server (electron/gui-debug-mcp/, registered in repo-rootopencode.json) gives a text-only coding agent the ability to inspect and actuate the live app GUI. Three tools return text:gui_snapshot,gui_see, andgui_act. Connected mode starts local OpenCode and reaches it through thePLAYWRIGHT_TEST-gated fixture endpoint; it does not set an external profile path. Ensurenpm run buildis current. Do not launch Electron yourself from a tool call. NOT part of the shipped app. - Cross-compile Windows installer from Linux —
npm run dist:wininelectron/runselectron-vite build && electron-builder --win nsis:x64 --publish never. The pinned electron-builder receives a narrow postinstall patch that uses its bundled cross-platform uninstaller extractor instead of Wine. The output goes toelectron/dist/.