ワンクリックで
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)