Hermes Agent Dashboard 二次开发指南

Hermes Agent Dashboard 二次开发指南

参考: 官方 Web DashboardExtending the Dashboard

源码版本: b63229016


一、技术栈

层级 技术 说明
前端框架 React 19 + TypeScript 单页应用(SPA)
构建工具 Vite 快速开发 + HMR
后端框架 FastAPI + uvicorn Python 异步 REST API
状态管理 React Context + Hooks Session Token、主题等
样式 Tailwind CSS v4 + CSS Variables shadcn/ui 风格组件
终端模拟 xterm.js + WebGL 嵌入式 TUI Chat
插件 SDK window.__HERMES_PLUGIN_SDK__ 不捆绑 React,通过 SDK 访问
国际化 react-i18next 侧边栏 / 页面文案

二、项目结构

复制代码
~/.hermes/hermes-agent/
├── hermes_cli/
│   ├── web_server.py          # FastAPI 后端(4,062 行)
│   │                              # 路由: /api/status、/api/config、/api/sessions 等
│   └── web_dist/               # npm build 产物(Vite 打包的 JS/CSS/HTML)
│
└── web/src/
    ├── App.tsx                  # React 根组件 + 路由配置
    ├── main.tsx                 # 入口
    ├── pages/                   # 内置页面组件
    │   ├── AnalyticsPage.tsx
    │   ├── ChatPage.tsx         # 嵌入式 TUI Chat(xterm.js)
    │   ├── ConfigPage.tsx
    │   ├── CronPage.tsx
    │   ├── DocsPage.tsx
    │   ├── EnvPage.tsx          # .env 编辑器(API Keys 页面)
    │   ├── LogsPage.tsx
    │   ├── ModelsPage.tsx       # 模型切换
    │   ├── PluginsPage.tsx
    │   ├── ProfilesPage.tsx
    │   ├── SessionsPage.tsx
    │   └── SkillsPage.tsx
    │
    ├── components/              # 共享组件
    │
    └── plugins/                 # Dashboard 插件系统
        ├── registry.ts          # 插件注册表
        ├── slots.ts             # Slot 定义
        ├── types.ts             # TypeScript 类型
        └── usePlugins.ts        # React Hook

三、FastAPI 后端路由(web_server.py)

完整端点一览

路由 方法 功能
/api/status GET Gateway 状态探针
/api/gateway/restart POST 重启 Gateway
/api/hermes/update POST 更新 Hermes
/api/actions/{name}/status GET 查询异步 Action 状态
/api/sessions GET 列出 sessions(分页)
/api/sessions/search GET 搜索 sessions
/api/sessions/{session_id} GET Session 详情
/api/sessions/{session_id}/messages GET 消息列表
/api/sessions/{session_id} DELETE 删除 Session
/api/config GET/PUT 读写 config.yaml
/api/config/defaults GET 默认配置
/api/config/schema GET 配置 schema
/api/model/info GET 模型信息
/api/model/options GET 可用模型列表
/api/model/auxiliary GET 辅助模型列表
/api/model/set POST 设置主/辅模型
/api/providers/oauth GET OAuth Provider 列表
/api/providers/oauth/{provider_id}/start POST 启动 OAuth
/api/providers/oauth/{provider_id}/submit POST 提交 OAuth Code
/api/providers/oauth/{provider_id}/poll/{session_id} GET 轮询 OAuth 状态
/api/providers/oauth/{provider_id} DELETE 断开 OAuth
/api/env GET/PUT/DELETE 读写 .env
/api/env/reveal POST 揭示掩码的 env 值
/api/logs GET 读取日志文件
/api/cron/jobs GET 列出 Cron Jobs
/api/cron/jobs/{job_id} GET Cron Job 详情
/api/cron/jobs/{job_id}/run POST 手动触发
/api/cron/jobs/{job_id}/pause POST 暂停
/api/cron/jobs/{job_id}/resume POST 恢复
/api/dashboard/plugins GET 插件 manifest 列表
/api/dashboard/plugins/rescan GET 强制重新扫描插件目录

认证机制

Dashboard 使用随机 _SESSION_TOKEN,通过 Bearer token 验证:

python 复制代码
_SESSION_TOKEN = secrets.token_urlsafe(32)

@app.middleware("http")
async def auth_middleware(request: Request, call_next):
    if request.url.path.startswith("/api/"):
        auth = request.headers.get("authorization", "")
        expected = f"Bearer {_SESSION_TOKEN}"
        if not hmac.compare_digest(auth.encode(), expected.encode()):
            raise HTTPException(status_code=401, detail="Unauthorized")

SPA HTML 在页面加载时通过 JS 动态注入该 token 到所有 API 请求中。


四、开发环境搭建

4.1 启动后端 API

bash 复制代码
hermes dashboard --no-open

4.2 启动 Vite 开发服务器(HMR)

bash 复制代码
# Terminal 2
cd ~/.hermes/hermes-agent/web
npm install
npm run dev

