Chuyển đến nội dung chính

dsh-model-hub

Đã xác minh

@fhxgs/dsh-model-hub · v0.2.3 · MIT · Giao diện web

DeepSeek Harness plugin: provider sign-in, model catalog, and selection routing over a loopback-only /model-hub channel

Cài đặt

dsh plugin add @fhxgs/dsh-model-hub

Xác nhận layer đã áp bằng dsh --profile default --dump-config — xem hướng dẫn cài plugin.

Mã nguồn

Thẻ

Tác giả

Readme

@fhxgs/dsh-model-hub

为 DeepSeek Harness 提供统一的模型提供方登录、目录管理与会话路由能力。

English | 简体中文

npm version license MIT node pnpm TypeScript DSH plugin


@fhxgs/dsh-model-hub 是 DeepSeek Harness (DSH) 的一体化模型管理插件。它在单个 npm 包中集成了 Node.js Host 服务端与按需加载的浏览器 Client 端。凡是本插件自己承担的业务——提供方生命周期、登录会话、模型目录、会话选择——都走它自有的、仅限本机访问的 /model-hub Loopback RPC 通道;凡是 Harness 本就承担的能力则继续走官方 /api:settings 的读写、凭据的描述与写入、端点模型发现,以及全局默认模型背后的宿主模型列表。插件是接入 Harness,而不是绕开它自己另开一套通道。

安装本插件后,它将完整接管原本分散的模型配置体验:提供统一的设置页面、输入框模型选择气泡以及 /model 快捷指令。

核心特性

功能模块 说明
提供方登录 (Sign-in) 在浏览器中直接驱动 OAuth / 设备码授权流程。支持实时状态轮询、交互问答面板以及 30 分钟 Host 硬超时。凭据直达底层安全存储,绝不进入快照、日志或监控指标。
生命周期管理 提供明确的写操作入口(activate 激活、deactivate 停用、logout 登出、useRecord 改用凭据),每次操作均附带明确的影响提示与二次确认,杜绝黑盒覆盖。
提供方参数编辑 支持安全写入 API Key(通过 credentials.set,绝不混入 settings 配置)、自定义 Base URL、通信协议、显示名称及模型映射,支持端点模型自动发现与自定义 Provider 声明。
增强型模型目录 原生 llm.models 的严格超集,补齐了原生裁剪掉的 inputModalities(输入模态)、contextWindow(上下文窗口)与 defaultMaxTokens。具备版本感知缓存、8 并发请求限流与单模型故障隔离。
模型策展策略 (Curation) 支持全量展示 (all) 或白名单筛选 (include)。设置页勾选与输入框选择器实时联动,确保两者看到的数据完全一致。
会话选择与路由 内存级会话模型绑定,配合 { prepend: true } 确保路由准确注入。子代理支持 3 级路由策略(显式配置 > 静态默认 > 继承父会话)。前端主动拦截不可路由的消息发送。
思考强度调节 (Effort) 离散滑块档位根据当前模型实际支持的 reasoning.efforts 动态派生(如 gpt-5.6-sol 显示 xhigh/max,非推理模型自动隐藏)。
极速模式 (Fast Mode) 元数据驱动的开关,仅对声明了加速服务梯队(如 service_tier: priority)的模型展示。
内置自建提供方 自带独立适配器与 OAuth 实现,内置支持 qwen-code(千问代码:基于 chat.qwen.ai 的 RFC 8628 设备码授权)与 codex(OpenAI Codex:授权码 + PKCE 回调,支持 7 款 GPT-5.x 模型)。
原生双语界面 深度集成 Harness 语言服务,界面完整支持简体中文与英文,随系统语言自动切换。
安全隔离 所有 /model-hub 接口均强制限制为 authority: 'loopback'(仅限本机),采用严格的 Zod Schema 校验,对外输出脱敏的统一错误结构。

安装说明

[!NOTE] dsh plugin 底层通过 pnpm 进行包管理 (spawnSync('pnpm')),请确保当前环境变量 PATH 中包含 pnpm。若未全局安装,可通过 corepack 启用:

corepack enable pnpm

将插件添加至目标 Profile:

dsh plugin --profile web add @fhxgs/dsh-model-hub

安装后请重启该 Profile(组合变更不支持热重载)。

插件会自动应用自带的补丁配置 (cordis.patch.yml),启用 @deepseek-ai/dsh-authorization 并自动禁用官方默认的 ui-settings-modelsui-model-selection 组件,无需手动修改配置

卸载插件:

dsh plugin --profile web remove @fhxgs/dsh-model-hub

卸载后会自动恢复官方原生的 Models 设置页与选择器。

关于 Peer 依赖告警

