dsh-meep(DeepSeek 桌宠)
August 14, 2026 · View on GitHub
meep 是一种鸟类的叫声,与 deep 发音相似——正如这只桌宠:大多数时候安静地待在 桌面角落,几乎不打扰你;只有当 DeepSeek Harness 里有值得注意的事(任务完成、出错、 需要你处理)时,它才会用音效和动画变化提醒你。

dsh-meep 是一只陪伴 DeepSeek Harness(dsh)的桌面小宠物,独立进程运行,不依赖浏览器:
- 安静:任务栏隐藏、默认置顶、默认不走动、表情平缓循环——你可以专心干别的事
- 只在关键时刻提醒:工作完成 / 出错 / 需要你处理时,才播放音效并切换动画
- 一键切到网页版:双击桌宠即可快速打开 dsh 网页界面,出现事件时第一时间处理
- 皮肤化:便签、右键菜单、动画(gif/图片)、前后景、气泡、音效全部由皮肤配置驱动
一、运行方式
Windows 桌面 WSL2(Linux)
┌──────────────────────────┐ ┌─────────────────────────────┐
│ dsh-meep(Electron 桌宠) │ 直连 127.0.0.1 │ dsh web(127.0.0.1:3080) │
│ - 透明置顶小窗(任务栏隐藏)│ ----------- > │ (Windows 经 WSL2 localhost │
│ - 托盘图标 + 右键菜单 │ HTTP/WS 无 │ 转发可达,无需任何配置) │
│ - 点击弹出便签文本框 │ Origin 头 │ │
└──────────────────────────┘ └─────────────────────────────┘
- dsh 与桌宠分开启动:dsh 在 WSL 里
pnpm dsh web;桌宠在 Windows 上双击windows/start-pet.bat(或cd desktop && npm start)。两个进程互不依赖: 桌宠在 dsh 没启动时照常运行(显示"离线"),可一键请求启动 dsh。 - 为什么能直连:dsh 的
/api信任围栏要求 Origin 与 Host 一致——浏览器页面 跨源会被拒,但桌宠的请求全部由 Electron 主进程(Node) 发出,不带 Origin 头、Host 是回环地址,天然通过围栏,因此不需要代理。 - 跨平台:Electron 原生支持 Windows / macOS / Linux(macOS 隐藏 Dock 图标, Linux 依窗口管理器支持 skipTaskbar)。dsh 也可以直接跑在 macOS/Linux 本机。
二、快速开始
:: Windows:双击即可(首次自动 npm install,需网络,约 100MB 的 Electron)
windows\start-pet.bat
:: 或手动:
cd desktop
npm install
npm start
macOS / Linux 同样 cd desktop && npm install && npm start。
可选环境变量:DSH_PET_URL 指定 dsh 地址(默认 http://127.0.0.1:3080)。
三、使用
| 操作 | 行为 |
|---|---|
| 单击桌宠 | 弹出/收起便签(独立可移动、任务栏隐藏、右上角 [×] 关闭) |
| 双击桌宠 | 快速打开 dsh 网页版(出现事件时最快切过去处理) |
| 按住拖拽 | 移动桌宠位置(便签同样可整张拖动) |
| 右键 | 自绘皮肤菜单:打开网页、音效/置顶/走动开关、大小、重启服务、调试、退出 |
| 右键菜单外点击 / Esc | 关闭菜单 |
| 单击托盘图标 / 双击托盘图标 | 打开 dsh 网页版 |
便签(信息文本框)
点击桌宠弹出的便签:
- 首要内容:最近事件 —— 服务器连接/断开、[启动] 开始工作、[完成]/[出错]/[等你] 等, 网页内的工作启动/停止会带上会话名称;消息完整展示不截断。
- 次要内容:状态与个数 —— 运行中会话数、后台任务数、等你数 + 桌宠当前状态。
- 底部设置条 —— 只保留:大小调整(−/+)、皮肤切换、退出(其余设置都在右键菜单)。
行为约定
- 默认安静:任务栏隐藏、窗口置顶(均可在便签/右键菜单调整);默认不走动 (非拖拽不随意移动);表情平缓、不镜像翻转、不弹无关气泡。
- 事件才提醒:只有状态切换(完成/出错/需要你/启动)才播放对应音效与气泡。
- 透明点击:桌宠本体/前景/后景可能是透明图——落在透明像素上不算点中桌宠, 且鼠标穿透(透明像素上的点击会透到后面的窗口/桌面)。
- 便签层级:便签始终在桌宠之下,不会遮挡桌宠。
四、事件与状态
| 状态 | 含义 | 是否提醒 |
|---|---|---|
offline | dsh 未启动/连不上 | 进入时气泡提示 |
sleeping | 待机(无任务,绝大部分时间) | 安静 |
working | 工作中(有会话/后台任务) | 安静(动画平缓轮换) |
waking | 开始干活(空闲 -> 有任务) | 气泡/音效 |
done | 任务完成(短暂庆祝) | 音效 + 动画 |
error | 出错 | 音效 + 动画 |
waiting | 需要你处理(审批/提问) | 音效 + 气泡 |
fatal | 连续出错阵亡 | 音效,点一下复活 |
- 事件出现时,双击桌宠(或托盘单击)即可快速打开 dsh 网页版去处理。
- 事件记录会写进便签"最近事件",并持久化(
userData/history.json)。
五、皮肤与动画自定义
皮肤 = pet/skins/<皮肤名>/skin.json,一个文件统管便签、菜单、动画、前后景、
气泡与音效(完整字段说明见 pet/skins/README.md)。
{
"note": { // 便签:背景图(可换)/文字配色/窗口尺寸
"image": "images/note.png", "textColor": "#334155", ...
},
"menu": { // 右键菜单:背景装饰(图或色)/文字配色
"backgroundImage": "images/menu-bg.png", "textColor": "#1f2937", ...
},
"animation": { // 动画子块(加权动画槽)
"pack": "yuyi", // 动画包名;null = 图片体系
"mode": "gif-first", // 图片选择的解析模式(gif-first | image)
"foreground": "front.png", // 前景(png/gif 均可)
"background": "back.png", // 后景(png/gif 均可)
"effects": { "sleeping": "breathe", ... }, // 图片特效兜底
"bubbles": { // 每状态气泡(文字 + 是否弹出,每皮肤可定制)
"offline": "找不到服务器…",
"working": ["工作中…", "努力干活中…"], // 数组 = 随机挑一个
"waking": { "text": "开工啦!", "show": false }, // show:false = 不弹气泡
...
},
"states": { // 每状态一个动画槽,槽内若干加权选择
"working": [ { "gif": "yuyi.gif", "weight": 2 }, { "image": "潜水", "weight": 1 } ],
"sleeping": [ { "gif": "yuyi.gif" } ],
"offline": [ { "gif": "yuyi.gif" } ],
"done": [ { "gif": "yuyi-loud.gif", "loop": 3, "sound": "yuyi-loud.mp3" } ],
...
}
}
}
动画槽模型:每个状态一个"动画槽",槽内是若干加权选择(weight 控制频率,
主状态定时按权重重新挑选)。每个选择可以是:
| 类型 | 写法 | 说明 |
|---|---|---|
| gif | { "gif": "a.gif", "loop": n, "speed": x, "durationMs": ms, "sound": "s", "weight": w } | loop 播 n 次后结束;durationMs 循环到超时;主状态无需这两项(无限播放) |
| 图片 | { "image": "表情key或文件", "effect": "breathe", "sound": "s", "weight": w } | 表情 key 走转换;effect 仅 image 体系生效 |
| 无 | { "none": true, "weight": w } | 小状态可无动画无音效 |
内置皮肤:default(表情体系示例)、meep、yuyi(gif 动画包示例)、simple
(image 体系示例)。换肤:便签底部"皮肤"下拉,或改 userData/settings.json 的
skin 字段。
皮肤现状:
default与simple目前作为测试皮肤,效果不佳(素材与动画 未打磨);meep、yuyi是主要演示皮肤。后期会对现有皮肤进行功能优化。欢迎贡献新皮肤:按上文方式做好
pet/skins/<名字>/后,把skin.json与 素材提交到本仓库(或 PR),大家都能用上你的皮肤 🎨
六、扩展
1. 新表情系列(零代码)
python3 tools/build-manifest.py --source "新表情目录" --pack 表情包名 --out pet/expressions
图片复制到 pet/expressions/src/<表情包名>/,manifest 记录 pack 字段;
然后在皮肤 animation.states 对应状态槽里引用({image: key})。
2. 动画 gif 包
建目录 pet/expressions/gif/<包名>/,放入各状态 gif/图片/音效,在皮肤
animation.states 里引用。tools/jpg2gif.ps1 可把 jpg 预转成动画 gif。
3. 新皮肤
复制一个现有皮肤为 pet/skins/<名字>,改 skin.json、换图片,再把名字加进
pet/js/config.js 的 skins 列表。
4. 新状态 / 自定义事件
在 config.js 的 states 里加一项,在皮肤 animation.states 加对应动画槽,
然后在 state-machine.js 事件处理里加触发规则(或 config.custom 钩子从外部触发)。
七、目录结构
deepseek-pet/
├── desktop/ # Electron 桌面程序(主产品)
│ ├── main.mjs # 主进程:窗口/托盘/自定义协议/IPC/皮肤菜单窗口
│ ├── preload.cjs # 安全桥(contextBridge)
│ ├── pet-core.mjs # 核心编排:直连 dsh + 状态机 + 快照(Node 可测)
│ ├── settings.mjs # 设置读写(userData/settings.json,含 skin)
│ ├── package.json # electron + electron-builder 配置
│ └── app/ # 渲染层:桌宠窗口 / 便签 / 自绘右键菜单
├── pet/ # 共享素材与核心逻辑
│ ├── js/
│ │ ├── config.js # 状态机/表情/音效配置
│ │ ├── skin.js # 皮肤加载
│ │ ├── skin-anim.js # 动画槽选择逻辑(归一化/加权挑选,纯函数可测)
│ │ ├── gif-encoder.js # 纯 JS GIF89a 编码器(量化/抠底/动画合成)
│ │ ├── gif-utils.js # GIF 字节补丁(NETSCAPE 循环次数 / GCE 帧延迟)
│ │ ├── expressions.js # 表情解析(gif/image 双体系)
│ │ ├── dsh-client.js # dsh 协议客户端(WS 双流 + SSE 降级 + RPC + 重连)
│ │ └── state-machine.js # 状态机(纯逻辑,可注入时钟测试,含事件记录)
│ ├── skins/ # 皮肤目录(default / meep / simple / yuyi)
│ ├── expressions/
│ │ ├── manifest.json # 表情清单(含 pack 字段)
│ │ ├── src/<表情包>/*.jpg # 表情素材
│ │ └── gif/<动画包>/… # 动画 gif 包(含前后景/音效)
│ └── assets/sounds/ # 全局提示音
├── assets/icon.png # 应用/托盘图标
├── windows/start-pet.bat # Windows 一键启动
├── tools/ # 构建与调试工具
└── tests/ # Node 单元测试 + mock dsh 服务器
八、测试
cd deepseek-pet
node --test tests/run-tests.mjs tests/desktop-core.test.mjs
40 项测试:GIF 编码往返与字节补丁(loop/speed/时长)、动画槽选择(加权挑选/ 气泡配置/旧格式兼容)、状态机全场景(三大主状态、事件记录与会话标题、后台任务 与会话隔离)、协议客户端(WS/SSE/重连)、PetCore 编排、设置读写。测试不依赖 dsh 本体(mock 服务器按当前 dsh 协议模拟)。
调试工具(可选):
node tools/probe-dsh.mjs [url]:用桌宠自己的 DshClient 直连 dsh,验证协议。node tools/cdp-drive.mjs:桌宠以--remote-debugging-port=9222启动后, 自动验证核心交互(默认场景 / 便签 / 皮肤菜单 / 皮肤切换)。