Loading...
Loading...
Compare original and translation side by side
~/.hermes/~/.hermes/~/.hermes/~/.hermes/git clone https://github.com/joeynyc/hermes-hudui.git
cd hermes-hudui
./install.sh
hermes-huduigit clone https://github.com/joeynyc/hermes-hudui.git
cd hermes-hudui
./install.sh
hermes-huduiundefinedundefinedundefinedundefinedcd hermes-hudui
source venv/bin/activate && hermes-huduicd hermes-hudui
source venv/bin/activate && hermes-huduiundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefined~/.hermes/~/.hermes/
├── identity.json # Agent identity and configuration
├── memory/ # Persistent memory store
├── sessions/ # Session logs and history
├── skills/ # Installed skills
├── cron/ # Scheduled jobs
├── projects/ # Project tracking
├── health/ # Health metrics
└── costs/ # Usage and cost tracking~/.hermes/~/.hermes/
├── identity.json # Agent identity and configuration
├── memory/ # Persistent memory store
├── sessions/ # Session logs and history
├── skills/ # Installed skills
├── cron/ # Scheduled jobs
├── projects/ # Project tracking
├── health/ # Health metrics
└── costs/ # Usage and cost tracking~/.hermes-hud/replays/
├── session_abc123/
│ ├── replay.redacted.json
│ ├── replay.md
│ ├── replay.html
│ ├── share-card.png
│ └── fork.json~/.hermes-hud/replays/
├── session_abc123/
│ ├── replay.redacted.json
│ ├── replay.md
│ ├── replay.html
│ ├── share-card.png
│ └── fork.jsonundefinedundefinedundefinedundefined| Key | Action |
|---|---|
| Switch between tabs 1-10 |
| Open theme picker |
| Open command palette |
| 按键 | 操作 |
|---|---|
| 在第1-10个标签页间切换 |
| 打开主题选择器 |
| 打开命令面板 |
tthermes-huduihermes-hudui~/.hermes/~/.hermes/replay.redacted.jsonreplay.mdreplay.htmlshare-card.pngfork.jsonassets/example-replay.redacted.jsonreplay.redacted.jsonreplay.mdreplay.htmlshare-card.pngfork.jsonassets/example-replay.redacted.jsonundefinedundefinedif event_type == "request_custom_metric":
metric_data = compute_custom_metric()
await send_message(websocket, {
"type": "custom_metric",
"payload": metric_data
})
Register the handler in `server/websocket.py`:
```python
from server.handlers.custom_handler import handle_custom_event
async def handle_message(websocket, message):
data = json.loads(message)
if data["type"] == "request_custom_metric":
await handle_custom_event(websocket, data)
# ... existing handlersif event_type == "request_custom_metric":
metric_data = compute_custom_metric()
await send_message(websocket, {
"type": "custom_metric",
"payload": metric_data
})
在`server/websocket.py`中注册处理器:
```python
from server.handlers.custom_handler import handle_custom_event
async def handle_message(websocket, message):
data = json.loads(message)
if data["type"] == "request_custom_metric":
await handle_custom_event(websocket, data)
# ... existing handlersimport json
from pathlib import Path
def load_hermes_identity():
"""Load agent identity from ~/.hermes/identity.json"""
hermes_dir = Path.home() / ".hermes"
identity_path = hermes_dir / "identity.json"
if identity_path.exists():
with open(identity_path, "r") as f:
return json.load(f)
return None
def get_recent_sessions(limit=10):
"""Get most recent session logs"""
sessions_dir = Path.home() / ".hermes" / "sessions"
if not sessions_dir.exists():
return []
session_files = sorted(
sessions_dir.glob("*.json"),
key=lambda p: p.stat().st_mtime,
reverse=True
)
sessions = []
for session_file in session_files[:limit]:
with open(session_file, "r") as f:
sessions.append(json.load(f))
return sessionsimport json
from pathlib import Path
def load_hermes_identity():
"""Load agent identity from ~/.hermes/identity.json"""
hermes_dir = Path.home() / ".hermes"
identity_path = hermes_dir / "identity.json"
if identity_path.exists():
with open(identity_path, "r") as f:
return json.load(f)
return None
def get_recent_sessions(limit=10):
"""Get most recent session logs"""
sessions_dir = Path.home() / ".hermes" / "sessions"
if not sessions_dir.exists():
return []
session_files = sorted(
sessions_dir.glob("*.json"),
key=lambda p: p.stat().st_mtime,
reverse=True
)
sessions = []
for session_file in session_files[:limit]:
with open(session_file, "r") as f:
sessions.append(json.load(f))
return sessions// frontend/src/components/CustomMetricWidget.jsx
import React, { useEffect, useState } from 'react';
import { useWebSocket } from '../hooks/useWebSocket';
export function CustomMetricWidget() {
const [metricData, setMetricData] = useState(null);
const { sendMessage, onMessage } = useWebSocket();
useEffect(() => {
// Request custom metric from backend
sendMessage({ type: 'request_custom_metric' });
// Listen for response
const unsubscribe = onMessage((data) => {
if (data.type === 'custom_metric') {
setMetricData(data.payload);
}
});
return unsubscribe;
}, []);
if (!metricData) return <div>Loading...</div>;
return (
<div className="metric-widget">
<h3>{metricData.metric_name}</h3>
<div className="metric-value">{metricData.value}</div>
<div className="metric-timestamp">{metricData.timestamp}</div>
</div>
);
}// frontend/src/components/CustomMetricWidget.jsx
import React, { useEffect, useState } from 'react';
import { useWebSocket } from '../hooks/useWebSocket';
export function CustomMetricWidget() {
const [metricData, setMetricData] = useState(null);
const { sendMessage, onMessage } = useWebSocket();
useEffect(() => {
// Request custom metric from backend
sendMessage({ type: 'request_custom_metric' });
// Listen for response
const unsubscribe = onMessage((data) => {
if (data.type === 'custom_metric') {
setMetricData(data.payload);
}
});
return unsubscribe;
}, []);
if (!metricData) return <div>Loading...</div>;
return (
<div className="metric-widget">
<h3>{metricData.metric_name}</h3>
<div className="metric-value">{metricData.value}</div>
<div className="metric-timestamp">{metricData.timestamp}</div>
</div>
);
}undefinedundefineddef export_json(self):
"""Export custom JSON format"""
output_path = self.output_dir / "custom-export.json"
custom_format = {
"version": "1.0",
"session_id": self.session_id,
"exported_at": datetime.utcnow().isoformat(),
"timeline": self.replay_data.get("timeline", []),
"metadata": self.replay_data.get("metadata", {})
}
with open(output_path, "w") as f:
json.dump(custom_format, f, indent=2)
return output_path
def export_csv(self):
"""Export timeline as CSV"""
import csv
output_path = self.output_dir / "timeline.csv"
timeline = self.replay_data.get("timeline", [])
if not timeline:
return None
with open(output_path, "w", newline="") as f:
writer = csv.DictWriter(f, fieldnames=timeline[0].keys())
writer.writeheader()
writer.writerows(timeline)
return output_pathundefineddef export_json(self):
"""Export custom JSON format"""
output_path = self.output_dir / "custom-export.json"
custom_format = {
"version": "1.0",
"session_id": self.session_id,
"exported_at": datetime.utcnow().isoformat(),
"timeline": self.replay_data.get("timeline", []),
"metadata": self.replay_data.get("metadata", {})
}
with open(output_path, "w") as f:
json.dump(custom_format, f, indent=2)
return output_path
def export_csv(self):
"""Export timeline as CSV"""
import csv
output_path = self.output_dir / "timeline.csv"
timeline = self.replay_data.get("timeline", [])
if not timeline:
return None
with open(output_path, "w", newline="") as f:
writer = csv.DictWriter(f, fieldnames=timeline[0].keys())
writer.writeheader()
writer.writerows(timeline)
return output_pathundefinedfrom pathlib import Path
import json
def check_agent_health():
"""Check Hermes agent health status"""
health_file = Path.home() / ".hermes" / "health" / "status.json"
if not health_file.exists():
return {"status": "unknown", "message": "Health file not found"}
with open(health_file, "r") as f:
health_data = json.load(f)
return {
"status": health_data.get("status"),
"last_check": health_data.get("timestamp"),
"issues": health_data.get("issues", [])
}from pathlib import Path
import json
def check_agent_health():
"""Check Hermes agent health status"""
health_file = Path.home() / ".hermes" / "health" / "status.json"
if not health_file.exists():
return {"status": "unknown", "message": "Health file not found"}
with open(health_file, "r") as f:
health_data = json.load(f)
return {
"status": health_data.get("status"),
"last_check": health_data.get("timestamp"),
"issues": health_data.get("issues", [])
}def get_cost_summary(model_name=None):
"""Get cost analytics, optionally filtered by model"""
costs_dir = Path.home() / ".hermes" / "costs"
if not costs_dir.exists():
return {"total": 0, "by_model": {}}
total_cost = 0
by_model = {}
for cost_file in costs_dir.glob("*.json"):
with open(cost_file, "r") as f:
data = json.load(f)
model = data.get("model")
cost = data.get("cost", 0)
if model_name and model != model_name:
continue
total_cost += cost
by_model[model] = by_model.get(model, 0) + cost
return {
"total": total_cost,
"by_model": by_model
}def get_cost_summary(model_name=None):
"""Get cost analytics, optionally filtered by model"""
costs_dir = Path.home() / ".hermes" / "costs"
if not costs_dir.exists():
return {"total": 0, "by_model": {}}
total_cost = 0
by_model = {}
for cost_file in costs_dir.glob("*.json"):
with open(cost_file, "r") as f:
data = json.load(f)
model = data.get("model")
cost = data.get("cost", 0)
if model_name and model != model_name:
continue
total_cost += cost
by_model[model] = by_model.get(model, 0) + cost
return {
"total": total_cost,
"by_model": by_model
}// frontend/src/hooks/useAgentHealth.js
import { useEffect, useState } from 'react';
import { useWebSocket } from './useWebSocket';
export function useAgentHealth() {
const [health, setHealth] = useState(null);
const { sendMessage, onMessage } = useWebSocket();
useEffect(() => {
// Request initial health status
sendMessage({ type: 'request_health' });
// Subscribe to health updates
const unsubscribe = onMessage((data) => {
if (data.type === 'health_update') {
setHealth(data.payload);
}
});
// Refresh every 30 seconds
const interval = setInterval(() => {
sendMessage({ type: 'request_health' });
}, 30000);
return () => {
unsubscribe();
clearInterval(interval);
};
}, []);
return health;
}// frontend/src/hooks/useAgentHealth.js
import { useEffect, useState } from 'react';
import { useWebSocket } from './useWebSocket';
export function useAgentHealth() {
const [health, setHealth] = useState(null);
const { sendMessage, onMessage } = useWebSocket();
useEffect(() => {
// Request initial health status
sendMessage({ type: 'request_health' });
// Subscribe to health updates
const unsubscribe = onMessage((data) => {
if (data.type === 'health_update') {
setHealth(data.payload);
}
});
// Refresh every 30 seconds
const interval = setInterval(() => {
sendMessage({ type: 'request_health' });
}, 30000);
return () => {
unsubscribe();
clearInterval(interval);
};
}, []);
return health;
}undefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefined~/.hermes-hud/replays/undefined~/.hermes-hud/replays/undefinedundefinedundefined./install.shnpm run buildundefined./install.shnpm run buildundefinedundefinedundefinedundefinedundefinedundefinedundefined