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

小智语音接入 toddpan/dsh-xiaozhi

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

dsh-xiaozhi · 小智语音接入 DSH

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

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

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

状态速览

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

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

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

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

读写/删除本地文件发起外部网络请求
查看扫描证据(共 84 处,展示前 52 条)
  • 发起网络请求
    client/client.js:456
    return fetch(ADMIN_BASE + path, init).then(res =>
  • 读取本地文件
    lib/config.js:235
    if (!existsSync(file))
  • 读取本地文件
    lib/config.js:237
    const parsed = JSON.parse(readFileSync(file, 'utf8'));
  • 写入本地文件
    lib/config.js:270
    mkdirSync(dir, { recursive: true });
  • 写入本地文件
    lib/config.js:271
    writeFileSync(file, `${JSON.stringify(next, null, 2)}\n`, 'utf8');
  • 删除本地文件
    lib/config.js:278
    rmSync(file, { force: true });
  • 读取本地文件
    lib/capabilities.js:916
    const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
  • 读取本地文件
    lib/dshapi/files.js:106
    while (existsSync(candidate)) {
  • 读取本地文件
    lib/dshapi/files.js:172
    if (!existsSync(target)) {
  • 读取本地文件
    lib/dshapi/files.js:177
    const st = await stat(target);
  • 读取本地文件
    lib/dshapi/files.js:182
    const dirents = await readdir(target, { withFileTypes: true });
  • 读取本地文件
    lib/dshapi/files.js:189
    const fst = await stat(full);
  • 读取本地文件
    lib/dshapi/files.js:235
    if (!existsSync(target)) {
  • 写入本地文件
    lib/dshapi/files.js:305
    await mkdir(cwd, { recursive: true });
  • 写入本地文件
    lib/dshapi/files.js:309
    await writeFile(target, f.data);
  • 写入本地文件
    lib/dshapi/files.js:370
    await mkdir(stageDirOf(cwd), { recursive: true });
  • 写入本地文件
    lib/dshapi/files.js:372
    await writeFile(paths.part, Buffer.alloc(0));
  • 写入本地文件
    lib/dshapi/files.js:374
    await writeFile(paths.meta, JSON.stringify(meta));
  • 写入本地文件
    lib/dshapi/files.js:422
    await appendFile(paths.part, chunk);
  • 删除本地文件
    lib/dshapi/files.js:455
    await rm(paths.meta, { force: true });
  • 删除本地文件
    lib/dshapi/files.js:479
    await rm(paths.part, { force: true });
  • 删除本地文件
    lib/dshapi/files.js:480
    await rm(paths.meta, { force: true });
  • 读取本地文件
    lib/dshapi/skills.js:72
    if (!existsSync(skillPath))
  • 读取本地文件
    lib/dshapi/skills.js:75
    const raw = await readFile(skillPath, 'utf-8');
  • 读取本地文件
    lib/dshapi/skills.js:108
    if (!existsSync(skillPath))
  • 读取本地文件
    lib/dshapi/skills.js:111
    const raw = await readFile(skillPath, 'utf-8');
  • 读取本地文件
    lib/dshapi/skills.js:134
    const targetDir = existsSync(dir) ? dir : undefined;
  • 读取本地文件
    lib/dshapi/skills.js:135
    if (!targetDir && !existsSync(flat))
  • 删除本地文件
    lib/dshapi/skills.js:152
    readStream.on('end', () => { void rm(tmpFile, { force: true }).catch(() => { }); });
  • 写入本地文件
    lib/dshapi/skills.js:252
    await writeFile(skillPath, newRaw, 'utf-8');
  • 删除本地文件递归
    lib/dshapi/skills.js:274
    await rm(target, { recursive: true, force: true });
  • 写入本地文件
    lib/dshapi/skills.js:297
    await mkdir(rootPath, { recursive: true });
  • 删除本地文件递归
    lib/dshapi/skills.js:304
    await rm(tmpDir, { recursive: true, force: true }).catch(() => { });
  • 删除本地文件递归
    lib/dshapi/skills.js:317
    await rm(tmpDir, { recursive: true, force: true }).catch(() => { });
  • 删除本地文件递归
    lib/dshapi/skills.js:323
    await rm(tmpDir, { recursive: true, force: true }).catch(() => { });
  • 删除本地文件递归
    lib/dshapi/skills.js:360
    await rm(targetDir, { recursive: true, force: true }).catch(() => { });
  • 写入本地文件
    lib/dshapi/skills.js:361
    await mkdir(targetDir, { recursive: true });
  • 写入本地文件
    lib/dshapi/skills.js:364
    await writeFile(path.join(targetDir, 'SKILL.md'), await readFile(located.skillPath));
  • 写入本地文件
    lib/dshapi/skills.js:383
    await writeFile(d, await readFile(s));
  • 写入本地文件
    lib/dshapi/skills.js:390
    await mkdir(targetDir, { recursive: true });
  • 读取本地文件
    lib/dshapi/skill-utils.js:44
    return { ok: true, root: { kind: 'project', path: existsSync(agents) ? agents : dsh } };
  • 读取本地文件
    lib/dshapi/skill-utils.js:59
    if (existsSync(path.join(current, '.git')))
  • 写入本地文件
    lib/dshapi/skill-utils.js:202
    await mkdir(destDir, { recursive: true });
  • 写入本地文件
    lib/dshapi/skill-utils.js:240
    await mkdir(path.dirname(target), { recursive: true });
  • 写入本地文件
    lib/dshapi/skill-utils.js:241
    await writeFile(target, out);
  • 写入本地文件
    lib/dshapi/skill-utils.js:281
    await writeFile(tmp, buffer);
  • 写入本地文件
    lib/dshapi/skill-utils.js:282
    await mkdir(destDir, { recursive: true });
  • 删除本地文件
    lib/dshapi/skill-utils.js:293
    await rm(tmp, { force: true }).catch(() => { });
  • 读取本地文件
    lib/dshapi/skill-utils.js:299
    if (!existsSync(root))
  • 读取本地文件
    lib/dshapi/skill-utils.js:303
    entries = await readdir(root);
  • 读取本地文件
    lib/dshapi/skill-utils.js:312
    st = await stat(full);
  • 读取本地文件
    lib/dshapi/skill-utils.js:321
    if (!existsSync(skillPath))
扫描于 2026/11/2(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

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

依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/schemastery@deepseek-ai/dsh-host-webserver
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 接入小智语音助手
  • 封装语音友好工具
  • 提供设置页
展开完整介绍(175 字)↓

把小智(Xiaozhi.me)语音助手接入 DSH Web:DSH 作为 MCP 工具服务商,把 35 个 DSH Web 接口封装成 16 个语音友好工具,自带 DSH Web 设置页。 · 把小智语音助手接入 DSH Web,作为 MCP 工具服务商:35 个 DSH Web 接口整合成 16 个语音友好工具,并带一个 DSH Web 设置页。

README

由 DeepSeek 最新模型翻译生成
dsh-xiaozhi · 小智语音接入 DSH 把小智(Xiaozhi)语音助手接入 DeepSeek Harness(DSH)Web:DSH 作为 MCP 工具提供方,通过 WebSocket 上的 JSON-RPC 2.0 把工作区、会话、模型、设置、文件等能力封装成工具,供小智语音调用。 中文 · English · 安装与验证 · 工具清单 把小智(Xiaozhi)语音助手接入 DSH Web:DSH 作为 MCP 工具提供方,把 35 个 DSH Web 接口封装成 16 个语音友好工具,自带 DSH Web 设置页。 Connect the Xiaozhi voice assistant to DSH Web as an MCP tool provider — 35 endpoints, 16 voice-friendly tools. 实施前的设计提案(架构 ADR、v2 复核、设置页 UX 走查)归档在 docs/design/,其中与实际交付不一致的地方已逐条注明。 1. 它解决什么问题 DSH Web 的能力都在 HTTP REST 接口上,而小智只认 MCP(Model Context Protocol)。
展开完整文档(README 剩余 1 节)
dsh-xiaozhi · 小智语音接入 DSH

把小智(Xiaozhi)语音助手接入 DeepSeek Harness(DSH)Web:DSH 作为 MCP 工具提供方,通过 WebSocket 上的 JSON-RPC 2.0 把工作区、会话、模型、设置、文件等能力封装成工具,供小智语音调用。

中文 · English · 安装与验证 · 工具清单

把小智(Xiaozhi)语音助手接入 DSH Web:DSH 作为 MCP 工具提供方,把 35 个 DSH Web 接口封装成 16 个语音友好工具,自带 DSH Web 设置页。
Connect the Xiaozhi voice assistant to DSH Web as an MCP tool provider — 35 endpoints, 16 voice-friendly tools.

实施前的设计提案(架构 ADR、v2 复核、设置页 UX 走查)归档在 docs/design/,其中与实际交付不一致的地方已逐条注明。

1. 它解决什么问题

DSH Web 的能力都在 HTTP REST 接口上,而小智只认 MCP(Model Context Protocol)。
本插件坐在两者中间:

你说一句话 │ ▼ ┌─────────────┐ MCP(JSON-RPC 2.0 / WebSocket) ┌──────────────────────────┐ │ 小智助手 │ ◄───────────────────────────────► │ dsh-xiaozhi (Host 半边) │
│ (App/硬件) │ initialize / tools/list / call │ ├ MCP 会话与工具注册表 │
└─────────────┘ │ ├ 能力→REST 路由映射 │ │ └ LocalInvoker(进程内调用)│ └───────────┬──────────────┘ │ 不走网络,直接调用 ▼ ┌──────────────────────────┐ │ DSH Web REST 路由(内置副本)│ └──────────────────────────┘

三个关键设计决定:

1. DSH 永远是 MCP 的“服务端/工具提供方”。 两种传输方式下都由 DSH 应答 initialize / ping / tools/list / tools/call,从不主动发起这些请求。
2. 默认主动外连(endpoint 模式)。 DSH 作为 WebSocket 客户端连到小智官方 MCP 接入点,所以不需要公网 IP、端口映射或反向代理。
3. 进程内调用,而不是回环 HTTP。 工具调用通过 LocalInvoker 直接打到内置的 DSH REST 路由,无需猜测 DSH 自身的 host/port/鉴权,也不依赖外部服务。

2. 快速开始(3 步)

前提:DSH Web 已经在跑(dsh web,默认 http://127.0.0.1:3080);你有小智账号并能打开它的「MCP 接入点」页面。

1. 安装插件(在本仓库目录下执行):

dsh plugin add https://github.com/toddpan/dsh-xiaozhi

也可以在 DSH Web 的「设置 → 插件」里用「安装本地目录」选择该目录。

2. 拿到接入点地址并添加设备:打开 DSH Web →「设置 → 小智接入」→「接入配置」,点「添加设备」,把小智后台的 MCP 接入点 WebSocket 地址(形如 wss://api.xiaozhi.me/mcp/?token=…)粘贴进去,点「保存并重载」。
可以重复添加,同时绑定多台小智设备/智能体,每台独立连接、独立显示状态。

3. 看状态:回到「状态」页签,连接状态应为 connected;状态页每 3 秒自动刷新,点「立即重连」或某台设备的「重连」后,无需手动刷新即可看到状态变化。
点一次「测试连接」可以看到真实握手结果。

然后在手机上对小智说:“用 dsh 看一下我的会话列表” 或 “让 DSH 汇报一下运行状态”。

接入点地址含 token,属于敏感信息。设置页读回时只显示 token=,保存时也会被识别为「未修改」,不会把掩码写进配置。详见 §7。

3. 两种传输方式

| | endpoint(默认,推荐) | server(自建服务端) |
| 谁发起连接 | DSH 主动外连小智接入点 | 小智服务端连到 DSH |
| 需要公网可达吗 | 不需要 | 需要(或反向代理 / 内网同段) |
| 主要配置 | endpoints(多设备列表)、endpointHeaders | serverPath、serverPort、serverToken |
| 适用场景 | 小智官方 MCP 接入点、家用/办公本机 | 自建 xiaozhi-esp32-server、内网统一网关 |

两种方式可以同时开启:mode 决定主通道,serverPort > 0 时会额外在 0.0.0.0 上监听一个独立端口。

多设备绑定:endpoint 模式支持同时绑定多台小智设备(多个智能体的 MCP 接入点)。
每台设备一条独立的 WebSocket 连接,各自带重连退避与心跳;设置页按设备显示状态、支持单台「重连 / 测试」,删除设备也只影响该设备。设备列表保存在 endpoints (见 §8);旧版单设备配置(endpointUrl)仍然生效,设置页读回时会显示为一台设备,首次保存后自动迁移为列表形态。

断线重连:endpoint 模式带指数退避(reconnectMinMs → reconnectMaxMs,含 ±20% 抖动)和 heartbeatMs 心跳;「状态」页的「重连次数」和日志可以看到全部过程。

4. 工具暴露方式:grouped(默认)还是 flat

小智的工具名会被清洗成 [A-Za-z0-9_\-中文],本插件的所有工具名都是该规则的不动点 (例如 dsh_session_history),因此不会在平台侧被改名。

| 模式 | 工具数 | 说明 |
| grouped(默认) | 16(关闭工具组后更少) | 按能力域合并,用 action 参数选择具体动作 |
| flat | 35 | 每个接口一个工具,名字与接口一一对应 |

默认选 grouped 的原因:语音模型在 35 个工具里挑一个的准确率明显低于在 16 个里挑。
超过 24 个工具时设置页会给出提示。完整对照表见 docs/TOOLS.md。

工具组开关:可在「接入配置 → 工具组开关」里关掉暂时不用的域(例如 docs、files)。
关闭整组会让对应工具(或分组工具里的对应动作)直接不可用。

写入开关:allowWriteTools = false 时,创建/修改/删除/发送类动作会被拒绝并返回一句可直接朗读的中文说明,只读动作照常可用(即使它们和写动作合并在同一个 grouped 工具里)。

5. 能力覆盖

35 个接口全部可达,两种工具模式下都覆盖 35/35:

| 能力域 | 能力数 | 对应接口 |
| 系统状态 | 1 | GET /system/status |
| 工作区 | 6 | /workspaces、/workspaces/:id、/workspaces/:id/sessions |
| 会话 | 13 | /sessions、/sessions/:id、history、stats、todos、skills、questions、answers、cancel、events |
| 文件 | 3 | /sessions/:id/files、/files/download |
| 对话 | 3 | /sessions/:id/prompt、/prompt-stream、/chat/completions |
| 模型与预设 | 5 | /models、/models/default、/providers、/presets |
| 系统设置 | 2 | /settings、/settings/:namespace |
| 接口文档 | 2 | /docs、/openapi.json |

其中 4 个能力在 MCP 语义下做了降级(不是缺失,但仍需你知情),详见下一节。

6. MCP 语义降级(务必阅读)

MCP 的 tools/call 是一问一答的,没有增量流式通道,而原 REST 接口里有几个是流式的。
本插件选择「尽量保住语义、并如实告知」而不是假装支持:

| 能力 | 原本形态 | 在 MCP 上的行为 | 你需要知道 |
| conversation.promptStream(dsh_say / dsh_conversation_promptstream) | text/event-stream,增量推送 | DSH 在服务端收集完整个流后一次性返回结果文本 | 语音端不会逐步流式;promptTimeoutMs 决定等待上限,超时返回「已提交、仍在运行」而不是错误 |
| sessions.events(dsh_session_watch) | 常驻 SSE 事件流 | 只在有限时间窗内(1–30 秒)收集事件后返回 | 只能当“看一眼最近的动静”,不能当实时监听;需要持续监听请用 sessions.stats 轮询 |
| files.download | 二进制文件流 | 文本文件回传正文(截断到 maxVoiceChars);二进制只回传摘要(大小、类型、路径) | 语音播报二进制内容本来也没有意义;需要真文件请走 DSH Web 界面或内置 REST 层 |
| docs.openapi | 完整 OpenAPI JSON | 返回结构摘要(openapi 版本、title、路径数、最多 100 条路径、字节数、原始 URL) | 完整规范请直接访问 apiBase/openapi.json |

另外两点:

dsh_say(wait=false) 用于「把话转给会话、不等结果」:它在约 1.5 秒预算内提交 prompt-stream,超时就静默返回「已提交」并附上会话当前状态,不会让你干等。
所有工具结果都会按 maxVoiceChars 截断成单个 text 块,避免语音播报冗长。

7. 安全模型(请按自己的部署范围核对)

| 面 | 默认 | 保护 |
| DSH Web 设置页 API /dsh-xiaozhi/admin | 仅本机可访问(DSH 默认绑定 127.0.0.1) | ①跨站 Origin 拒绝 ②sec-fetch-site: cross-site 拒绝 ③每个请求(含读取)都必须带 x-dsh-xiaozhi-admin: 1 自定义头;跨站表单/图片无法设置自定义头,跨域 fetch 会触发预检而本路由 cors: false 从不放行 ④若宿主存在 connection 服务,先由它做浏览器 cookie + Host/Origin 判定(401/403) |
| 内置 DSH REST 层 /dsh-xiaozhi/api/v1 | 默认开启 | 设置 apiKey 后需 Authorization: Bearer … 或 X-API-Key;未设置密钥时会给出警告 |
| MCP 工具(对外) | 默认开启,默认允许写 | allowWriteTools=false 关闭全部写操作;disabledGroups 缩小攻击面 |
| server 模式独立端口 | 默认关闭(serverPort=0) | 填端口会在 0.0.0.0 监听,必须设置 serverToken,否则设置页会警告 |

密钥掩码:设置页读回配置时,apiKey、serverToken、接入点 URL 里的 token=(含 endpoints 里每台设备的 URL)以及 endpointHeaders / 设备 headers 的所有值 都被替换成掩码(•••••• / ),但会保留 header 名字。
保存时掩码会被识别为“未修改”并按存储值还原,不会用掩码覆盖真实密钥。

endpointHeaders(全局兜底)只能新增/覆盖,不能通过设置页删除(底层是合并写入);要删掉某个全局请求头,手工编辑 settings.json。设备级 headers 则以整行替换的方式保存,在设备卡片里删掉对应行并保存即可删除该键。

8. 配置项

配置分三层,优先级从低到高:

1. 代码默认值(src/config.ts 的 DEFAULTS)
2. 插件行的 config(profile 的 cordis.patch.yml)
3. 设置页保存的覆盖(<homeDir>/settings.json)

| 配置 | 默认 | 说明 |
| enabled | true | 关闭后小智无法调用任何工具 |
| mode | endpoint | endpoint / server |
| endpoints | [] | 小智 MCP 设备列表({id?, name?, url, headers?}),非空时优先生效;设置页「添加设备」写的就是这个键 |
| endpointUrl | '' | (旧单设备)小智 MCP 接入点;endpoints 非空时被忽略 |
| endpointHeaders | {} | 接入点附加请求头(所有设备的兜底,设备级 headers 覆盖同名键) |
| serverPath | /mcp/xiaozhi | server 模式的路径(须含 /mcp/) |
| serverPort | 0 | 0 复用 DSH Web 服务器;>0 额外监听 0.0.0.0 |
| serverToken | '' | serverPort>0 时强烈建议设置 |
| toolMode | grouped | grouped / flat |
| disabledGroups | [] | 关闭的能力域 |
| allowWriteTools | true | 是否允许写操作 |
| promptTimeoutMs | 120000 | 语音指令等待上限(须小于内置 REST 层的 180000) |
| maxVoiceChars | 700 | 单条回复截断长度 |
| listLimit | 10 | 列表类结果条数 |
| heartbeatMs | 30000 | 心跳间隔 |
| reconnectMinMs / reconnectMaxMs | 1000 / 30000 | 重连退避区间 |
| apiPathPrefix | /dsh-xiaozhi/api | 内置 REST 层前缀(设置页 API 固定在 /dsh-xiaozhi/admin) |
| exposeDshApi | true | 是否挂载内置 DSH REST 层 |
| apiKey | '' | 内置 REST 层鉴权密钥 |
| cors | false | 内置 REST 层是否允许跨域 |
| defaultCwd | '' | 创建会话的默认目录 |
| maxUploadBytes | 104857600 | 上传上限 |
| homeDir | '' | 只能在插件行 config 里设置(见下) |
| logToolCalls | true | 记录每次工具调用 |
| sendInitializedNotification | true | MCP 握手后发送 notifications/initialized |
| serverName | DSH | 对外声明的服务名 |

为什么 homeDir 不在设置页里? 它决定设定文件本身放在哪,如果允许从该文件里读,就会出现“设置页显示新目录、覆盖却仍写旧目录”的自相矛盾。因此 homeDir 固定只从插件行 config 读取,设置页只做只读展示。环境变量 DSH_XIAOZHI_HOME 亦可指定。

9. 设置页

DSH Web →「设置 → 小智接入」,共 5 个页签:

状态:连接状态徽标、传输方式、接入点(已掩码)、每台已绑定设备的状态行、已连接客户端数、重连次数、最近错误、需要注意的警告、对外地址、工具/能力计数、各工具组开关状态;可「测试连接」「立即重连」「刷新」。状态每 3 秒自动刷新,重连后无需手动点刷新。
接入配置:基础项 + 小智 MCP 设备列表(添加/重命名/删除,每台可单独「重连」「测试」)
+ 工具组开关 + 折叠的高级项;「保存并重载」会写覆盖文件并重启运行时,「恢复默认」清空全部覆盖。
* 工具清单:当前实际暴露的工具、读写属性、覆盖的能力数。
* 能力清单:35 个能力按域列出,含方法与路径。
* 日志:插件环形日志(默认 300 条),可打开 5 秒自动刷新。

页面只用 DSH 主题 token(--dsw-alias-)着色,不依赖 dsh-client-ui-primitives,因此明暗主题都跟随宿主,样式不会和宿主冲突。

10. 开发与验证

cd dsh-xiaozhi
bash scripts/build.sh # 需要 DSH 源码 checkout 提供 tsc(自动探测)
node --test --test-timeout=30000 "test/.test.mjs"

测试覆盖(127 个用例):

| 文件 | 覆盖内容 |
| test/protocol.test.mjs | MCP 报文、工具名清洗不动点、信封解析 |
| test/ws.test.mjs | RFC 6455 分帧、掩码方向、分片、关闭握手 |
| test/config.test.mjs | 三层配置合并、密钥掩码、homeDir 不可覆盖 |
| test/endpoints.test.mjs | 多设备:endpoints 归一化与旧键兼容、设备掩码还原、每台设备独立拨号与状态 |
| test/coverage.test.mjs | 35 个接口逐条钉住;两种工具编织都全覆盖;工具名是清洗不动点 |
| test/dispatcher.test.mjs | 进程内调用:JSON、查询串、请求体、流式响应、404、超时 504 |
| test/mcp-session.test.mjs | 真实 socket 上的握手 → tools/list → tools/call,含并发与协议错误 |
| test/routes.test.mjs | 用真实路由表验证 35 个能力都命中已注册路由;分组工具端到端 |
| test/client.test.mjs | 浏览器半边的常量一致性、双语字典完整性、helpers、react-dom/server 渲染 |
| test/admin.test.mjs | 设置页 API:页面调用的每个路由都必须以正确方法可达;三层守卫;密钥掩码剥离 |
| test/docs.test.mjs | 文档与代码一致性:工具名/能力/计数不得漂移 |

src/dshapi/ 是上游 @dsh-external/dsh-web-service v0.1.11 的逐字拷贝(BSD-3-Clause),唯一的新文件是把它组装成单个路由的 src/dshapi/service.ts,这样上游更新时仍是干净的三方 diff。
详见 NOTICE。

11. 常见问题

| 现象 | 原因与处理 |
| 状态一直是 disconnected | 设备的接入点地址没填或填错(必须是 ws:///wss:// 且含 /mcp/,且不能含 key/call 字样);看「日志」页首个错误;多台设备时看「状态」页对应设备行的错误说明 |
| 小智能看到工具但调用失败 | 检查 allowWriteTools;写入类动作被关闭时会返回明确的中文说明 |
| 小智看不到任何工具 | enabled=false,或所有工具组都被关闭 |
| 语音里会话/工作区 id 说不清 | grouped 工具返回的 id 都做了缩短(如 sess-123);也可以用名称调用 |
| 局域网里的自建小智连不上 | server 模式且 serverPort=0 时只监听 DSH 服务器(默认仅本机);填端口并设置 serverToken |
| 修改 apiPathPrefix 后设置页没变 | 符合预期:设置页 API 固定在 /dsh-xiaozhi/admin,apiPathPrefix 只影响内置 REST 层 |

12. 许可

BSD-3-Clause。派生自 @dsh-external/dsh-web-service v0.1.11(Copyright © 2026 toddpan 潘祖继),同一许可。见 LICENSE 与 NOTICE。
预览分享卡片长什么样 ↓toddpan/dsh-xiaozhi
分享 · DeepSeek Harness Hub
toddpan/dsh-xiaozhi

dsh-xiaozhi · 小智语音接入 DSH

这是你的仓库?认领条目

回到顶部