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

鲸灵语音宠物 regrevia/dsh-pet-voice

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

用手机语音唤醒并指挥电脑上的像素鲸鱼娘干活

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

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

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add regrevia/dsh-pet-voice
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:仅索引
是什么
dsh 原生插件 · other
装得上吗
本站尚未做安装检查
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 2 天前
档位由哪些信号合成

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

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

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

可执行外部命令读写/删除本地文件写入剪贴板(不计风险)读取剪贴板或密钥环境变量发起外部网络请求
查看扫描证据(共 261 处,展示前 103 条)
  • 执行外部命令来自配置/变量
    mcp/pet-bridge/selftest.mjs:59
    this.proc = spawn(process.execPath, [SERVER], {
  • 读取密钥环境变量
    mcp/pet-bridge/selftest.mjs:224
    let token = (process.env.PET_TOKEN || '').trim()
  • 发起网络请求
    mcp/pet-bridge/selftest.mjs:239
    const r = await fetch(`${base}/pet-voice/api/state`, {
  • 发起网络请求
    mcp/pet-bridge/selftest.mjs:282
    const init = await cli.request('initialize', {
  • 发起网络请求
    mcp/pet-bridge/selftest.mjs:296
    const ping = await cli.request('ping')
  • 发起网络请求
    mcp/pet-bridge/selftest.mjs:301
    const list = await cli.request('tools/list')
  • 发起网络请求
    mcp/pet-bridge/selftest.mjs:316
    const nf = await cli.request('tools/nonexistent')
  • 发起网络请求
    mcp/pet-bridge/selftest.mjs:320
    const ip = await cli.request('tools/call', { name: 'pet_switch_project', arguments: {} })
  • 读取密钥环境变量
    mcp/pet-bridge/selftest.mjs:358
    const token = (process.env.PET_TOKEN || '').trim() || (() => {
  • 读取密钥环境变量
    mcp/pet-bridge/server.mjs:77
    const fromEnv = (process.env.PET_TOKEN || '').trim()
  • 发起网络请求
    mcp/pet-bridge/server.mjs:225
    return await fetch(url, {
  • 发起网络请求
    mcp/pet-bridge/server.mjs:316
    resp = await fetch(`${base}/pet-voice/api/stream?sessionId=${encodeURIComponent(sessionId)}`, {
  • 读取密钥环境变量
    mcp/pet-bridge/server.mjs:1145
    const acToken = (process.env.PET_HTTP_TOKEN || '').trim()
  • 发起网络请求
    mcp/pet-bridge/xiaoai-selftest.mjs:129
    const res = await fetch(url, {
  • 发起网络请求
    mcp/pet-bridge/xiaoai-selftest.mjs:375
    const headerRes = await fetch(`${server.base}${PATH}`, {
  • 读取本地文件
    mcp/pet-bridge/xiaoai-selftest.mjs:417
    const saved = JSON.parse(readFileSync(slowStore, 'utf8'))
  • 读取本地文件
    mcp/pet-bridge/xiaoai-selftest.mjs:425
    const after = JSON.parse(readFileSync(slowStore, 'utf8'))
  • 发起网络请求
    mcp/pet-bridge/xiaoai-selftest.mjs:450
    const res = await fetch(`${server.base}${PATH}?k=${encodeURIComponent(SECRET)}`)
  • 发起网络请求
    mcp/pet-bridge/xiaoai-selftest.mjs:467
    const bad = await fetch(`${server.base}${PATH}?k=${encodeURIComponent(SECRET)}`, {
  • 发起网络请求
    mcp/pet-bridge/xiaoai-selftest.mjs:476
    const empty = await fetch(`${server.base}${PATH}?k=${encodeURIComponent(SECRET)}`, { method: 'POST' })
  • 删除本地文件递归
    mcp/pet-bridge/xiaoai-selftest.mjs:481
    rmSync(dir, { recursive: true, force: true })
  • 发起网络请求
    mcp/pet-bridge/xiaoai-selftest.mjs:504
    const res = await fetch(`${server.base}${PATH}?k=${URL_SECRET}`, {
  • 删除本地文件递归
    mcp/pet-bridge/xiaoai-selftest.mjs:556
    rmSync(dir, { recursive: true, force: true })
  • 读取本地文件
    mcp/pet-bridge/xiaoai.mjs:298
    raw = JSON.parse(readFileSync(file, 'utf8'))
  • 读取本地文件
    mcp/pet-bridge/xiaoai.mjs:417
    const parsed = JSON.parse(readFileSync(file, 'utf8'))
  • 写入本地文件
    mcp/pet-bridge/xiaoai.mjs:427
    mkdirSync(dirname(file), { recursive: true })
  • 写入本地文件
    mcp/pet-bridge/xiaoai.mjs:428
    writeFileSync(file, `${JSON.stringify(cache, null, 2)}\n`, { mode: 0o600 })
  • 发起网络请求
    plugins/dsh-pet-voice/client.js:182
    const res = await fetch(`${API}${path}`, {
  • 发起网络请求
    plugins/dsh-pet-voice/client.js:352
    fetch(`${API}/qr.svg?text=${encodeURIComponent(pairingUrl)}`, { credentials: 'same-origin' })
  • 写入剪贴板
    plugins/dsh-pet-voice/client.js:420
    await navigator.clipboard.writeText(pairingUrl)
  • 读取本地文件
    plugins/dsh-pet-voice/e2e.mjs:28
    process.env.PV_TOKEN || readFileSync(join(homedir(), '.dsh', 'pet-voice', 'token'), 'utf8').trim()
  • 读取密钥环境变量
    plugins/dsh-pet-voice/e2e.mjs:28
    process.env.PV_TOKEN || readFileSync(join(homedir(), '.dsh', 'pet-voice', 'token'), 'utf8').trim()
  • 发起网络请求
    plugins/dsh-pet-voice/e2e.mjs:51
    const stateRes = await fetch(`${BASE}/pet-voice/api/state`, { headers: H })
  • 发起网络请求
    plugins/dsh-pet-voice/e2e.mjs:61
    await fetch(`${BASE}/pet-voice/api/new`, {
  • 发起网络请求
    plugins/dsh-pet-voice/e2e.mjs:99
    res = await fetch(`${BASE}/pet-voice/api/stream?sessionId=${encodeURIComponent(sid)}`, {
  • 发起网络请求
    plugins/dsh-pet-voice/e2e.mjs:139
    const res = await fetch(
  • 发起网络请求
    plugins/dsh-pet-voice/e2e.mjs:161
    const disp = await fetch(`${BASE}/pet-voice/api/dispatch`, {
  • 读取本地文件
    plugins/dsh-pet-voice/index.js:81
    return String(JSON.parse(readFileSync(join(HERE, 'package.json'), 'utf8')).version ?? '')
  • 写入本地文件
    plugins/dsh-pet-voice/index.js:321
    mkdirSync(CLIENT_LOG_DIR, { recursive: true })
  • 写入本地文件
    plugins/dsh-pet-voice/index.js:322
    appendFileSync(path, `${new Date().toISOString()} ${flat}\n`)
  • 读取本地文件
    plugins/dsh-pet-voice/index.js:323
    if (statSync(path).size > FLOW_LOG_MAX) {
  • 读取本地文件
    plugins/dsh-pet-voice/index.js:324
    writeFileSync(path, readFileSync(path, 'utf8').slice(-FLOW_LOG_MAX / 2))
  • 写入本地文件
    plugins/dsh-pet-voice/index.js:324
    writeFileSync(path, readFileSync(path, 'utf8').slice(-FLOW_LOG_MAX / 2))
  • 读取本地文件
    plugins/dsh-pet-voice/index.js:523
    const raw = JSON.parse(readFileSync(STATE_PATH, 'utf8'))
  • 写入本地文件
    plugins/dsh-pet-voice/index.js:536
    mkdirSync(HOME_DIR, { recursive: true, mode: 0o700 })
  • 写入本地文件
    plugins/dsh-pet-voice/index.js:538
    writeFileSync(STATE_PATH, `${JSON.stringify(state, null, 2)}\n`, { mode: 0o600 })
  • 读取本地文件
    plugins/dsh-pet-voice/index.js:803
    body = readFileSync(full)
  • 读取本地文件
    plugins/dsh-pet-voice/index.js:832
    return existsSync(TTS_PYTHON) && existsSync(TTS_SCRIPT)
  • 发起网络请求
    plugins/dsh-pet-voice/index.js:916
    const res = await fetch(`${ASR_ENDPOINT}/health`, { signal: AbortSignal.timeout(timeout) })
  • 写入本地文件
    plugins/dsh-pet-voice/index.js:951
    mkdirSync(ASR_DIR, { recursive: true, mode: 0o700 })
  • 删除本地文件
    plugins/dsh-pet-voice/index.js:969
    unlinkSync(ASR_START_LOCK_PATH)
  • 执行外部命令固定命令
    plugins/dsh-pet-voice/index.js:1019
    const out = execFileSync('/bin/ps', ['-axo', 'pid=,command='], { encoding: 'utf8', timeout: 3000 })
  • 执行外部命令来自配置/变量
    plugins/dsh-pet-voice/index.js:1102
    child = spawn(
  • 执行外部命令来自配置/变量
    plugins/dsh-pet-voice/index.js:1274
    child = spawn(TTS_PYTHON, [join(TTS_DIR, 'tts-daemon.py'), '--serve'], {
  • 发起网络请求
    plugins/dsh-pet-voice/index.js:1303
    const res = await fetch(`${ASR_ENDPOINT}/asr`, {
  • 执行外部命令固定命令
    plugins/dsh-pet-voice/index.js:3696
    execFileSync('git', args, {
  • 执行外部命令来自配置/变量
    plugins/dsh-pet-voice/index.js:5552
    child = spawn(TTS_PYTHON, [TTS_SCRIPT, voice, text, rate, pitch], {
  • 读取本地文件
    plugins/dsh-pet-voice/lan.mjs:34
    const raw = JSON.parse(readFileSync(CONFIG_PATH, 'utf8'))
  • 写入本地文件
    plugins/dsh-pet-voice/lan.mjs:45
    mkdirSync(DIR, { recursive: true })
  • 写入本地文件
    plugins/dsh-pet-voice/lan.mjs:46
    writeFileSync(CONFIG_PATH, `${JSON.stringify({ port }, null, 2)}\n`, 'utf8')
  • 写入本地文件
    plugins/dsh-pet-voice/lan.mjs:47
    chmodSync(CONFIG_PATH, 0o600)
  • 读取本地文件
    plugins/dsh-pet-voice/panel.mjs:78
    return JSON.parse(readFileSync(path, 'utf8'))
  • 写入本地文件
    plugins/dsh-pet-voice/panel.mjs:86
    mkdirSync(PANEL_DIR, { recursive: true })
  • 写入本地文件
    plugins/dsh-pet-voice/panel.mjs:89
    writeFileSync(tmp, JSON.stringify(value), { mode: 0o600 })
  • 写入本地文件
    plugins/dsh-pet-voice/panel.mjs:90
    renameSync(tmp, path)
  • 删除本地文件
    plugins/dsh-pet-voice/panel.mjs:93
    rmSync(tmp, { force: true })
  • 读取本地文件
    plugins/dsh-pet-voice/panel.mjs:462
    return readFileSync(TOKEN_PATH, 'utf8').trim()
  • 读取本地文件
    plugins/dsh-pet-voice/public.mjs:36
    raw = JSON.parse(readFileSync(CONFIG_PATH, 'utf8'))
  • 读取本地文件
    plugins/dsh-pet-voice/public.mjs:87
    if (!existsSync(abs)) throw new Error('找不到这个私钥文件')
  • 写入本地文件
    plugins/dsh-pet-voice/public.mjs:98
    mkdirSync(PUBLIC_DIR, { recursive: true, mode: 0o700 })
  • 写入本地文件
    plugins/dsh-pet-voice/public.mjs:99
    writeFileSync(CONFIG_PATH, JSON.stringify(cfg, null, 2) + '\n', { mode: 0o600 })
  • 写入本地文件
    plugins/dsh-pet-voice/public.mjs:101
    chmodSync(CONFIG_PATH, 0o600)
  • 读取本地文件
    plugins/dsh-pet-voice/public.mjs:108
    if (!existsSync(LEAF_PATH) || !existsSync(LEAF_KEY_PATH)) return { ok: false }
  • 读取本地文件
    plugins/dsh-pet-voice/public.mjs:110
    const pem = readFileSync(LEAF_PATH, 'utf8')
  • 读取本地文件
    plugins/dsh-pet-voice/public.mjs:135
    keyExists: cfg.keyPath ? existsSync(cfg.keyPath) : false,
  • 读取本地文件
    plugins/dsh-pet-voice/public.mjs:175
    const key = readFileSync(LEAF_KEY_PATH)
  • 执行外部命令固定命令
    plugins/dsh-pet-voice/public.mjs:251
    const child = spawn('ssh', args, { stdio: ['ignore', 'ignore', 'pipe'] })
  • 执行外部命令固定命令
    plugins/dsh-pet-voice/public.mjs:286
    const child = spawn('ssh', sshArgs(cfg), { stdio: ['ignore', 'ignore', 'pipe'] })
  • 读取本地文件
    plugins/dsh-pet-voice/selftest.mjs:25
    if (existsSync(file)) savedPanelFiles.set(name, readFileSync(file))
  • 读取本地文件
    plugins/dsh-pet-voice/selftest.mjs:25
    if (existsSync(file)) savedPanelFiles.set(name, readFileSync(file))
  • 写入本地文件
    plugins/dsh-pet-voice/selftest.mjs:38
    writeFileSync(join(PANEL_DIR, name), buf)
  • 删除本地文件
    plugins/dsh-pet-voice/selftest.mjs:164
    rmSync(join(homedir(), '.dsh/pet-voice/state.json'), { force: true })
  • 发起网络请求
    plugins/dsh-pet-voice/selftest.mjs:266
    await fetch(`${origin}/pet-voice/`)
  • 读取本地文件
    plugins/dsh-pet-voice/selftest.mjs:267
    const token = readFileSync(join(homedir(), '.dsh/pet-voice/token'), 'utf8').trim()
  • 发起网络请求
    plugins/dsh-pet-voice/selftest.mjs:276
    const gate = await fetch(`${origin}/pet-voice/`)
  • 发起网络请求
    plugins/dsh-pet-voice/selftest.mjs:282
    const handoff = await fetch(`${origin}/pet-voice/?k=${token}`, { redirect: 'manual' })
  • 发起网络请求
    plugins/dsh-pet-voice/selftest.mjs:293
    const page = await fetch(`${origin}/pet-voice/`, { headers: { cookie } })
  • 发起网络请求
    plugins/dsh-pet-voice/selftest.mjs:306
    const res = await fetch(`${origin}/pet-voice/assets/${file}`)
  • 发起网络请求
    plugins/dsh-pet-voice/selftest.mjs:313
    const evil = await fetch(`${origin}/pet-voice/assets/..%2findex.js`)
  • 读取本地文件
    plugins/dsh-pet-voice/selftest.mjs:456
    const xiaoaiCfg = JSON.parse(readFileSync(join(homedir(), '.dsh/pet-voice/xiaoai.json'), 'utf8'))
  • 读取本地文件
    plugins/dsh-pet-voice/selftest.mjs:484
    const pinned = JSON.parse(readFileSync(petSessionPath, 'utf8'))
  • 读取本地文件
    plugins/dsh-pet-voice/selftest.mjs:508
    const moved = JSON.parse(readFileSync(petSessionPath, 'utf8'))
  • 读取本地文件
    plugins/dsh-pet-voice/token.mjs:19
    if (readFileSync(bakPath, 'utf8').trim() === value) return
  • 写入本地文件
    plugins/dsh-pet-voice/token.mjs:24
    mkdirSync(homeDir, { recursive: true, mode: 0o700 })
  • 写入本地文件
    plugins/dsh-pet-voice/token.mjs:25
    writeFileSync(bakPath, `${value}\n`, { mode: 0o600 })
  • 写入本地文件
    plugins/dsh-pet-voice/token.mjs:26
    chmodSync(bakPath, 0o600)
  • 写入本地文件
    plugins/dsh-pet-voice/token.mjs:33
    mkdirSync(homeDir, { recursive: true, mode: 0o700 })
  • 写入本地文件
    plugins/dsh-pet-voice/token.mjs:34
    writeFileSync(tokenPath, `${value}\n`, { mode: 0o600 })
  • 写入本地文件
    plugins/dsh-pet-voice/token.mjs:36
    chmodSync(tokenPath, 0o600)
  • 读取本地文件
    plugins/dsh-pet-voice/token.mjs:44
    const existing = readFileSync(tokenPath, 'utf8').trim()
  • 读取本地文件
    plugins/dsh-pet-voice/token.mjs:55
    const backup = readFileSync(bakPath, 'utf8').trim()
  • 读取本地文件
    plugins/dsh-pet-voice/token.mjs:78
    stat = statSync(tokenPath)
  • 读取本地文件
    plugins/dsh-pet-voice/token.mjs:91
    const value = readFileSync(tokenPath, 'utf8').trim()
扫描于 2026/10/31(1 天前) · 范围 全包扫描 · 来源 GitHub 仓库

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

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

手机端的语音宠物:悬浮窗像素鲸鱼娘 + 语音对话,网关跑在你自己电脑的 DeepSeek Harness 上(自签 APK,无第三方服务器)

README

由 DeepSeek 最新模型翻译生成
鲸灵 · 随身宠物 一只住在你电脑里的像素鲸鱼娘。屏幕和麦克风在你手上,大脑跑在你自己电脑上的 DeepSeek Harness 会话里。 DSH 的 App 端,还融合了桌宠玩法:语音唤醒、随口说话、语音指挥。远程操作电脑上的 DeepSeek Harness 有三种连法——局域网、自建域名、服务器中转;也能直接和工作在项目里的小鲸鱼聊天,一句话指挥它干活。出门在外,再也不用担心没带电脑。 不运营任何服务器、不用注册账号,会话与记忆都在你自己电脑上。APK 是 30 MB 的 Android 自签名包,装完扫码就能用。 三步用起来 1. 电脑上装插件 rsync -a --delete plugins/dsh-pet-voice/ ~/.dsh/plugins/dsh-pet-voice/ 然后重启一次桌面端,让它重读 cordis.patch.yml 装好后桌面端侧栏出现「大肥鱼」,点开是它的面板。 2. 电脑上出配对码 面板 →「连上手机」→ 选一种连法 → 二维码自己出来(或复制链接)。链接长这样: https://<你的地址>/pet-voice/?k=<一次性配对码> 3. 手机上扫码 装 APK(Releases)→ 首页「扫码绑定」→ 扫上一步的二维码 →「立即对话」。 悬浮窗出现后,说「大肥鱼」唤醒,或者直接点它说话。
展开完整文档(README 剩余 1 节)
鲸灵 · 随身宠物

一只住在你电脑里的像素鲸鱼娘。屏幕和麦克风在你手上,大脑跑在你自己电脑上的 DeepSeek Harness 会话里。

DSH 的 App 端,还融合了桌宠玩法:语音唤醒、随口说话、语音指挥。远程操作电脑上的 DeepSeek Harness 有三种连法——局域网、自建域名、服务器中转;也能直接和工作在项目里的小鲸鱼聊天,一句话指挥它干活。出门在外,再也不用担心没带电脑。

不运营任何服务器、不用注册账号,会话与记忆都在你自己电脑上。APK 是 30 MB 的 Android 自签名包,装完扫码就能用。

三步用起来

1. 电脑上装插件

rsync -a --delete plugins/dsh-pet-voice/ ~/.dsh/plugins/dsh-pet-voice/
然后重启一次桌面端,让它重读 cordis.patch.yml

装好后桌面端侧栏出现「大肥鱼」,点开是它的面板。

2. 电脑上出配对码

面板 →「连上手机」→ 选一种连法 → 二维码自己出来(或复制链接)。链接长这样:

https://<你的地址>/pet-voice/?k=<一次性配对码>

3. 手机上扫码

装 APK(Releases)→ 首页「扫码绑定」→ 扫上一步的二维码 →「立即对话」。
悬浮窗出现后,说「大肥鱼」唤醒,或者直接点它说话。

第一次用、什么都没有?照 docs/quickstart.md 走一遍。

三种连法

手机和电脑必须能互相访问。开源版默认不使用作者的任何基础设施,代码里没有硬编码的域名或 IP,配置只存在你这台电脑上。

| 连法 | 适合 | 需要 | 面板选哪档 |
| 局域网 | 手机和电脑同一个 WiFi | 无 | 局域网 |
| 自建域名 | 长期用、自己有域名 | 一台能跑 nginx/caddy 的机器 + 证书 | 自建域名,填自己的域名 |
| 服务器中转 | 在外面也想用,不想碰路由器和证书 | 装 cloudflared / ngrok,或自己的中转服务器 | 自建域名,填工具给的 https 地址 |

细节、证书与排错:docs/self-host.md。不想买域名也能 https:docs/tls-self-signed.md。

现在能用吗

| 项 | 状态 |
| APK | 0.9.33(versionCode 103),Android 8.0+(API 26),targetSdk 35 |
| 验证环境 | Android 11 模拟器跑通全链路:配对、语音、回复、动作、历史、检查更新 |
| 真机 | 厂商权限跳转与硬件麦克风只在 AOSP 页面验证过,各家 ROM 仍可能不同,欢迎回报 |
| Android 14/15 | 未做实机验证:后台恢复、麦克风前台服务在收紧后的行为【需真机确认】,见 docs/android-app.md |
| iOS | 不支持(悬浮窗与常听麦克风是 Android 特性) |

内置信任锚:APK 里带了一张示例自签 CA 的公钥证书(res/raw/pet_ca.pem,无私钥),让「不买域名、机器上跑自签 https」的人开箱能连。换成自己的 CA 请跑 scripts/tls-ca.sh 再重新出包;只用系统 CA 的部署不受影响。别人的 CA 不要信,也不要装来路不明的 APK。

它能做什么

- 说停就停:尾静音 1 秒判定说完;悬浮窗里可以连续对话。
- 回复一定到:先试长连接,拿不到立刻转轮询;断网期间的话不会丢。
- 动作有反馈:听、想、说、待机四种状态各有动作,时长按素材走。
- 能指挥项目:手机上直接切项目、切人设、开新会话,语音就把活派出去。
- 8 套衣服:水手裙 / 女仆装 / 鲸鱼卫衣 / 冬日斗篷 / 深海公主 / 深海研究员 / 星潮小魔女 / 泡泡睡衣。
- 面板可配:连法、人设、输出长度、气泡开关、唤醒词都在插件面板里改。

手机上的对话页

它怎么工作

手机(悬浮窗 WebView) 电脑(DSH 宿主进程)说话 ──► /api/asr ──► 识别文本 ──► /api/command ──► 切项目 / 派发 / 停 ──► /api/dispatch ──► 专属会话(人设走隐藏提示词)听 ◄── SSE 增量 / /api/poll 兜底 ◄── 逐字生成念 ◄── /api/tts ◄── 句子级音频

端点、鉴权、帧格式、错误码见 docs/pet-contract.md;手机端实现与限制见 docs/android-app.md。

两条外部依赖:默认语音合成走微软 Edge 的在线语音接口(edge-tts,免 key),手机端唤醒与识别走 Android 系统语音服务(SpeechRecognizer,是否上传音频由系统/厂商决定)。换本地 TTS 或换音色也能用,但这两条链路本身不是纯离线的。

开发与构建

前置:Node.js ≥ 22.15.0(插件静态引用 node:zlib 的 zstdDecompressSync)、JDK 17、Android SDK。

自测(离线部分不碰真实配置)
SKIP_LIVE=1 bash scripts/verify.sh

编 APK(JDK 17,别用 21+)
export JAVA_HOME=/path/to/jdk17/Contents/Home
android/gradlew -p android :app:assembleRelease
产物在 android/app/build/outputs/apk/release/app-release.apk。要发布自己的版本,用同一个 keystore 签名才能覆盖安装、保住配对信息,流程见 docs/android-app.md 的发布一节。

参与开发前请读 CONTRIBUTING.md;安全问题走 SECURITY.md。

目录

plugins/dsh-pet-voice/ DSH 插件:/pet-voice/ 页面、语音闭环、配对、更新通道 android/ Android 客户端(Kotlin,零 androidx) docs/ 契约、安装、自接入、真机、架构 scripts/ 自测、验收、素材生成、发布

许可

代码与文档 MIT。像素素材由本项目生成,同样 MIT。

“DeepSeek Harness” 是它自己的项目,与它的许可证一致;本仓库只通过插件接口与它交互。
预览分享卡片长什么样 ↓regrevia/dsh-pet-voice
分享 · DeepSeek Harness Hub
regrevia/dsh-pet-voice

用手机语音唤醒并指挥电脑上的像素鲸鱼娘干活

回到顶部