| name | project-structure |
| description | Use when orienting within the Hermes Agent CN Desktop codebase, locating source files, understanding the Tauri+React architecture, finding Rust commands or frontend modules, or navigating the monorepo. Triggers: project structure, ้กน็ฎ็ปๆ, where is, ๅจๅช้, codebase overview, ไปฃ็ ๆฆ่ง, repository layout, how is this repo organized, ไปๅบ็ปๆ, workspace layout. |
Hermes Agent CN Desktop โ Project Structure
Overview
Hermes Agent CN Desktop is a standalone desktop application built with Tauri v2 + React, replacing the original Electron shell. It pairs with the Hermes-CN-Core backend runtime (the CN community runtime, originally named hermes-agent-cn).
- Version: 0.6.3
- Bundle identifier:
cn.org.hermesagent.desktop (DO NOT CHANGE โ used for upgrade path)
- Rust crate name:
hermes_agent_cn (lib), hermes-agent-cn-desktop (package)
- Package manager: pnpm 9.15.0 (monorepo)
The desktop-managed runtime defaults to port 9120, avoiding port 9119 used by the user's global Hermes Agent.
Top-Level Directory Layout
Hermes-CN-Desktop/
โโโ src/ Rust Tauri backend (~24,000 lines)
โโโ web/ React frontend (Vite + TanStack Query + Jotai)
โโโ packages/
โ โโโ protocol/ Shared Zod schemas, IPC types, session log parsing
โ โโโ shared-ui/ Design tokens, shared React components, hooks
โโโ e2e/ Playwright E2E (real web โ real Core backend โ fake model)
โโโ tests/ Rust integration tests (crate: hermes_agent_cn)
โโโ scripts/ Build & dev automation scripts (.mjs)
โโโ static/ Stage targets for bundled builds
โ โโโ bundled-runtime/ Managed runtime binaries
โ โโโ bundled-skills/ Bundled skill packs
โ โโโ bundled-plugins/ Bundled plugins
โ โโโ dashboard/ Dashboard web dist
โโโ docs/ Project documentation & PRD specs
โโโ icons/ App icons (Windows/macOS/Linux)
โโโ installer/ NSIS installer customization
โโโ legal/ EULA & license files
โโโ gen/schemas/ Generated JSON schemas
โโโ capabilities/ Tauri capability definitions
โโโ .github/workflows/ CI/CD pipelines
โโโ Cargo.toml Rust dependencies & build config
โโโ tauri.conf.json Tauri window/bundle/CSP configuration
โโโ pnpm-workspace.yaml pnpm monorepo (web + packages/* + e2e)
โโโ package.json Workspace root scripts
Rust Backend (src/)
The Rust crate hermes_agent_cn is the Tauri backend. All source lives under src/.
Entry Points & Core Modules
| File | Purpose |
|---|
main.rs | Entry point: resolves HERMES_HOME, starts dashboard, registers 60 commands (generate_handler!), system tray |
lib.rs | Library root: declares 18 public modules |
state.rs | AppState (Mutex<AppStateInner>) โ shared state injected into every Tauri command |
error.rs | AppError unified domain error type (thiserror + Serialize) |
tray.rs | System tray menu |
Bootstrap & Environment
| File | Purpose |
|---|
bootstrap.rs | Startup sequence: probe/spawn dashboard, connect backend, fetch token |
environment.rs | Environment resolution and validation |
connection.rs | Connection backend + mode (local/remote), remote-mode WS connection |
path_resolver.rs | PATH/PATHEXT resolution for child processes |
env_file.rs | Parse $HERMES_HOME/.env for per-spawn child env injection |
Runtime & Process Management
| File | Purpose |
|---|
supervisor.rs | Child process supervision |
prevent_sleep.rs | Keep-awake during long-running tasks |
cron_runs.rs | Scheduled task orchestration |
update_stage.rs | Update staging logic |
process/ | Subprocess management |
process/dashboard.rs | Dashboard subprocess: probe/spawn/port fallback |
process/gateway.rs | Gateway subprocess + conflict detection |
process/runtime.rs | Managed runtime install/signature verification |
process/instance.rs | Single-instance guard per runtime root |
process/port_lock.rs | Port lock management |
Session & Logging
| File | Purpose |
|---|
session_archive.rs | Session archiving |
session_log.rs | Session log reading |
oauth_session.rs | OAuth session handling |
Other Core Modules
| File | Purpose |
|---|
coding_agents.rs | Coding agent management |
desktop_control.rs | Desktop-level controls |
ui_store.rs | UI state persistence |
util.rs | Shared utilities |
Commands (src/commands/) โ 60 Tauri IPC Commands
| Module | Purpose |
|---|
api_proxy.rs | HTTP proxy: api_request, external_request, upload_file |
ws_proxy.rs | /api/ws WebSocket relay (fallback when webview native WS blocked) |
gateway.rs | Runtime config + gateway URL refresh |
runtime_manager.rs | Managed runtime download/update/rollback |
desktop_update.rs | Desktop self-update |
profiles.rs | Profile switching (incl. fault recovery) |
config_migration.rs | Configuration migration |
im_onboarding.rs | Feishu/DingTalk/WeCom/WeChat onboarding |
connection.rs | Connection management commands |
connection_auth.rs | OAuth-based connection authentication |
backup.rs | Backup operations |
memory.rs | Memory management |
skills.rs | Skill management (hidden from mod.rs) |
terminal.rs | Embedded terminal (portable-pty) |
log_export.rs | Log export |
debug_bundle.rs | Debug bundle generation |
notify.rs | Desktop notifications |
preview.rs | File preview with native filesystem watch |
environment.rs | Environment variables |
file_dialogs.rs | Native file dialogs |
restart.rs | App restart |
ui_store.rs | UI state persistence commands |
yolo.rs |
React Frontend (web/)
Built with Vite + React 19 + TanStack Query + Jotai. CSS Modules (no Tailwind).
Key Files & Directories
web/src/
โโโ main.tsx App entry
โโโ App.tsx Root component
โโโ lib/ Core library (~156 files, most with co-located tests)
โ โโโ tauri-bridge.ts Tauri invoke wrapper + hermesDesktop shim
โ โโโ runtime.ts Platform detection (web / electron / tauri)
โ โโโ transport.ts HTTP routing (native IPC vs fetch) + auth header injection
โ โโโ gateway-client.ts Gateway WS client (JSON-RPC over /api/ws, backoff/reconnect)
โ โโโ gateway-socket-path.ts Native WS vs Rust relay socket path selection
โ โโโ ... ~150 other lib modules (models, skills, sessions, etc.)
โโโ hooks/ React hooks (~42 files)
โ โโโ use-gateway.ts Gateway WebSocket connection hook
โ โโโ use-config.ts Configuration hook
โ โโโ use-sessions.ts Sessions management
โ โโโ use-skills.ts Skills management
โ โโโ ... Many more domain hooks
โโโ stores/ Jotai atoms (~13 files)
โ โโโ chat.ts Chat state
โ โโโ panel.ts Panel state
โ โโโ ui.ts UI state
โ โโโ ...
โโโ routes/ Page components (~39 files)
โ โโโ guide.tsx Onboarding guide
โ โโโ health.tsx Health dashboard
โ โโโ settings.tsx Settings page
โ โโโ chat.tsx Chat interface
โ โโโ ... Many more page routes
โโโ components/ UI components
โ โโโ app-shell/ App shell layout
โ โโโ chat/ Chat components (incl. preview-rail)
โ โโโ composer/ Message composer (incl. workspace-picker)
โ โโโ console/ Console/terminal
โ โโโ settings/ Settings panels
โ โโโ sidebar/ Sidebar navigation
โ โโโ top-bar/ Top navigation bar
โ โโโ command-palette/ Command palette (โK)
โ โโโ mcp/ MCP server management
โ โโโ profiles/ Profile management
โ โโโ projects/ Project/workspace management
โ โโโ session-actions/ Session actions
โ โโโ panel/ Panel components
โ โโโ brand/ Branding components
โ โโโ ui/ Generic UI primitives
โโโ styles/ Global styles
โโโ types/ TypeScript type definitions
โโโ assets/ Static assets (incl. provider-icons)
State Management Strategy
| Layer | Technology |
|---|
| Server state (REST API data) | TanStack Query |
| Local / real-time stream state | Jotai atoms |
| Rust-side state | AppState (Mutex<AppStateInner>) via tauri::State |
Key Architecture Patterns
- Transport: All HTTP requests go through
transport.ts (auth header injection, native IPC vs fetch routing). NEVER hand-write fetch elsewhere.
- Gateway: JSON-RPC over WebSocket at
/api/ws. Use use-gateway.ts hook, never call gateway-client.ts raw socket directly.
- Tauri Bridge:
tauri-bridge.ts mounts Tauri invoke wrappers onto window.hermesDesktop at startup. Existing code checking window.hermesDesktop?.someMethod works unchanged.
- CSS: CSS Modules only โ no Tailwind, no styled-components. Design tokens in
packages/shared-ui/src/tokens/.
Packages (packages/)
@hermes/protocol
Shared type definitions and validation:
- Zod schemas for the Hermes API (
hermes-api.ts)
- IPC types
- Session log parsing (
session-log.ts)
- MCP API schemas
- Channel types
@hermes/shared-ui
Shared UI primitives:
- Design tokens (
tokens/): colors, typography, spacing, motion, z-index, component tokens, semantic tokens, primitives
- Components: alert, badge, button, card, copy-button, empty-state, field, input
- Composites: dialog, popover
- Hooks
- Utilities
Testing
Unit Tests (Vitest)
- ~93 test files across the monorepo
web/src/lib/ โ most modules have co-located .test.ts files
packages/protocol/ โ Zod schema tests
- Run:
pnpm test:unit (serial per workspace)
Rust Integration Tests (tests/)
- Crate name:
hermes_agent_cn
- Mock-based:
wiremock for HTTP, tempfile::TempDir for FS
- Tests:
api_proxy.rs, connection_config.rs, connection_ws_e2e.rs, dashboard_probe.rs, dashboard_spawn_retry.rs, dashboard_token.rs, runtime_manifest.rs
- Run:
cargo test --all-features
E2E Tests (e2e/)
- Playwright (real web โ real Core backend โ local fake model)
- Specs: chat-loop, guide-layout, image-paste, models-cli-custom-provider, skills-provenance
- Fake model server:
e2e/fake-model/server.py
- Harness: config, global-warmup, protocol-smoke, start-backend, wait
- Run:
pnpm test:e2e
Scripts (scripts/)
| Script | Purpose |
|---|
tauri-dev-managed.mjs | Install backend into dev-runtime, launch Tauri dev |
tauri-dev-external.mjs | Tauri dev with external backend |
install-local-runtime.mjs | Copy Hermes-CN-Core into dev-runtime |
install-release-dmg.mjs | Download & install release DMG |
stage-bundled-runtime.mjs | Stage runtime for bundled build |
stage-dashboard-web-dist.mjs | Stage dashboard web dist |
stage-bundled-skills.mjs | Stage bundled skills |
stage-bundled-plugins.mjs | Stage bundled plugins |
sync-desktop-version.mjs | Sync version across packages |
generate-license-rtf.mjs | Generate license RTF |
migrate-runtime-trees.mjs | Migrate polluted runtime trees |
package-portable-windows.mjs | Create Windows portable package |
package-portable-macos.mjs | Create macOS portable package |
only-pnpm.mjs | Enforce pnpm as package manager |
cdp-eval.mjs | Chrome DevTools Protocol evaluation |
Configuration Files
| File | Purpose |
|---|
Cargo.toml | Rust dependencies (tauri 2, tokio, reqwest, rusqlite, etc.) |
tauri.conf.json | Tauri window config, CSP, bundle targets (NSIS/DMG/deb/AppImage) |
package.json | Root workspace scripts (version sync, build, test) |
pnpm-workspace.yaml | Workspace members: web, packages/*, e2e |
web/vite.config.ts | Vite config (dev server on port 9545, strictPort) |
CI/CD (.github/workflows/)
| Workflow | Trigger | Purpose |
|---|
rust-test.yml | PR / push to main | cargo fmt --check, cargo clippy -D warnings, cargo test |
web-test.yml | PR / push to main | TypeScript typecheck + vitest unit tests |
web-e2e.yml | PR / push to main | Playwright E2E (checkout Hermes-CN-Core + fake model) |
release-desktop.yml | Tag push | Release build & publish |
Documentation (docs/)
| File | Content |
|---|
agents/ | Coding-agent policy + human git workflow๏ผ็ผ็ ไปฃ็ไธๆง่ก git ๅๆไฝ๏ผไบบๅทฅๅไปๅๆญฅ/worktree/commit/push/PR/tag/Landing ๅๆญฅ่ง git-workflow.md๏ผ |
desktop-prd/ | Product Requirements Document (6 docs: feature inventory, PRD, IA, specs, backend contract, parity gap) |
gateway-connection-overhaul.md | Gateway connection architecture |
managed-runtime.md | Managed runtime design |
hot-update.md | Hot update design๏ผ็ปไธ่ชๆดๆฐ + UI ็ญๆด + ๅผๅ็ญๆด + ไฝฟ็จ/้ช่ฏ๏ผ |
macos-signing-and-notarization.md | macOS code signing |
portable-mode.md | Portable mode |
yolo-mode.md | YOLO mode |
custom-model-context-window.md | Custom model context window |
Port Conventions
| Port | Purpose |
|---|
| 9120 | Hermes Dashboard (desktop managed runtime) |
| 9119 | User global Hermes Agent (AVOID โ managed runtime only) |
| 9545 | Vite dev server (strictPort) |
Dev vs Production Mode
| Aspect | Dev | Production |
|---|
| WebView loads | http://localhost:9545 (Vite) | Bundled web/dist/ |
| REST API | Vite proxy โ dashboard (same-origin) | Rust IPC proxy (api_request command) |
| Gateway events | WebSocket โ Vite proxy /api/ws | Official /api/ws, fallback to Rust WS relay (ws_proxy.rs) |
| Session token | Vite /__hermes_token endpoint | Rust get_runtime_config command |
apiBaseUrl | Not set (relative path) | Set to dashboard URL |
Rust Testing Conventions
- Unit tests:
#[cfg(test)] mod tests { ... } inline in source files; can access private functions
- Integration tests: In
tests/ directory, only use pub API via hermes_agent_cn crate
- Env-dependent tests: Must use
#[serial_test::serial]
- Filesystem tests: Use
tempfile::TempDir; never write to /tmp, cwd, or fixed paths
- HTTP tests: Use
wiremock::MockServer; never real network
- Assertions: Prefer
pretty_assertions::assert_eq
- Pre-commit:
cargo test --all-features
Key Dependencies
Rust
- Tauri v2 with
tray-icon and devtools features
- tokio (full), reqwest (rustls-tls), tokio-tungstenite
- tauri-plugin-dialog, tauri-plugin-notification, tauri-plugin-clipboard-manager
- rusqlite (bundled), zip, sha2, ed25519-dalek
- thiserror, serde/serde_json, portable-pty, notify
- Platform: windows-sys + winreg (Windows), objc2 (macOS)
Frontend
- React 19, react-router 7, TanStack Query 5, Jotai 2
- @tauri-apps/api, @tauri-apps/plugin-clipboard-manager
- streamdown (Markdown renderer with CJK/math/mermaid extensions)
- Radix UI (dialog, dropdown-menu, popover)
- xterm (terminal), cmdk (command palette), recharts, lucide-react
Architecture Rules (DO NOT Violate)
- โ Don't hand-write
fetch outside web/src/lib/transport.ts โ auth header injection lives there
- โ Don't call
gateway-client.ts raw socket directly โ use hooks/use-gateway.ts
- โ Don't put business logic in
web/src/routes/ โ extract to hooks/ or lib/
- โ Don't hardcode colors in components โ use CSS variables from
packages/shared-ui/src/tokens/
- โ Don't change the bundle identifier
cn.org.hermesagent.desktop
- โ Don't use port 9119 (reserved for user's global Hermes Agent)
Commit Convention
- Conventional Commits:
feat / fix / style / docs / refactor / chore
- English subject line, imperative mood ("add ...", "fix ...", "rework ...")
- Description can mix Chinese/English; explain "why" not "what"