DeepSeek Harness Hub
← 返回列表

CLI 模型后端切换f25h-233/dsh-cli-switch

DeepSeek Harnessspec-screened在 GitHub 查看 ↗
未验证

把本地 AI CLI 接成模型后端,在模型选择器里一键热切换

尚未跑自动兼容性验证,可查看页面内的依赖与入口分析。 · 最近上游提交 2026/8/28 · 已提供中文文档

LLM-provider plugin for DeepSeek Harness: use local AI CLIs (claude / opencode / gemini / cursor / codex) as model backends, hot-switch in the model selector. DSH 的 LLM provider 层插件:本地 AI CLI 当模型后端,一键热切换。

综合分
28.8
GitHub 分
28.8
用户评分
★ Stars
1
周下载量
安装插件(需先安装 dsh CLI 引擎:npm install -g @deepseek-ai/dsh)
dsh plugin --profile web add f25h-233/dsh-cli-switch
该插件未发布到 npm,走 GitHub 源安装(pnpm 若拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑)
数据截至 2026/9/16(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
依赖的 DSH / Cordis 模块
@deepseek-ai/dsh-attachment@deepseek-ai/dsh-brand@deepseek-ai/dsh-invariants@deepseek-ai/dsh-llm@deepseek-ai/dsh-subprocess@deepseek-ai/dsh-timeout@deepseek-ai/schemastery@deepseek-ai/cordis
用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动

README

dsh-cli-switch

⚠️ 开发中(WIP)· Work in Progress

本仓库处于早期开发阶段:M1(opencode-cli 文本对话)+ M2(claude-cli 完全剥壳)冒烟通过,工具执行链路尚未开放(设计见 ROADMAP M4 专题 A)。欢迎围观、试用、提 issue——但不建议生产使用;接口与行为可能随时变更,恕不另行通知。

DeepSeek Harness 的 LLM provider 层插件:把各家本地 AI CLI(claude / opencode / gemini / cursor / codex…)接成 DSH 的"模型后端",模型选择器里一键热切换。

定位一句话:壳是 DSH 的(agent loop、工具、沙箱、日志、UI 全归 DSH),芯是各家 CLI 里的模型——各家 CLI 在本插件中只保留"思考 + 生成 tool_use"的能力,手和脊髓全部截掉换成 DSH 的。

状态:M1 冒烟通过(2026-08-20:opencode-cli 文本对话 + 不落盘 + T5' 剥壳实证);M2 冒烟通过(同日:claude-cli 完全剥壳——SDK 路线工具往返 + 生产环境 24 工具全可见零泄漏 + 不落盘);M3 冒烟通过(2026-08-21:五家 route 全在模型选择器 + opencode/claude 回归 + gemini/cursor 快速失败;codex 因区域封锁未达快速失败,入 M4)。计划见 ROADMAP.md,冒烟证据见 docs/smoke/m1.md、docs/smoke/m2.md 与 docs/smoke/m3.md。

1. 背景与生态位

- DSH(deepseek-ai/deepseek-harness)是 DeepSeek 官方 agent harness,2026-08-13 发布,7 天 16.6 万星,MIT / TypeScript / Cordis 插件架构,"Everything is a Plugin"。
- DSH 官方已在 subagent 层做了 claude-code / codex 集成(dsh-subagent-claude-code 用 Agent SDK、dsh-subagent-codex 用 codex app-server)——那是"临时工"模式:one-shot 委派任务给产品、拿回最终答案,一个任务一个进程,官方明确不做流式/续传。
- 本插件做的是 provider 层——"模型脑"模式:产品只当模型通道,持续会话、工具执行、沙箱审批、会话日志、UI 渲染全部是 DSH 的。这个生态位还没人做。
- 已存在的 katsos/dsh-claude-cli(4 星)验证了 claude 单家剥壳可行,本插件是其泛化版本。

2. 核心概念:剥壳 = 截肢 + 接管反射弧

任何完整 agent(有脑有手有循环)塞进 LLM provider 插槽时:

| 部件 | SDK 家族(claude) | ACP 家族(opencode/cursor/gemini) | 处置 |
|---|---|---|---|
| 脑(模型思考 + tool_use 决策) | ✔ | ✔ | 保留——这正是 provider 的职责 |
| 脊髓(自己的 agent loop) | ✘ | ✘ | 砍掉——DSH 的 agent loop 接管驱动(ACP 家族:loop 在 agent 侧,客户端只收通知,见"双层现实") |
| 手(内置工具) | ✘ disallowedTools 按名前缀过滤(R3 实证:'' 会把我们声明的工具也移除) | ✘ 项目级 opencode.jsonc tools:{...:false} 按名禁用(R1 实证 8 原生全移除) | 剁掉——模型看不到任何原生工具 |
| 记忆/设置/MCP/钩子 | ✘ | ✘ | 清除——统一走 DSH 的会话与凭据体系 |
| 假手(MCP bridge) | + tool_use 流式回客户端(input_json_delta,R3)→ DSH 执行 | + 工具由 agent 服务端执行,客户端只收 tool_call 通知(R4) | 接上——DSH 工具伪装成 MCP server 喂给模型 |

双层现实(M1.5 调研实证)——剥壳有两个层面,两个家族的边界不同:

- ACP 家族(opencode / cursor / gemini)= 工具面可控 + 执行在 agent 侧:原生工具可按名禁用(项目级 opencode.jsonc,R1 实证 8/8 从模型可见面移除),但 MCP 工具由 agent 服务端执行,tool_use 从不回客户端——ACP 规范没有"客户端执行工具并回传结果"的消息类型,这是遥控器设计而非 opencode 特例(R4:opencode 源码级证据——event.ts 的 handleToolPart() 把服务端 ToolPart 状态机翻译成 tool_call/tool_call_update 通知,客户端纯旁观;gemini/cursor 走官方 ACP SDK 同构受限)。客户端能做的:收 tool_call/tool_call_update 通知做 wire 级剥壳断言、权限请求自动拒、桥接报错回传。完整工具执行链 = M4 专题 A(SSE bridge + ctx.tools.execute,R2 实证 API 存在)。
- SDK 家族(claude)= 完全剥壳:input_json_delta 把 tool_use 参数逐字流式回客户端(R3 实证),DSH 执行工具、结果回灌模型——本 README 此前的"tool_use 转发回 DSH 执行"只在本家族成立。
工具名单源原则:工具名永远由 DSH 定义(模型只会“看到什么 schema 发什么 tool_use”),不需要映射表/正则——但协议层会加命名空间前缀:MCP 挂载后模型看到的工具名 = _(实证:dsh-cli-switch-bridge_fs_read,R1),claude SDK 路线 = mcp____(实证:mcp__dsh-bridge__fs_read,R3)→ 客户端按单一前缀规则(非映射表)翻译回 DSH 裸名。翻译回裸名后:未注册工具名 = 剥壳不彻底,处理方式是拒绝 + 明确报错,绝不模糊匹配(那是绕过 schema 校验和 approval 的漏洞)。

3. DSH 侧接口(插件必须遵守)

插件 = Cordis 插件,核心就一个注册动作:

class MyAdapter extends LlmAdapter {
async  stream(options: GenerateOptions): AsyncIterable { … }
}
export function apply(ctx: Context, config: Config) {
ctx.llm.registerAdapter(['my-provider'], new MyAdapter(…))
}

StreamChunk 协议铁律(官方 cookbook:docs/cookbook/adding-an-llm-adapter.md):
- 流类型:block-start / text-delta / reasoning-delta / tool-call-delta / block-end / usage / finish
- usage 必须在 finish 之前发;finish 之后什么都不发
- 工具 arguments 是 raw JSON 字符串端到端;流式片段用 argumentsDelta
- 块 index 按首次出现顺序分配,同一块的每个 delta 复用同一 index
- 失败归一化为 finish { kind: 'error' | 'aborted', failure }——错误也是终态
- 凭据用 cordis 原生 schemastery(!!js process.env.XXX),绝不在代码里读 key 文件
- 无 API key 场景官方认可:“profile 命名 no credential 时通过 provider 自己的 ambient discovery 或 OAuth 认证”——这正是各家 CLI 本地登录态的用武之地

参考实现:packages/llm/llm-deepseek(直接 HTTP+SSE)、packages/llm/llm-pi-ai(包装第三方库)。官方 subagent 包 packages/subagent/subagent-claude-code、packages/subagent/subagent-codex 有进程生命周期与认证的现成代码可借。

4. 各家 CLI 可剥壳性矩阵(2026-08-20 调研结论)

| CLI | headless | 工具禁用 | MCP stdio | ACP | 总评 |
|---|---|---|---|---|---|
| claude (2.1.237 / SDK 0.3.220) | -p | disallowedTools 按名前缀过滤(R3:'*' 会误伤自己声明的工具) | SDK mcpServers 挂 bridge(需 NDJSON 帧,R3) | ❌ 无(Agent SDK 私有流) | ✅ M2 冒烟通过(完全剥壳:工具往返 + 24 工具零泄漏 + 不落盘) |
| opencode (1.18.18) | run --format json | opencode.jsonc tools:{...:false} 按名禁用(R1 实证) | opencode.json mcp;session/new 只收 http/sse 挂载 | opencode acp | ✅ M1 冒烟通过(文本 + 剥壳实证) |
| gemini-cli | -p --output-format stream-json | Policy Engine deny | settings.json mcpServers | --acp(实验性,未实证) | 🟡 route 就绪(R-M3-1 降级:无凭据未实证;凭据到位后探针修正) |
| cursor | agent -p | ❌ 禁不掉(只读模式兜底) | .cursor/mcp.json | cursor-agent acp + request_permission | 🟡 route 就绪(R-M3-1 降级:无订阅未实证) |
| codex (0.148.0) | exec --json | ❌ 无通用禁用 | config.toml mcp_servers | ❌ 无 ACP;codex mcp-server = 标准 MCP(NDJSON/2025-11-25,探针实证) | 🟡 route 就绪(R-M3-1 降级:未登录 + 区域封锁 403 未达快速失败,M4 处理) |
| windsurf | ❌ 无程序化 CLI | — | — | — | ❌ 不可行(已并入 Devin Desktop) |

协议格局(M3 探针实证,R-M3-2/3):opencode / gemini / cursor 全有 ACP(session/prompt 流式 + request_permission 可拒 = 天然的“吐 tool_use 不执行”机制)→ 一个 AcpAdapter 基类(M3-T1 抽取)吃下三家;codex 的 mcp-server 是标准 MCP(NDJSON stdio + tools/call 阻塞式 {threadId, content},不是 ACP 风格 thread/turn)→ 独立 CodexAdapter(M3-T4 独立 wire);claude 走 Agent SDK 私有流,单独一个 adapter。

5. 架构设计

浏览器 Web UI(DSH 原装,零改动)
│ session/event + API Gateway
▼
DSH agent loop(turn/step 驱动、prompt 组装、工具调度)
│ ctx.llm.stream()
▼
┌─────────────────────────────────────────────────────────┐
│ dsh-cli-switch(Cordis 插件,注册多个 provider route)     │
│                                                         │
│  ┌─────────────────────┐   ┌──────────────┐ ┌─────────┐ │
│  │ AcpAdapter 基类     │   │ CodexAdapter │ │ClaudeSdk │ │
│  │ (ACP client 骨架)   │   │ (独立 MCP    │ │Adapter  │ │
│  │  ├ opencode route   │   │  wire:       │ │(@anthrop-│ │
│  │  ├ gemini route     │   │  mcp-server  │ │ ic-ai/  │ │
│  │  └ cursor route     │   │  NDJSON +    │ │ sdk     │ │
│  │  (R-M3-2: codex 非  │   │  tools/call  │ │ query() │ │
│  │   ACP,独立 wire)   │   │  阻塞式)     │ │ 流式)   │ │
│  └─────────┬───────────┘   └──────┬───────┘ └────┬────┘ │
│            │ 进程生命周期/认证/错误分类   │               │
│  ┌─────────▼───────────────────────────▼─────────────┐  │
│  │ 公共框架:spawnCli / stream 协议翻译 / MCP bridge   │  │
│  │ bridge.mjs = DSH 工具 → MCP server(工具名单源)     │  │
│  └───────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────┘
│           │
▼           ▼
opencode 子进程   claude 子进程(SDK)
(ACP stdio)      (私有消息流)

6. 设计原则(已定案)

快死是默认,降级必须显式:
1. provider 不可用(没装/没登录/版本不兼容)→ 明确诊断(稳定 code + cause chain),不自动换芯——用户意图明确,静默换模型违反 DSH "model-visible means logged" 不变式
2. 瞬时故障(网络抖、启动闪断)→ 分类重试(复用 dsh-llm-retry 的 retryableCodes:RATE_LIMIT/超时可重试,INVALID_CREDENTIAL/QUOTA 不重试)
3. 能力差异(某家不流 reasoning、不支持多模态)→ 声明式协商:注册时用 resolveModelInfo() 声明能力,UI 据此禁用/标记,运行时只走支持的路——这是"优雅降级"唯一合法形态
4. 协议/剥壳错误(未注册工具名、帧损坏)→ 终态,绝不模糊。适用范围:SDK 家族(claude)——tool_use 回客户端,收到未注册工具名(mcp__ 前缀翻译回裸名后查表)直接拒绝;ACP 家族——tool_use 从不回客户端(R4),剥壳断言以 wire 级 tool_call/tool_call_update 通知为准(T5' 模式),权限请求自动拒 + 明确诊断;T5' 实证 bridge 报错后模型可能编造内容(幻觉防护 = M4 专题 A)
5. fallback 链(高级选项):用户配置显式声明(如 codex-cli → claude-cli → deepseek-official)+ 每次降级写进会话日志(UI 可见)——降级本身变成可重放的事实

7. 开发环境

- DSH 要求:Node 22.19+ / 24+,pnpm 11.7(corepack),pnpm install + pnpm run typecheck
- DSH 源码:D:\github\free_workspace\deepseek-harness\(tarball 解压,git clone 直连不稳)
- 插件安装:dsh plugin --profile web add ../dsh-cli-switch(profile 层栈启动时读)或 --patch 一次性覆盖
- 验证:dsh --profile web --dump-config 看插件行;模型选择器(ui-model-selection)里按 provider 分组出现
- Windows 下 opencodeBin 解析:npm 全局装的 opencode 是 .cmd shim,spawn 无 shell 直接 ENOENT → resolveOpencodeBin() 自动解析为真实 .exe(%APPDATA%\npm\node_modules\opencode-ai\bin\opencode.exe、%NPM_CONFIG_PREFIX%/%LOCALAPPDATA% 全局路径探测;src/opencode/bin-resolve.ts + 6 用例测试)
- 本机 claude 2.1.237 已装(-p/--mcp-config/--output-format stream-json 实锤可用;SDK 0.3.220 工具往返 R3 实测);opencode 1.18.18 已装(C:/Users/qwe13/AppData/Roaming/npm/node_modules/opencode-ai/bin/opencode.exe)

8. 风险与开放问题

- ToS:Claude Code 订阅条款对自动化调用的限制;DSH 官方把产品集成标"生产安装排除"——玩玩可以,商用前查条款
- DSH 兼容性破坏期:官方明确 "THERE WILL BE COMPATIBILITY-BREAKING CHANGES",插件要跟随
- 各家 CLI 版本漂移:codex 0.147.0、SDK 0.3.220 等 pin 版本要定期刷新
- ACP client 端:DSH 只有 ACP server(packages/acp),client 端要自己实现(协议公开,codex app-server / opencode acp 是参考实现)
- Windows 优先:本机 Windows,claude.exe 无 batch shim(官方已验证),子进程生命周期(process-tree 终止)要按 Windows 语义写

9. 参考

- DSH 架构:deepseek-harness/docs/architecture.md、docs/cookbook/adding-an-llm-adapter.md
- 官方产品集成(可借代码):packages/subagent/subagent-claude-code/、packages/subagent/subagent-codex/、Agent Note 2026-08-04-claude-code-and-codex-subagent-backends.md
- 先例插件:katsos/dsh-claude-cli(bridge.mjs 思路)
- 调研原始数据:D:\github\free_workspace\.cache\research\cli_backend\

上游仓库有新提交时邮件通知你(每天最多一封,无更新不打扰),随时一键退订。

💬 加入 DPharness 群聊

插件用法、部署报错、新插件第一时间同步——群里问,比一个人翻文档快。

点击加入 QQ 群
DPharness 群聊二维码,手机 QQ 扫码进群
扫码进群