# AGENTS.md - Desktop App Guidelines Electrobun desktop shell — loads the server app in a native window. > **⚠️ This project uses Bun — NOT Node.js / npm. All commands use `bun`. Never use `npm`, `npx`, or `node`.** ## Architecture - **Type**: Electrobun desktop application - **Design**: Bun main process + CEF renderer (Chromium Embedded Framework) - **Dev mode**: Connects to `localhost:3000` (requires server dev running) - **Prod mode**: Embeds server bundle and starts local HTTP server - **Config**: `electrobun.config.ts` — app metadata, build entrypoint, platform options ## Commands ```bash # Development (from apps/desktop/) bun dev # Start Electrobun dev mode # Build bun build # Build canary release (current platform) bun build:all # Build canary release (all platforms) bun build:stable # Build stable release (current platform) bun build:stable:all # Build stable release (all platforms) # Code Quality bun fix # Biome auto-fix (lint + format) bun typecheck # TypeScript check ``` ## Directory Structure ``` apps/desktop/ ├── src/ │ └── bun/ │ └── index.ts # Main process entry (BrowserWindow + server wait) ├── electrobun.config.ts # App name, identifier, version, build config ├── package.json ├── tsconfig.json # Extends @furtherverse/tsconfig/bun.json └── turbo.json # Build output config ``` ## Development Workflow 1. **Start server dev first**: `bun dev` from `apps/server/` 2. **Start Electrobun**: `bun dev` from `apps/desktop/` 3. Desktop app polls `localhost:3000` until server responds, then opens window ## Electrobun Patterns ### Config (`electrobun.config.ts`) ```typescript import type { ElectrobunConfig } from 'electrobun' export default { app: { name: 'MyApp', identifier: 'com.example.myapp', version: '0.1.0', }, build: { bun: { entrypoint: 'src/bun/index.ts' }, linux: { bundleCEF: true }, }, } satisfies ElectrobunConfig ``` ### BrowserWindow ```typescript import { BrowserWindow } from 'electrobun/bun' new BrowserWindow({ title: 'My App', url: 'http://localhost:3000', frame: { x: 100, y: 100, width: 1200, height: 800 }, renderer: 'cef', }) ``` ### Server Readiness Check ```typescript // Poll server before opening window — don't block indefinitely async function waitForServer(url: string, timeoutMs = 30000): Promise { const start = Date.now() while (Date.now() - start < timeoutMs) { try { const response = await fetch(url, { method: 'HEAD' }) if (response.ok) return true } catch { await Bun.sleep(100) } } return false } ``` ### Application Events ```typescript import Electrobun from 'electrobun/bun' Electrobun.events.on('will-quit', () => { console.log('App quitting...') }) ``` ## Critical Rules **DO:** - Run server dev before desktop dev - Use `catalog:` for dependency versions - Wait for server readiness before opening BrowserWindow - Handle server timeout gracefully (exit with helpful error message) **DON'T:** - Use `npm`, `npx`, `node`, `yarn`, `pnpm` — always use `bun` / `bunx` - Hardcode dependency versions (use catalog) - Block main thread with synchronous waits - Use `unwrap()`-style patterns without error handling