dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
DSH (DeepSeek Harness) 的 CC Switch 桥接插件:把 farion1231/cc-switch 里管理的账号直接变成 DSH 的模型,每次操作都重新读取 ~/.cc-switch/cc-switch.db,与 CC Switch 保持同步,无需重启 DSH。
| CC Switch | DSH |
|---|---|
| harness(claude / claude-desktop / codex / opencode / grokbuild …) | provider 路由(如 cc-switch/claude → 选择器里显示 "Claude Code") |
| 账号(MiniMax、DeepSeek、Xiaomi MiMo …) | 该类别下的模型(显示为 MiniMax · claude-opus-4 [200k] · anthropic) |
anthropic-messages / openai-completions / openai-responses,对应 claude* / openai 类路由。内部复用 DSH 自带的 @deepseek-ai/dsh-llm-pi-ai,流式、工具调用、重试语义与官方适配器一致。tp- 开头的 token-plan 密钥自动改用 Authorization: Bearer 发送(默认前缀可配置)。baseURL 在缓存时通过 scheme + host allowlist 校验,不在白名单的行直接跳过——这是对 cc-switch.db 内一行恶意 env 的最后一道护盾。CC Switch app_type | 支持情况 | 备注 |
|---|---|---|
claude | ✅ | 通过 ANTHROPIC_AUTH_TOKEN / ANTHROPIC_BASE_URL 解析,模型来自 ANTHROPIC_DEFAULT_*_MODEL / ANTHROPIC_MODEL / CLAUDE_CODE_SUBAGENT_MODEL |
claude-desktop | ✅ | 同上 |
codex | ✅ | 通过 auth.json + model_providers TOML + modelCatalog,支持 wire_api = chat | responses |
opencode | ✅ | 根据 npm 自动选 anthropic-messages 还是 openai-completions;AI-SDK 风格 options.baseURL |
grokbuild | ✅ | TOML [model.<profile>] + models.default;api_backend = chat | responses |
gemini · openclaw · hermes · pi | ⏸ | 是 CC Switch 的合法 app_type,但 settings_config JSON shape 在上游 farion1231/cc-switch 尚未稳定到可直接写一个准确 parser 的程度;列在源码注释里,等上游 schema 锁定再加 parser |
~/.cc-switch/,即 cc-switch.db + settings.json)@deepseek-ai/dsh-llm-pi-ai 与 @earendil-works/pi-ai)| 本插件 | 已验证的 DSH | 已验证的 @earendil-works/pi-ai |
|---|---|---|
| 0.3.1 | @deepseek-ai/dsh-llm / dsh-llm-pi-ai 0.2.0-rc.2(DSH Desktop 0.2.0-rc.2,Node 24.18.1 / Electron 44) | 0.87.1 |
| 0.3.0 | 0.1.5-rc.3 | 0.85.1 |
@deepseek-ai/dsh-llm / dsh-llm-pi-ai 的 peer range 是 >=0.1.5-rc.3 <0.3.0-0,@earendil-works/pi-ai 是 >=0.85.1 <0.88.0-0。适配器直接依赖 PiAiAdapter 合成的 profile 形状,所以 peer range 只覆盖实际验证过的版本线;DSH 升级后如果 profile 形状再变,test/adapter/profile-shape.vitest.test.js 会先失败而不是在选择器里失败。
在目标 profile 的 package.json 中加入本地依赖并把它追加到 dsh.profile.bundles,然后:
dsh plugin --profile <name> install
启动 DSH 后在选择器里可以看到新类别;CC Switch 里增删改账号无需重启即可在下次选择/请求时生效。
cordis.patch.yml 中该插件的 config)- insert:
- id: cc-switch
name: 'dsh-llm-cc-switch'
config:
# source: cc-switch # 改写时使用的账号源,详见 lib/source/index.js
# dbPath: 'C:/.../cc-switch.db' # 默认 ~/.cc-switch/cc-switch.db
appTypes: [claude, claude-desktop, codex, opencode, grokbuild]
contextWindow: 200000 # 未声明上下文的模型的兜底窗口
maxTokens: 64000 # 请求输出上限
streamIdleTimeoutMs: 300000
bearerAuthPrefixes: [tp-] # 这些前缀开头的密钥走 Authorization: Bearer
urlAllowlist: # baseURL 护栏——下面的任一项都可在 config 里覆盖
schemes: ["http:", "https:", "ws:", "wss:"] # Scheme allowlist(注意结尾冒号,与 URL.protocol 一致);其他 scheme 全部拒绝
# hosts: [api.openai.com, anthropic.com] # 仅允许这些 host 后缀(可选)
opencodeSession: # OpenCode Go / Zen 的按会话 session 头
enabled: true # false = 完全不发(退回旧行为)
header: x-opencode-session # OpenCode 也认 session_id / x-session-id
# hosts: [opencode.ai] # 默认 opencode.ai 及其子域
urlAllowlist.schemes/hosts 是闭集 allowlist,不是配置 = 拒绝。这给了"你从 CC Switch 数据库加载,但实际不允许模型路由到某 host"的能力。
自 2026-09-06 起 opencode.ai 会拒绝没有稳定会话 id 的请求(400 MissingSessionID),且只认 x-opencode-session / session_id / x-session-id 这三个名字——pi-ai 自带的 session-affinity 在 Anthropic 协议上发的是 x-session-affinity,OpenCode 不认;DSH 目前在 pi-ai 适配器路径上也不发任何会话头(上游见 discussion #5495)。所以凡 baseURL host 命中 opencodeSession.hosts 的账号,本插件都会补上这个头:值取 DSH 的会话 id(每个会话稳定,OpenCode 才能据此做路由与提示词缓存),调用方没带会话 id 时(模型探测、手工单次调用)用本进程内稳定的兜底值——同样不会再 400。enabled: false 可以整体关掉。
import { CcSwitchStore } from "dsh-llm-cc-switch/source/internal";
const store = new CcSwitchStore({ dbPath: "/custom/cc-switch.db" });
await store.accounts(); // 解析后的账号列表,按 (appType, current, name) 排序
store.stamp; // mtime:size;为空表示还没读过
store.lastError; // 最近一次读库的 error
await store.routes(); // 当前能挂的 harness 路由
await store.account("<uuid>"); // 按 id 取一个(同一 id 可能出现在多个 app_type,取排序后的第一个)
await store.accountOf("claude-desktop", "<uuid>"); // 按 (app_type, id) 取,唯一
账号身份是 (appType, accountId) 这一对,而不是裸 id:CC Switch 会把同一个
provider 行同时镜像到 claude 和 claude-desktop 两个 app_type(UUID
完全相同,providers 表主键是 (id, app_type))。所以内部路由是
cc-switch-account/<appType>:<accountId>,Account 上也带 accountKey;选择器里
展示的 model id 仍是 <account-uuid>::<wire model id>,老会话/默认模型配置照常
解析。
新增一种账号源只要写一个 AccountStore 子类,然后调用:
import { registerAccountSource } from "dsh-llm-cc-switch/source/internal";
registerAccountSource("my-source", (config) => new MyStore(config));
并在 cordis.patch.yml 的 config.source 里写 "my-source" 即可,不改这个插件的代码。
settings_config 直读到请求,无中间缓存层。readOnly: true 打开 cc-switch.db——这个插件从不修改 CC Switch 的状态。logger.warn 打印的 row info 走 redactAccountShape + redactUrl,只出现 sk-t…abcd 这种前缀/后缀保留形式;Authorization / x-api-key / Bearer … 在日志头里一律替换为 ***redacted***。urlAllowlist,每行数据从 store 加载前就被过滤,不在白名单直接跳过。weichen.work@qq.com(见 SECURITY.md)。npm install # 会装上 devDependencies 里的 harness 包,adapter 测试才能跑
npm test # vitest run:parser / allowlist / redact / 注册表 / adapter seam
npm run test:integration # node --experimental-sqlite,跑真实 SQLite fixture
npm run test:all # 两套都跑
npm run test:watch # vitest --watch
devDependencies 固定了本插件验证过的 @deepseek-ai/dsh-llm / dsh-llm-pi-ai / @earendil-works/pi-ai 版本,test/adapter/ 下的两个 seam 测试直接 import 它们;只装 vitest 的话这两个文件会因为解析不到 peer 而整文件失败(其余 8 个文件仍然通过)。
test:integration 走的是 node --test 而不是 vitest——vite-node 的 pre-bundle 在 Windows 上不能解析 node:sqlite 内建,所以单独拆开;两套测试都通过才算过 CI。
api_key 直接出现;env_key(环境变量名)等价形式需要等 CC Switch 上游 grokBuildConfig.ts 提供解析后产物。gemini / openclaw / hermes / pi 这四种 cc-switch 已经识别的 app_type,暂不在默认 appTypes 里——它们的 settings_config JSON 形态未稳定到能负责任地写 parser。