-
Notifications
You must be signed in to change notification settings - Fork 3
electron_standalone_app_plan
A plan for packaging coding-agent-loop as a downloadable Mac app that runs the agent and workspace servers automatically—no browser or manual server startup required.
Today the app runs in a browser: users start the Go agent server (and optionally the workspace server), then open the frontend. This plan describes a standalone Electron app for Runloop that:
- Bundles the agent server and workspace server as binaries inside the app.
- Starts both servers automatically when the user launches the app.
- Opens a single window loading the existing React UI, which talks to localhost.
- Stops both servers when the user quits the app.
- Produces a shareable Mac package (e.g.
.dmg) that users can download and run.
Scope: Mac only (Apple Silicon primary; Intel/universal optional). Distribution is a single downloadable artifact (e.g. via GitHub Releases).
┌─────────────────────────────────────────────────────────────────┐
│ Electron App (user double-clicks) │
├─────────────────────────────────────────────────────────────────┤
│ Main Process (Node) │
│ 1. Check ports 45678 / 45679 availability │
│ 2. Spawn workspace-server (bundled binary) → port 45679 │
│ 3. Spawn agent-server (bundled binary) → port 45678 │
│ 4. Poll /health on both until ready │
│ 5. Create BrowserWindow → load http://127.0.0.1:45678 │
│ 6. On quit: kill both child processes │
├─────────────────────────────────────────────────────────────────┤
│ Renderer (existing React frontend) │
│ - API base URL: http://127.0.0.1:45678 │
│ - Workspace API URL: http://127.0.0.1:45679 │
└─────────────────────────────────────────────────────────────────┘
- Servers run on the user's machine as child processes of the Electron app.
-
Binaries live inside the app bundle (e.g.
MyApp.app/Contents/Resources/). - No Docker or extra runtime required; the app is self-contained.
| Step | Task | Notes |
|---|---|---|
| 1.1 | Add desktop/ package with Electron + electron-builder |
Done. package.json and main.js initialized. |
| 1.2 | Implement server spawn in main process | Done. Uses obscure ports 45678/45679. |
| 1.3 | Port Availability Check | Done. Uses detect-port to prevent conflicts. |
| 1.4 | Health-check and window launch | Done. Polls /health endpoints with timeout. |
| 1.5 | Cleanup on quit | Done. Kills children on quit/close. |
| Step | Task | Notes |
|---|---|---|
| 2.1 | Build agent server for Mac | Done. Compiled for darwin/arm64. |
| 2.2 | Build workspace server for Mac | Done. Compiled for darwin/arm64. |
| 2.3 | Workspace config and data dir | Done. Uses app.getPath('userData'). |
| 2.4 | Agent config | Done. Configured to talk to workspace on 45679. |
| Step | Task | Notes |
|---|---|---|
| 3.1 | Build frontend for Electron | Done. Built with VITE_API_BASE_URL pointing to 45678/45679. |
| 3.2 | Serve frontend from agent server | Done. Frontend files moved to agent_go/static/. |
| 3.3 | (Future) Dynamic port | Deferred as fixed obscure ports are working well. |
| Step | Task | Notes |
|---|---|---|
| 4.1 | electron-builder config | Target mac (dmg/zip). Include both Go binaries in extraResources. Point app at agent’s static dir. |
| 4.2 | Local Build Script | Created desktop/dev-setup.sh to build binaries and setup environment. |
| 4.3 | Verify Standalone App | ✅ Verified. Fixed hardcoded paths in workspace and agent servers. |
| 4.4 | Verify Port Conflict Handling | Verified logic in desktop/main.js. |
| Step | Task | Notes |
|---|---|---|
| 4.5.1 | Fix hardcoded paths | ensure agent/workspace accept flags for all paths (DB, logs, docs, data, mcp-config). Done for workspace/root.go (data-dir) and agent. |
| 4.5.2 | Verify read-only filesystem compatibility | ✅ Done. Agent/Workspace now write to userData (db, logs, data). Default config is copied to userData. |
| 4.5.3 | Bundle static assets | ✅ Done via extraResources in package.json. |
| Step | Task | Notes |
|---|---|---|
| 5.1 | Code Signing |
Skipped for local build. Requires Apple Developer ID identity. npm run dist produces a functional but unsigned .dmg. |
| 5.2 | Notarization | Skipped (requires signing). Users will need to right-click -> Open to bypass Gatekeeper on first launch. |
| 5.3 | CI/CD Pipeline |
✅ Done. GitHub Action .github/workflows/desktop-release.yml created and verified. Automatically builds DMG/ZIP on tag push. |
| 5.4 | Distribution | Artifacts created automatically in GitHub Releases. Version synced from git tag. |
- Status: Released.
- Notes: Dynamic ports implemented. Frontend sync features refined. Binaries built and packaged.
| Step | Task | Notes |
|---|---|---|
| 6.1 | README / install instructions |
Done. Updated desktop/README.md with build and run instructions. |
| 6.2 | In-app messaging | Polished error dialogs for startup failures (e.g. port conflicts). |
| 6.3 | CDP Connectivity Helper | Add a UI status/button to help users connect to their local Chrome via --remote-debugging-port=9222. |
| 6.4 | Logs and debugging |
Done. Updated main.js to pipe stdout/stderr from agent/workspace processes to userData/logs/agent.log and userData/logs/workspace.log. |
These steps are recommended for a commercial or wide public release to ensure robustness and security.
| Step | Task | Details |
|---|---|---|
| 7.1 | Dynamic Port Allocation | ✅ Done. Implemented in v1.21.0. Servers bind to port 0 (dynamic), Electron parses the assigned port from stdout, and injects it into the frontend via IPC. No more port conflicts or hardcoded ports. |
| 7.2 | IPC Authentication |
Critical for security. Generate a random session token in Electron on startup. Pass it to Go processes as an env var. Require this token in an Authorization header for all API requests to prevent unauthorized access from other local software/scripts. |
| 7.3 | Zombie Process Prevention | Reliability. Implement a "parent heartbeat" or PID monitoring in the Go servers. If the Electron parent process dies unexpectedly (crash/force quit), the Go servers should automatically shut down to prevent orphaned background processes. |
| 7.4 | Auto-Update | Integrate electron-updater to pull new releases from GitHub. |
The standalone Electron application has been successfully packaged and released as v1.21.0.
-
DMG:
Runloop-1.21.0-arm64.dmg -
ZIP:
Runloop-1.21.0-arm64-mac.zip
- Dynamic Port Allocation: Servers now bind to random free ports provided by the OS, eliminating port conflicts with other running instances or services.
-
Zero-Dependency Startup: Spawns
agent-serverandworkspace-serversidecar binaries automatically on launch. -
Filesystem Persistence: Configured Go servers to use
app.getPath('userData')for databases, logs, and search indices. -
Integrated Frontend: Frontend is served directly via the
agent-server. -
Reliable Dev Mode:
npm start(withDEV_URL) correctly connects to external backends on standard ports (8000/8081) while packaged app uses internal dynamic ports.
- Code Signing: To distribute outside of local environments, the build process needs an Apple Developer ID.
-
Universal Build: Configure
electron-builderto produce universal binaries (arm64 + x64) for broader Mac compatibility. - Security Hardening: Implement Phase 7.2 (IPC Authentication) to secure the local API.
coding-agent-loop/
desktop/ # New: Electron app
package.json # electron, electron-builder, main script
main.js # Main process: spawn servers, health check, window, cleanup
preload.js # Optional: expose API base URL to renderer
resources/ # Dev: place Go binaries here for local run
agent_go/
cmd/server/ # Agent server (build → agent-server for Mac)
static/ # Built frontend (Vite build output)
workspace/ # Workspace server (build → workspace-server for Mac)
frontend/ # Build with VITE_API_BASE_URL etc. for Electron
Packaged app (simplified):
MyApp.app/
Contents/
MacOS/Electron
Resources/
app.asar (or unpacked) # Electron app code
agent-server # Go binary
workspace-server # Go binary
-
Docker: Run agent and workspace via
docker composefrom the Electron app. Possible, but requires users to have Docker installed; not chosen for “download and run” simplicity. - Hosted backend: No local servers; app talks to cloud API. Not in scope for this plan; would be a different product.
- Windows / Linux: Out of scope for this plan; can be added later using the same pattern (build Go for each OS, electron-builder targets).
- User downloads a single Mac artifact (e.g.
.dmg) from a release page. - User opens the app; agent and workspace start automatically; window shows the existing UI.
- No browser, Docker, or manual server startup required.
- User quits the app; both servers stop; no lingering processes.
- (With signing/notarization) App opens without Gatekeeper blocking it.
- Current browser-based setup: Go server serves frontend from
./static/; frontend usesVITE_API_BASE_URLandVITE_WORKSPACE_API_URL(seefrontend/src/services/api.ts). - Workspace server:
workspace/, typically port 8081; docker-compose in repo root. - Agent server:
agent_go/cmd/server/server.go; static files from./static/.
Auto-synced from docs/ on main. Edit there, not here.