养亲密度、开番茄钟,让桌宠陪你专注工作
29.9/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/10 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add youhuangqing/dsh-pet-plus该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:待复验
是什么
dsh 原生插件 · security
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 22 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 153 处,展示前 123 条)
- 读取本地文件runtime/electron-helper/main.js:162
const v = Number(JSON.parse(readFileSync(dpiCacheFile(), 'utf8')).scaleFactor); - 写入本地文件runtime/electron-helper/main.js:171
writeFileSync(dpiCacheFile(), JSON.stringify({ scaleFactor: value }), 'utf8'); - 执行外部命令来自配置/变量runtime/electron-helper/main.js:195
out = execFileSync(process.execPath, [__filename, '--dsh-pet-plus-dpi-probe'], opts); - 读取本地文件runtime/electron-helper/main.js:602
const data = await fsPromises.readFile(resp.file); - 写入本地文件runtime/electron-helper/main.js:996
writeFileSync(smokeOut, image.toPNG()); - 发起网络请求runtime/electron-helper/renderer.js:49
const res = await fetch(BASE + '/config', { cache: 'no-store' }); - 发起网络请求runtime/electron-helper/sprite.js:1097
fetch(BASE + '/config', { - 发起网络请求runtime/electron-helper/sprite.js:1117
fetch(BASE + '/config', { - 发起网络请求runtime/electron-helper/sprite.js:1149
fetch(BASE + '/pomodoro', { method: 'POST', headers: { 'content-type': 'application/json' }, body }) - 发起网络请求runtime/electron-helper/sprite.js:1157
const r = await fetch(BASE + (kind === 'stats' ? '/stats?days=1' : '/pomodoro')); - 发起网络请求runtime/electron-helper/sprite.js:1324
fetch(BASE + '/reload', { method: 'POST' }).catch((e) => { - 发起网络请求src/client/notify.ts:129
const r = await fetch('/dsh-pet-plus-7340/config'); - 发起网络请求src/client/pet.ts:349
fetch(assetUrl, { cache: 'default', signal: ac.signal }) - 发起网络请求src/client/pet.ts:1354
void fetch('/dsh-pet-plus-7340/pomodoro', { - 发起网络请求src/client/pet.ts:1370
void fetch('/dsh-pet-plus-7340/config', { - 发起网络请求src/client/pet.ts:1392
void fetch('/dsh-pet-plus-7340/config', { - 发起网络请求src/client/pet.ts:1413
const r = await fetch(url); - 发起网络请求src/client/pet.ts:1687
const r = await fetch('/dsh-pet-plus-7340/config'); - 发起网络请求src/client/settings.ts:932
fetch('/dsh-pet-plus-7340/config/meta') - 发起网络请求src/client/settings.ts:974
fetch('/dsh-pet-plus-7340/config') - 发起网络请求src/client/settings.ts:1014
fetch('/dsh-pet-plus-7340/models') - 发起网络请求src/client/settings.ts:1156
const res = await fetch('/dsh-pet-plus-7340/config' + (force === true ? '?force=1' : ''), { - 发起网络请求src/client/settings.ts:1193
const res = await fetch('/dsh-pet-plus-7340/config', { method: 'POST' }); - 写入本地文件src/host/broadcast.test.ts:44
mkdirSync(memesDir, { recursive: true }); - 写入本地文件src/host/broadcast.test.ts:45
writeFileSync(join(memesDir, `${IN_POOL}.png`), 'x'); - 写入本地文件src/host/broadcast.test.ts:46
writeFileSync(join(memesDir, `${IN_POOL2}.png`), 'x'); - 删除本地文件递归src/host/broadcast.test.ts:61
rmSync(tempRoot, { recursive: true, force: true }); - 写入本地文件src/host/broadcast.test.ts:200
mkdirSync(ownDir, { recursive: true }); - 写入本地文件src/host/broadcast.test.ts:201
writeFileSync(join(ownDir, '自家图.png'), 'x'); - 写入本地文件src/host/config.test.ts:94
writeFileSync(paths.defaultFile, JSON.stringify(BASE)); - 写入本地文件src/host/config.test.ts:95
writeFileSync(paths.userFile, JSON.stringify(suite.overlay)); - 删除本地文件递归src/host/config.test.ts:102
rmSync(dir, { recursive: true, force: true }); - 写入本地文件src/host/config.test.ts:175
writeFileSync(paths.defaultFile, JSON.stringify({ ...BASE, ...baseExtra })); - 写入本地文件src/host/config.test.ts:176
if (overlay) writeFileSync(paths.userFile, JSON.stringify(overlay)); - 写入本地文件src/host/config.test.ts:194
writeFileSync(paths.defaultFile, JSON.stringify(BASE)); - 写入本地文件src/host/config.test.ts:195
if (raw !== null) writeFileSync(paths.userFile, raw); - 读取本地文件src/host/config.test.ts:216
assert.equal(existsSync(paths.legacyUserFile as string), false, '旧文件应已重命名走'); - 读取本地文件src/host/config.test.ts:217
assert.equal(readFileSync(paths.userFile, 'utf8'), raw, '新文件内容必须与旧文件逐字节一致'); - 删除本地文件递归src/host/config.test.ts:220
rmSync(dir, { recursive: true, force: true }); - 读取本地文件src/host/config.test.ts:229
assert.equal(existsSync(paths.legacyUserFile as string), true, '旧文件保持原样'); - 删除本地文件递归src/host/config.test.ts:232
rmSync(dir, { recursive: true, force: true }); - 读取本地文件src/host/config.test.ts:240
assert.equal(existsSync(paths.userFile), false, '不凭空造文件'); - 删除本地文件递归src/host/config.test.ts:242
rmSync(dir, { recursive: true, force: true }); - 删除本地文件递归src/host/config.test.ts:253
rmSync(dir, { recursive: true, force: true }); - 删除本地文件递归src/host/config.test.ts:265
rmSync(dir, { recursive: true, force: true }); - 读取本地文件src/host/config.test.ts:294
assert.throws(() => JSON.parse(readFileSync(paths.userFile, 'utf8')), '前置:严格 JSON.parse 确实读不了'); - 读取本地文件src/host/config.test.ts:729
assert.equal(readFileSync(paths.userFile, 'utf8'), raw, '用户层必须是默认文件的原文(含注释)'); - 读取本地文件src/host/config.ts:105
const raw = JSON.parse(stripJsonc(readFileSync(path, 'utf8'))) as unknown; - 读取本地文件src/host/config.ts:158
if (existsSync(paths.userFile)) return paths.userFile; - 读取本地文件src/host/config.ts:159
if (paths.legacyUserFile && existsSync(paths.legacyUserFile)) return paths.legacyUserFile; - 读取本地文件src/host/config.ts:174
if (!legacy || !existsSync(legacy) || existsSync(paths.userFile)) return false; - 写入本地文件src/host/config.ts:176
renameSync(legacy, paths.userFile); - 读取本地文件src/host/config.ts:194
entries = readdirSync(petDir, { withFileTypes: true }); - 读取本地文件src/host/config.ts:800
const raw = readFileSync(paths.defaultFile, 'utf8'); - 写入本地文件src/host/config.ts:801
mkdirSync(dirname(paths.userFile), { recursive: true }); - 写入本地文件src/host/config.ts:802
writeFileSync(paths.userFile, raw, 'utf8'); - 读取本地文件src/host/desktop-reload.test.ts:27
const readSource = (rel: string): string => readFileSync(fileURLToPath(new URL(rel, import.meta.url)), 'utf8'); - 删除本地文件递归src/host/helper-process.test.ts:46
rmSync(dir, { recursive: true, force: true }); - 写入本地文件src/host/helper-process.test.ts:86
writeFileSync(fake, ''); - 写入本地文件src/host/helper-process.test.ts:88
writeFileSync(other, ''); - 写入本地文件src/host/helper-process.test.ts:96
writeFileSync(fake, ''); - 写入本地文件src/host/helper-process.test.ts:104
writeFileSync(viaEnv, ''); - 写入本地文件src/host/helper-process.test.ts:119
writeFileSync(fake, ''); - 写入本地文件src/host/helper-process.test.ts:127
writeFileSync(fake, ''); - 读取本地文件src/host/helper-process.ts:100
return list.find((value) => existsSync(value)); - 读取本地文件src/host/helper-process.ts:183
if (existsSync(exe)) return exe; - 写入本地文件src/host/helper-process.ts:191
mkdirSync(targetDir, { recursive: true }); - 读取本地文件src/host/helper-process.ts:229
if (!existsSync(exe)) { - 执行外部命令来自配置/变量src/host/helper-process.ts:312
const child = spawn(command, args, { - 发起网络请求src/host/helper-process.ts:402
void fetch(cb, { - 读取本地文件src/host/host-liveness.test.ts:35
const readSource = (rel: string): string => readFileSync(fileURLToPath(new URL(rel, import.meta.url)), 'utf8'); - 读取本地文件src/host/index.ts:177
return candidate && existsSync(candidate) ? candidate : undefined; - 读取本地文件src/host/index.ts:190
const stream = createReadStream(file); - 读取本地文件src/host/index.ts:303
if (own !== undefined && existsSync(own)) return [own]; - 读取本地文件src/host/index.ts:451
raw = await readFile(memoryPath, 'utf8'); - 写入本地文件src/host/index.ts:464
await mkdir(userRoot, { recursive: true }); - 写入本地文件src/host/index.ts:465
await writeFile(`${memoryPath}.bak-${Date.now()}`, raw, 'utf8'); - 写入本地文件src/host/index.ts:474
await mkdir(userRoot, { recursive: true }); - 写入本地文件src/host/index.ts:475
await writeFile(memoryPath, JSON.stringify(mem, null, 2), 'utf8'); - 读取本地文件src/host/index.ts:628
const raw = JSON.parse(readFileSync(join(PACKAGE_ROOT, 'package.json'), 'utf8')) as { version?: unknown }; - 写入本地文件src/host/index.ts:919
await mkdir(userRoot, { recursive: true }); - 写入本地文件src/host/index.ts:920
await writeFile(userConfigPath, JSON.stringify(clean, null, 2), 'utf8'); - 读取本地文件src/host/index.ts:1286
extraAnimDir !== undefined && existsSync(extraAnimDir) - 写入本地文件src/host/memes.test.ts:23
mkdirSync(memesDir, { recursive: true }); - 写入本地文件src/host/memes.test.ts:24
for (const n of names) writeFileSync(join(memesDir, n + '.png'), 'x'); - 删除本地文件递归src/host/memes.test.ts:27
rmSync(dir, { recursive: true, force: true }); - 删除本地文件递归src/host/memes.test.ts:78
rmSync(dir, { recursive: true, force: true }); - 读取本地文件src/host/memes.ts:39
if (!dirs.some((dir) => existsSync(join(dir, name + '.png')))) continue; - 读取本地文件src/host/pointer-target.test.ts:29
const readSource = (rel: string): string => readFileSync(fileURLToPath(new URL(rel, import.meta.url)), 'utf8'); - 写入本地文件src/host/routes.test.ts:71
mkdirSync(join(userRoot, 'pet', `${CN_PET}-animation`), { recursive: true }); - 写入本地文件src/host/routes.test.ts:72
writeFileSync(join(userRoot, 'pet', `${CN_PET}-animation`, 'test.webm'), OWN); - 写入本地文件src/host/routes.test.ts:73
mkdirSync(join(userRoot, 'main-animation', 'webm'), { recursive: true }); - 写入本地文件src/host/routes.test.ts:74
writeFileSync(join(userRoot, 'main-animation', 'webm', 'mainidle.webm'), MAIN); - 写入本地文件src/host/routes.test.ts:76
mkdirSync(join(userRoot, 'memes'), { recursive: true }); - 写入本地文件src/host/routes.test.ts:77
writeFileSync(join(userRoot, 'memes', '共用图.png'), SHARED_PIC); - 删除本地文件递归src/host/routes.test.ts:163
rmSync(dir, { recursive: true, force: true }); - 写入本地文件src/host/storage-paths.test.ts:112
mkdirSync(join(dir, 'dsh-pet-plus'), { recursive: true }); - 删除本地文件递归src/host/storage-paths.test.ts:115
rmSync(dir, { recursive: true, force: true }); - 读取本地文件src/host/storage-paths.test.ts:135
const src = readFileSync(new URL('../../runtime/electron-helper/main.js', import.meta.url), 'utf8'); - 读取本地文件src/host/storage-paths.ts:108
return items.map((it) => ({ ...it, exists: existsSync(it.path) })); - 删除本地文件递归src/host/work-status-lifecycle.test.ts:137
rmSync(dir, { recursive: true, force: true }); - 读取本地文件src/shared/balance.test.ts:34
const readSource = (rel: string): string => readFileSync(fileURLToPath(new URL(rel, import.meta.url)), 'utf8'); - 发起网络请求src/shared/chat.ts:24
const res = await fetch(baseUrl, { - 读取本地文件src/shared/config.test.ts:41
const readSource = (rel: string): string => readFileSync(fileURLToPath(new URL(rel, import.meta.url)), 'utf8'); - 删除本地文件递归src/shared/config.test.ts:66
rmSync(dir, { recursive: true, force: true }); - 读取本地文件src/shared/pickers.test.ts:171
const browserShell = readFileSync(fileURLToPath(new URL('../client/pet.ts', import.meta.url)), 'utf8'); - 读取本地文件src/shared/pickers.test.ts:172
const desktopShell = readFileSync( - 发起网络请求src/shared/state.ts:45
const res = await fetch(baseUrl, { cache: 'no-store' }); - 发起网络请求src/shared/state.ts:237
const res = await fetch(baseUrl, { - 读取本地文件src/standalone/check.ts:26
const parsed = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')) as { version?: unknown }; - 读取本地文件src/standalone/check.ts:46
`用户配置 ${paths.userFile}${existsSync(paths.userFile) ? '' : '(不存在 → 用包内默认)'}`, - 读取本地文件src/standalone/check.ts:47
`文件宠物目录 ${paths.petDir}${existsSync(paths.petDir) ? '' : '(不存在)'}`, - 读取本地文件src/standalone/check.ts:48
`内置默认配置 ${paths.defaultFile}${existsSync(paths.defaultFile) ? '' : ' ✗ 缺失'}`, - 读取本地文件src/standalone/check.ts:59
if (!existsSync(paths.defaultFile)) { - 读取本地文件src/standalone/cli.ts:43
if (!existsSync(paths.defaultFile)) { - 读取本地文件src/standalone/cli.ts:101
if (!existsSync(paths.userFile)) configSource = `${paths.userFile}(不存在 → 用包内默认)`; - 删除本地文件递归src/standalone/integration.test.ts:73
rmSync(home, { recursive: true, force: true }); - 发起网络请求src/standalone/integration.test.ts:78
const response = await fetch(`${base}${path}`); - 发起网络请求src/standalone/integration.test.ts:158
const response = await fetch(`${base}/`); - 发起网络请求src/standalone/integration.test.ts:167
const response = await fetch(`${base}/shutdown`, { method: 'POST' }); - 发起网络请求src/standalone/server.test.ts:210
const response = await fetch(`http://127.0.0.1:${server.port}/`); - 发起网络请求src/standalone/server.test.ts:229
const inside = await fetch(`http://127.0.0.1:${server.port}/dsh-pet-plus-7340/state`); - 发起网络请求src/standalone/server.test.ts:231
const outside = await fetch(`http://127.0.0.1:${server.port}/whatever`);
扫描于 2026/11/1(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/dsh-agent-default-model@deepseek-ai/dsh-client-connection@deepseek-ai/dsh-client-ui-primitives@deepseek-ai/dsh-client-ui-slots@deepseek-ai/dsh-commands@deepseek-ai/dsh-credentials@deepseek-ai/dsh-home-paths@deepseek-ai/dsh-host-webserver@deepseek-ai/dsh-llm给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
功能亮点
- 亲密度养成
- 番茄钟
- 语音朗读
DSH 桌宠插件 dsh-pet 的二创版:新增亲密度养成、番茄钟、语音朗读、音效、增强菜单、每日统计;可与原版并存
README
由 DeepSeek 最新模型翻译生成dsh-pet-plus
DSH(DeepSeek Harness)桌面宠物插件 —— 基于 dsh-pet 0.3.6 的二次创作。
保留上游全部基础功能(动画、漫游、拖拽抛掷物理、余额、碎碎念、对话与记忆、系统通知、工作状态联动、多开与布局、透明置顶桌面小窗),并新增六组功能。与上游可以并存(改名隔离,见下)。
二创来源与权利声明
- 上游:dsh-pet 0.3.6,作者 PC2005-cloud,MIT 许可(<https://github.com/PC2005-cloud/dsh-pet>)。
- 本二创:dsh-pet-plus,作者 youhuangqing,就本仓库新增与修改的部分同样以 MIT 释出。
上游 LICENSE 原文完整保留,两者许可互相独立、互不影响。
- 素材例外:动画 / 提示词 / 源视频沿用上游声明 —— 允许开源使用,禁止商用。
- 详见 THIRD_PARTY_NOTICES.md。
〇、路径约定
下文用 <DSH安装目录> 表示你的 DeepSeek Harness 安装位置(不是固定值,取决于你安装时选的路径)。
dsh.cmd 与 pnpm 都在它下面。
展开完整文档(README 剩余 1 节)
dsh-pet-plus
DSH(DeepSeek Harness)桌面宠物插件 —— 基于 dsh-pet 0.3.6 的二次创作。
保留上游全部基础功能(动画、漫游、拖拽抛掷物理、余额、碎碎念、对话与记忆、系统通知、工作状态联动、多开与布局、透明置顶桌面小窗),并新增六组功能。与上游可以并存(改名隔离,见下)。
二创来源与权利声明
- 上游:dsh-pet 0.3.6,作者 PC2005-cloud,MIT 许可(<https://github.com/PC2005-cloud/dsh-pet>)。
- 本二创:dsh-pet-plus,作者 youhuangqing,就本仓库新增与修改的部分同样以 MIT 释出。
上游 LICENSE 原文完整保留,两者许可互相独立、互不影响。
- 素材例外:动画 / 提示词 / 源视频沿用上游声明 —— 允许开源使用,禁止商用。
- 详见 THIRD_PARTY_NOTICES.md。
〇、路径约定
下文用 <DSH安装目录> 表示你的 DeepSeek Harness 安装位置(不是固定值,取决于你安装时选的路径)。
dsh.cmd 与 pnpm 都在它下面。不确定就查:
Get-Command dsh | Select-Object -ExpandProperty Source # dsh 已在 PATH 时
拿到的路径形如 <DSH安装目录>\resources\runtime\cli\bin\dsh.cmd,往上退三级就是 <DSH安装目录>。
一、环境要求
| 项 | 要求 |
| DSH | 桌面版 0.2.0-rc.1 / 0.2.0-rc.2(0.1.7-rc.2 不兼容) |
| 系统 | Windows 优先(桌面小窗与语音朗读依赖 Windows 自带能力);其它平台可用浏览器内的那只宠物 |
| 网络 | 仅在首次会下载 Electron 运行时(约 200MB,用于桌面小窗)。之后离线可用 |
| 语音朗读 | 走 Windows 系统语音(离线),或用 Edge 联网音色(可选) |
不需要额外安装 Node.js —— 构建产物已随包提供。
二、安装
假设你的配置档名是 desktop(DSH 桌面版默认)。
方式 1:一行命令(推荐,直接从 Release 装)
dsh plugin --profile desktop add https://github.com/youhuangqing/dsh-pet-plus/releases/download/v0.1.0/dsh-pet-plus-0.1.0.tgz
dsh 不在 PATH 时用全路径:& '<DSH安装目录>\resources\runtime\cli\bin\dsh.cmd' plugin --profile desktop add <上面的网址或本地 tgz 路径>。
方式 2:从本地 tgz 装
& '<DSH安装目录>\resources\runtime\cli\bin\dsh.cmd' plugin --profile desktop add file:C:\path\to\dsh-pet-plus-0.1.0.tgz
装完重启 DeepSeek Harness 即可(插件在启动时加载)。
若该包日后发布到 npm,也可以按包名安装:dsh plugin --profile desktop add dsh-pet-plus。
为什么装完要重启
DSH 在启动时组合插件树,运行中不热加载第三方插件。
安装后怎么确认装上了
浏览器开发者控制台(Ctrl+Shift+I)里搜 dsh-pet-plus,应能看到功能就绪日志;或访问 http://127.0.0.1:19387/dsh-pet-plus-7340/state,返回的 sections 里应含 social / pomodoro / stats / sfx / tts。
三、功能一览
| 功能 | 怎么用 |
| 亲密度 + 喂食养成 | 点击宠物摸头;右键「互动」→ 摸摸头 / 喂小鱼干;/social 看状态 |
| 番茄钟 | 右键「专注」→ 开始 25 / 10 / 45 分钟;菜单标题实时显示倒计时 |
| 语音朗读 | 自动朗读宠物说的话;右键「偏好 → 语音朗读」开关;/tts status 看服务商 |
| 音效 | 自动(点击 / 落地 / 余额 / 番茄钟结束 / 升级 / 喂食);右键「偏好 → 音效」开关 |
| 增强菜单 | 右键新增「互动 / 专注 / 统计 / 偏好」四组,浏览器里那只与桌面上那只完全一致 |
| 每日统计与记账 | 右键「统计」;/stats;/dsh-pet-plus-7340/stats?days=7 |
上游原有功能(余额、碎碎念、对话、系统通知、工作状态联动、多开布局、设置页)全部保留。
四、配置
首次运行后配置文件在:
C:\Users\<你>\.dsh\dsh-pet-plus\main-config.jsonc
改完即时生效(每个请求重读),不用重启、不用重装。包内 assets/config.jsonc 是带完整注释的默认值,想看所有可配项就看它。
语音音色(最常问的一个)
在 main-config.jsonc 里写 tts 段,然后让它说句话(或 /tts 你好呀)即可听效果:
{
"tts": {
"enabled": true,
"provider": "edge", // "auto"(Windows→系统语音)| "sapi" | "edge" | "stub" "voice": "zh-CN-XiaoyiNeural", // ← 音色名写这里 "rate": 0, // 语速 -10~10 "volume": 100 } }
音色名怎么选:
- provider: "sapi"(离线,系统自带):音色名要写已安装的 Windows 语音名。列出本机已装的: Add-Type -AssemblyName System.Speech (New-Object System.Speech.Synthesis.SpeechSynthesizer).GetInstalledVoices() |
ForEach-Object { $_.VoiceInfo.Name + ' [' + $_.VoiceInfo.Culture.Name + ']' }
Windows 中文版通常只有 Microsoft Huihui Desktop(成年女声)。想要少女音/萝莉音,需要另外装 Windows 语音包,或者用下面的 Edge。
- provider: "edge"(联网,微软 Edge「大声朗读」接口,音色明显更好且免 key):候选音色名包括 zh-CN-XiaoyiNeural(少女)、zh-CN-XiaoshuangNeural(童声/萝莉)、zh-CN-XiaoxiaoNeural(默认女声)、 zh-CN-XiaomengNeural、zh-CN-XiaohanNeural。
⚠️ 这是未公开接口,随微软改动可能失效。为此做了三重保护:默认不用它、返回字节必须是合法 MP3 才采纳、失败自动回落系统语音。所以最坏结果只是「Edge 没生效、退回系统语音」,不会更坏。
/tts status 会显示实际生效的提供方;应用日志里会写明是否发生过回落。
- provider: "stub":只产出静音,用来验证链路(排查用,不出声)。
改设置页里那一行的名字
src/client/settings.ts 里 zh 字典的 nav 字段(以及 en 的),改完重新构建:
& '<DSH安装目录>\resources\runtime\pnpm\bin\pnpm.mjs' run build
(仓库里 lib/ 是构建产物、不入库,所以要自己构建一次;详见第九节。)
五、命令
| 命令 | 说明 |
| /pet | 选择当前桌宠(支持选择框或手输 id/名字) |
| /chat | 与桌宠对话 |
| /balance | 查询余额 |
| /social | 亲密度 / 小鱼干状态 |
| /pomodoro | 番茄钟(start / stop / status …) |
| /stats | 每日统计 |
| /tts | 语音朗读(status / 直接给一句试听文本) |
与上游 dsh-pet 并存时,/pet /chat /balance 三个短名归先加载的原版,本插件会自动改用 /pet-plus /chat-plus /balance-plus(注册失败只告警,不会让插件加载失败)。
卸掉原版后短名自动归本插件,无需改配置。
六、卸载
& '<DSH安装目录>\resources\runtime\cli\bin\dsh.cmd' plugin --profile desktop remove dsh-pet-plus
然后重启。用户数据(~/.dsh/dsh-pet-plus/)不会自动删除,需要时手动删。
七、与上游并存
本插件把所有注册进 DSH 全局命名空间的标识都加了 -plus:包名、cordis 行 id、路由前缀(/dsh-pet-plus-7340)、桥协议 scheme、用户数据目录(~/.dsh/dsh-pet-plus/)、Electron 应用名、环境变量前缀、客户端插件名与插槽 id、locale 命名空间(pet-plus.config)、斜杠命令。
两套可以同时装、互不影响(会看到两只宠物、设置页两个分区)。想只用一套就卸掉另一个。
⚠️ Electron 运行时目录(~/.dsh/electron/)是两套共用的,这是刻意的 —— 避免重复下载 200MB。
八、已知限制
| 项 | 说明 |
| 悬停快捷条 | 未实现(计划里有)。它要改桌面端「整窗点击穿透」的判定,改错会让鼠标移过去就穿透、按钮点不到,在缺少真机验证条件下风险过高。 |
| Edge 联网音色 | 协议为未公开接口,未经运行时验证(有三重保护,最坏退回系统语音)。 |
| token 用量统计 | 会话事件是否携带 usage 未核实,故留空而非填猜测值。 |
九、开发
仓库不含 lib/(构建产物),clone 后先装依赖再构建:
1) 装依赖(需要网络;peer 依赖 @deepseek-ai/* 是公共 npm 包) & '<DSH安装目录>\resources\runtime\pnpm\bin\pnpm.mjs' install
2) 构建与自测 $env:ELECTRON_RUN_AS_NODE = '1' $env:PATH = '<DSH安装目录>\resources\runtime\bin;' + $env:PATH $exe = '<DSH安装目录>\DeepSeek Harness.exe' $pnpm = '<DSH安装目录>\resources\runtime\pnpm\bin\pnpm.mjs'
& $exe --expose-internals $pnpm run typecheck # 类型检查 & $exe --expose-internals $pnpm run build # tsdown + 重建 shared-core.js & $exe --expose-internals $pnpm test # 上游测试套件(553 个用例) & $exe --expose-internals $pnpm run test:single # 受限环境用这个(不 spawn 子进程)
3) 独立模式(不需要 DSH、不用重启应用) $env:DSH_HOME = '<工作区内的临时目录>' & node.cmd lib/standalone.js --port 3099
$exe --expose-internals $pnpm 是「用 DSH 自带的 Node 跑 pnpm」的写法。
你也可以直接用系统里的 pnpm(如果有):pnpm install && pnpm run build。
runtime/electron-helper/*.js 不在 tsc 范围内,改动后要单独做语法检查:node --check <文件>。
打分发用的包:pnpm pack → 产出 dsh-pet-plus-<版本>.tgz,把它作为 Release 附件发布。
十、许可与署名
代码 MIT(见 LICENSE)。素材来自上游 dsh-pet:代码 MIT;动画 / 提示词 / 源视频允许开源使用,禁止商用。 详细说明见 THIRD_PARTY_NOTICES.md。预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
youhuangqing/dsh-pet-plus
养亲密度、开番茄钟,让桌宠陪你专注工作
相关插件 · 安全 / 权限
智能体逆向工程morluto/rea
用智能体逆向工程任何东西,从应用行为到原生二进制。
★ 415,267已汉化DSH
73.3
智能体集群框架ruvnet/ruflo
面向 Claude Code 与 Codex 的智能体元基座meta-harness。
★ 80,414已汉化MCP
71.9
WorkBuddy 模型接入corrinehu/dsh-workbuddy-connect
2026 年 10 月 20 日起,WorkBuddy…
★ 1,604已汉化DSH
71.7
破甲一键通z91772524-ai/pojia-next
十五个 AI…
★ 967已汉化DSH
71.6
AI 代理虚拟终端strukto-ai/mirage
Mirage 是给 AI 智能体用的虚拟终端:一层 shell,统管数据、命令行和运行时。Unix…
★ 3,756已汉化DSH
70.8
Agent 操作层agentic-os-org/ANOLISA
ANOLISA(Agentic Nexus Operating Layer & Interface System Architecture)|智能体操作系统,带运行时、安全、可观测性,以及 Tokenless 响应压缩,降低 token 用量与成本。
★ 649已汉化DSH
70.7