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)
This commit is contained in:
312
dev-docs/architecture.md
Normal file
312
dev-docs/architecture.md
Normal file
@@ -0,0 +1,312 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user