让外部 AI 客户端与豆包互发消息和任务
30/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/3 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add ganmayou2333/dsh-ai-bridge该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:待复验
是什么
dsh 原生插件 · model
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 29 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件读取剪贴板或密钥环境变量发起外部网络请求
查看扫描证据(共 176 处,展示前 118 条)
- 读取本地文件doubao-cdp/cdp.mjs:227
const cached = JSON.parse(await readFile(file, 'utf8')) - 写入本地文件doubao-cdp/cdp.mjs:237
await writeFile(file, `${JSON.stringify({ mode: mode.id, raw: mode.raw, at: Date.now() })}\n`, 'utf8').catch(() => {}) - 发起网络请求doubao-cdp/cdp.mjs:243
const response = await fetch(`http://127.0.0.1:${PORT}/json/list`) - 执行外部命令来自配置/变量doubao-cdp/dispatch-selftest.mjs:50
const child = spawn(process.execPath, [STATUS, ...args], { - 执行外部命令来自配置/变量doubao-cdp/dispatch-selftest.mjs:66
const child = spawn(process.execPath, [DISPATCH, ...args], { - 读取本地文件doubao-cdp/dispatch-selftest.mjs:264
const dispatchSource = await readFile(DISPATCH, 'utf8') - 写入本地文件doubao-cdp/dispatch-selftest.mjs:281
await mkdir(join(scratch, 'with-client'), { recursive: true }) - 写入本地文件doubao-cdp/dispatch-selftest.mjs:282
await writeFile( - 写入本地文件doubao-cdp/dispatch-selftest.mjs:338
mkdirSync(lateDir, { recursive: true }) - 写入本地文件doubao-cdp/dispatch-selftest.mjs:344
appendFileSync(join(lateDir, 'status.jsonl'), `${JSON.stringify({ v: 1, type: 'status', job: 'LATE-1', state: 'started', at: Date.now() })}\n`) - 删除本地文件递归doubao-cdp/dispatch-selftest.mjs:360
rmSync(scratch, { recursive: true, force: true }) - 读取本地文件doubao-cdp/dispatch.mjs:49
const parsed = JSON.parse(await readFile(join(resolveQueueDir(), 'clients.json'), 'utf8')) - 执行外部命令来自配置/变量doubao-cdp/dispatch.mjs:72
const child = spawn(process.execPath, [CDP, ...args], { cwd: HERE, stdio: ['ignore', 'pipe', 'pipe'] }) - 执行外部命令来自配置/变量doubao-cdp/dispatch.mjs:118
const child = spawn(process.execPath, [join(HERE, 'status.mjs'), 'last', job], { - 发起网络请求doubao-cdp/doubao.mjs:181
const response = await fetch(`http://127.0.0.1:${port}/json/version`, { - 执行外部命令固定命令doubao-cdp/doubao.mjs:207
const result = spawnSync('tasklist', ['/FI', 'IMAGENAME eq Doubao.exe', '/FO', 'CSV', '/NH'], { - 执行外部命令固定命令doubao-cdp/doubao.mjs:225
const result = spawnSync( - 读取本地文件doubao-cdp/doubao.mjs:245
const existing = candidatePaths().find((candidate) => existsSync(candidate)) - 发起网络请求doubao-cdp/doubao.mjs:257
const response = await fetch(`http://127.0.0.1:${port}/json/list`, { signal: AbortSignal.timeout(3000) }) - 写入本地文件doubao-cdp/doubao.mjs:379
await mkdir(dir, { recursive: true }) - 写入本地文件doubao-cdp/doubao.mjs:380
await writeFile(probe, '', 'utf8') - 删除本地文件doubao-cdp/doubao.mjs:381
await rm(probe, { force: true }) - 发起网络请求doubao-cdp/doubao.mjs:405
const response = await fetch(`http://127.0.0.1:${port}/json/list`, { signal: AbortSignal.timeout(3000) }) - 执行外部命令来自配置/变量doubao-cdp/preflight-selftest.mjs:34
const child = spawn(process.execPath, [script, ...args], { - 读取本地文件doubao-cdp/preflight-selftest.mjs:308
existsSync(overrideFile) && readFileSync(overrideFile, 'utf8').includes('"type":"override"'), - 读取本地文件doubao-cdp/preflight-selftest.mjs:308
existsSync(overrideFile) && readFileSync(overrideFile, 'utf8').includes('"type":"override"'), - 写入本地文件doubao-cdp/preflight-selftest.mjs:314
mkdirSync(busyQueue, { recursive: true }) - 写入本地文件doubao-cdp/preflight-selftest.mjs:315
writeFileSync( - 读取本地文件doubao-cdp/preflight-selftest.mjs:339
!existsSync(queueFile), - 删除本地文件递归doubao-cdp/preflight-selftest.mjs:355
rmSync(scratch, { recursive: true, force: true }) - 执行外部命令来自配置/变量doubao-cdp/status-push-selftest.mjs:31
const child = spawn(process.execPath, [script, ...args], { - 读取本地文件doubao-cdp/status-push-selftest.mjs:52
return (await readFile(file, 'utf8')).split('\n---\n').filter((entry) => entry.trim().length > 0) - 写入本地文件doubao-cdp/status-push-selftest.mjs:85
mkdirSync(fakeDir, { recursive: true }) - 写入本地文件doubao-cdp/status-push-selftest.mjs:87
writeFileSync( - 写入本地文件doubao-cdp/status-push-selftest.mjs:103
writeFileSync(join(fakeDir, 'fake-dsh.cmd'), '@echo off\r\nnode "%~dp0fake-dsh.mjs"\r\n', 'utf8') - 写入本地文件doubao-cdp/status-push-selftest.mjs:106
mkdirSync(work, { recursive: true }) - 写入本地文件doubao-cdp/status-push-selftest.mjs:109
writeFileSync( - 写入本地文件doubao-cdp/status-push-selftest.mjs:144
mkdirSync(createdWork, { recursive: true }) - 读取本地文件doubao-cdp/status-push-selftest.mjs:164
const cursor = JSON.parse(readFileSync(join(createdWork, '.push-cursor.json'), 'utf8')) - 读取本地文件doubao-cdp/status-push-selftest.mjs:175
check('假 CLI 的日志文件确实存在(证明走的是真实推送路径)', existsSync(fakeLog)) - 删除本地文件递归doubao-cdp/status-push-selftest.mjs:205
rmSync(scratch, { recursive: true, force: true }) - 读取本地文件doubao-cdp/status-push.mjs:56
const parsed = JSON.parse(await readFile(cursorPath(env), 'utf8')) - 写入本地文件doubao-cdp/status-push.mjs:65
await mkdir(dirname(file), { recursive: true }) - 写入本地文件doubao-cdp/status-push.mjs:66
await writeFile(file, `${JSON.stringify(cursor, null, 2)}\n`, 'utf8') - 执行外部命令来自配置/变量doubao-cdp/status-push.mjs:95
const child = spawn(useShell ? line : dsh, useShell ? [] : args, { - 读取本地文件doubao-cdp/status-push.mjs:212
if (!existsSync(file)) { - 执行外部命令来自配置/变量doubao-cdp/status-selftest.mjs:30
const child = spawn(process.execPath, [STATUS, ...args], { - 读取本地文件doubao-cdp/status-selftest.mjs:51
return (await readFile(file, 'utf8')) - 写入本地文件doubao-cdp/status-selftest.mjs:200
mkdirSync(controlled, { recursive: true }) - 写入本地文件doubao-cdp/status-selftest.mjs:202
writeFileSync( - 写入本地文件doubao-cdp/status-selftest.mjs:238
await appendFile(tornFile, '{"v":1,"type":"status","job":"TORN","sta', 'utf8') - 删除本地文件递归doubao-cdp/status-selftest.mjs:249
rmSync(scratch, { recursive: true, force: true }) - 读取本地文件doubao-cdp/status.mjs:113
if (existsSync(iniPath)) { - 读取本地文件doubao-cdp/status.mjs:115
const parsed = parseIni(readFileSync(iniPath, 'utf8')) - 读取本地文件doubao-cdp/status.mjs:152
text = await readFile(file, 'utf8') - 发起网络请求doubao-status-panel/client.js:532
request = fetch(url, { headers: { accept: 'application/json' }, cache: 'no-store' }) - 发起网络请求doubao-status-panel/client.js:561
request = fetch(MODE_PATH, { - 发起网络请求doubao-status-panel/client.js:865
fetch('/doubao-status/api?sample=0') - 读取本地文件doubao-status-panel/index.js:702
parsed = JSON.parse(await readFile(file, 'utf8')) - 读取本地文件doubao-status-panel/panel-selftest.mjs:87
const source = readFileSync(join(HERE, 'client.js'), 'utf8') - 写入本地文件doubao-status-panel/panel-selftest.mjs:226
mkdirSync(dirname(file), { recursive: true }) - 写入本地文件doubao-status-panel/panel-selftest.mjs:227
appendFileSync(file, events.map((event) => `${JSON.stringify(event)}\n`).join(''), 'utf8') - 读取本地文件doubao-status-panel/panel-selftest.mjs:241
const manifest = JSON.parse(readFileSync(join(HERE, 'package.json'), 'utf8')) - 读取本地文件doubao-status-panel/panel-selftest.mjs:260
const patch = readFileSync(join(HERE, 'cordis.patch.yml'), 'utf8') - 读取本地文件doubao-status-panel/panel-selftest.mjs:268
const hostSource = readFileSync(join(HERE, 'index.js'), 'utf8') - 读取本地文件doubao-status-panel/panel-selftest.mjs:271
const clientSource = readFileSync(join(HERE, 'client.js'), 'utf8') - 写入本地文件doubao-status-panel/panel-selftest.mjs:798
mkdirSync(join(connectorHome, 'mcp-connector'), { recursive: true }) - 写入本地文件doubao-status-panel/panel-selftest.mjs:800
appendFileSync( - 写入本地文件doubao-status-panel/panel-selftest.mjs:832
mkdirSync(join(brokenHome, 'mcp-connector'), { recursive: true }) - 写入本地文件doubao-status-panel/panel-selftest.mjs:833
appendFileSync(join(brokenHome, 'mcp-connector', 'clients.json'), '{ not json', 'utf8') - 删除本地文件递归doubao-status-panel/panel-selftest.mjs:857
rmSync(connectorHome, { recursive: true, force: true }) - 删除本地文件递归doubao-status-panel/panel-selftest.mjs:858
rmSync(emptyHome, { recursive: true, force: true }) - 删除本地文件递归doubao-status-panel/panel-selftest.mjs:859
rmSync(brokenHome, { recursive: true, force: true }) - 删除本地文件递归doubao-status-panel/panel-selftest.mjs:1246
rmSync(scratch, { recursive: true, force: true }) - 写入本地文件dsh-mcp-connector/queue.mjs:55
await mkdir(dirname(target), { recursive: true }) - 写入本地文件dsh-mcp-connector/queue.mjs:60
await mkdir(lockDir) - 读取本地文件dsh-mcp-connector/queue.mjs:65
const info = await stat(lockDir) - 删除本地文件递归dsh-mcp-connector/queue.mjs:67
await rm(lockDir, { recursive: true, force: true }) - 删除本地文件递归dsh-mcp-connector/queue.mjs:82
await rm(lockDir, { recursive: true, force: true }).catch(() => {}) - 读取本地文件dsh-mcp-connector/queue.mjs:90
raw = await readFile(file, 'utf8') - 写入本地文件dsh-mcp-connector/queue.mjs:184
await mkdir(dirname(file), { recursive: true }) - 写入本地文件dsh-mcp-connector/queue.mjs:186
await appendFile(file, `${prefix}${JSON.stringify(record)}\n`, 'utf8') - 写入本地文件dsh-mcp-connector/selftest.mjs:63
mkdirSync(SCRATCH_HOME, { recursive: true }) - 删除本地文件递归dsh-mcp-connector/selftest.mjs:76
rmSync(SCRATCH_HOME, { recursive: true, force: true }) - 执行外部命令来自配置/变量dsh-mcp-connector/selftest.mjs:83
const child = spawn(process.execPath, [SERVER_PATH], { - 发起网络请求dsh-mcp-connector/selftest.mjs:145
const init = await server.request('initialize', { - 发起网络请求dsh-mcp-connector/selftest.mjs:156
const list = await server.request('tools/list', {}) - 发起网络请求dsh-mcp-connector/selftest.mjs:165
const info2 = await server.request('tools/call', { name: 'dsh_cli_info', arguments: {} }, 90_000) - 发起网络请求dsh-mcp-connector/selftest.mjs:170
const bad = await server.request('tools/call', { name: 'nope', arguments: {} }) - 发起网络请求dsh-mcp-connector/selftest.mjs:174
const status = await server.request('tools/call', { name: 'connector_status', arguments: {} }) - 写入本地文件dsh-mcp-connector/selftest.mjs:188
await writeFile( - 发起网络请求dsh-mcp-connector/selftest.mjs:206
await statusServer.request('initialize', { protocolVersion: '2025-11-25', clientInfo: { name: 'selftest-status', version: '1' } }) - 读取本地文件dsh-mcp-connector/selftest.mjs:216
const recorded = JSON.parse(await readFile(statusArgvFile, 'utf8')) - 删除本地文件dsh-mcp-connector/selftest.mjs:249
rmSync(fakeStatus, { force: true }) - 删除本地文件dsh-mcp-connector/selftest.mjs:250
rmSync(statusArgvFile, { force: true }) - 写入本地文件dsh-mcp-connector/selftest.mjs:281
mkdirSync(join(linkDir, 'real'), { recursive: true }) - 写入本地文件dsh-mcp-connector/selftest.mjs:284
await writeFile( - 读取本地文件dsh-mcp-connector/selftest.mjs:304
if (existsSync(junctionPath)) { - 读取本地文件dsh-mcp-connector/selftest.mjs:322
linked.result?.isError === false && existsSync(guardedMark), - 删除本地文件递归dsh-mcp-connector/selftest.mjs:329
rmSync(linkDir, { recursive: true, force: true }) - 写入本地文件dsh-mcp-connector/selftest.mjs:341
mkdirSync(flowDir, { recursive: true }) - 写入本地文件dsh-mcp-connector/selftest.mjs:342
await writeFile( - 读取本地文件dsh-mcp-connector/selftest.mjs:367
const events = (await readFile(flowQueue, 'utf8')) - 读取本地文件dsh-mcp-connector/selftest.mjs:391
const reportedArgv = (await readFile(flowArgvFile, 'utf8')) - 读取本地文件dsh-mcp-connector/selftest.mjs:441
const foldedDirect = (await readFile(flowQueue, 'utf8')) - 删除本地文件递归dsh-mcp-connector/selftest.mjs:454
rmSync(flowDir, { recursive: true, force: true }) - 执行外部命令来自配置/变量dsh-mcp-connector/selftest.mjs:513
const child = spawn(process.execPath, [SERVER_PATH, '--http', '--port', String(port), '--token', token], { - 删除本地文件递归dsh-mcp-connector/selftest.mjs:627
rmSync(join(HERE, '.selftest-queue'), { recursive: true, force: true }) - 执行外部命令来自配置/变量dsh-mcp-connector/selftest.mjs:699
const child = spawn(process.execPath, [join(HERE, 'queue.mjs'), ...argv], { - 执行外部命令来自配置/变量dsh-mcp-connector/selftest.mjs:773
const child = spawn( - 执行外部命令来自配置/变量dsh-mcp-connector/server.mjs:126
child = spawn(plan.file, plan.argv, { - 读取本地文件dsh-mcp-connector/server.mjs:424
const parsed = JSON.parse(await readFile(file, 'utf8')) - 写入本地文件dsh-mcp-connector/server.mjs:450
await mkdir(dir, { recursive: true }) - 写入本地文件dsh-mcp-connector/server.mjs:451
await writeFile(file, `${JSON.stringify(state, null, 2)}\n`, 'utf8') - 读取本地文件dsh-mcp-connector/server.mjs:461
const parsed = JSON.parse(await readFile(join(resolveQueueDir(), CLIENT_STATE_FILE), 'utf8')) - 读取本地文件dsh-mcp-connector/server.mjs:516
if (!existsSync(candidate)) continue - 执行外部命令来自配置/变量dsh-mcp-connector/server.mjs:540
child = spawn(process.execPath, [script, ...args], { - 读取密钥环境变量dsh-mcp-connector/server.mjs:1065
token: process.env.DSH_MCP_TOKEN?.trim() || undefined,
扫描于 2026/10/31(0 天前) · 范围 全包扫描 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
功能亮点
- 接入本地DSH
- 消息送入DSH
- 派发任务给豆包
把外部 AI 客户端接到本地 DeepSeek Harness(DSH):零依赖 MCP 服务器,把消息送进 DSH;再加 CDP 驱动,把任务派发给豆包桌面应用。
README
由 DeepSeek 最新模型翻译生成dsh-ai-bridge
把外部 AI 客户端和本机 DeepSeek Harness(DSH)接起来的两件小工具,零依赖,不动 DSH 本体。
两件零依赖工具,把外部 AI 客户端和本机 DeepSeek Harness(DSH)接起来:一个 MCP server,让任何支持 MCP 的客户端把消息发进 DSH;一个 CDP 驱动,把任务下发进豆包桌面版。
外部 AI 客户端 ──MCP──▶ dsh-mcp-connector ──▶ dsh --profile headless ──▶ DSH (Claude Code / Cursor / VS Code / Codex / Kimi Code / CodeBuddy / Qoder / TRAE / ZCode / Step Code / MiniMax Code / Coze …
展开完整文档(README 剩余 1 节)
dsh-ai-bridge
把外部 AI 客户端和本机 DeepSeek Harness(DSH)接起来的两件小工具,零依赖,不动 DSH 本体。
两件零依赖工具,把外部 AI 客户端和本机 DeepSeek Harness(DSH)接起来:一个 MCP server,让任何支持 MCP 的客户端把消息发进 DSH;一个 CDP 驱动,把任务下发进豆包桌面版。
外部 AI 客户端 ──MCP──▶ dsh-mcp-connector ──▶ dsh --profile headless ──▶ DSH (Claude Code / Cursor / VS Code / Codex / Kimi Code / CodeBuddy / Qoder / TRAE / ZCode / Step Code / MiniMax Code / Coze …)
DSH / 脚本 / HTTP ──入队──▶ tasks.jsonl ──task_claim──▶ 任意 MCP 客户端(拉取式任务队列)
脚本 / DSH ──CDP──▶ doubao-cdp ──▶ 豆包桌面版(把任务下发进它的聊天框)
为什么需要它
DSH 随发行体只带了 MCP 客户端(dsh-mcp-client),没有 MCP server 半区;它的 Web API 又需要浏览器 cookie 与 Host/Origin 围栏,第三方程序难以直接持有。所以「让别的 AI 给 DSH 发消息」必须有一个独立桥——这就是本仓库的第一个工具。
反方向(把任务下发给别家 AI)没有任何官方入站 API,只能走 GUI 自动化——这是第二个工具。
而 MCP 工具只在客户端自己的回合里执行,推送不进去;所以真正能自动化的派活方式是拉取:生产者入队,客户端领取。这部分已经做进第一个工具(task_claim / task_complete / task_publish / task_list + POST /tasks)。
目录
| 路径 | 作用 |
| dsh-mcp-connector/ | MCP server(stdio + Streamable HTTP 双传输),暴露 dsh_ask / dsh_cli_info / connector_status 与四个任务队列工具,驱动 dsh --profile headless |
| doubao-cdp/ | 用 Chrome DevTools Protocol 驱动豆包桌面版(Electron):cdp.mjs 下发并读回,dispatch.mjs 带副作用校验的派发(拒绝相信客户端的自述) |
| skills/dsh-mcp-connector/ | 给 AI agent 用的技能包:17 个 MCP 客户端的配置矩阵、陷阱表、以及「未证实项」清单 |
| docs/design-notes.md | 设计说明:为什么必须有独立的桥、DSH 的五个机器入口面、队列为何是拉取式、两个方向的合规结论、已知失败模式 |
| scripts/register.mjs | 注册助手:把连接器写进各 MCP 客户端的配置文件,不用手抄 JSON/TOML |
各自的使用方法见对应 README。
快速开始
git clone https://github.com/ganmayou2333/dsh-ai-bridge
cd dsh-ai-bridge
npm test # 两套密封测试:不需要 DSH,也不需要豆包
cd dsh-mcp-connector
node selftest.mjs # 完整自测(多跑两项需要本机 DSH 的检查)
然后在你的 MCP 客户端里加一条 stdio server:命令: node 参数: <你的路径>/dsh-ai-bridge/dsh-mcp-connector/server.mjs 环境: DSH_BIN=<dsh 可执行文件> DSH_WORKSPACE=<DSH 会话工作目录> 逐客户端配置见 dsh-mcp-connector/README.md
用注册助手代替手抄配置
node scripts/register.mjs --list # 看每个客户端的配置文件位置与注册状态
node scripts/register.mjs --client cursor --dry-run # 只打印将要写入的内容,不落盘
node scripts/register.mjs --client vscode # 写入(已存在的文件会先备份成 .bak)
node scripts/register.mjs --client all --dry-run # 一次看全部
支持的客户端与各自的坑都编码在里面(ZCode 的 mcp.servers、Step Code 的 TOML、CodeBuddy/MiniMax 必须显式 type: stdio、VS Code 的 servers)。claude-code 是 CLI 管理的,脚本不写文件,只打印该运行的 claude mcp add 命令。
写入前建议先 --dry-run 看一眼;测试用 --root/--project 重定向到临时目录,不会碰你的真实配置。
要求:Node ≥ 20(本机用 v24 验证)、一个可用的 DSH 安装(dsh 命令)。
验证状态
| 项 | 证据 |
| dsh-mcp-connector 自测 | 61 项断言全过,退出码 0(密封 DSH_HOME 下 headless profile 首次初始化、stdio 往返、HTTP 鉴权 401/403/404/405/202、生产者路由 201、队列语义与 6 路并发领取无碰撞、自观测与流水线握手闸门) |
| 离线自测(CI 用) | 58 项通过 / 2 项跳过,退出码 0;跳过的两项需要本机装好 DSH。工作流见 .github/workflows/ci.yml,矩阵为 Ubuntu/Windows × Node 20/24 |
| 真实模型调用 | 一条消息 3.7 秒返回,拿到 sessionId;带该 id 再发一条成功续接同一会话 |
| 真实 MCP 客户端握手 | Claude Code 2.1.226 完成握手并协商到协议 2025-11-25,证据是连接器自己记录的 clients.json(该客户端当时未登录,故工具调用未执行) |
| doubao-cdp 真实往返 | 连续 3 次下发-回传成功,最后一次为 send + wait 单次调用完成 |
| dispatch.mjs 校验逻辑 | 成功路径(队列完成 → 退出 0 VERIFIED)与失败路径(客户端嘴上说做了、队列仍 pending → 退出 2 UNVERIFIED)双向实测;密封测试 15 项断言全过(含发送前预检),不需要豆包与 CDP |
| 豆包启动确认 | 14 项断言全过:端口关闭 / 端口通但无匹配页面 / 就绪 / --no-preflight 逃生门 四种状态,外加「确认失败不得留下孤儿任务」。用假 CDP 端点密封测,不需要豆包 |
| 状态回报 | 28 项断言全过:固定词表与别名、ini 开关、被关掉的状态静默 no-op、限流(关键状态永不丢)、截断、终态不可覆盖、崩溃半行、时间线排序、忙闲与疑似卡死 |
| 调用前五维识别 | 23 项断言全过:连通 / 模式 / 命令能力 / 忙闲 / 通道,三态区分(0 可发 / 3 未就绪 / 8 无法识别),模式不匹配与忙闲都拒绝,--force 放行并留 override 记录 |
| 状态推送(P2) | 16 项断言全过:只推阶段转换(progress 不推)、游标幂等、新增事件会推、缺会话时明确报错、--create-session 只建一次会话 |
| 统一入口 | 仓库根 npm test = 连接器离线自测 + 派发器 + 豆包确认 + 状态 + 状态推送 + 注册助手,共六套,退出码 0 |
自测默认不产生模型费用;DSH_MCP_CONNECTOR_LIVE=1 node selftest.mjs 会追加一次真实调用。
安全须知
- dsh-mcp-connector 的 stdio 模式由客户端进程本身充当信任边界;HTTP 模式强制 Bearer token,默认只绑 127.0.0.1,未列入白名单的 Origin 一律 403。要上公网请再套 TLS 反向代理并换掉示例 token。
- 外部投递的消息在 DSH 内以 {kind:"user"} 进入会话,与真人输入不可区分——请让下游在任务文本里自带来源标记。
- 不要相信任何 AI 客户端的"我已经调用了工具"。实测:豆包在连接器未启用时会回复「已调用 task_claim,status 为 ok」,而队列里那条任务仍是 pending。核实副作用(队列事件、文件、日志)才是唯一证据——doubao-cdp/dispatch.mjs 就是按这个原则写的。
- doubao-cdp 需要给豆包加 --remote-debugging-port,这意味着本机任何进程都能接管你的豆包(读聊天、以你身份发消息)。不用时退出并正常启动。
- 两者都只做消息投递:不要把外部订阅凭证当作 DSH 的模型后端,那违反各家消费者条款。
许可
MIT,见 LICENSE。预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
ganmayou2333/dsh-ai-bridge
让外部 AI 客户端与豆包互发消息和任务
相关插件 · 模型接入
API 中继安全审计toby-bridges/api-relay-audit
面向 AI API 中转与 LLM 代理的本地安全审计。
★ 897已汉化DSH
69.4
免费搜索插件DDDMUC/dsh-free-search
DeepSeek Harness 免费搜索插件 —— 无需 API key,零成本,多引擎可切换。 一个给…
★ 761已汉化DSH
68.7
免费联网搜索liustack/modsearch
在官方 App 里模型能联网,切到 API 就不能了。ModSearch 把联网补回来:网页搜索、X…
★ 863已汉化DSH
68.5
OpenCode 免费模型FishBottle7/opencode2dsh
在 DSHDeepSeek Harness里原生使用 OpenCode Zen 的免费匿名模型。
★ 379已汉化DSH
67.8
AI 模型网关tingly-dev/tingly-box
Tingly Box Web UI 演示
★ 373已汉化DSH
67.4
WorkBuddy 账号池XDTrees/dsh-xdpool
把你能白嫖的模型额度接进 DSH 同一个池:WorkBuddy 多账号自动容错、十三家扫码登录、76…
★ 359已汉化DSH
67.1