Back to home@CARVIN94

dsh-router

No description

Stars
0
Language
TypeScript
Created
Aug 28, 2026
Updated
Aug 29, 2026
GitHub repo

Introduction

dsh-router

插件版的 9router —— 不是另开一个网关服务,而是直接作为 DSH 插件嵌进 DSH web(和「记忆系统」同侧边栏),在 http://localhost:3080/v1 上原生暴露 OpenAI 兼容端点,把请求路由到内部供应商。装好即用,不用多开一个 9router、 不用维护第二个端口、不用在网关和 DSH 之间搬配置。

为什么更优雅

  • 零额外进程:dsh-router 就是 DSH 插件,随 dsh web 启停,天然同源 (/router/api/* 无 CORS、面板内嵌侧边栏),不像 9router 要独立跑一个 Next.js 服务再对接;
  • 供应商即插即拔:内置供应商(如 opencode / openrouter / nvidia)随插件分发; 更多供应商 = 装一个 DSH 插件(dsh-router-*)经 cordis service router.suppliers 注册,面板自动出现、热加载/卸载;也可以放一个自定义 js 文件到 ~/.dsh/profiles/web/suppliers/ 就注册一个新供应商—— 无需改核心代码、无需重编译;
  • 模型不内置:供应商只实现差异化能力(列模型/调上游/登录),模型拉取与 缓存由核心统一管,不写死、不过时;
  • 通用能力只写一次:连接池回退、账号冷却、签到规则、凭证存储、模型管理都由 核心提供。连「测试模型」都是核心走真实对话路径跑的——供应商不重写这份逻辑, 就不会出现「只试第一个账号」这种分不清额度还是模型的测试;
  • 凭证 SQLite 单库:auths/credentials.sqlite,供应商凭证不透明 blob, 核心统一生命周期,干净可备份;
  • 复用 9router 思路:面板布局、组合 fallback、连接池/账号池、API key 管理都贴近 9router,但按 DSH「一切皆插件」的方式重组得更轻。

供应商开发与接入规范见 docs/suppliers.md (契约 / 加载顺序 / 模型统一策略 / 内置供应商参考实现)。

左侧边栏「记忆系统」上方有 路由系统 入口,点击打开中心栏面板:

  • 返回会话 — 左侧按钮,关闭面板回到聊天;
  • 供应商 — 供应商卡片(内置 / 插件分组),点击进入详情:
    • 链接池 — 账号列表(冷却/禁用/健康数/积分),支持删除;
    • 加链接 — 按供应商能力弹出不同流程:URL 登录(生成链接 → 浏览器登录 → 回调)、 API key 弹窗(填名字 + key)、轮询登录(登录后自动取凭证);
    • 签到 — 供应商实现了签到的才显示(如 codebuddy:每日 100 积分,连续第 7 天 1000)。签哪些账号由核心通用策略定(所有链接 / 仅首个);上游「今日已签到」按成功 处理(幂等),账号额度或凭证失效会单独标出;
    • 可用模型 — 模型列表,逐个启用/禁用 + 自定义模型(通用能力,持久化到 data/supplier-config.json,/v1/models 与 chat 只接受启用的模型);单个模型可 「测试」,走真实对话路径并按账号池依次回退,所以能分清是这个账号额度没了还是 该模型真的不支持;
  • 组合 — fallback 链(免费优先),可自定义。组合即模型:建好的组合会自动带出 为 DSH 模型目录里的 router provider 选项(设置 → 模型直接选组合名即可用),请求 按组合策略命中其中一个供应商模型;
  • 端点与密钥 — 端点核心(无隧道/Tailscale):
    • API 端点 URL(http://localhost:3080/v1,可复制);
    • 鉴权设置 requireApiKey 开关;
    • API Keys 管理:创建 / 启用切换 / 显示 / 复制 / 删除(持久化到 data/keys.json)。

API 端点(OpenAI 兼容,:3080/v1)

# 模型列表
curl http://localhost:3080/v1/models

# 对话(流式/非流式)
curl -X POST http://localhost:3080/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"glm-5.2","messages":[{"role":"user","content":"你好"}],"stream":false}'

任何支持 OpenAI 兼容 API 的工具(Claude Code、Cline、DSH 设置-模型 等)都可以把 baseURL 指向 http://localhost:3080/v1

鉴权:默认 requireApiKey=false,/v1/* 不要求鉴权(本地使用,与 9router 一致)。 在「端点与密钥」页开启「要求 API Key」后,请求必须带 Authorization: Bearer <库内启用的 Key 或 TW2A_API_KEY>

面板 API(/router/api/*,同源)

端点方法说明
/healthGET供应商列表(含来源/能力)
/statusGET全部账号(含供应商 id)
/modelsGET合并模型列表(已过滤禁用)
/combosGET组合 fallback 链
/keysGET/POST密钥列表(含完整 key)/ 创建 {name} → 返回明文一次
/keys/togglePOST{id, isActive}
/keys/deletePOST{id}
/settingsGET/PATCH{requireApiKey}
/suppliers/:id/loginPOST生成登录链接
/suppliers/:id/login/callbackPOST{callbackUrl} → 加账号
/suppliers/:id/modelsGET模型 + 启用状态
/suppliers/:id/models/togglePOST{id, enabled}

架构

浏览器(client 半)
  └─ 侧边栏「路由系统」+ 中心栏面板(RouterView, 含返回会话按钮)
       ├─ RouterView        tab: 供应商 / 组合 / 端点与密钥
       ├─ SupplierDetail    供应商详情:链接池 + 加链接 + 可用模型
       ├─ EndpointTab       端点 URL + requireApiKey + 密钥管理
       └─ fetch /router/api/*            (同源,无 CORS)
            └─ host 半(src/index.ts)
                 ├─ /v1/models + /v1/chat/completions   (OpenAI 兼容, KeysStore 鉴权)
                 ├─ KeysStore(src/keys.ts)              密钥库 + requireApiKey
                 └─ Router(路由器) → suppliers[]
                      ├─ OpenCodeSupplier(lib/suppliers/opencode.js) 无账号免费直连
                      ├─ OpenRouterSupplier(lib/suppliers/openrouter.js) API key 账号
                      └─ NvidiaSupplier(lib/suppliers/nvidia.js)       API key 账号
                      └─ 外部插件供应商(经 router.suppliers service 注册)
  • 供应商抽象:可插拔 js 模块只提供差异化能力(status/listModels/getAlias/chatCompletions
    • 可选登录/签到/测模型);通用能力(连接池排序/策略、模型启用/自定义、别名、凭证) 由核心统一管。
  • 供应商加载(三来源,见 docs/suppliers.md):
    1. 内置:lib/suppliers/*.js(随插件分发,如 opencode)
    2. 用户:~/.dsh/profiles/web/suppliers/*.js
    3. 外部插件:其他 DSH 插件通过 cordis service router.suppliers (值为 { [supplierId]: (env) => SupplierModule })暴露供应商, dsh-router ctx.inject(['router.suppliers']) 延迟加载。
  • 模型统一策略:插件不内置、不缓存模型;listModels 每次从上游拉取, 缓存由核心按 60s TTL 统一管(/suppliers/:id/models),/v1/models 保持实时。
  • 凭证存储:SQLite 单库 {authDir}/credentials.sqlite(表 credentials(supplier, uid, data), 凭证为供应商不透明 JSON blob)。
  • /v1/* 鉴权:由 KeysStore.requireApiKey 控制。关闭 → 不鉴权; 开启 → Bearer 必须是「库内启用的 key」或 TW2A_API_KEY env。

环境变量

变量默认说明
TW2A_AUTH_DIR<dataDir>/auths凭证目录(dsh-router 核心统一管,credentials.sqlite SQLite 库)
TW2A_STATE_FILEdata/state.json状态持久化(同目录放 keys.json / combos.json / supplier-config.json)
TW2A_API_KEY/v1 Bearer 鉴权(与库内 key 等效)

构建

pnpm install
pnpm build        # lib/index.js(host) + lib/client.js / lib/client-registry.js(browser)
pnpm typecheck

安装(DSH)

用 dsh CLI 装到 profile(web 是 DSH 插件宿主):

dsh plugin --profile web add dsh-router-core

该命令会在 profile 里 pnpm add,并自动把 dsh-router-core 加入 dsh.profile.bundles(包声明了 dsh.bundle.patch,即 cordis.patch.yml)。

然后重启 dsh web。侧边栏出现「路由系统」入口,打开即面板。

更多供应商:DSH 插件形态的供应商各自发 npm 包,同样 dsh plugin --profile web add <包名> 即可;供应商接入与开发见 docs/suppliers.md

本地开发版:不用 npm,直接 dependencies"dsh-router-core": "link:/path/to/dsh-router" 指向本地仓库。

前提

  • 凭证由 dsh-router 核心统一管(SQLite 库 <dataDir>/auths/credentials.sqlite);
  • 供应商接入与开发见 docs/suppliers.md;
  • 重启 DSH 后 /v1/* 即生效;面板管理账号、模型与密钥。

致谢

感谢以下项目给的灵感: