Files
NomadArch/dev-docs/architecture.md
Gemini AI 157449a9ad restore: recover deleted documentation, CI/CD, and infrastructure files
Restored from origin/main (b4663fb):
- .github/ workflows and issue templates
- .gitignore (proper exclusions)
- .opencode/agent/web_developer.md
- AGENTS.md, BUILD.md, PROGRESS.md
- dev-docs/ (9 architecture/implementation docs)
- docs/screenshots/ (4 UI screenshots)
- images/ (CodeNomad icons)
- package-lock.json (dependency lockfile)
- tasks/ (25+ project task files)

Also restored original source files that were modified:
- packages/ui/src/App.tsx
- packages/ui/src/lib/logger.ts
- packages/ui/src/stores/instances.ts
- packages/server/src/server/routes/workspaces.ts
- packages/server/src/workspaces/manager.ts
- packages/server/src/workspaces/runtime.ts
- packages/server/package.json

Kept new additions:
- Install-*.bat/.sh (enhanced installers)
- Launch-*.bat/.sh (new launchers)
- README.md (SEO optimized with GLM 4.7)
2025-12-23 13:03:48 +04:00

11 KiB

CodeNomad Architecture

Overview

CodeNomad is a cross-platform desktop application built with Electron that provides a multi-instance, multi-session interface for interacting with OpenCode servers. Each instance manages its own OpenCode server process and can handle multiple concurrent sessions.

High-Level Architecture

┌─────────────────────────────────────────────────────────┐
│                    Electron Main Process                │
│  - Window management                                    │
│  - Process spawning (opencode serve)                    │
│  - IPC bridge to renderer                               │
│  - File system operations                               │
└────────────────┬────────────────────────────────────────┘
                 │ IPC
┌────────────────┴────────────────────────────────────────┐
│                  Electron Renderer Process              │
│  ┌──────────────────────────────────────────────────┐  │
│  │              SolidJS Application                 │  │
│  │  ┌────────────────────────────────────────────┐  │  │
│  │  │  Instance Manager                          │  │  │
│  │  │  - Spawns/kills OpenCode servers           │  │  │
│  │  │  - Manages SDK clients per instance        │  │  │
│  │  │  - Handles port allocation                 │  │  │
│  │  └────────────────────────────────────────────┘  │  │
│  │  ┌────────────────────────────────────────────┐  │  │
│  │  │  State Management (SolidJS Stores)         │  │  │
│  │  │  - instances[]                             │  │  │
│  │  │  - sessions[] per instance                 │  │  │
│  │  │  - normalized message store per session    │  │  │
│  │  └────────────────────────────────────────────┘  │  │
│  │  ┌────────────────────────────────────────────┐  │  │
│  │  │  UI Components                             │  │  │
│  │  │  - InstanceTabs                            │  │  │
│  │  │  - SessionTabs                             │  │  │
│  │  │  - MessageSection                        │  │  │
│  │  │  - PromptInput                             │  │  │
│  │  └────────────────────────────────────────────┘  │  │
│  └──────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────┘
                 │ HTTP/SSE
┌────────────────┴────────────────────────────────────────┐
│           Multiple OpenCode Server Processes            │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  │
│  │ Instance 1   │  │ Instance 2   │  │ Instance 3   │  │
│  │ Port: 4096   │  │ Port: 4097   │  │ Port: 4098   │  │
│  │ ~/project-a  │  │ ~/project-a  │  │ ~/api        │  │
│  └──────────────┘  └──────────────┘  └──────────────┘  │
└─────────────────────────────────────────────────────────┘

Component Layers

1. Main Process Layer (Electron)

Responsibilities:

  • Create and manage application window
  • Spawn OpenCode server processes as child processes
  • Parse server stdout to extract port information
  • Handle process lifecycle (start, stop, restart)
  • Provide IPC handlers for renderer requests
  • Manage native OS integrations (file dialogs, menus)

Key Modules:

  • main.ts - Application entry point
  • process-manager.ts - OpenCode server process spawning
  • ipc-handlers.ts - IPC communication handlers
  • menu.ts - Native application menu

2. Renderer Process Layer (SolidJS)

Responsibilities:

  • Render UI components
  • Manage application state
  • Handle user interactions
  • Communicate with OpenCode servers via HTTP/SSE
  • Real-time message streaming

Key Modules:

  • App.tsx - Root component
  • stores/ - State management
  • components/ - UI components
  • contexts/ - SolidJS context providers
  • lib/ - Utilities and helpers

3. Communication Layer

HTTP API Communication:

  • SDK client per instance
  • RESTful API calls for session/config/file operations
  • Error handling and retries

SSE (Server-Sent Events):

  • One EventSource per instance
  • Real-time message updates
  • Event type routing
  • Reconnection logic

