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

鲸鱼娘桌面挂件 crossoverthere/dsh-whale-desktop

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

桌面级鲸鱼娘桌宠:基于dsh-whale-widgetDSH 插件版,做成透明置顶、可点击穿透的 Windows…

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

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

该插件未发布到 npm,按作者文档从 GitHub 源安装即可(静态检查只覆盖 npm 路径,不代表插件不可用)。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add crossoverthere/dsh-whale-desktop
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

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

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

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查需源码安装

仓库 package.json 标记 private,未发布到 npm,需从源码安装

展开查看检查明细 ↓

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✗npm 包dsh-whale-desktop(未发布到 npm,仅可源码安装)
✓Node 引擎未声明 engines.node
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明

源码安装时若 pnpm 拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑。

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/13 14:48:57

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

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

可执行外部命令读写/删除本地文件读取剪贴板或密钥环境变量发起外部网络请求
查看扫描证据(共 50 处,展示前 37 条)
  • 写入本地文件
    src/main.js:127
    fs.appendFileSync(logFile(), line + '\n');
  • 读取本地文件
    src/main.js:165
    return { ...DEFAULT_CONFIG, ...JSON.parse(fs.readFileSync(configPath(), 'utf8')) };
  • 写入本地文件
    src/main.js:172
    fs.mkdirSync(path.dirname(configPath()), { recursive: true });
  • 写入本地文件
    src/main.js:173
    fs.writeFileSync(configPath(), `${JSON.stringify(config, null, 2)}\n`);
  • 读取本地文件
    src/main.js:189
    if (!fs.existsSync(file)) continue;
  • 写入本地文件
    src/main.js:855
    fs.mkdirSync(path.dirname(target), { recursive: true });
  • 写入本地文件
    src/main.js:856
    fs.writeFileSync(target, image.toPNG());
  • 写入本地文件
    src/main.js:975
    fs.mkdirSync(path.dirname(file), { recursive: true });
  • 发起网络请求
    src/main.js:1219
    const res = await fetch('http://127.0.0.1:3020/balance', { signal: AbortSignal.timeout(10000) });
  • 执行外部命令固定命令
    src/main.js:1469
    const child = spawn(
  • 执行外部命令固定命令
    src/main.js:1487
    spawn('taskkill', ['/PID', String(pid), '/T', '/F'], { windowsHide: true, stdio: 'ignore' });
  • 删除本地文件
    src/main.js:1623
    fs.unlinkSync(marker);
  • 读取本地文件
    src/main.js:1639
    const spawnedAnyway = fs.existsSync(marker);
  • 发起网络请求
    src/main.js:1765
    const request = http.get(`${server.url}/__shell/state`, { timeout: 2000 }, (response) => {
  • 读取本地文件
    src/server.js:44
    if (!file || !fs.existsSync(file) || !fs.statSync(file).isFile()) {
  • 读取本地文件
    src/server.js:54
    fs.createReadStream(file).pipe(res);
  • 读取本地文件
    src/server.js:94
    const stat = fs.statSync(file);
  • 读取本地文件
    src/server.js:96
    const original = fs.readFileSync(file, 'utf8');
  • 读取本地文件
    src/dsh-state.js:50
    entries = fs.readdirSync(dir, { withFileTypes: true });
  • 读取本地文件
    src/dsh-state.js:60
    const stat = fs.statSync(full);
  • 读取本地文件
    src/dsh-state.js:213
    stat = fs.statSync(this.filePath);
  • 读取本地文件
    src/balance-proxy.js:40
    const text = fs.readFileSync(file, 'utf8');
  • 读取本地文件
    src/balance-proxy.js:56
    const key = fs.readFileSync(file, 'utf8').trim();
  • 读取密钥环境变量
    src/balance-proxy.js:69
    const fromEnv = (process.env.DEEPSEEK_API_KEY || '').trim();
  • 发起网络请求
    src/balance-proxy.js:130
    const upstream = await fetch(UPSTREAM, {
  • 读取本地文件
    src/price-table.js:30
    const raw = JSON.parse(fs.readFileSync(file, 'utf8'));
  • 读取本地文件
    src/price-table.js:127
    const text = fs.readFileSync(file, 'utf8');
  • 读取本地文件
    src/usage-today.js:61
    raw = JSON.parse(fs.readFileSync(this.file, 'utf8'));
  • 写入本地文件
    src/usage-today.js:82
    fs.mkdirSync(path.dirname(this.file), { recursive: true });
  • 写入本地文件
    src/usage-today.js:83
    fs.writeFileSync(this.file, `${JSON.stringify(this.day, null, 2)}\n`);
  • 发起网络请求
    src/open-dsh.js:146
    request = http.get(url, { timeout: timeoutMs }, (response) => {
  • 读取本地文件
    src/open-dsh.js:191
    const stat = fs.statSync(file);
  • 读取本地文件
    src/open-dsh.js:298
    const exists = options.exists || ((file) => fs.existsSync(file));
  • 写入本地文件
    src/open-dsh.js:473
    fs.mkdirSync(path.dirname(plan.logPath), { recursive: true });
  • 发起网络请求
    src/pet/settings.js:512
    const res = await fetch(endpoint, { cache: 'no-store' });
  • 发起网络请求
    src/pet/shell.js:442
    const geo = await (await fetch(
  • 发起网络请求
    src/pet/shell.js:449
    const wx = await (await fetch(
扫描于 2026/11/2(1 天前) · 范围 入口闭包 + 全包扫描 · 来源 GitHub 仓库

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

给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 透明置顶桌面挂件
  • 可点击穿透
  • Electron桌面应用

README

由 DeepSeek 最新模型翻译生成
鲸鱼娘桌宠 · dsh-whale-desktop 把 dsh-whale-musume 的桌宠本体从 DSH 网页里解放出来,做成 Windows 桌面上的独立挂件: - 透明:只有鲸鱼娘本身可见,没有窗口边框、没有底色 - 置顶:浮在其它窗口之上 - 点击穿透:指针不在她身上时,鼠标事件全部穿过,不挡你操作桌面 - 零改动复用上游:立绘、动作、互动、养成、成就、小游戏、齿轮设置面板全部原样可用 - DSH 工作状态联动:思考中 / 工作中 / 完成 / 出错 会切换对应立绘与状态签,而且按工具类型换姿势(跑 shell → 摸鱼打电话,写文件 → 开会,搜索 → 灵光一现) - 花费播报:每个任务结束时她说一句「这次任务花了 X tokens,约 ¥Y」(读 DSH 会话里的真实用量,按官方峰谷价估算;低于阈值不打扰) - 余额查询:气泡里点一下就念一整句 ——「当前余额为 CNY 41.11,状态为充裕,今日共计消耗 35.2 万 tokens,消费 0.53 元」 - 打开 DSH:右键菜单里一条「打开DSH」——已经在跑就只开浏览器,没跑才把它拉起来(绝不重启,免得把你正在聊的会话带走) - 托盘常驻:显示/隐藏、置顶、跟随 DSH 开关、开机自启、缩放、重置位置、退出 这不是 DSH 插件,而是一个独立的 Electron 应用。
展开完整文档(README 剩余 1 节)
鲸鱼娘桌宠 · dsh-whale-desktop

把 dsh-whale-musume 的桌宠本体从 DSH 网页里解放出来,做成 Windows 桌面上的独立挂件:

- 透明:只有鲸鱼娘本身可见,没有窗口边框、没有底色
- 置顶:浮在其它窗口之上
- 点击穿透:指针不在她身上时,鼠标事件全部穿过,不挡你操作桌面
- 零改动复用上游:立绘、动作、互动、养成、成就、小游戏、齿轮设置面板全部原样可用
- DSH 工作状态联动:思考中 / 工作中 / 完成 / 出错 会切换对应立绘与状态签,而且按工具类型换姿势(跑 shell → 摸鱼打电话,写文件 → 开会,搜索 → 灵光一现)
- 花费播报:每个任务结束时她说一句「这次任务花了 X tokens,约 ¥Y」(读 DSH 会话里的真实用量,按官方峰谷价估算;低于阈值不打扰)
- 余额查询:气泡里点一下就念一整句 ——「当前余额为 CNY 41.11,状态为充裕,今日共计消耗 35.2 万 tokens,消费 0.53 元」
- 打开 DSH:右键菜单里一条「打开DSH」——已经在跑就只开浏览器,没跑才把它拉起来(绝不重启,免得把你正在聊的会话带走)
- 托盘常驻:显示/隐藏、置顶、跟随 DSH 开关、开机自启、缩放、重置位置、退出

这不是 DSH 插件,而是一个独立的 Electron 应用。它不依赖 DSH 运行,也不需要浏览器标签页。DSH 那边的插件可以照常装着,两者互不干扰。
工作状态联动是只读 DSH 的会话文件(~/.dsh/sessions/),不往 DSH 里装任何东西。

要接手维护它?先读 docs/HANDOVER.md:现状一页纸、铁律、自检矩阵、环境事实、踩过的坑、常见维护动作(加菜单项 / 加设置项 / 发版 / 重启实例)都在那里。"为什么是这样设计"看 docs/DEVELOPMENT.md; "整体长什么样"看 docs/ARCHITECTURE-DIAGRAM.md (带排版版本:ARCHITECTURE-DIAGRAM.html)。

快速开始

npm install # 若 Electron 二进制没下来,见下方"常见问题"
npm start # 启动桌宠
npm run dev # 带 DevTools

启动后:

| 操作 | 效果 |
| 左键点她 | 脸红 / 爱心 |
| 快速连点三次 | 星星眼庆祝 + 粒子特效 |
| 按住拖动 | 切换「被拎起来」立绘,松手后位置自动记住 |
| 右键她 | 互动 + 唤起 DSH:投喂 / 戳一下 / 夸夸 / 两个小游戏 / 回到原位 / 打开DSH |
| 齿轮 ⚙ | 头顶浮层:天气 / 余额 两行状态 + 并排的两个入口「日常养成」「余额查询」;点任一项浮层自动收起(点天气或余额那行去设置) |
| 托盘右键 | 显示桌宠 / 总是置顶 / 跟随 DSH 工作状态 / 开机自启 / 大小 / 重置到默认位置 / 重新加载页面 / 打开数据目录 / 打开日志 / 设置… / 退出 |
| 托盘 →「设置…」 | 独立设置窗口,二级菜单:左侧 6 个分类(看板娘 · 窗口与状态 · 大小与位置 · 维护 · 天气与余额 · 花费播报),点哪一类右侧就显示哪一屏 |
| 齿轮 ⚙ →「日常养成」 | 养成窗口,五个标签页;称号与成就也在这里(不再占用托盘菜单) |
| Alt+Shift+W | 显示 / 隐藏 |
| 托盘图标单击 | 显示 / 隐藏 |

分工原则:和她互动的事在桌宠右键菜单;高频全局动作与快速开关在托盘;需要解释的配置项集中在设置窗口(按分类分屏);养成/图鉴统一走气泡里的「日常养成」入口 —— 托盘只留"随手就要用"的东西,避免菜单越堆越长。
托盘与设置窗口共用同一份配置,任何一处改动都会同步(所有 setter 末尾都会刷新托盘菜单)。

养成 / 图鉴

入口:气泡齿轮 ⚙ →「日常养成」(托盘菜单里不再单独放称号/成就 —— 它们本来就是养成的一部分,单独占两行只会让托盘越堆越长)。窗口内五个标签页:

| 标签页 | 内容 |
| 今日任务 | 每天 3 个任务(签到 / 摸头 / 跑工具…),达成后一键领取,奖励好感与心情 |
| 本周签到 | 一周七格签到表 + 连续签到天数 + 1/3/7 天里程碑 |
| 称号 | 按等级解锁的称号,可佩戴(会显示在头顶浮层里) |
| 成长日记 | 她记录下来的成长事件(本地 80 条,不上传) |
| 成就 | 39 个成就;已解锁高亮显示名称与描述,未解锁灰显并隐藏名称 |

实现上直接复用上游的纯状态机(vendor/whale/whale-moe-core.js 是 UMD,浏览器里挂 window.DshWhaleMoeCore、Node 里可 require)——成就表、称号表、任务池、等级与签到规则全用它,没有重写任何养成算法。

存档(养成数据、成就、位置、偏好)全部在浏览器 localStorage 里,键名前缀 whale-moe:,存放于 Electron 的 userData 目录,关掉应用不会丢。

花费播报

每个任务结束后她用一句台词报账:这次任务花了 154.7 万 tokens,约 ¥0.0518。

| 环节 | 实现 |
| 用量从哪来 | DSH 会话文件里每条 assistant/message 的 usage,按 turn/start…turn/end 累加 |
| 怎么算钱 | src/price-table.js 按当前模型从本地价目表取价 → src/pricing.js 乘用量 |
| 什么时候说 | 一个 turn 正常结束且金额 ≥ 阈值(默认 0.01 元) |
| 怎么开口 | 上游没有对外的"说话"接口,壳在返回桌宠脚本时注入一段 __dshWhaleMoeSay(vendor/whale/ 磁盘文件不改) |
| 在哪配置 | 设置窗口 →「花费播报」:开关、阈值、当前模型与价格(只读)、手动配置、测试播报 |

两个必须记住的计费细节(都踩过):

- 缓存命中与未命中分开计价,价差约 50 倍;混算会高估几十倍
- reasoningTokens 是 outputTokens 的子集,不能再加一遍

已知边界(都写进注释了):不处理中国法定节假日(那几天官方按空闲计价,我们会高估一倍);统计范围是“一个 turn 内的全部记录”,子代理(subagent)消耗的 token 也计入。

单价从哪来:本地价目表,只人工刷新

价格写在仓库根目录的 pricing.json 里 —— 桌宠运行时只读这个文件,不联网。
DeepSeek 没有价格 API,官网只有一张给人看的网页表格;爬页面一旦结构变动就会静默失效,而计费算错是最难发现的一类 bug(她照样说话,只是数字不对)。所以刷新是人工动作:

npm run refresh:prices # 抓官方页面 → 解析 → 打印 diff → 写文件(不自动提交)
npm run refresh:prices -- --dry-run # 只看结果不写文件
npm run refresh:prices -- --from-file x.html # 用已保存的页面(没网时)

脚本解析不出完整表格时会报错退出且不写文件(宁可手动改,也不写半张表)。

价目表按模型分档,并带旧模型名别名:

| 模型 | 缓存命中(空闲/高峰) | 缓存未命中 | 输出 |
| deepseek-flash | 0.02 / 0.04 | 1 / 2 | 4 / 8 |
| deepseek-v4-pro | 0.15 / 0.30 | 4.5 / 9 | 13.5 / 27 |

(元 / 百万 tokens;高峰 = 北京时间周一至周五 9:00-12:00、14:00-18:00)

当前用哪个模型是自动识别的:会话记录 request/header 里的 data.header.config.model(没读到就用 ~/.dsh/settings.yaml 的默认模型)。
所以中途换模型不用重启,设置窗口里那行“当前模型 + 价格”每 2 秒自动跟上。

设置窗口 →「花费播报」分两块:

- 当前模型 / 价格(只读):显示模型名、它在价目表里的空闲与高峰价、当前用哪一档
- 手动配置(可改):只有当模型不在价目表里时才参与计算(高峰统一 ×2)

余额查询(气泡里那个按钮)

点一下,她把三件事念成一句话:

当前余额为 CNY 41.11,状态为充裕,今日共计消耗 35.2 万 tokens,消费 0.53 元

- 余额与状态:页面里上游自己按 60 秒刷新的余额(状态词沿用上游那套已见底 / 告急 / 偏紧 / 正常 / 充裕 / 很充裕)
- 今日共计消耗:主进程的今日账本(src/usage-today.js)——按 turn 记账、落盘、跨天归零,重启不丢,启动时还会从会话文件回放当天已有的 turn 来补账(所以中途才把她叫起来也不会少算)。按 会话#turn 去重,回放不会算两遍;早于今天的 turn 直接丢弃,不会把账本弄脏。
- 没启用余额 / 接口没响应时不会念一串“不可用”,而是换成一句提示(去设置里开、看看接口)
- 播报类台词打完字后停留 5 秒;看完不用等,任意点击立刻收起

已知边界:账本只统计当前正在跟的那个会话(换会话前的账在旧会话文件里,不会合计);不处理中国法定节假日(与花费播报同一限制)。

打开 DSH(右键菜单里那条)

右键她 →「打开DSH」,她会看一眼 DSH 在不在,然后:

| 情况 | 她做什么 | 她说什么 |
| 已经在跑 | 只在默认浏览器里打开它(不重启、不再起一个服务) | 「DSH 已经开着啦,这就给你打开~」 |
| 没在跑 | 把它拉起来(见下),等到监听就绪再打开浏览器 | 「DSH 起来了,浏览器已经给你打开~」 |
| 起不来 | 提前退出就立刻报错,否则等到超时(默认 60 秒),不假装成功 | 「DSH 没起来…你看看运行日志吧」 |

没在跑时用什么把它拉起来(按顺序挑第一个能用的):

| 优先级 | 用什么 | 说明 |
| 1 | 你配置的命令/脚本 | 设置窗口 →「维护」→「DSH WebUI」那一行可以填 |
| 2 | 工作空间里的启动脚本 | 程序目录或它的上级目录里的 Start-DSH-Web-Background.bat / .cmd;地址被改成非默认端口时跳过(脚本不接受 --port) |
| 3 | dsh CLI | 直接 node /lib/bin.js web --no-open --port ;找不到 bin.js 就退回 cmd /c dsh.cmd,再不行 npx @deepseek-ai/dsh |

设置窗口那一行会写清这次走的是哪条路(鼠标悬停有完整命令行与日志路径)。

几个刻意的取舍:

- 绝不重启正在跑的 DSH。它每次启动都会换一个浏览器信任 token,重启等于把你正在聊的会话连同页面一起打断;所以“在跑”就只开浏览器,真正的启停由你决定。
- 401 也算“在跑”:DSH 的 browser-trust 会拒绝没有 cookie 的请求,而那恰恰证明服务在监听(你的浏览器里本来就有那个 cookie)。把它当“没在跑” 就会平白多起一个服务,那更糟。
- 进程交给 shell 起,不挂在自己身上:桌宠是 GUI 进程,直接 spawn 出来的子进程会跟它共用一个隐藏控制台 —— 那个控制台随时可能被一个 Ctrl+C / 关闭事件顺手清掉,服务就死得没头没脑(0.12.0 就是这么栽的,日志里只有一个 ^C)。
现在走 Start-Process -WindowStyle Hidden,服务有自己独立的隐藏窗口,桌宠退出也不牵连它。
- 拉起前摘掉 DSH_SHELL / DSH_SESSION_ID / DSH_WEB_URL:它们描述的是 “我正处在哪个会话里”,对一个新起的服务是错的(谁把桌宠从 DSH 会话里启动的,这些标记就会一路继承下去)。DSH_HOME 保留。
- 浏览器由我们开(--no-open),因为这样才能把“起没起来”回报给她;自己拉起来的这一次还能从输出里拿到带 token 的地址并优先用它打开(旧 token 一律 401,只有本次启动的 token 有效)。
- 地址写在配置里(默认 http://127.0.0.1:3080),设置窗口 → 维护 →「DSH WebUI」里可以直接改,也能从那儿点「打开」。
- 拉起过程的输出(含 token 地址)落在数据目录的 dsh-web.log;用启动脚本那条路时,日志在脚本旁边的同名文件(例如 D:\DSWorkspace\dsh-web.log)。

项目结构

dsh-whale-desktop/
├─ src/
│ ├─ main.js 主进程:窗口/托盘/IPC/自检探针 │ ├─ preload.js contextBridge 通道(页面拿不到 node) │ ├─ server.js 本地静态服务器:/assets/* → vendor/whale/(+ 说话钩子补丁) │ ├─ dsh-state.js 读 DSH 会话文件 → 工作状态 + turn 用量 + 当前模型 │ ├─ pricing.js 怎么乘(纯函数) │ ├─ price-table.js 单价从哪来(读 pricing.json,按模型 + 峰谷取价) │ ├─ usage-today.js 今日消耗账本(落盘、跨天归零、按 会话#turn 去重) │ ├─ open-dsh.js 打开 DSH:探活 / token 地址 / 启动计划 / 交接起进程(纯 node) │ ├─ balance-proxy.js 内置余额代理(自动读 DSH 里的 DeepSeek Key) │ └─ pet/ │ ├─ index.html 只负责按顺序引入上游三个文件 │ ├─ shell.css 透明画布 + 选中/滚动等宿主适配 │ ├─ shell.js 点击穿透判定 + 头顶浮层 + 日志回传 │ ├─ settings. 独立设置窗口 │ └─ growth.* 养成 / 图鉴窗口 ├─ pricing.json 本地价目表(运行时只读,人工刷新) ├─ vendor/whale/ 上游桌宠素材(MIT,见 THIRD-PARTY.md) ├─ scripts/ │ ├─ sync-upstream.mjs 从上游 tag 同步素材 │ ├─ check-vendor.mjs 素材完整性校验 │ ├─ verify-shell.ps1 点击穿透自动化验证(真鼠标) │ ├─ open-dsh-e2e.ps1 「打开DSH」端到端(真鼠标点菜单项,隔离 DSH_HOME) │ ├─ cold-start-check.mjs 真机冷启动 dsh web(独立 DSH_HOME + 空闲端口) │ ├─ check-desktop-input.ps1 这套环境能不能驱动鼠标(真鼠标类脚本的前置检查) │ ├─ test-cost.mjs 计价 / 账本 / 价目表单测(npm run test:cost) │ ├─ refresh-prices.mjs 刷新价目表(npm run refresh:prices) │ └─ push.ps1 走代理推送 └─ docs/ ├─ HANDOVER.md 接手维护先看这份(现状/铁律/自检/环境/坑) ├─ DEVELOPMENT.md 架构细节与调试方法 ├─ ARCHITECTURE-DIAGRAM.md 一页看完的项目框架图(拓扑/数据流/职责/自检) └─ ARCHITECTURE-DIAGRAM.html 同一份框架图的带排版版本(浏览器打开)

上游素材一个字都没改——这是刻意的,见 docs/DEVELOPMENT.md 里"为什么不需要改上游"。

常见问题

npm install 之后启动报找不到 Electron
npm 11+ 默认拦截依赖的生命周期脚本,Electron 的二进制就下不来。手动补一次:

node node_modules/electron/install.js

鲸鱼娘没出现 / 位置跑到屏幕外托盘右键 →「重置到默认位置」。

天气不显示设置面板里城市留空时完全不联网;填了城市会请求 api.open-meteo.com。

余额那里报错默认走内置余额代理(127.0.0.1:3020,随桌宠启停,Key 自动从 ~/.dsh/.credentials.yaml 的 DEEPSEEK_API_KEY 读取),不需要手工配置。
只有在你把余额接口改成别的地址、而那个服务又没起来时才会报错。
设置窗口 →「天气与余额」→「测试」会直接把接口返回摊开给你看。

点了「打开DSH」没反应 / 浏览器里是 401 先看服务自己的日志(其中 token 地址就在里面):

- 没在跑时需要它自己起来:日志在数据目录的 dsh-web.log(用工作空间启动脚本那条路时,日志在脚本旁边的同名文件,例如 D:\DSWorkspace\dsh-web.log),失败原因也会写进 shell.log(托盘 →「打开日志」)。设置窗口 →「维护」→「DSH WebUI」那一行会告诉你走的是哪条路,想手工试就跑一遍同样的命令。
- 已经在跑却打不开:多半是浏览器丢了 DSH 的信任 cookie(有效期 30 天)。
让 DSH 重启一次会打印带 token 的地址,用它打开一次就能重新拿到 cookie;桌宠不会替你重启(那会打断你正在聊的会话)。

真鼠标的验证脚本(verify:shell / e2e:open-dsh)不是"过时不过" 它们会真的移动系统指针;锁屏 / RDP 断开 / 快速用户切换时 SetCursorPos 会被拒,表现就是"鼠标移不到她身上"。先跑前置检查看退出码:

powershell -NoProfile -ExecutionPolicy Bypass -File scripts/check-desktop-input.ps1

退出码 0 = 能驱动(可以跑那两条),3 = 环境不可驱动(只跑无头探针,并在结论里写明)。
无头探针(test:cost / open-dsh:probe / settings:probe / menu:probe 等)不受影响。

git push 报 Recv failure: Connection was reset
这台机器上 GitHub 的 API 与 codeload 都直连正常,但 git 的 push 端点(git-receive-pack)会被重置。用仓库自带的推送助手:

powershell -NoProfile -ExecutionPolicy Bypass -File scripts/push.ps1 # 检测到本机 Clash 代理就自动走代理

它只对本次命令生效,不写进 git 全局配置。也可以手动:

git -c http.proxy=http://127.0.0.1:7890 push origin main

路线图

- [ ] 多显示器支持(当前铺满主显示器工作区)
- [ ] electron-builder 打包成免安装 exe
- [ ] 全局快捷键自定义
- [ ] 单实例之外的"每屏一只"
- [ ] 花费播报:节假日按空闲价、按会话区分主/子代理用量

许可

- 本项目代码:MIT
- 上游桌宠素材:MIT,版权归 Sutera-Diffusus 及其贡献者,详见 THIRD-PARTY.md 与 vendor/whale/LICENSE
预览分享卡片长什么样 ↓crossoverthere/dsh-whale-desktop
分享 · DeepSeek Harness Hub
crossoverthere/dsh-whale-desktop

桌面级鲸鱼娘桌宠:基于dsh-whale-widgetDSH 插件版,做成透明置顶、可点击穿透的 Windows…

回到顶部