Skip to content
dsh.fish
Bundle

dsh-whale-tools

dsh-whale-widget 的附属插件:任意图片自动抠图替换小鲸鱼 + 会话头部「重启 dsh」按钮 + 浏览器心跳 + 启动自愈

Source
ankhishtar2-lang
License
MIT
Updated
Updated 5 hours ago

Readme

# dsh-whale-tools

> [!WARNING]
> **本仓库是 vibe coding 产物。** 代码由作者与 AI(DeepSeek Harness 会话)对话生成、
> 多轮迭代而来,**没有经过人工逐行审计**,也只在作者本机环境(WSL2 + dsh `0.1.5-rc.1` 系列)
> 实测过。请自行审阅后再使用;**不要直接用于生产或安全敏感场景**。
> 代码按 MIT「按原样(AS IS)」提供,不附带任何担保,风险自负。

> A vibe-coded satellite plugin for [`dsh-whale-widget`](https://github.com/MeteorNOX/DeepSeek-Balance-Whale-Widget):
> drop in **any image** and the built-in cutout pipeline replaces the whale mascot;
> plus a session-header **restart dsh** button, a browser heartbeat and a boot-time self-heal.

给 dsh 的小鲸鱼挂件配上「**任意图片一键换装**」,顺手补两个运维小工具。

---

## 1. 它做什么

| 能力 | 说明 |
|---|---|
| **设置项:小鲸鱼图片(自动抠图)** | 设置 → 外观 页最下方:选图 → 服务端跑抠图流水线(裁剪 → 边界洪水填充去白底 → 只留最大连通域 → 居中缩放 610×610 透明 PNG)→ 同时覆盖「用户路径图」与「抠图真源」 |
| **立即换图,不依赖补丁** | 浏览器半把 `.dshwv-img` 的 `src` 指向 `/dsh-whale-tools/current.png`(每次请求现读文件),抠图成功后**当场换图,无需重启** |
| **图片持久化** | 配合 `tools/dsh-whale-mascot-apply.sh` 给 `dsh-whale-widget` 打「用户路径优先」补丁,并把它铺到 `$DSH_HOME/dsh-whale-widget/image.png` |
| **启动自愈** | 插件 boot 时幂等重放上面那个补丁脚本 —— 因为 **pnpm 任何 install/add/update 都会重链 `node_modules`,把手改的补丁冲掉** |
| **会话头部「重启 dsh」按钮** | `order: -20`(在「打开文件管理器」左边),点一下走完整重启流程:确认 → 遮罩 → 调服务重启 → 轮询探测 → 自动刷新 |
| **浏览器心跳** | 每 30 秒 `GET /dsh-whale-tools/heartbeat`,host 半 touch `$DSH_HOME/.dsh-browser-heartbeat`;启动脚本据此判断「浏览器里已经有 DSH 页面」从而**不再新开页面** |
| **手机端拖得动小鲸鱼** | 注入一段 CSS 修掉挂件的触屏缺陷(见第 7 节) |

---

## 2. 依赖声明(重要)

### 2.1 硬依赖:`dsh-whale-widget` 插件

必须已安装官方鲸鱼挂件(本插件接管它的图片显示、并给它打补丁):

- 挂件类名:`.dshwv-root` / `.dshwv-img`(浏览器半按这两个类名工作);
- 挂件图片候选列表 `IMAGE_CANDIDATES`(apply 脚本按这个锚点注入用户路径)。

### 2.2 硬依赖:外部程序

| 程序 | 必需 | 用途 | 缺失时 |
|---|---|---|---|
| `sharp`(Node 库) | ✅ | `tools/dsh-whale-mascot-cutout.cjs` 的图像处理 | 抠图报错;`npm i sharp` 或用 `DSH_SHARP_PATH` 指向已有安装 |
| `python3` | ✅ | `tools/dsh-whale-mascot-apply.sh` 做文本补丁注入 | 自愈与打补丁跳过 |
| Node | ✅ | `>= 20`(全局 `fetch`) | — |
| `systemctl --user` | ❌ | 头部「重启 dsh」按钮 | 按钮仍在,但重启会失败 |

### 2.3 dsh 版本与平台

| 项目 | 要求 |
|---|---|
| dsh 核心 | 在 `0.1.5-rc.1` 系列的 web profile 上验证;其它版本自测 |
| profile | `web`(`$DSH_HOME/profiles/web`) |
| 平台 | Linux / WSL2 |
| Node | `>= 20` |

### 2.4 `package.json` 里的声明

```json
"peerDependencies": { "dsh-whale-widget": "*" },
"dshDependencies": {
  "plugins": [{ "name": "dsh-whale-widget", "required": true, "provides": "小鲸鱼挂件本体" }],
  "programs": [{ "name": "sharp", "required": true }, { "name": "python3", "required": true }]
}
```

---

## 3. 安装

```bash
git clone https://github.com/ankhishtar2-lang/dsh-whale-tools.git
cd dsh-whale-tools
bash scripts/install.sh
systemctl --user restart dsh-web-profiled.service      # 由你手动执行
```

手动安装 = 把整个目录拷到 `$DSH_HOME/profiles/web/node_modules/dsh-whale-tools/`,
再把 `"dsh-whale-tools"` 追加进 profile `package.json` 的 `dsh.profile.bundles`,然后重启。

> ⚠️ **硬红线**:host 半必须存在 `export function apply`。dsh 的插件加载器是 **fail-fast 且不隔离**的,
> 缺 `apply` 会让**整棵插件树 boot 失败**、`dsh web` 完全起不来。改这个插件的 host 半之前先记住这条。

### 3.1 准备你自己的图片

抠图需要一个「真源」图片。三种方式任选:

1. 放到 `assets/whale-mascot/DSniang1.custom.png`(仓库自带该目录,但**不含图片** —— 作者的自定义图有版权,不随仓库分发);
2. 放到 `$DSH_HOME/dsh-whale-widget/source.png`;
3. 用 `DSH_WHALE_SOURCE_IMAGE=<绝对路径>` 指定。

> 也可以完全不用真源:直接在设置项里上传图片走抠图流水线,结果会写进
> `$DSH_HOME/dsh-whale-widget/image.png` 并同步真源。

---

### 安装方式补充:从 npm 安装(可选)

本包的 `package.json` 已按 npm 发布要求准备好(去掉 `private`、用 `files` 白名单控制内容)。
发布到 npm 后即可用 dsh 自己的命令安装(`dsh plugin add` 本质就是 `pnpm add`):

```bash
dsh plugin --profile web add dsh-whale-tools
```

自己发布(**需要你自己的 npm 账号**;`scripts/publish.sh` 不接触也不保存任何 token):

```bash
npm login
bash scripts/publish.sh --dry     # 自检 + 列出将要发布的文件,不发布
bash scripts/publish.sh           # 真正发布(改过代码要先升 version,同版本号不可覆盖)
```

> 发布后请同步更新 README 与上游收录表的描述,保持「描述属实」这一条成立。

## 4. 配置(环境变量)

| 变量 | 默认 | 说明 |
|---|---|---|
| `DSH_WHALE_SOURCE_IMAGE` | 见 3.1 的探测顺序 | 抠图真源图片 |
| `DSH_WHALE_CUTOUT_CLI` | `tools/dsh-whale-mascot-cutout.cjs` | 抠图 CLI |
| `DSH_WHALE_APPLY_SCRIPT` | `tools/dsh-whale-mascot-apply.sh` | 补丁/铺图脚本 |
| `DSH_WHALE_ALLOW_REMOTE_ADMIN` | 未设 | 设为 `1` 时取消「仅电脑端」闸门(见第 6 节,不建议) |
| `DSH_HOME` | `~/.dsh` | dsh 数据目录(用户图片路径由它推导) |

路径解析顺序:**环境变量 → 插件包内自带(`tools/`、`assets/`)→ 作者本机历史路径**。

---

## 5. HTTP 路由

注册在 dsh web(默认 3080)上,只接受回环来源:

| 方法 | 路径 | 仅电脑端 | 说明 |
|---|---|---|---|
| GET | `/dsh-whale-tools/status` | 否 | 当前图状态(是否存在/字节数/mtime/md5) |
| GET | `/dsh-whale-tools/current.png` | 否 | 当前图(`no-store`,现读文件)—— 挂件图片就走这里 |
| GET | `/dsh-whale-tools/heartbeat` | 否 | 浏览器心跳(touch 心跳文件,204) |
| POST | `/dsh-whale-tools/cutout` | ✅ | 上传图片字节(≤20 MB)→ 跑抠图 → 覆盖用户图与真源 |
| POST | `/dsh-whale-tools/restart` | ✅ | 先回包、再延迟 300 ms `systemctl --user restart` |

---

## 6. 安全说明

`dsh-mobile` 的移动网关会把**已配对设备**的请求带着特权 cookie 转发到 3080,
来源地址也是 `127.0.0.1`。作者实测:加固前,手机经隧道 `POST /dsh-whale-tools/restart`
返回 200,**dsh 真的被重启了**。

因此变更类路由要求一个自定义头(网关转发时会剥掉它,电脑端直连时保留):

```
x-dsh-whale-tools-desktop: 1
```

- 手机端照旧可以**看状态**与**显示小鲸鱼**(只读路由不设闸门);
- 手机端不能换图、不能重启 dsh;
- 逃生舱:`DSH_WHALE_ALLOW_REMOTE_ADMIN=1`。

---

## 7. 关于触屏:为什么还要注入一段 CSS

`dsh-whale-widget` 是为**鼠标**写的:`.dshwv-root` / `.dshwv-img` 都是 `pointer-events: none`,
靠 document 级捕获 + canvas 像素 alpha 命中来决定要不要开始拖拽,全篇**没有一个 `touch-action`**。
在触屏上,手指按下的命中目标是下层的应用元素,浏览器据此把这次手势判定为「页面滚动」→
抛 `pointercancel` → 挂件直接 `endDrag()`。结果就是**鼠标能拖、手指拖不动**。

本插件注入:

```css
.dshwv-img { touch-action: none !important; pointer-events: auto !important }
.dshwv-root.dshwv-dragging, .dshwv-root.dshwv-dragging * { touch-action: none !important }
```

只作用于鲸鱼图片那一小块,不影响页面其它区域滚动,也不改变挂件自己的命中逻辑。
(注意:CDP 合成触摸**无法**验证这类修复——它绕过合成器的滚动判定——只能真机确认。)

---

## 8. 已验证 / 已知限制

**已验证**

- 抠图流水线在本机跑通(610×610 透明 PNG,结果 md5 与预览一致);
- 设置项、头部按钮、心跳、启动自愈的桩测试全部通过;`dump-config` 退出码 0;
- 手机端拖动修复:作者真机确认可用(代码层面无法用合成事件验证)。

**已知限制**

- `dsh-whale-widget` 在**模块 import 期**就定死了 `IMAGE_CANDIDATES`,所以「打补丁」这件事
  要到**下一次重启**才生效;本插件的浏览器半因此做了主防线(直接改 `img.src`),
  不再依赖补丁才能换图;
- pnpm 重链会冲掉对 `node_modules` 里挂件的任何手改,所以自愈脚本是必需的,不是可选项;
- 抠图对「白底、单一主体」的图效果最好;复杂背景可能残留杂边,可调
  `tools/dsh-whale-mascot-cutout.cjs` 里的阈值与裁剪比例参数;
- 「重启 dsh」按钮依赖 systemd user 服务名 `dsh-web-profiled.service`(写死在 host 半,
  要换服务名请自行改 `lib/index.js`)。

---

## 9. 卸载

```bash
bash scripts/uninstall.sh
systemctl --user restart dsh-web-profiled.service
```

卸载**不会**还原小鲸鱼图片、也不会撤销打过的挂件补丁。想回到官方小鲸鱼:
删掉 `$DSH_HOME/dsh-whale-widget/image.png`,并让 `dsh-whale-widget` 重新安装一次
(或在它的 `lib/index.js` 里移除带 `>>> dsh-whale-mascot-apply` 标记的那一行)。

---

## 10. 目录结构

```
dsh-whale-tools/
├── package.json                         # dsh.bundle / dsh.client / dshDependencies
├── cordis.patch.yml                     # loader 条目:id=whale-tools
├── lib/
│   ├── index.js                         # host 半:5 条路由 + 启动自愈 + 桌面闸门
│   └── client.js                        # 浏览器半:设置项 + 头部重启按钮 + 图片接管 + 触屏 CSS
├── tools/
│   ├── dsh-whale-mascot-cutout.cjs      # 抠图流水线(sharp)
│   └── dsh-whale-mascot-apply.sh        # 铺用户图 + 给挂件打补丁(幂等)
├── assets/whale-mascot/                 # 放你自己的真源图(默认空,见 3.1)
├── scripts/                             # install.sh / uninstall.sh
├── LICENSE
└── README.md
```

---

## 11. 许可

MIT © 2026 ankhishtar2-lang —— 见 [LICENSE](LICENSE)。
再次提醒:**vibe coding 产物,未经人工逐行审计,按「原样」提供,风险自负。**

Install

dsh plugin --profile web add github:ankhishtar2-lang/dsh-whale-tools

Profile: web

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source