Vite 开发服务器在 http://localhost:5173,代理 /api 请求到 http://127.0.0.1:9119


五、添加新页面

5.1 前端: 创建页面组件

web/src/pages/ 下创建 MyPage.tsx:

tsx 复制代码
import React from "react";

export default function MyPage() {
  return (
    <div className="p-6">
      <h1 className="text-2xl font-bold mb-4">My Page</h1>
      <p>This is a custom page.</p>
    </div>
  );
}

5.2 前端: 注册路由

App.tsx 中添加路由:

tsx 复制代码
import MyPage from "./pages/MyPage";

function App() {
  return (
    <Routes>
      <Route path="/" element={<Layout />}>
        <Route index element={<ConfigPage />} />
        <Route path="my-page" element={<MyPage />} />
      </Route>
    </Routes>
  );
}

5.3 前端: 添加到侧边栏导航

编辑对应侧边栏组件,找到导航菜单配置,添加导航项。


六、后端 API 开发

6.1 新增 REST 端点

hermes_cli/web_server.py 中添加:

python 复制代码
from fastapi import Request, HTTPException
import hmac

@app.get("/api/my-resource")
async def get_my_resource(request: Request):
    # 认证已由中间件处理
    return {"data": "hello"}

6.2 读写 config.yaml

python 复制代码
from hermes_cli.config import read_config, write_config

@app.get("/api/my-config")
async def get_my_config():
    cfg = read_config()
    return cfg.get("my_section", {})

@app.put("/api/my-config")
async def update_my_config(body: dict):
    cfg = read_config()
    cfg["my_section"] = body
    write_config(cfg)
    return {"status": "ok"}

6.3 WebSocket(PTY Bridge)

python 复制代码
from fastapi import WebSocket

@app.websocket("/api/pty")
async def pty_bridge(websocket: WebSocket):
    await websocket.accept()
    # 创建 PTY,将 xterm.js 数据转发到 PTY
    # 将 PTY 输出回传给前端

七、插件系统

7.1 架构概述

Dashboard 插件由三部分组成:

复制代码
manifest.json     插件配置(tab、slots、entry、api)
dist/index.js     预编译 JS bundle(IIFE,无构建步骤)
plugin_api.py     后端 FastAPI 路由(可选)

7.2 插件目录结构

复制代码
~/.hermes/plugins/<name>/
├── plugin.yaml              可选 --- CLI/Gateway 插件清单
├── __init__.py              可选 --- CLI/Gateway hooks
└── dashboard/               Dashboard 扩展(可选)
    ├── manifest.json         必选 --- tab 配置、icon、entry point
    ├── dist/
    │   ├── index.js         必选 --- 预编译 JS bundle(IIFE)
    │   └── style.css        可选 --- 自定义 CSS
    └── plugin_api.py        可选 --- 后端 FastAPI 路由

7.3 manifest.json 字段

字段 必选 说明
name 唯一标识符
label 导航栏显示名称
tab.path URL 路径,如 /my-plugin
tab.position 位置: end(默认)、after:<path>before:<path>
tab.hidden true 时仅注册 slots,不显示导航 tab
tab.override 设为内置路径可替换整个页面
slots 此插件填充的 slot 名称列表
entry JS bundle 路径,相对于 dashboard/
css CSS 文件路径
api Python 文件路径,FastAPI 路由

7.4 Plugin SDK

插件通过 window.__HERMES_PLUGIN_SDK__ 访问一切,不捆绑 React:

javascript 复制代码
const SDK = window.__HERMES_PLUGIN_SDK__;

// React + hooks
SDK.React
SDK.hooks.useState
SDK.hooks.useEffect

// UI 组件(shadcn/ui 原语)
SDK.components.Card
SDK.components.Button
SDK.components.Input
SDK.components.PluginSlot  // 渲染命名 slot

// Hermes API 客户端
SDK.api.getStatus()
SDK.api.getSessions()

// 工具函数
SDK.utils.cn       // Tailwind class merger
SDK.utils.timeAgo  // "5m ago" from unix timestamp

7.5 注册插件

javascript 复制代码
// ~/.hermes/plugins/my-plugin/dashboard/dist/index.js
(function () {
  "use strict";

  const SDK = window.__HERMES_PLUGIN_SDK__;
  const { React } = SDK;
  const { Card, CardHeader, CardTitle, CardContent } = SDK.components;

  function MyPage() {
    return React.createElement(Card, null,
      React.createElement(CardHeader, null,
        React.createElement(CardTitle, null, "My Plugin"),
      ),
      React.createElement(CardContent, null,
        React.createElement("p", { className: "text-sm text-muted-foreground" },
          "Hello from my custom dashboard tab."
        ),
      ),
    );
  }

  window.__HERMES_PLUGINS__.register("my-plugin", MyPage);
})();

7.6 Shell Slots

应用级 slots(在应用外壳任意位置注入组件):

