Files
seastem-electronjs/apps/desktop/AGENTS.md

5.0 KiB

AGENTS.md - Desktop App Guidelines

Thin Electrobun shell hosting the fullstack server app.

Tech Stack

⚠️ This project uses Bun — NOT Node.js / npm. All commands use bun. Never use npm, npx, or node.

  • Type: Electrobun desktop application
  • Design: Server-driven desktop (thin native shell hosting web app)
  • Runtime: Bun (Main process) + CEF (Chromium Embedded Framework)
  • Framework: Electrobun
  • Build: Electrobun CLI + Turborepo

Architecture

  • Server-driven design: The desktop app is a "thin" native shell. It does not contain UI or business logic; it merely hosts the apps/server TanStack Start application in a native window.
  • Dev mode: Connects to an external Vite dev server at localhost:3000. Requires apps/server to be running separately.
  • Prod mode: Spawns an embedded TanStack Start server (Nitro) as a child process and loads the dynamically assigned local URL.
  • Config: electrobun.config.ts manages app metadata (identifier, name), build entries, and asset bundling.

Commands

# Development
bun dev                    # Start Electrobun dev mode (requires server dev running)

# Build
bun build                  # Build stable release (all platforms)

# Code Quality
bun fix                    # Biome auto-fix
bun typecheck              # TypeScript check

Directory Structure

.
├── src/
│   └── bun/
│       └── index.ts       # Main process entry (Window management + server lifecycle)
├── electrobun.config.ts   # App metadata and build/copy configuration
├── package.json           # Scripts and dependencies
├── turbo.json             # Build pipeline dependencies (depends on server build)
└── AGENTS.md              # Desktop guidelines (this file)

Development Workflow

  1. Start server: In apps/server, run bun dev.
  2. Start desktop: In apps/desktop, run bun dev.
  3. Connection: The desktop app polls localhost:3000 until responsive, then opens the native window.

Production Architecture

Build Pipeline

The desktop build is orchestrated by Turbo. It depends on the server's production build:

  • turbo.json: @furtherverse/desktop#build depends on @furtherverse/server#build.
  • electrobun.config.ts: Copies ../server/.output to server-output folder within the app bundle.

Server Lifecycle

In production, the main process manages the embedded server:

  • Spawn: Spawns server from server-output/server/index.mjs using Bun.spawn.
  • Port Allocation: A free port is pre-allocated via node:net (createServer on 127.0.0.1:0), then passed to the server as the PORT environment variable.
  • Readiness Check: The main process polls the server URL with fetch until it responds, rather than parsing stdout.
  • Retry: If the server fails to become ready (timeout or early exit), the process is killed and a new attempt is made with a fresh port (up to 3 retries).
  • Lifecycle: The server process is tied to the app; it is killed on SIGTERM, SIGINT, or app exit. If the server process crashes, the app exits with an error.

Environment Detection

The application determines its environment via the ELECTROBUN_BUILD_ENV variable, automatically set by the Electrobun CLI:

const isDev = () => {
  const env = process.env.ELECTROBUN_BUILD_ENV
  return !env || env === 'dev'
}
  • dev: Development mode (connects to external host).
  • canary / stable: Production mode (starts embedded server).

Environment Variables

  • ELECTROBUN_BUILD_ENV: Auto-set by CLI. Determines whether to use local dev server or embedded server.
  • DATABASE_URL: Required by the server process. Must be passed through from the parent environment to the spawned child process.

Electrobun Patterns

BrowserWindow Configuration

The main window uses the CEF renderer for consistency across platforms.

new BrowserWindow({
  title: 'Furtherverse',
  url: serverUrl,
  frame: {
    x: 100,
    y: 100,
    width: 1200,
    height: 800,
  },
  renderer: 'cef',
})

Path Aliases

The main process uses electrobun/bun for native APIs and PATHS for locating bundled assets.

import { BrowserWindow, PATHS } from 'electrobun/bun'

// Locate the embedded server bundle
const serverEntryPath = join(PATHS.VIEWS_FOLDER, '..', 'server-output', 'server', 'index.mjs')

Critical Rules

DO:

  • Use arrow functions for all components and utility functions.
  • Ensure apps/server is built before building apps/desktop (handled by Turbo).
  • Pre-allocate a free port and pass it via PORT env var instead of parsing stdout.
  • Handle server process termination to avoid orphan processes.
  • Use catalog: for all dependency versions in package.json.

DON'T:

  • Use npm, npx, node, yarn, or pnpm. Always use bun.
  • Hardcode localhost:3000 for production builds.
  • Include UI components or business logic in the desktop app (keep it thin).
  • Use as any or @ts-ignore.