← 返回列表
未验证
接入本地 Hermes 大脑,让桌宠记住你并真实对话
尚未跑自动兼容性验证,可查看页面内的依赖与入口分析。 · 最近上游提交 2026/8/28 · 已提供中文文档
一个独立的桌面宠物,用于 DeepSeek Harness 网页 GUI,由你本地的 Hermes Agent 驱动——具备真实对话、长期记忆和人格。
综合分
28.8
GitHub 分
28.8
用户评分
—
★ Stars
1
周下载量
—
安装插件(需先安装 dsh CLI 引擎:npm install -g @deepseek-ai/dsh)
dsh plugin --profile web add Theathens/dsh-pet-hermes该插件未发布到 npm,走 GitHub 源安装(pnpm 若拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑)
数据截至 2026/9/16(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/dsh-client-runtime@deepseek-ai/dsh-client-connection@deepseek-ai/dsh-host-webserver@deepseek-ai/dsh-settings用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动
README
dsh-pet-hermes(pet 2.0 beta)
一只接了 Hermes Agent 大脑的独立桌宠,跑在 DeepSeek Harness(DSH)Web GUI 里。
她长得像原 @linxin666/dsh-pet 的鲸鱼娘,但会真的和你对话——背后是你本地配置的 Hermes,带着长期记忆和拟人人格,记得你是谁、你们聊过什么。
- 版本:0.1.0-beta.0
- 状态:beta(独立插件,已构建 + 隔离验证通过,尚未默认安装进 DSH)
- 许可:MIT(本插件代码);桌宠精灵图素材沿用原 whale-refined 的 MIT 许可
- 独立性:与原 @linxin666/dsh-pet 零耦合——自己的路由前缀、自己的素材、自己的 host/client,不修改、不依赖原插件
0. 使用前提
这个插件本身不内置任何大模型——它的“大脑”是你本地跑着的 Hermes Agent。装之前,确认下面四件事都满足,否则桌宠会出现但“大脑离线”。
前提 1:本机已装 DSH 并能跑 web GUI
dsh --profile web # 能打开 http://127.0.0.1:3080 即可
前提 2:本机有 Hermes Agent 的 gateway 在运行
本插件只通过 HTTP 调 Hermes 的 API Server,不直接碰 Hermes 进程。参考 Hermes Agent。
API Server 是 Hermes gateway 的一个 platform adapter(gateway/platforms/api_server.py),不是独立进程。它只在 gateway 运行时才会监听端口——所以前提 2 实际要求“有一个正在跑的 Hermes gateway,且启用了 API Server”。只跑交互式 hermes CLI(不启 gateway)时,127.0.0.1:8642 上没有任何监听者,插件的 chat-status 会报 healthy:false。
前提 3(关键):Hermes 的 API Server 网关已开启
Hermes 默认不开 API Server(API_SERVER_ENABLED 默认 false),而本插件只能通过这个网关对话。所以你必须先把 Hermes 的 API Server 网关配好。
Hermes API Server 网关的配置细节
Hermes 的 API Server 是一个 OpenAI 兼容的 HTTP 网关,由以下项控制(环境变量,或 ~/.hermes/config.yaml 的 gateway.api_server: 段,config.yaml 优先级更高——同名项以 config.yaml 为准):
| 环境变量 | 默认值 | 说明 | 本插件要求 |
|---|---|---|---|
| API_SERVER_ENABLED | false | 是否开启 API Server | 必须 true,否则插件连不上 |
| API_SERVER_PORT | 8642 | HTTP 端口 | 插件默认连 8642;改端口要在插件 endpoint 同步改 |
| API_SERVER_HOST | 127.0.0.1 | 绑定地址(默认仅本机) | 保持 127.0.0.1(插件强制 loopback,不接远程 Hermes) |
| API_SERVER_KEY | _(必填)_ | Bearer 鉴权密钥 | 必须有值,且 ≥16 字符、不能是占位符(hermes / example / sk-… 之类会被启动守卫直接拒);插件用它调 Hermes(见下) |
| API_SERVER_CORS_ORIGINS | _(无)_ | 允许的浏览器来源 | 本插件用不到(插件走 host 端转发,不是浏览器直连) |
| API_SERVER_MODEL_NAME | default profile → hermes-agent;命名 profile → 该 profile 名 | /v1/models 里的 model 名 | 插件默认用 hermes-agent;若你用的是命名 profile 且没设 API_SERVER_MODEL_NAME,这里实际是 profile 名,插件 model 要改成它(/v1/models 可查) |
开启步骤(二选一)
方式 A:环境变量 / .env 文件
两种方式,config.yaml 的 gateway.api_server: 段会覆盖环境变量(见方式 B),二者等价但 config.yaml 优先。
- 进程级 env(Hermes 启动前 export):
export API_SERVER_ENABLED=true
export API_SERVER_PORT=8642
export API_SERVER_KEY=*** # ≥16 字符,非占位符
- ~/.hermes/.env 文件(推荐,跨重启持久):在文件里加 API_SERVER_KEY=。Hermes 的 secret 读取链(hermes_cli/auth.py::has_usable_secret / _get_scoped_secret)会优先从 .env 读,避免被父进程遗留的旧 export 遮蔽——如果你既 export 过又写进 .env,以 .env 为准。
API_SERVER_ENABLED 这个 env 开关是最弱的启用路径:gateway 的 config loader(gateway/config.py)实际的主检查是“key 是否存在且够强”,key 一到位平台就会注册,enabled 只是显式标记。所以把 key 放对位置就够了,enabled: true 是双保险。
方式 B:配置文件 ~/.hermes/config.yaml
gateway:
api_server:
enabled: true
port: 8642
host: 127.0.0.1
key: # ≥16 字符,非占位符;与 .env 里的值一致
注意:
- key 的读取位置:默认 profile 读 ~/.hermes/.env;命名 profile 读 ~/.hermes/profiles//.env。config.yaml 的 gateway.api_server.key 若设了,会覆盖 .env(api_server.py:1376 的 extra.get("key", ...) 优先)。
- key 的强度是硬门槛:启动守卫(api_server.py::_api_key_passes_startup_guard)要求 API_SERVER_KEY ≥16 字符且非占位符,否则拒绝启动——不是警告,是 fail-closed,监听器根本不会起来,gateway 日志会打 Refusing to start: API_SERVER_KEY ...。弱 key 不会"降级运行",只会让 8642 端口没有服务。
- 即使 loopback 绑定也强制要 key:API_SERVER_HOST=127.0.0.1 不是免鉴权,所有部署(含仅本机)都必须有可用 key。
验证 Hermes 网关已就绪
1) 网关开了吗(无需鉴权)
应返回 {"status":"ok","platform":"hermes-agent","version":...}
curl http://127.0.0.1:8642/health -UseBasicParsing
2) model 名是什么(需要 Bearer;插件的 model 要匹配这个 id)
应返回 {"object":"list","data":[{"id":"hermes-agent",...}]}
curl http://127.0.0.1:8642/v1/models -UseBasicParsing -Headers @{ Authorization = "Bearer 你的API_SERVER_KEY" }
3)(可选)机器可读能力清单,确认流式/会话头都支持
curl http://127.0.0.1:8642/v1/capabilities -UseBasicParsing -Headers @{ Authorization = "Bearer 你的API_SERVER_KEY" }
- health 通 = gateway 在跑且 API Server 起来了(/health 无需鉴权,所以这一步通不代表 token 对)
- /v1/models 返回的 id(如 hermes-agent)= 插件 model 要填的值。这一步需要正确的 Bearer——如果返回 401,说明 key 错了,先修 key 再继续
- /v1/capabilities 会列出 session_continuity_header: X-Hermes-Session-Id 和 session_key_header: X-Hermes-Session-Key——这是 Hermes 长期记忆的作用机制(见下"插件如何拿到记忆")
- 记下你的 API_SERVER_KEY 值——下一步要把它放进插件的 token.txt
chat-status 绿 ≠ token 对:插件的 chat-status 端点只探 /health(无鉴权),所以它报 healthy:true 只能证明"gateway 活着",不能证明 token 能用。token 对不对,只有真正发一次 /v1/chat/completions(需要 Bearer)才知道——第一次对话 401 就是 token 的锅。
插件如何拿到"长期记忆"(机制说明)
Hermes 的长期记忆(memory provider)是按 session key 作用域的。API Server 提供两个 opt-in 头(api_server.py:2046-2081):
- X-Hermes-Session-Id:继续某个已存在的对话 session(历史从 SessionDB 读,不用在 body 里重发)
- X-Hermes-Session-Key:把长期记忆固定到一个稳定的 per-channel 标识,跨多轮对话持久
本插件目前两个头都不发(src/chat.ts 只发 Authorization + model + messages),靠"每轮把完整 messages 数组重发 + system prompt 里 prepend persona"来维持对话连续性。Hermes 侧的长期记忆(memory provider 的跨会话知识)仍然生效——因为记忆是 Hermes Agent 进程级的,不依赖客户端传 header。但插件的对话 transcript 本身是浏览器内存态,刷新即丢(Hermes 侧"记得你",但面板里的聊天记录不持久,见已知限制)。
后续优化方向:让 host 给每轮请求带上 X-Hermes-Session-Key: agent:main:pet-hermes:,把桌宠对话绑定到一个稳定的记忆作用域,这样 Hermes 的 memory provider 能更精准地跨会话记住"这只桌宠和这个用户的对话"。
前提 4:把 Hermes key 交给插件
插件怎么知道你的 API_SERVER_KEY?按以下优先级从高到低解析(见配置):
1. settings.yaml 显式 token 字段(最优先,一般不用)
2. 环境变量 DSH_PET_HERMES_TOKEN=
3. tokenFile 文件内容——tokenFile 留空时(settings.yaml 默认),回退到插件根目录的 token.txt(最省事,推荐首次安装用这个)
部署后的"插件根目录" = ~/.dsh/profiles/web/node_modules/dsh-pet-hermes/(即 DSH profile 的 node_modules 下的插件目录,不是你的开发目录)。token.txt 内容就是一整行你的 API_SERVER_KEY,无引号、无多余空格/换行。
前提速查
| # | 前提 | 不满足的后果 |
|---|---|---|
| 1 | DSH 能跑 web GUI | 插件无处可挂 |
| 2 | Hermes gateway 在运行(API Server 是它的 platform adapter) | 桌宠出现但“大脑离线” |
| 3 | API Server 开启(API_SERVER_KEY ≥16 字符且非占位符,key 到位即注册) | 同上(healthy:false / 401) |
| 4 | 插件拿到了 API_SERVER_KEY(token.txt / env / tokenFile) | chat-token-missing |
全部满足后,curl http://127.0.0.1:3080/api/pet-hermes/chat-status 返回 healthy:true 代表 gateway 链路通了(Hermes 活着、API Server 起来了)。但 token 对不对只有真正对话一次才知道(chat-status 只探 /health,无鉴权)——第一次对话 401 就是 token 的锅。
1. 立项
为什么做这个
原 @linxin666/dsh-pet 是一只很出色的观赏型桌宠:随 agent 状态切动画和台词、有碎碎念、有亲密度/喂食互动。但它的“说话”都是内置台词库——几百句轮换文案,它不认识你、不记得你。
我们想要的不只是“会动的小宠物”,而是一个有记忆、有人格、认识你的桌宠。假如你的本地恰好跑着一个 Hermes Agent(NousResearch/hermes-agent),它自带:
- 长期记忆:跨会话持久,记得你是谁、你的项目、你们的对话
- 拟人人格:可定制 system prompt
- 对外 API:OpenAI 兼容的 /v1/chat/completions(SSE 流式)
那么把两者结合:桌宠的“身体”(精灵渲染 + 交互)+ Hermes 的“大脑”(记忆 + 推理),便能得到不一样的体验。
为什么不直接改原插件
评估过直接给原 dsh-pet 加对话功能,但遇到两个硬障碍:
1. 原插件的 npm 包不附带 tsconfig.json,整包 tsc -b && tsdown 在 node_modules 安装环境里跑不起来(官方构建依赖 monorepo 的 tsconfig 体系)。
2. Hermes API Server 拒绝浏览器 CORS(preflight 403),桌宠浏览器端无法直调 Hermes,必须有个 host 端代理转发。
与其在原插件上打补丁、承担回归风险,不如做一个完全独立的 beta 插件:
- 原插件照常用,互不影响
- 新插件专注“能对话的拟人桌宠”这一件事,轻量、可控
- 迭代/回滚都不碰原插件
beta 范围
做:桌宠本体(精灵动画)+ Hermes 对话(流式 + 记忆)+ 独立路由
不做(留待后续):原插件的亲密度/喂食/多会话气泡/状态联动动画、Live2D 渲染、语音
2. 架构
┌─────────────────────────────────────────────────────────────┐
│ DSH Web GUI(浏览器) │
│ |
│ ┌──────────────────────────────────────────——┐ |
│ │ dsh-pet-hermes client (lib/client.js) | |
│ │ | |
│ │ PetHermesEntry | |
│ │ ├─ PetSprite 精灵动画(帧步进/拖拽) | |
│ │ └─ PetChatPanel 对话面板(打字机流式) | |
│ │ | |
│ │ PetHermesSettingsCard 设置卡(一级设置页) | |
│ │ └─ 绑定 settingsScope(pet-hermes 段) | |
│ │ | |
│ │ pet-store(transcript + 面板 + enabled) | |
│ └───────────────┬──────────────────────────——┘ |
│ │ fetch(同源,无 CORS 问题) |
│ ▼ |
└──────────────────────────────────────────────────────────────┘
│
│ /api/pet-hermes/*
▼
┌─────────────────────────────────────────────────────────────┐
│ dsh-pet-hermes host 半 (lib/index.js) |
│ |
│ GET /api/pet-hermes/pet → 桌宠定义(atlas+tracks) |
│ GET /api/pet-hermes/chat-status → Hermes 健康探测 |
│ POST /api/pet-hermes/chat → 转发 Hermes,NDJSON 流式 |
│ GET /pet-hermes/ → 精灵素材 |
│ |
│ chat.ts:读 token(host 侧,永不发浏览器)+ SSE 解析 + 降级 |
└───────────────┬──────────────────────────────────────────────┘
│ Bearer token(host 侧读取)
▼
┌─────────────────────────────────────────────────────────────┐
│ 本地 Hermes Agent API Server(127.0.0.1:8642) |
│ /v1/chat/completions(OpenAI 兼容,SSE 流式) |
│ 长期记忆 + 拟人人格 + 自我进化 |
└─────────────────────────────────────────────────────────────┘
关键设计
| 决策 | 说明 |
|---|---|
| 浏览器拥有对话历史 | client 每轮把完整 messages 数组发给 host(无状态转发),host 不存会话。简单、无服务端状态 |
| token 只在 host 侧 | 浏览器永远拿不到 Hermes 密钥;host 从文件/env 读取,加 Authorization: Bearer |
| 强制 loopback | 所有 /api/pet-hermes/ 路由只接受本机请求;Hermes endpoint 也必须是 127.0.0.1/localhost |
| NDJSON 流式 | host 把 Hermes 的 SSE 转成 {'delta':...} 逐行 + {'done':true} 终止,浏览器打字机渲染 |
| 降级 | Hermes 挂了 → chat-status 报 healthy:false,面板显示"大脑离线",桌宠本体不受影响 |
| 独立素材 | 精灵图拷进插件自己的 assets/pet/,host 用 realpath containment 安全服务,不引用原插件 |
3. 目录结构
dsh-pet-hermes/
├─ package.json 插件清单(name: dsh-pet-hermes, dsh.bundle/client 声明)
├─ cordis.patch.yml 注册行(id: pet-hermes)
├─ tsconfig.json 构建类型配置
├─ tsdown.config.ts 构建入口(复用 shared 的 clientBundle preset)
├─ shared/ 构建助手(tsdown.client.ts / web-platform.ts)
├─ src/
│ ├─ index.ts host 半:name/inject/apply + 极简 registry + 路由
│ ├─ chat.ts Hermes 对话桥(token/转发/SSE/健康探测,可复用)
│ └─ client/
│ ├─ index.ts client 半入口:挂载 + SSE chatSend + Hermes 探测 + settings 注册
│ ├─ PetHermesEntry.tsx 组合入口(精灵 + 面板 + 召唤/隐藏,enabled 门控)
│ ├─ PetSprite.tsx 精灵渲染(帧动画/拖拽/点击开对话)
│ ├─ PetChatPanel.tsx 对话面板(输入/流式渲染/状态栏)
│ ├─ PetHermesSettingsCard.tsx 设置卡(一级设置页,读写 settings.yaml)
│ ├─ pet-store.ts 状态(桌宠定义 + transcript + 面板 + enabled/visible)
│ ├─ pet.module.css 样式(蓝玻璃主题,和原插件同族)
│ └─ settings-card.module.css 设置卡样式
├─ assets/pet/ 独立精灵素材
│ ├─ pet.json 动画轨道定义(9 tracks)
│ ├─ spritesheet.webp 精灵图(9 行 × 不等列)
│ └─ previews/idle.gif 预览
├─ lib/ 构建产物(部署时只用这个)
│ ├─ index.js host 半
│ └─ client.js client 半(ModuleLoader 格式)
└─ isolation-host.mjs 隔离验证脚本(不碰真实 DSH)
9 种动画轨道
idle(待机)/ running-right / running-left / waving(挥手)/ jumping(跳跃)/ failed / waiting / running / review
4. 安装
⚠️ 安装会修改 DSH profile 配置并需要重启 DSH。建议先备份(见下文回滚)。
前置条件
1. 本地已有一个运行中的 Hermes Agent,且开了 API Server(OpenAI 兼容端点,默认 http://127.0.0.1:8642)
2. Hermes 的 API 密钥可被 host 读取——首次安装把你的 key 放进插件根目录的 token.txt(见首次安装)
3. DSH 已装好(dsh --profile web 能跑)
步骤
1. 备份(务必先做)
备份原 profile 配置(含 cordis.patch.yml)
$web = "$env:USERPROFILE\.dsh\profiles\web"
Copy-Item "$web\cordis.patch.yml" "$web\cordis.patch.yml.bak-pet-hermes"
备份原 dsh-pet 插件(确认没被误碰)
Copy-Item "$web\node_modules\@linxin666\dsh-pet" "$env:TEMP\dsh-pet-backup" -Recurse
2. 拷贝插件到 profile
部署只需 lib/ + assets/ + 3 个清单文件(不需要 src/、shared/、node_modules/、.ts):
$web = "$env:USERPROFILE\.dsh\profiles\web"
改成你的 dsh-pet-hermes 源码目录(你 clone/开发的位置)
$src = ""
$dst = "$web\node_modules\dsh-pet-hermes"
New-Item -ItemType Directory -Force -Path $dst | Out-Null
Copy-Item "$src\lib" $dst\lib -Recurse -Force
Copy-Item "$src\assets" $dst\assets -Recurse -Force
Copy-Item "$src\package.json" $dst\package.json -Force
Copy-Item "$src\cordis.patch.yml" $dst\cordis.patch.yml -Force
插件的运行时依赖(@deepseek-ai/cordis、dsh-client-runtime、dsh-host-webserver、react、clsx)由 DSH profile 的既有 node_modules 提供(peer/runtime 依赖),无需在插件目录单独装。
3. 注册插件行
在 $web\cordis.patch.yml 里追加一行 insert(和 mcp-browser 那行并列)。
文件当前形如:
[ { insert: [ { id: mcp-browser, name: '@deepseek-ai/dsh-mcp-client', config: { ... } } ] } ]
改成(追加第二个 insert 元素):
[
{ insert: [ { id: mcp-browser, name: '@deepseek-ai/dsh-mcp-client', config: { serverName: browser, transport: stdio, command: npx, args: [ '-y', '@playwright/mcp@latest', '--browser', 'msedge' ] } } ] },
{ insert: [ { id: pet-hermes, name: 'dsh-pet-hermes' } ] }
]
注意 id: pet-hermes 必须和 cordis.patch.yml(插件自带那份)里的 id 一致;name 是包名。
4. 重启 DSH
dsh --profile web
重启后,Web GUI 右下角会出现第二只鲸鱼娘(pet 2.0 beta),和原桌宠并存。点她或她身上的 💬 按钮,弹出对话面板。
验证安装成功
Hermes 健康(应 healthy:true)
curl http://127.0.0.1:3080/api/pet-hermes/chat-status
桌宠定义(应返回 whale-girl-refined)
curl http://127.0.0.1:3080/api/pet-hermes/pet
端口以你 DSH Web 实际监听为准(默认通常是 3080,以 dsh --profile web 启动时打印的 URL 为准)。
5. 配置
对话桥的配置在 host 侧(src/chat.ts),有默认值,开箱即用——指向本地 Hermes 默认端点、默认 token 文件。
5.1 通过 GUI 设置卡配置(推荐)
插件在 DSH Web 的设置 → Hermes 桌宠提供了一级设置页(和原 @linxin666/dsh-pet 的"宠物"页并列)。点进去可改:
| 字段 | 说明 |
|---|---|
| 启用桌宠 | 持久总开关。关 = 整个入口(精灵 + 召唤按钮)完全不渲染,唯一恢复方式是在这里改回"开";开 = 精灵按 visible 状态显示 |
| Hermes API 地址 | Hermes 的 OpenAI 兼容网关基址(如 http://127.0.0.1:8642)。必须 loopback |
| 模型名 | /v1/models 里的 model id(如 hermes-agent) |
| 人设 | system prompt,留空用内置鲸鱼娘人设 |
| 历史轮数上限 | 每轮转发给 Hermes 的最大消息数(1–200) |
| 请求超时(毫秒) | 单次对话超时上限(1000–600000) |
保存后行为:
- 改 启用桌宠 → 立即生效(关 = 精灵和按钮全消失;开 = 精灵出现),重启 DSH 后保持(写进 ~/.dsh/settings.yaml 的 pet-hermes: 段)
- 改 Hermes API 地址 → 保存后立即重新探测 Hermes 状态("大脑在线/离线"标签更新)
- 改其他字段 → 保存后立即生效(host 每次请求时从 settings 读最新值)
与精灵右上角 × 按钮的区别:× 是临时隐藏(visible 状态,关后显示"🐋 召唤鲸鱼娘"按钮,点按钮可临时召唤回来);设置卡的"启用桌宠"是持久开关(enabled,关后完全消失、无召唤按钮,只能在设置里重新开)。两者独立:enabled=false 时 × 按钮和召唤按钮都不渲染。
5.2 通过 settings.yaml 直接配置
设置卡的字段持久化到 ~/.dsh/settings.yaml 的 pet-hermes: 段(由 @deepseek-ai/dsh-settings-file provider 管理)。你也可以直接编辑这个文件。
只写显式 override 的字段:pet-hermes: 段里只出现你在设置卡里改过(或手动加过)的字段,没改过的字段不写文件——它们走 5.3 的 schema 默认值。所以首次只改过“启用桌宠”时,文件里通常只有一行:
yaml
pet-hermes:
enabled: true
改过 endpoint、model 等字段后才会逐行追加,例如:
yaml
pet-hermes:
enabled: true
endpoint: http://127.0.0.1:8642
model: hermes-agent
“恢复默认”:设置卡里点某字段的“恢复默认”(或手动删掉文件里那行)= 把该字段从 user 层移除,回退到 schema 默认值。字段不存在 ≠ 错误,就是“用默认”。
外部编辑热生效:dsh-settings-file 用 chokidar 监听文件变化,你在编辑器里改 settings.yaml 的 pet-hermes: 段并保存,DSH 会不重启地重新解析并生效(和设置卡保存等效)。
5.3 配置项(host 侧 src/chat.ts 默认值)
所有字段都有默认值,开箱即用。settings 段里的字段会覆盖这些默认值(resolution 层叠:schema 默认 → composition base → user 段)。
| 项 | 默认值 | 说明 |
|---|---|---|
| endpoint | http://127.0.0.1:8642 | Hermes API Server 地址,必须 loopback |
| model | hermes-agent | Hermes 暴露的 model id(GET /v1/models 可查) |
| token | —(见下) | Bearer 密钥,优先级最高 |
| tokenFile | 插件根目录的 token.txt(留空即此默认) | 密钥文件路径(一行一个 token)。首次安装把你的 key 放进这里,或填绝对路径指向别处 |
| persona | 内置鲸鱼娘人设 | 每轮 prepend 的 system prompt |
| maxHistory | 20 | 每轮转发给 Hermes 的最大历史消息数(超出截断最旧) |
| timeoutMs | 120000 | 单轮超时(ms),超时中止流式 |
| enabled | true | 持久总开关,false 时整个入口不渲染(chat 路由仍返回 409) |
token 不是 settings 字段(密钥不进 settings.yaml,避免被共享/提交)。token 的读取见下节。
Token 读取优先级
1. 显式 token 配置值
2. 环境变量 DSH_PET_HERMES_TOKEN
3. tokenFile 文件内容 —— tokenFile 留空(settings.yaml 默认)时,回退到插件根目录的 token.txt
安全:token 永远只在 host 进程里读取,不会出现在浏览器、网络响应或日志里。不要把密钥值写进任何前端可见的地方;token.txt 建议加入你的 .gitignore(如果这个目录在版本库里)。
首次安装:把你的 Hermes 接上
这是首次安装者唯一必须做的事——告诉插件“你的 Hermes 在哪、key 在哪”。
1. 确认你的 Hermes 在跑且开了 API Server:
powershell
curl http://127.0.0.1:8642/health -UseBasicParsing
应返回 {"status":"ok","platform":"hermes-agent",...}
- 端口不是 8642?记下实际端口,下面 endpoint 要改。
- 没开 API Server?先去 Hermes 那边开(见 Hermes 文档的 api-server 功能)。
2. 把你的 Hermes API key 放进插件根目录的 token.txt(最省事):
- 插件根 = ~/.dsh/profiles/web/node_modules/dsh-pet-hermes/(部署后的位置)
- 新建 token.txt,内容就是一整行你的 Hermes API key(无引号、无多余空格/换行):
你的hermes-api-key-贴这一行
- 不想放默认位置?在 settings.yaml 里给 tokenFile 填你自己的绝对路径即可(见下)。
3. (可选)改 endpoint / persona 等:在 ~/.dsh/settings.yaml 加块(只有改过才会写入,不改就走默认):
yaml
pet-hermes:
endpoint: http://127.0.0.1:8642 # 你的 Hermes 端口
model: hermes-agent # 一般不用改
tokenFile: "" # 留空=用插件根/token.txt;或填你的 key 路径
persona: "" # 留空=内置鲸鱼娘人设
maxHistory: 20
timeoutMs: 120000
4. 重启 DSH + 验证:
powershell
dsh --profile web
curl http://127.0.0.1:3080/api/pet-hermes/chat-status -UseBasicParsing
应返回 {"ok":true,"enabled":true,"healthy":true,...}
healthy: true = 连上了。去 GUI 点桌宠试对话。
首次配置错误对照
| chat-status / 对话现象 | 原因 | 解决 |
|---|---|---|
| healthy: false | Hermes 没起 / 端口不对 | curl /health 确认通;改 endpoint |
| chat-token-missing (409) | token.txt 没放 / 路径错 | 把 key 放进插件根 token.txt;或 settings.yaml 填 tokenFile;或设 env |
| 对话 401 | key 值不对 | token.txt 要纯 token,无空格/引号/换行残留 |
| chat-endpoint-not-loopback (400) | endpoint 不是 127.0.0.1/localhost | Hermes 必须本机;改 endpoint 为 http://127.0.0.1:端口 |
改 Hermes 端点/密钥(不改代码)
最省事的方式是环境变量(在启动 DSH 前设置):
powershell
$env:DSH_PET_HERMES_TOKEN = "你的hermes密钥"
dsh --profile web
或改 src/chat.ts 顶部的 DEFAULT_CHAT_ 常量后重新构建(见构建)。
改人设(persona)
默认 persona 在 src/chat.ts 的 DEFAULT_CHAT_PERSONA:
你是用户的桌面鲸鱼娘伙伴,住在他的电脑右下角。用轻松、俏皮、简短的中文陪他聊天,
像会说话的小宠物:有温度、偶尔撒娇、记得他告诉你的事。回答尽量控制在两三句内,
除非他要求详细。不要自称 AI 助手或模型。
改它 → 重新构建。这是控制"她说话什么口吻"的地方。
6. 构建与开发
前置
- Node.js ^22.19 || >=24
- 能访问 npm registry
安装开发依赖
powershell
cd ""
npm install
若系统 npm cache 有权限问题,指定一个可用的 cache 目录:npm install --cache ""
构建
powershell
npm run build # 等价 tsdown
产出 lib/index.js(host)+ lib/client.js(client,ModuleLoader 格式 + CSS 内联)。
隔离验证(不碰真实 DSH)
powershell
node isolation-host.mjs
→ http://127.0.0.1:8811
起一个最小 host,加载工作区 lib/index.js 注册路由,可 curl 验证 chat 桥 / 桌宠定义 / 素材,完全不碰真实 DSH 和原插件。适合改完 host 逻辑后快速回归。
改代码的流程
改 src/ → npm run build → (可选)node isolation-host.mjs 验证 → 重新部署 lib/ 到 profile → 重启 DSH
7. 回滚
出问题想卸载 pet 2.0 beta:
powershell
$web = "$env:USERPROFILE\.dsh\profiles\web"
1. 删插件目录
Remove-Item "$web\node_modules\dsh-pet-hermes" -Recurse -Force
2. 还原 profile 配置
Copy-Item "$web\cordis.patch.yml.bak-pet-hermes" "$web\cordis.patch.yml" -Force
3. 重启 DSH
dsh --profile web
原 @linxin666/dsh-pet 全程未被修改,回滚后不受任何影响。
7.5 故障排查
症状:重启后 GUI 打不开,显示 "Failed to load plugins"
web boot: 1 entry did not activate
dsh-pet-hermes: pending (waiting for service: runtime)
这是本插件首版(0.1.0-beta.0)踩过的坑,已修复。 记录在此以防回归。
- 根因:client 入口 src/client/index.ts 的 export const inject 里写了 DSH 未注册的服务短名(首版误写为 ['connection', 'runtime'])。DSH client 加载器按 inject 列表去找对应服务,找不到 runtime 就永久 pending,进而 1 entry did not activate,整个 web boot 失败(不是只这一个插件挂,是 GUI 完全打不开)。
- 正确做法:本插件的 client 是自包含的——store 通过 import ... from '@deepseek-ai/dsh-client-runtime/client' 拿(是模块导入,不是 injectable 服务),fetch 同源,React 走 createRoot。所以顶层的 export const inject 应为空数组 [](client 加载器按这个列表等 service,多声明一个未注册的就会永久 pending):
ts
// src/client/index.ts
export const inject: string[] = []
注意区分:顶层 export const inject(加载器声明)和 ctx.inject([...], cb)(cordis 运行时 API,在 apply 里调用)是两回事。设置卡用 ctx.inject(['slots', 'settingsScope'], cb) 是运行时声明子 fiber 等 service——它不写进顶层 inject,所以不触发上面的 pending 问题(ctx.inject 的 callback 在 service 不可用时不执行,不 throw、不卡 boot)。
- 判别:inject 里只能填 DSH client 已注册的服务短名(如 slots / locale / connection / settingsScope / remote / sessions,参照原 dsh-pet 的 client inject)。包名(@deepseek-ai/dsh-client-runtime)不是服务短名,不能填进 inject——包名只出现在 package.json 的 dsh.client.inject(那是“依赖哪些包”的声明,机制不同)。
- 应急:出现此症状先按回滚恢复,再确认 lib/client.js 里是 const inject = [](Select-String lib\client.js "const inject =")。
症状:桌宠出现了,但图像“从左到右移动循环”/帧漂移
- 根因:精灵图几何参数(cell 尺寸 / 列数)填错。host 的 petDefinition() 早期 beta 硬编码了 cell: {width:128,height:128} + columns: 9,而原 dsh-pet 对 whale-refined 精灵图的实际默认是 cell: {width:192,height:208} + columns: 8。尺寸不符 → backgroundSize 把 atlas 缩放到错误宽度 → framePosition 每步偏移和实际格子对不上 → 鲸鱼每帧逐渐右移、循环时跳回,观感就是“从左往右飘”。
- 修复:src/index.ts 的 petDefinition() 用原 dsh-pet 同款默认值(192/208 / 8 列),并优先读 pet.json 的 cell/columns(若存在):
ts
const cell = {
width: raw.sprite2d.cell?.width ?? 192,
height: raw.sprite2d.cell?.height ?? 208,
}
const columns = raw.columns ?? 8
- 判别:curl http://127.0.0.1:3080/api/pet-hermes/pet 返回的 cell 应是 {width:192,height:208}、columns: 8。若返回 128/128 + 9,说明部署的不是修复版 lib(见下“部署未覆盖”)。
- 通用教训:精灵图的 cell 尺寸/列数必须和 atlas 实际像素一致,不能凭空估。本插件的 whale-refined 精灵图实测 1536×1872px,正好 = 192×208 cell × 8 列 × 9 行,可据此反推校验。换素材时要么在 pet.json 里写 cell/columns,要么量图确认默认值匹配。
症状:改了 lib 重新部署,但行为没变 / 还是旧 bug(部署未覆盖)
- 现象:源码改了、也 npm run build 了、也跑了一遍“拷贝 lib 到 profile”,重启后却还是旧行为(例如 cell 漂移依旧)。curl /api/pet-hermes/pet 返回的还是旧值。
- 根因:DSH 进程运行时会占用 lib/.js 文件,Copy-Item src\lib dst\lib 会静默失败(文件被锁,新内容没写进去),但命令不报错,看起来“部署成功”了。部署目录里仍是旧 lib。
- 正确做法(三步,缺一不可):
1. 先停 DSH(关 GUI / 停 dsh 进程)——释放 lib 文件锁
2. 删旧再拷新:Remove-Item $dst\lib -Recurse -Force 然后 Copy-Item $src\lib $dst\lib -Recurse -Force
3. 验证内容:Select-String $dst\lib\index.js "192" 确认新值真写进去了,再重启
- 判别:部署后不重启先 curl /api/pet-hermes/pet(若 DSH 还开着会读到内存旧值,所以这步要在停 DSH、拷完、重启后做)。返回新值才说明覆盖成功。
- 通用教训:对正在被进程占用的目录做覆盖,必须“停进程 → 删旧 → 拷新 → 验证内容”,不能只信 Copy-Item 没报错。
症状:GUI 能开,但右下角没有第二只鲸鱼娘
- client 半挂载问题。看浏览器 DevTools 控制台(F12)的报错,常见是 lib/client.js 里某个 @deepseek-ai/ import 在真实 DSH 环境解析不到。
- 确认插件 client bundle 被加载:控制台看是否有 dsh-pet-hermes 相关的模块加载日志/报错。
症状:桌宠出现了,但对话报错 / 状态栏显示“大脑离线”
- 大脑离线:Hermes 没起,或 endpoint 配错。curl http://127.0.0.1:8642/health 应返回 {"status":"ok",...}。
- chat 401/403:token 不对。确认插件根目录的 token.txt 内容正确(纯 token,无空格/引号),或 settings.yaml 的 tokenFile 指向对的 key 文件,或设 $env:DSH_PET_HERMES_TOKEN。
- chat 409 chat-token-missing:token 三处来源(config / env / 文件)都没读到,检查 token 文件路径。
- chat 超时:Hermes 响应慢(全量记忆注入,首字延迟高属正常),可调大 timeoutMs。
8. 已知限制(beta)
| 限制 | 说明 / 后续方向 |
|---|---|
| 每次对话注入全量记忆 | Hermes 的拟人代价:每轮 prompt 含该用户 Hermes 记忆库的全量 token(取决于记忆积累量,可能数万),首字延迟偏高、token 消耗大。后续可加轻量模式/频控 |
| 对话历史仅浏览器内存 | 刷新页面 transcript 清空(Hermes 侧记忆仍在,她会“记得”,但面板里的对话记录不持久)。后续可加 localStorage 持久化 |
| 桌宠动画不联动 agent 状态 | 目前只播 idle/waving 等 ambient 动画,不随 DSH agent 思考/运行切状态(原插件有,本 beta 未做) |
| 单 persona | 一个固定鲸鱼娘人设,未做多人格切换 |
| 无语音 | 纯文字对话 |
✅ 已在真实 DSH 里端到端验证(2026-08-26):装进 ~/.dsh/profiles/web 后,GUI 正常启动、右下角出现 pet 2.0 鲸鱼娘、点她/💬 弹出对话面板、Hermes 流式回复正常(实锤长期记忆——与 Hermes Agent 记忆数据库互通)。期间踩的两个坑(client inject 误用未注册服务名 / cell 几何填错 + 部署未覆盖)均已修复并记入故障排查。
9. 与原 dsh-pet 的关系
| | 原 @linxin666/dsh-pet | 本 dsh-pet-hermes (pet 2.0) |
|---|---|---|
| 定位 | 观赏型桌宠(状态联动 + 互动) | 对话型拟人桌宠(Hermes 大脑) |
| "说话" | 内置台词库(不记得你) | 真对话(Hermes 长期记忆 + 人格) |
| 路由前缀 | /api/pet/ | /api/pet-hermes/ |
| 素材 | 插件内 assets | 插件内 assets(独立拷贝) |
| 依赖 | 彼此独立 | 彼此独立 |
| 形象 | whale / whale-refined | whale-refined(同款素材) |
两者可并存:原桌宠照旧观赏,pet 2.0 负责陪你聊天。
10. 版权说明
- 本插件代码(src/、构建配置、隔离脚本):MIT,可自行使用/修改。
- 桌宠精灵图(assets/pet/spritesheet.webp):沿用原 whale-refined 的 MIT 许可(pet.json 内 "license": "MIT"),来源为原 dsh-pet 内置素材的拷贝。
- Hermes Agent:NousResearch/hermes-agent,其自身许可见该仓库;本插件只通过其 HTTP API 调用,不内置 Hermes 代码。
立项阶段按约定"先不管版权"快速推进;正式分发前请核对精灵图素材的授权范围。
文档随代码演进更新。当前版本对应 lib/ 构建产物 2026-08-26 的隔离验证通过状态。*扫码进群