CLI Proxy Paths:

  • The CLI server terminates all HTTP/SSE traffic and forwards it to the correct OpenCode instance.
  • Each WorkspaceDescriptor exposes proxyPath (e.g., /workspaces/<id>/instance), which acts as the base URL for both REST and SSE calls.
  • The renderer never touches the random per-instance port directly; it only talks to window.location.origin + proxyPath so a single CLI port can front every session.

Data Flow

Instance Creation Flow

  1. User selects folder via Electron file dialog
  2. Main process receives folder path via IPC
  3. Main process spawns opencode serve --port 0
  4. Main process parses stdout for port number
  5. Main process sends port + PID back to renderer
  6. Renderer creates SDK client for that port
  7. Renderer fetches initial session list
  8. Renderer displays session picker

Message Streaming Flow

  1. User submits prompt in active session
  2. Renderer POSTs to /session/:id/message
  3. SSE connection receives MessageUpdated events
  4. Events are routed to correct instance → session
  5. Message state updates trigger UI re-render
  6. Messages display with auto-scroll

Child Session Creation Flow

  1. OpenCode server creates child session
  2. SSE emits SessionUpdated event with parentId
  3. Renderer adds session to instance's session list
  4. New session tab appears automatically
  5. Optional: Auto-switch to new tab

State Management

Instance State

instances: Map<instanceId, {
  id: string
  folder: string
  port: number
  pid: number
  proxyPath: string // `/workspaces/:id/instance`
  status: 'starting' | 'ready' | 'error' | 'stopped'
  client: OpenCodeClient
  eventSource: EventSource
  sessions: Map<sessionId, Session>
  activeSessionId: string | null
  logs: string[]
}>

Session State

Session: {
  id: string
  title: string
  parentId: string | null
  messages: Message[]
  agent: string
  model: { providerId: string, modelId: string }
  status: 'idle' | 'streaming' | 'error'
}

Message State

Message: {
  id: string
  sessionId: string
  type: 'user' | 'assistant'
  parts: Part[]
  timestamp: number
  status: 'sending' | 'sent' | 'streaming' | 'complete' | 'error'
}

Tab Hierarchy

Level 1: Instance Tabs

Each tab represents one OpenCode server instance:

  • Label: Folder name (with counter if duplicate)
  • Icon: Folder icon
  • Close button: Stops server and closes tab
  • "+" button: Opens folder picker for new instance

Level 2: Session Tabs

Each instance has multiple session tabs:

  • Main session tab (always present)
  • Child session tabs (auto-created)
  • Logs tab (shows server output)
  • "+" button: Creates new session

Tab Behavior

Instance Tab Switching:

  • Preserves session tabs
  • Switches active SDK client
  • Updates SSE event routing

Session Tab Switching:

  • Loads messages for that session
  • Updates agent/model controls
  • Preserves scroll position

Technology Stack

Core

  • Electron - Desktop wrapper
  • SolidJS - Reactive UI framework
  • TypeScript - Type safety
  • Vite - Build tool

UI

  • TailwindCSS - Styling
  • Kobalte - Accessible UI primitives
  • Shiki - Code syntax highlighting
  • Marked - Markdown parsing

Communication

  • OpenCode SDK - API client
  • EventSource - SSE streaming
  • Node Child Process - Process spawning

Error Handling

Process Errors

  • Server fails to start → Show error in instance tab
  • Server crashes → Attempt auto-restart once
  • Port already in use → Find next available port

Network Errors

  • API call fails → Show inline error, allow retry
  • SSE disconnects → Auto-reconnect with backoff
  • Timeout → Show timeout error, allow manual retry

User Errors

  • Invalid folder selection → Show error dialog
  • Permission denied → Show actionable error message
  • Out of memory → Graceful degradation message

Performance Considerations

Note: Performance optimization is NOT a focus for MVP. These are future considerations.

Message Rendering (Post-MVP)

  • Start with simple list rendering - no virtual scrolling
  • No message limits initially
  • Only optimize if users report issues
  • Virtual scrolling can be added in Phase 8 if needed

State Updates

  • SolidJS fine-grained reactivity handles most cases
  • No special optimizations needed for MVP
  • Batching/debouncing can be added later if needed

Memory Management (Post-MVP)

  • No memory management in MVP
  • Let browser/OS handle it
  • Add limits only if problems arise in testing

Security Considerations

  • No remote code execution
  • Server spawned with user permissions
  • No eval() or dangerous innerHTML
  • Sanitize markdown rendering
  • Validate all IPC messages
  • HTTPS only for external requests

Extensibility Points

Plugin System (Future)

  • Custom slash commands
  • Custom message renderers
  • Theme extensions
  • Keybinding customization

Configuration (Future)

  • Per-instance settings
  • Global preferences
  • Workspace-specific configs
  • Import/export settings