dsh plugin add 底层调用 pnpm,而 pnpm 只按 Profile 目录本身解析 peer 依赖。Harness 并不会把自己的包装进那个目录:Host 侧的 @deepseek-ai/* 导入与浏览器侧的平台模块,都由运行中的宿主在运行期通过模块表提供。因此这 18 个 peer 已声明为 optional——它们的版本范围表达的是「本次构建针对哪一版 Harness 编写」,而不是「请包管理器去下载」——冷装隔离 Profile 时不再报告它们缺失。

保留为必需的只有两个,因为它们是真的可能缺失、告警值得保留信号的两个:

Issues with peer dependencies found
✕ missing peer @earendil-works/pi-ai
✕ missing peer react

冷装时出现这段告警属于预期,插件照常加载——@earendil-works/pi-ai 由 Host 运行闭包提供,react 由 Web 宿主在任何插件工厂运行之前就写入冻结模块表。若告警里出现的是别的包名,那才值得深究。

快速上手

  1. 运行 DSH 的本机浏览器 中打开 Web 界面(所有操作均需 Loopback 权限)。
  2. 进入 设置 → Model Hub → 提供方。选择目标提供方并完成授权登录。
  3. 在卡片中点击 激活 启用该路由。
  4. 切换至 目录 选项卡,勾选希望在对话框中使用的模型。
  5. 在任意会话中,点击输入框下方的模型选择气泡或输入 /model 即可切换模型与思考强度。

配置项

插件在 settings 的 model-hub 命名空间下管理配置:

model-hub:
  picker:
    mode: include                 # 'all' | 'include' (默认展示所有模型)
    include:                      # 当 mode 为 'include' 时生效
      - { provider: kimi-coding, model: k3 }
    fastMode:                     # 已开启加速梯队的路由
      - { provider: codex, model: gpt-5.6-sol }
    preferredEffort: high         # 可选:默认思考强度偏好
  subagent: inherit               # 'inherit' | { provider, model, reasoningEffort? }
  • 空配置解析:缺省时解析为 { picker: { include: [], fastMode: [] }, subagent: 'inherit' }
  • 全局默认模型:仍保存在官方 agent-default-model 命名空间中。Host 端仅读取该配置,仅当用户在前端破坏性确认框中明确勾选时才会调用官方 CAS 接口进行修改。
  • 内置 Provider 状态:独立保存在 model-hub-providers 命名空间,避免策展策略变更时误触发模型目录缓存失效。

界面预览

截自 DSH 0.1.1-rc.2 冷装隔离 profile,暗色主题。

Composer 选择气泡(Simple) Providers 面板 Catalog 面板
Composer 选择气泡与推理力度滑块 Model Hub 设置页 Providers 面板 Model Hub 设置页 Catalog 面板

架构概览

插件采用单包同构设计:

  • Host 端 (Node.js ESM):注册 /model-hub RPC 路由、Settings 命名空间、内置 Provider 适配器以及模型拦截分发钩子。
  • Client 端 (按需 CJS):在浏览器中动态挂载,提供设置页面、输入框选择组件与 /model 弹窗。
src/
├── index.ts              # Host 插件入口:注册 RPC 通道、命名空间与装配生命周期
├── rpc/                  # 单层 Wire 信封定义、路由器与 7 个标准错误码
├── auth/                 # Authorization 交互桥接与 auth.state 两轴状态投影
├── provider/             # 适配器绑定与四大生命周期动作 (activate/deactivate/logout/useRecord)
├── provider/native/      # 自建 Provider 域 (qwen-code 与 codex 的 OAuth 流程及适配器)
├── catalog/              # 增强型模型目录、策展过滤逻辑与 LRU 缓存
├── selection/            # 会话模型选择、思考强度决策链与子代理 3 级路由
├── settings/             # Settings Schema 定义
└── client/               # 浏览器 UI 源码 (设置卡片、输入框气泡、交互面板等)

打包产物位于 lib/index.jslib/invariant.jslib/client.jslib/types/** 以及 cordis.patch.yml

[!IMPORTANT] ./client 导出仅供 Loader 使用。 lib/client.js 并不是任何一方去 import 的模块,它的整个函数体就是一次 window.__ModuleLoader__.load({ ... }) 调用,由宿主把该文件读出来直接投给浏览器,而不是去解析它——无论文件后缀是什么,Node 的 import() 与打包器的 require() 都会抛错。声明这个导出只是为了让宿主能按名字定位该文件,它不是对外 API,DSH 浏览器运行时之外没有任何一方能消费它。这也正是 publint 对本包只报一条发现(建议把该文件改成 .cjs)、而这条被明确记录并驳回而非静音的原因:改名只会让告警消失,并不会让这个导出变得可用。完整理由写在 scripts/verify-manifest.mjs 里,豁免按报告原文逐字匹配,因此换成另一条发现照样会让门禁变红。

开发与验证

corepack pnpm install
corepack pnpm run build      # 编译 lib/types (tsc) 与运行时产物 (tsdown)
corepack pnpm run verify     # 五道门:产物纯度、Patch 键名、SourceMap 链接、发布路径、manifest
corepack pnpm run test       # 运行 Vitest 单元测试
corepack pnpm run smoke:p0   # 将 Tarball 冷装至隔离临时 Profile 进行冒烟验证
  • prepack 会在 npm packnpm publish 前执行 verify 门禁,拦截脏产物或路径泄漏;prepublishOnly 还会先清空 lib/ 重新构建并跑全量测试,因此一次发布不可能带上陈旧产物。
  • 冒烟测试 (smoke:p0) 使用一次性临时目录 (DSH_HOME=$(mktemp -d)),绝对不会影响本地的 ~/.dsh 配置。
  • 关键功能与阻断项审计清单请参见 scripts/gate-p1.zh.mdscripts/gate-p3.zh.md
  • 标准发布流程请参见 scripts/release-checklist.zh.md

上述三份文档位于 scripts/,该目录刻意不进 npm 发布包,因此这里一律使用绝对链接:无论从 npm 页面还是从代码检出,都能正常打开。

兼容性

依赖项 支持范围
Node.js ^22.19 || >=24
DeepSeek Harness 0.1.1-rc.2
@deepseek-ai/* 依赖 ^0.1.1-rc.2
@deepseek-ai/cordis ^4.0.1
@earendil-works/pi-ai ~0.82.1(Peer 依赖,由 Host 运行闭包提供)
React ^18.2.0(Peer 依赖)

已知限制

  1. 仅限本机访问 (Loopback Only):非本机请求访问 /model-hub 接口均返回 403。远程访问时界面展示只读标识,如需修改模型配置请在运行 DSH 的机器上操作。
  2. 官方模型设置接管:插件生效期间,官方原生 Models 页面与相关向导步骤会被自动禁用。
  3. 无后台主动长连接推送:界面刷新依赖 Host 事件转发以及登录面板活跃期的定时轮询。
  4. 零消息会话选择不持久化:未发送过任何消息的新建会话,其临时选中的模型在服务重启后不会保留(与官方 /model 行为一致)。
  5. 前端输入拦截边界:前端输入框的未配置路由拦截仅作用于 Web 界面;Headless 或 SDK 发起的请求由 Host 服务端直接校验并拦截。
  6. Codex 计费显示为零:由于 OpenAI Codex 属于套餐订阅制,与普通 API Token 计费模式不同,因此用量计费显示为 0。
  7. OAuth 回调端口固定:内置 Codex 路由只能在其客户端注册的那一个端口上接收浏览器回调(http://localhost:1455/auth/callback),因此同一台机器同时只能进行一次该路由的登录;端口被占用时登录会立即以 LOOPBACK_PORT_IN_USE 结束而不是干等,且该路由没有设备码回退方式。回调接收器会同时绑定两个回环地址;若 ::1 无法绑定,登录继续在 127.0.0.1 上完成,并记录 LOOPBACK_IPV6_UNAVAILABLE
  8. Grant 可指定 API Host:OAuth 授权可以带回该账号被路由到的 API Host,但只有其颁发方确实会下发该字段的路由才会采纳(Qwen Code 会,Codex 不会——该路由上存储的 Host 一律忽略)。被采纳的 Host 必须是 HTTPS,且不得带用户名密码、查询串或片段,也不得是另一个内置路由自己的 Host;不满足时该字段被丢弃,路由回落到厂商默认地址。明文 HTTP 一律拒绝,回环地址也不例外。
  9. 会话选择与消息准入分属两个 Owner:在本插件里选模型,和 Core 判定是否接收一条消息,是彼此独立的两套状态。Core 对每条消息都按自己的链(picked ▸ 请求头 ▸ 全局默认)判定,而本插件刻意从不写这条链。由此有两个后果:切换到支持图片的模型,并不能让已经放进输入框的那张图片发出去——Core 仍按自己链上的模型判定,拒绝信息里写的也是那个模型,而不是刚选中的;以及当全局默认指向没有任何适配器提供的 Provider 时,对尚未成功发出过请求的会话,Core 会拒绝所有消息(文本同样如此,不只是图片),无论这里选了什么,需要先改选或清除全局默认才能解除。
  10. 会话选择的持久化边界:上文零消息会话选择不持久化的成因。会话的选择存放在内存引用里,真正的持久记录是 request/header 事件,只有成功发出一次请求才会写下。在那次请求之前,任何读取该持久记录的一方都看不到这次选择——包括 Core 自己的准入链——服务重启后该会话回落到全局默认。
  11. 被 Core 自身模型切换接管过的会话:即使官方 Models 页面被禁用,Core 自己的 session.selectModel 仍可被 ACP 与 SDK 客户端调用;它写入的那一层在 Core 链中优先级高于请求头,且在该会话的 Agent 存活期内没有任何清除路径。被这样切换过的会话,此后一律按 Core 的选择做准入,在本插件里的选择不再与之收敛,无论该会话之后又成功发出过多少次请求。

开源协议

MIT © 2026 FHGS