بنقرة واحدة
hermes-cn-desktop
Hermes Agent CN Desktop - Windows-first AI agent desktop app built with Tauri, TypeScript, and Rust
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Hermes Agent CN Desktop - Windows-first AI agent desktop app built with Tauri, TypeScript, and Rust
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Browser-based interface for viewing and filtering OpenClaw session tool call history with zero dependencies for local network deployment.
AI-powered quantitative research and backtesting platform with end-to-end workflow from research to strategy publication
Give your AI assistant a phone — OpenClaw plugin for real phone calls via Twilio + OpenAI Realtime API with in-call tools, transcripts, and call screening
Run multi-model consensus panels (Lite or Heavy) with your own agent backends—no hosted middleware, your models, your rules.
Build a multi-role JARVIS-style voice assistant with local ASR/TTS, OpenClaw LLM gateway, voice wake words, HUD effects, and speaker verification
Use 37 battle-tested marketing skills covering CRO, copywriting, SEO, paid ads, email, growth, and strategy with real data connectors for Google Ads, Search Console, Meta Ads, and X/Twitter
| name | hermes-cn-desktop |
| description | Hermes Agent CN Desktop - Windows-first AI agent desktop app built with Tauri, TypeScript, and Rust |
| triggers | ["build hermes agent desktop app","develop tauri desktop application","integrate hermes agent core","setup hermes cn desktop dev environment","create hermes agent ui components","manage hermes runtime installation","debug tauri rust commands","implement hermes agent features"] |
Skill by ara.so — Hermes Skills collection.
Hermes Agent CN Desktop is a native desktop client for Hermes Agent, built with Tauri v2, Rust, React, and TypeScript. It provides a Windows-first, cross-platform desktop experience with embedded Hermes Agent core runtime, supporting local and cloud LLM providers with Chinese ecosystem integration.
Hermes CN Desktop wraps the Hermes Agent dashboard in a native desktop application that:
# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Install Node.js 20+ and pnpm 9+
npm install -g pnpm
# macOS only: Install Xcode Command Line Tools
xcode-select --install
git clone https://github.com/Eynzof/Hermes-CN-Desktop.git
cd Hermes-CN-Desktop
pnpm install
You need a running Hermes Dashboard. Either:
Option A: Use Hermes-CN-Core
# Clone and run Hermes-CN-Core in parallel
git clone https://github.com/Eynzof/Hermes-CN-Core.git
cd Hermes-CN-Core
hermes dashboard --host 127.0.0.1 --port 9120 --no-open
Option B: Use local Hermes CLI
hermes dashboard --host 127.0.0.1 --port 9120 --no-open
Start desktop dev mode:
# Terminal 1: Start Vite dev server
pnpm web:dev
# Terminal 2: Run Tauri app
cargo run
# Or use combined command
pnpm tauri:dev
| Command | Description |
|---|---|
pnpm web:dev | Start Vite dev server (port 9545) |
cargo run | Compile and run Tauri desktop window |
pnpm tauri:dev | Combined Tauri dev mode (auto-starts Vite) |
pnpm typecheck | Run TypeScript type checking |
pnpm test:unit | Run Vitest unit tests |
cargo check | Run Rust compile check |
cargo test --all-features | Run Rust tests |
# Production build for current platform
pnpm tauri:build
# Debug build with symbols
pnpm tauri:build:debug
Build artifacts:
target/release/bundle/target/debug/bundle/# Frontend checks
pnpm typecheck
pnpm test:unit
# Rust checks
cargo fmt --all -- --check
cargo clippy --all-targets -- -D warnings
cargo test --all-features --no-fail-fast
hermes-cn-desktop/
├── src/ # Rust backend (Tauri commands, process mgmt)
│ ├── main.rs # Entry point
│ ├── commands/ # Tauri IPC commands
│ ├── runtime/ # Runtime manager
│ └── proxy/ # REST/SSE proxy layer
├── web/ # React frontend (Vite + TanStack)
│ ├── src/
│ │ ├── components/ # UI components
│ │ ├── hooks/ # React hooks
│ │ ├── stores/ # Jotai state
│ │ └── api/ # API clients
│ └── vite.config.ts
├── packages/
│ ├── protocol/ # Zod schemas, API contracts
│ └── shared-ui/ # Design tokens, shared components
├── static/ # Bundled Dashboard & runtime
├── scripts/ # Build & staging scripts
└── tauri.conf.json # Tauri configuration
tauri.conf.json){
"productName": "Hermes Agent CN Desktop",
"identifier": "cn.hermesagent.desktop",
"build": {
"beforeDevCommand": "pnpm web:dev",
"beforeBuildCommand": "pnpm web:build",
"devUrl": "http://localhost:9545",
"frontendDist": "../web/dist"
},
"windows": [
{
"title": "Hermes Agent CN",
"width": 1200,
"height": 800,
"minWidth": 800,
"minHeight": 600
}
]
}
# Development
VITE_API_BASE_URL=http://127.0.0.1:9120
VITE_DEV_MODE=true
# Runtime configuration (passed to Hermes Core)
HERMES_YOLO_MODE=false # Auto-approve dangerous commands
HERMES_PORT=9120
HERMES_HOST=127.0.0.1
// src/commands/runtime.rs
use tauri::State;
use crate::runtime::RuntimeManager;
#[tauri::command]
pub async fn start_runtime(
runtime_manager: State<'_, RuntimeManager>,
) -> Result<String, String> {
runtime_manager
.start()
.await
.map(|pid| format!("Runtime started with PID: {}", pid))
.map_err(|e| format!("Failed to start runtime: {}", e))
}
#[tauri::command]
pub async fn get_runtime_health(
runtime_manager: State<'_, RuntimeManager>,
) -> Result<serde_json::Value, String> {
runtime_manager
.health_check()
.await
.map_err(|e| e.to_string())
}
// web/src/components/RuntimeStatus.tsx
import { invoke } from '@tauri-apps/api/core';
import { useQuery } from '@tanstack/react-query';
export function RuntimeStatus() {
const { data: health, isLoading } = useQuery({
queryKey: ['runtime-health'],
queryFn: async () => {
return await invoke<HealthStatus>('get_runtime_health');
},
refetchInterval: 5000,
});
if (isLoading) return <div>Loading runtime status...</div>;
return (
<div className="runtime-status">
<h3>Runtime Health</h3>
<div>Status: {health?.status}</div>
<div>Version: {health?.version}</div>
<div>Uptime: {health?.uptime}s</div>
</div>
);
}
// web/src/api/runtime.ts
import { z } from 'zod';
const RuntimeConfigSchema = z.object({
port: z.number(),
host: z.string(),
yolo_mode: z.boolean(),
});
export type RuntimeConfig = z.infer<typeof RuntimeConfigSchema>;
export async function updateRuntimeConfig(
config: RuntimeConfig
): Promise<void> {
const validated = RuntimeConfigSchema.parse(config);
await fetch('/api/runtime/config', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(validated),
});
}
// src/commands/proxy.rs
use reqwest::Client;
use serde_json::Value;
#[tauri::command]
pub async fn proxy_request(
url: String,
method: String,
body: Option<Value>,
) -> Result<Value, String> {
let client = Client::new();
let request = match method.as_str() {
"GET" => client.get(&url),
"POST" => client.post(&url).json(&body),
"PUT" => client.put(&url).json(&body),
"DELETE" => client.delete(&url),
_ => return Err("Unsupported HTTP method".to_string()),
};
let response = request
.send()
.await
.map_err(|e| e.to_string())?;
response
.json::<Value>()
.await
.map_err(|e| e.to_string())
}
// web/src/stores/settings.ts
import { atom } from 'jotai';
import { atomWithStorage } from 'jotai/utils';
export const themeAtom = atomWithStorage<'light' | 'dark'>(
'hermes-theme',
'light'
);
export const yoloModeAtom = atom(false);
export const modelProviderAtom = atomWithStorage(
'hermes-model-provider',
{
provider: 'openai',
apiKey: '',
baseUrl: '',
}
);
// src/runtime/manager.rs
use std::process::{Child, Command};
use tokio::sync::RwLock;
pub struct RuntimeManager {
process: RwLock<Option<Child>>,
config_path: PathBuf,
}
impl RuntimeManager {
pub async fn start(&self) -> Result<u32, RuntimeError> {
let mut process_lock = self.process.write().await;
if process_lock.is_some() {
return Err(RuntimeError::AlreadyRunning);
}
let child = Command::new("hermes")
.arg("start")
.arg("--config")
.arg(&self.config_path)
.spawn()?;
let pid = child.id();
*process_lock = Some(child);
Ok(pid)
}
pub async fn stop(&self) -> Result<(), RuntimeError> {
let mut process_lock = self.process.write().await;
if let Some(mut child) = process_lock.take() {
child.kill()?;
child.wait()?;
}
Ok(())
}
}
// web/src/hooks/useRuntimeStatus.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { invoke } from '@tauri-apps/api/core';
export function useRuntimeStatus() {
const queryClient = useQueryClient();
const status = useQuery({
queryKey: ['runtime-status'],
queryFn: () => invoke<RuntimeStatus>('get_runtime_status'),
refetchInterval: 3000,
});
const start = useMutation({
mutationFn: () => invoke('start_runtime'),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['runtime-status'] });
},
});
const stop = useMutation({
mutationFn: () => invoke('stop_runtime'),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['runtime-status'] });
},
});
return { status, start, stop };
}
// web/src/api/streaming.ts
import { invoke } from '@tauri-apps/api/core';
export async function streamChatResponse(
message: string,
onChunk: (chunk: string) => void,
onError: (error: string) => void
) {
try {
await invoke('stream_chat', {
message,
onChunk: (chunk: string) => {
onChunk(chunk);
},
});
} catch (error) {
onError(error as string);
}
}
# Check runtime binary exists
ls static/runtime/
# Verify runtime permissions (macOS/Linux)
chmod +x static/runtime/hermes
# Check runtime logs
tail -f ~/.hermes/logs/runtime.log
# Test runtime independently
./static/runtime/hermes start --debug
Development mode runs Vite on localhost:9545 accessing Dashboard on 127.0.0.1:9120. Use proxy command:
// Instead of direct fetch
const response = await fetch('http://127.0.0.1:9120/api/chat');
// Use Tauri proxy
const response = await invoke('proxy_request', {
url: 'http://127.0.0.1:9120/api/chat',
method: 'GET',
});
# Clear build cache
cargo clean
rm -rf web/dist web/node_modules/.vite
# Reinstall dependencies
pnpm install
# Check Rust toolchain
rustup update stable
# Check Node version
node -v # Should be 20+
# Development: disable code signing temporarily
export TAURI_SKIP_DEVSERVER_PROMPT=true
# Production: configure in tauri.conf.json
{
"bundle": {
"macOS": {
"signingIdentity": "-", # Ad-hoc signing
"entitlements": null
}
}
}
# Ensure WiX toolset is installed
# Download from https://wixtoolset.org/
# Or use NSIS instead (tauri.conf.json)
{
"bundle": {
"targets": ["nsis"]
}
}
// Force runtime update
#[tauri::command]
pub async fn force_runtime_update(
runtime_manager: State<'_, RuntimeManager>,
) -> Result<(), String> {
runtime_manager.stop().await?;
runtime_manager.download_latest().await?;
runtime_manager.verify_signature().await?;
runtime_manager.start().await?;
Ok(())
}
// Proper cleanup in React components
useEffect(() => {
const unlisten = listen('runtime-event', (event) => {
console.log(event.payload);
});
return () => {
unlisten.then(f => f()); // Cleanup listener
};
}, []);
Create and push a SemVer tag:
git tag v0.3.2
git push origin v0.3.2
This triggers .github/workflows/release-desktop.yml which:
Build artifacts include:
Hermes.Agent.CN.Desktop_0.3.2_aarch64.dmg (macOS ARM)Hermes.Agent.CN.Desktop_0.3.2_x64.dmg (macOS Intel)Hermes.Agent.CN.Desktop_0.3.2_x64-setup.exe (Windows)