🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

Hermes 侧边栏面板 GuanYang14700/dsh-hermes-panel

DeepSeek Harness
GuanYang1470014 天前有提交已提供中文文档扫描:含敏感能力

把 Hermes 放进 DSH 右侧栏:启停网关和 dashboard,聊天旁边就能看 dashboard,再把…

30.2/100
综合分
1
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/10/25 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add GuanYang14700/dsh-hermes-panel
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:已验证
是什么
dsh 原生插件 · browser
装得上吗
本站已在 dsh 0.2.1-rc.5 下真实安装成功
非静态推断
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 14 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 211 处,展示前 125 条)
  • 读取本地文件
    index.js:111
    if (existsSync(candidate)) return candidate;
  • 读取本地文件
    index.js:237
    target = statSync(script);
  • 读取本地文件
    index.js:243
    const other = statSync(candidate);
  • 读取本地文件
    index.js:317
    contents = readFileSync(envPath, 'utf8');
  • 读取本地文件
    index.js:368
    if (existsSync(candidate)) return candidate;
  • 读取本地文件
    index.js:391
    if (existsSync(join(dir, 'official-web', '.env'))) return 'official-web';
  • 写入本地文件
    index.js:1886
    copyFileSync(config, backup);
  • 写入本地文件
    index.js:1887
    writeFileSync(config, after, 'utf8');
  • 写入本地文件
    index.js:1894
    copyFileSync(backup, config);
  • 写入本地文件
    index.js:2396
    mkdirSync(path, { recursive: true });
  • 写入本地文件
    index.js:2407
    writeFileSync(context, AGENTS_TEMPLATE, 'utf8');
  • 发起网络请求
    client.js:836
    const response = await fetch(`${API}/status`, {
  • 发起网络请求
    client.js:863
    const response = await fetch(`${API}/${action}`, {
  • 发起网络请求
    client.js:883
    const response = await fetch(`${API}/health`, {
  • 发起网络请求
    client.js:913
    const response = await fetch(`${API}/update/check`, {
  • 发起网络请求
    client.js:944
    const response = await fetch(`${API}/update`, { headers: { accept: 'application/json' }, cache: 'no-store' });
  • 发起网络请求
    client.js:963
    const response = await fetch(`${API}/update/apply`, {
  • 读取本地文件
    verify/bridge-render.mjs:56
    if (existsSync(candidate)) candidates.push([candidate]);
  • 执行外部命令来自配置/变量
    verify/bridge-render.mjs:60
    const probe = spawnSync(candidate[0], [...candidate.slice(1), '-c', 'import sys; print(sys.version_info[0])'], {
  • 读取本地文件
    verify/bridge-render.mjs:69
    check('the renderer ships beside the bridge', existsSync(SCRIPT), SCRIPT);
  • 执行外部命令来自配置/变量
    verify/bridge-render.mjs:78
    const run = spawnSync(python[0], [...python.slice(1), SCRIPT, '--selftest'], {
  • 执行外部命令来自配置/变量
    verify/bridge-server.mjs:51
    const probe = spawnSync(candidate[0], [...candidate.slice(1), '-c', 'import sys; print(sys.version_info[0])'], {
  • 执行外部命令来自配置/变量
    verify/bridge-server.mjs:62
    const run = spawnSync(python[0], [...python.slice(1), '-c', body], {
  • 读取本地文件
    verify/bridge-server.mjs:72
    check('the server ships beside the renderer', existsSync(SERVER), SERVER);
  • 执行外部命令来自配置/变量
    verify/bridge-server.mjs:82
    const run = spawnSync(python[0], [...python.slice(1), SERVER, '--selftest'], {
  • 读取本地文件
    verify/bridge-server.mjs:95
    const source = readFileSync(SERVER, 'utf8');
  • 写入本地文件
    verify/bridge.mjs:110
    mkdirSync(join(home, 'official-web'), { recursive: true });
  • 写入本地文件
    verify/bridge.mjs:111
    mkdirSync(venv, { recursive: true });
  • 写入本地文件
    verify/bridge.mjs:113
    mkdirSync(dirname(exe), { recursive: true });
  • 写入本地文件
    verify/bridge.mjs:114
    writeFileSync(exe, '');
  • 写入本地文件
    verify/bridge.mjs:115
    writeFileSync(join(venv, 'python.exe'), '');
  • 写入本地文件
    verify/bridge.mjs:116
    writeFileSync(
  • 删除本地文件递归
    verify/bridge.mjs:198
    rmSync(root, { recursive: true, force: true });
  • 读取本地文件
    verify/bridge.mjs:206
    return readdirSync(dir).filter((name) => name.startsWith('config.yaml.bak-'));
  • 读取本地文件
    verify/bridge.mjs:271
    const after = readFileSync(h.configPath, 'utf8');
  • 读取本地文件
    verify/bridge.mjs:307
    const backupText = readFileSync(join(dirname(h.configPath), backups(h.configPath)[0]), 'utf8');
  • 读取本地文件
    verify/bridge.mjs:317
    check('the text is now identical to the first write', readFileSync(h.configPath, 'utf8') === after, '');
  • 读取本地文件
    verify/bridge.mjs:335
    check('the file is byte-identical to the original', readFileSync(h.configPath, 'utf8') === CONFIG_FIXTURE, '');
  • 读取本地文件
    verify/bridge.mjs:361
    const after = readFileSync(h.configPath, 'utf8');
  • 删除本地文件递归
    verify/bridge.mjs:444
    rmSync(dirname(workspace), { recursive: true, force: true });
  • 删除本地文件递归
    verify/bridge.mjs:459
    rmSync(root, { recursive: true, force: true });
  • 删除本地文件递归
    verify/bridge.mjs:489
    rmSync(root, { recursive: true, force: true });
  • 删除本地文件递归
    verify/bridge.mjs:504
    rmSync(root, { recursive: true, force: true });
  • 读取本地文件
    verify/client.mjs:214
    const source = readFileSync(CLIENT_PATH, 'utf8');
  • 读取本地文件
    verify/client.mjs:222
    const manifest = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
  • 读取本地文件
    verify/client.mjs:242
    && !readFileSync(new URL('../index.js', import.meta.url), 'utf8').includes('@local/'),
  • 写入本地文件
    verify/concurrency.mjs:39
    mkdirSync(join(home, 'official-web'), { recursive: true });
  • 写入本地文件
    verify/concurrency.mjs:41
    writeFileSync(exe, '');
  • 写入本地文件
    verify/concurrency.mjs:42
    writeFileSync(
  • 删除本地文件递归
    verify/concurrency.mjs:123
    rmSync(root, { recursive: true, force: true });
  • 读取本地文件
    verify/config.mjs:92
    claimed = pattern.exec(readFileSync(path, 'utf8'))?.[1] ?? null;
  • 写入本地文件
    verify/config.mjs:120
    mkdirSync(join(home, 'official-web'), { recursive: true });
  • 写入本地文件
    verify/config.mjs:122
    writeFileSync(exe, '');
  • 写入本地文件
    verify/config.mjs:123
    writeFileSync(
  • 读取本地文件
    verify/config.mjs:196
    const patch = readFileSync(join(PACKAGE_DIR, 'cordis.patch.yml'), 'utf8');
  • 读取本地文件
    verify/config.mjs:200
    const clientSource = readFileSync(join(PACKAGE_DIR, 'client.js'), 'utf8');
  • 写入本地文件
    verify/config.mjs:221
    mkdirSync(join(liveHome, 'official-web'), { recursive: true });
  • 写入本地文件
    verify/config.mjs:222
    writeFileSync(
  • 删除本地文件递归
    verify/config.mjs:293
    rmSync(root, { recursive: true, force: true });
  • 写入本地文件
    verify/gateway-status.mjs:54
    mkdirSync(join(home, 'official-web'), { recursive: true });
  • 写入本地文件
    verify/gateway-status.mjs:55
    mkdirSync(join(home, 'state'), { recursive: true });
  • 写入本地文件
    verify/gateway-status.mjs:57
    writeFileSync(exe, '');
  • 写入本地文件
    verify/gateway-status.mjs:58
    writeFileSync(
  • 写入本地文件
    verify/gateway-status.mjs:64
    writeFileSync(
  • 写入本地文件
    verify/gateway-status.mjs:71
    writeFileSync(heartbeat, '');
  • 删除本地文件递归
    verify/gateway-status.mjs:124
    rmSync(root, { recursive: true, force: true });
  • 执行外部命令来自配置/变量
    verify/health-e2e.mjs:49
    spawn(spec) {
  • 执行外部命令来自配置/变量
    verify/health-e2e.mjs:50
    const child = spawn(spec.argv[0], spec.argv.slice(1), {
  • 写入本地文件
    verify/health.mjs:73
    mkdirSync(join(home, 'official-web'), { recursive: true });
  • 写入本地文件
    verify/health.mjs:75
    writeFileSync(exe, '');
  • 写入本地文件
    verify/health.mjs:76
    writeFileSync(
  • 写入本地文件
    verify/health.mjs:80
    writeFileSync(join(home, 'official-web', 'doctor.ps1'), '# fixture\n');
  • 删除本地文件递归
    verify/health.mjs:148
    rmSync(root, { recursive: true, force: true });
  • 写入本地文件
    verify/host-csrf.mjs:43
    mkdirSync(join(home, 'official-web'), { recursive: true });
  • 写入本地文件
    verify/host-csrf.mjs:45
    writeFileSync(exe, '');
  • 写入本地文件
    verify/host-csrf.mjs:46
    writeFileSync(
  • 删除本地文件递归
    verify/host-csrf.mjs:177
    rmSync(root, { recursive: true, force: true });
  • 执行外部命令来自配置/变量
    verify/host.mjs:49
    spawn(spec) {
  • 执行外部命令来自配置/变量
    verify/host.mjs:50
    const child = spawn(spec.argv[0], spec.argv.slice(1), {
  • 发起网络请求
    verify/host.mjs:189
    const page = await fetch(`http://127.0.0.1:${DASHBOARD_PORT}/`);
  • 发起网络请求
    verify/host.mjs:199
    const asset = await fetch(`http://127.0.0.1:${DASHBOARD_PORT}${assetMatch[1]}`);
  • 读取本地文件
    verify/market-entry.mjs:32
    raw = readFileSync(entryPath, 'utf8').replace(/^ FEFF/, '')
  • 读取本地文件
    verify/package.mjs:18
    const read = (name) => readFileSync(join(PACKAGE_DIR, name), 'utf8');
  • 读取本地文件
    verify/package.mjs:30
    if (!existsSync(path)) continue;
  • 读取本地文件
    verify/package.mjs:31
    const raw = readFileSync(path);
  • 读取本地文件
    verify/package.mjs:161
    check('the bundle patch is declared and exists', existsSync(join(PACKAGE_DIR, String(manifest.dsh?.bundle?.patch ?? '').replace('./', ''))), String(ma
  • 读取本地文件
    verify/package.mjs:174
    check(`exports["${name}"] points at a real file`, existsSync(join(PACKAGE_DIR, file)), file);
  • 读取本地文件
    verify/package.mjs:178
    check(`files lists something that exists: ${entry}`, existsSync(join(PACKAGE_DIR, entry)), entry);
  • 读取本地文件
    verify/smoke.mjs:89
    claimed = pattern.exec(readFileSync(path, 'utf8'))?.[1] ?? null;
  • 执行外部命令来自配置/变量
    verify/stop-foreign.mjs:44
    spawn(spec) {
  • 执行外部命令来自配置/变量
    verify/stop-foreign.mjs:45
    const child = spawn(spec.argv[0], spec.argv.slice(1), {
  • 执行外部命令固定命令
    verify/stop-foreign.mjs:156
    execFileSync('taskkill', ['/PID', String(launcherPid), '/T', '/F'], { stdio: 'ignore' });
  • 执行外部命令来自配置/变量
    verify/stop-foreign.mjs:169
    const foreign = spawn(
  • 执行外部命令来自配置/变量
    verify/stop-gateway.mjs:45
    spawn(spec) {
  • 执行外部命令来自配置/变量
    verify/stop-gateway.mjs:46
    const child = spawn(spec.argv[0], spec.argv.slice(1), {
  • 执行外部命令来自配置/变量
    verify/stop-gateway.mjs:153
    const child = spawn(HERMES_EXE, ['cron', 'status'], {
  • 执行外部命令固定命令
    verify/stop-gateway.mjs:178
    execFileSync('taskkill', ['/PID', String(foreign.pid), '/T', '/F'], { stdio: 'ignore' });
  • 执行外部命令来自配置/变量
    verify/stop-gateway.mjs:195
    foreign = spawn(HERMES_EXE, ['gateway', 'run'], {
  • 读取本地文件
    verify/styles.mjs:20
    const source = readFileSync(resolve(PACKAGE_DIR, 'client.js'), 'utf8');
  • 读取本地文件
    verify/target.mjs:32
    text = readFileSync(envPath, 'utf8');
  • 读取本地文件
    verify/target.mjs:85
    if (!existsSync(envPath)) continue;
  • 读取本地文件
    verify/target.mjs:93
    if (!existsSync(exe)) {
  • 写入本地文件
    verify/tmp.mjs:55
    mkdirSync(root, { recursive: true });
  • 写入本地文件
    verify/update.mjs:79
    mkdirSync(join(home, 'official-web'), { recursive: true });
  • 写入本地文件
    verify/update.mjs:81
    writeFileSync(exe, '');
  • 写入本地文件
    verify/update.mjs:82
    writeFileSync(
  • 写入本地文件
    verify/update.mjs:87
    writeFileSync(join(home, 'official-web', name), '# fixture\n');
  • 删除本地文件递归
    verify/update.mjs:151
    rmSync(root, { recursive: true, force: true });
  • 写入本地文件
    verify/upstream.mjs:47
    mkdirSync(join(home, 'skills'), { recursive: true });
  • 写入本地文件
    verify/upstream.mjs:48
    mkdirSync(join(home, 'logs'), { recursive: true });
  • 写入本地文件
    verify/upstream.mjs:50
    writeFileSync(join(home, 'config.yaml'),
  • 写入本地文件
    verify/upstream.mjs:52
    writeFileSync(join(home, '.env'), 'DEEPSEEK_API_KEY=sk-not-a-real-key\n');
  • 写入本地文件
    verify/upstream.mjs:53
    writeFileSync(join(home, 'state.db'), 'SQLite format 3\u0000');
  • 写入本地文件
    verify/upstream.mjs:59
    mkdirSync(bin, { recursive: true });
  • 删除本地文件递归
    verify/upstream.mjs:77
    rmSync(root, { recursive: true, force: true });
  • 删除本地文件递归
    verify/upstream.mjs:165
    rmSync(empty, { recursive: true, force: true });
  • 删除本地文件
    verify/upstream.mjs:271
    rmSync(join(box.home, 'config.yaml'), { force: true });
  • 删除本地文件递归
    verify/upstream.mjs:294
    rmSync(blank, { recursive: true, force: true });
  • 写入本地文件
    verify/workspace-records.mjs:66
    mkdirSync(shared, { recursive: true });
  • 写入本地文件
    verify/workspace-records.mjs:81
    writeFileSync(journal, `${driven.map((entry) => JSON.stringify(entry)).join('\n')}\n`, 'utf8');
  • 写入本地文件
    verify/workspace-records.mjs:85
    mkdirSync(pluginDir, { recursive: true });
  • 删除本地文件递归
    verify/workspace-records.mjs:324
    rmSync(root, { recursive: true, force: true });
  • 读取本地文件
    verify/workspace-records.mjs:384
    check('AGENTS.md is really there', readFileSync(join(h.shared, 'AGENTS.md'), 'utf8').length > 0, '');
  • 写入本地文件
    verify/workspace-records.mjs:549
    mkdirSync(pluginDir, { recursive: true });
  • 删除本地文件递归
    verify/workspace-records.mjs:588
    rmSync(root, { recursive: true, force: true });
扫描于 2026/11/4(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

依赖的 DSH / Cordis 模块
@deepseek-ai/schemastery@deepseek-ai/dsh
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

DSH 侧边栏里的 Hermes:网关 + 面板控制、Hermes 自己的健康报告,以及 DSH <-> Hermes 桥接。由 plugins/hermes-panel 生成的镜像,别在这里改。

README

由 DeepSeek 最新模型翻译生成
dsh-hermes-panel 把 Hermes 放进 DSH 右侧栏:启停网关和 dashboard,聊天旁边就能看 dashboard,再把 DSH 和 Hermes 接起来。 只针对原生 Hermes(hermes-agent),不是 hermes-web-ui / Ekko Studio。 面板 右侧栏 ⊕ 列表 →「打开 Hermes」。顶部一行图标切三个视图,同一行还有启动/停止、重启、在新标签打开: - 仪表盘:iframe 里的 Hermes dashboard,左边照常对话。 - 记录:Hermes 与 DSH 的往来消息,读 DSH 自己的会话日志。 - 设置:体检、升级、DSH 桥、共享工作区。 体检和升级跑的是 official-web 发行版自带的 PowerShell 脚本。pip 装的上游 Hermes 没有这些脚本,这两个按钮会禁用并说明原因;其余功能任何装法都能用。 安装 dsh plugin --profile web add @yang3333/dsh-hermes-panel 装完重启 dsh:Host 半是 Node 模块,刷新页面不会重新加载。 需要 DSH >= 0.2.0-rc.2,以及已经装好的 Hermes(插件不负责装 Hermes,只负责找到它,或者由你告诉它在哪)。仅支持 Windows。
展开完整文档(README 剩余 1 节)
dsh-hermes-panel

把 Hermes 放进 DSH 右侧栏:启停网关和 dashboard,聊天旁边就能看 dashboard,再把 DSH 和 Hermes 接起来。

只针对原生 Hermes(hermes-agent),不是 hermes-web-ui / Ekko Studio。

面板

右侧栏 ⊕ 列表 →「打开 Hermes」。顶部一行图标切三个视图,同一行还有启动/停止、重启、在新标签打开:

- 仪表盘:iframe 里的 Hermes dashboard,左边照常对话。
- 记录:Hermes 与 DSH 的往来消息,读 DSH 自己的会话日志。
- 设置:体检、升级、DSH 桥、共享工作区。

体检和升级跑的是 official-web 发行版自带的 PowerShell 脚本。pip 装的上游 Hermes 没有这些脚本,这两个按钮会禁用并说明原因;其余功能任何装法都能用。

安装

dsh plugin --profile web add @yang3333/dsh-hermes-panel

装完重启 dsh:Host 半是 Node 模块,刷新页面不会重新加载。

需要 DSH >= 0.2.0-rc.2,以及已经装好的 Hermes(插件不负责装 Hermes,只负责找到它,或者由你告诉它在哪)。仅支持 Windows。

设置

两个目录,都可以留空,留空用 DSH home 下的默认值:

- 运行时:Hermes 跑在哪个 Python 环境。默认 <dsh home>/hermes-panel/runtime。
- 数据目录:HERMES_HOME,放 profiles、会话、config.yaml。默认 <dsh home>/hermes-panel/home。

其余:dashboardPort(0 = 读 .env,否则 9150)、sharedWorkspace、manageGateway、autostart。

在侧边栏的「插件」面板 → Official →「Hermes Panel settings」里改。

桥

打开 DSH 桥后,面板会写 Hermes 侧的配置(mcp_servers.dsh)到 config.yaml,并检查每个 profile 的 @deepseek-ai/dsh-mcp-client 是否和当前 DSH 一致;不一致会给出修复命令。

安全

HTTP 路由只监听回环地址,校验 Origin 和 Sec-Fetch-Site。解析 .env 只保留 HERMES_HOME、VENV、HERMES_EXE、DASHBOARD_PORT,其余不落任何地方。插件启用时不要把 DSH 绑到 0.0.0.0。

开发

npm test # 无副作用,Hermes 开着也能跑
npm run test:e2e # 真跑脚本和真实 dashboard

测试在 verify/ 下,一个文件一块,文件头写了它防的是什么回归。verify/smoke.mjs 用空配置加载 Host 半,确认注册 17 条路由,激活时不启动任何进程,再在没有 Hermes 的情况下调一次状态接口。verify/config.mjs 确认配置的 6 个字段 全部是 volatile、激活后保存的值就是面板读到的值,以及 .env 只保留四个白名单键。

许可

MIT
预览分享卡片长什么样 ↓GuanYang14700/dsh-hermes-panel
分享 · DeepSeek Harness Hub
GuanYang14700/dsh-hermes-panel

把 Hermes 放进 DSH 右侧栏:启停网关和 dashboard,聊天旁边就能看 dashboard,再把…

这是你的仓库?认领条目

回到顶部