Slot 位置
backdrop 背景层(noise 层之上)
header-left 顶部栏左侧
header-right 顶部栏右侧(主题/语言切换器左侧)
header-banner 导航栏下方全宽条幅
sidebar 驾驶舱侧边栏(仅 layoutVariant: cockpit 时渲染)
pre-main 主内容区之前
post-main 主内容区之后
footer-left / footer-right 底部栏左右侧
overlay 固定定位层(最顶层,用于覆盖物)

页面级 slots (仅在指定页面渲染,page:top 为内容区顶部,page:bottom 为底部):

Slot 位置
sessions:top / sessions:bottom Sessions 页面
analytics:top / analytics:bottom Analytics 页面
logs:top / logs:bottom Logs 页面(:top 在过滤器工具栏上方,:bottom 在日志查看器下方)
cron:top / cron:bottom Cron 页面
skills:top / skills:bottom Skills 页面
config:top / config:bottom Config 页面
env:top / env:bottom Keys(Env)页面
docs:top / docs:bottom Docs 页面
chat:top / chat:bottom Chat 页面(仅嵌入式 TUI 启用时)

7.7 注册 Shell Slot

javascript 复制代码
// 注册一个 sidebar slot
window.__HERMES_PLUGINS__.registerSlot("my-plugin", "sidebar", MySidebarComponent);

// 注册页面级 slot
window.__HERMES_PLUGINS__.registerSlot("my-plugin", "sessions:top", MyBannerComponent);

7.8 后端 API 路由

插件可注册 FastAPI 路由,路由挂载在 /api/plugins/<name>/*:

python 复制代码
# ~/.hermes/plugins/my-plugin/dashboard/plugin_api.py
from fastapi import APIRouter

router = APIRouter()

@router.get("/data")
async def get_data():
    return {"items": ["one", "two", "three"]}

@router.post("/action")
async def do_action(body: dict):
    return {"ok": True, "received": body}

前端通过 SDK.fetchJSON("/api/plugins/my-plugin/data") 调用。

7.9 主题系统

主题是 YAML 文件,放在 ~/.hermes/dashboard-themes/ 目录:

yaml 复制代码
# ~/.hermes/dashboard-themes/my-theme.yaml
name: my-theme
label: My Theme
description: Custom theme

palette:
  background: { hex: "#0a1628", alpha: 1.0 }
  midground: { hex: "#a8d0ff", alpha: 1.0 }
  foreground: { hex: "#ffffff", alpha: 0.0 }

typography:
  fontSans: "Poppins, system-ui, sans-serif"
  fontMono: "Fira Code, ui-monospace, monospace"
  baseSize: "15px"

layout:
  radius: "0.75rem"
  density: comfortable

layoutVariant: standard   # standard | cockpit | tiled

八、常见问题

8.1 Dashboard build 失败

bash 复制代码
cd ~/.hermes/hermes-agent/web
NODE_ENV=development npm install
NODE_ENV=development npm run build

8.2 401 Unauthorized

bash 复制代码
curl -H "Authorization: Bearer $_SESSION_TOKEN" http://127.0.0.1:9119/api/status

8.3 插件 tab 不显示

  1. 确认 manifest 在 ~/.hermes/plugins/<name>/dashboard/manifest.json
  2. 强制重新扫描: curl http://127.0.0.1:9119/api/dashboard/plugins/rescan
  3. 浏览器 DevTools → Network 确认 manifest.jsonindex.js 无 404
  4. 确认 window.__HERMES_PLUGINS__.register() 的 name 与 manifest.json:name 一致

8.4 插件后端路由返回 404

  1. 确认 manifest 有 "api": "plugin_api.py"
  2. 重启 hermes dashboard(插件 API 路由在启动时挂载,不支持热重载)
  3. 确认 plugin_api.py 导出 router = APIRouter()
相关推荐
MATLAB代码顾问1 小时前
RAG技术详解:从检索增强生成到知识库问答实战
人工智能
东方佑1 小时前
色块语义Token化器V3:用语义压缩重构图像编码
人工智能·计算机视觉·重构
沐泽__1 小时前
欧氏距离、余弦相似度(cosin)、点积 区别与用途详解(附实例)
人工智能·机器学习
victory04311 小时前
DeepSeek-V4知识点讲解记录
人工智能
Python私教1 小时前
如意Agent对话持久化与滚动记忆引擎设计:让AI记住你们聊过的每一句话
人工智能
这张生成的图像能检测吗1 小时前
(论文速读)SPR-YOLO:面向模糊场景的轻量级交通流检测算法
人工智能·yolo·计算机视觉·目标追踪
独隅1 小时前
Anaconda 配置 Keras 环境的详细流程指南
人工智能·深度学习·keras
恋猫de小郭1 小时前
AI 时代开源协议将消亡,malus 讽刺性展示了这一点
前端·人工智能·ai编程
数字化顾问1 小时前
(97页PPT)麦肯锡战略规划制定方法及模板制品(附下载方式)
人工智能·物联网