# dsh-model-list

dsh-model-list logo

**dsh-model-list** 是一个 [DeepSeek Harness(DSH)](https://github.com/deepseek-ai/deepseek-harness)插件: 浏览 OpenRouter 的**官方模型目录**(openrouter.ai/models 同款数据),一键把其中任何模型—— 包括免费变体——添加到 DSH 模型列表。 - **模型聚合平台入口(设置 → 模型聚合平台)**:设置入口内按平台分 tab(当前内置 OpenRouter; `PLATFORMS` 注册表让后续平台接入只需一行)。拉取逻辑在宿主侧完成,tab 是主要入口。 - **「在菜单中显示」滑动开关**:位于平台页最顶部。开启后,宿主侧栏底部 footerAction 区出现 【模型聚合平台】入口按钮,点击在弹层中打开同样的目录。偏好存 `localStorage`(默认关闭)。 - **弹框配色**:弹层与弹框与 dsh-get-balance 同一套 —— `rgba(0,0,0,.32)` 蒙版 + `blur(12px) saturate(1.2)`、 `color-mix(bg-layer-1 78%)` 玻璃面板 + `border-l2` 细描边 + 14px 圆角 + `0 16px 48px rgba(0,0,0,.28)` 投影、 `border-l1` 分隔线;主按钮与选中的 tab / 二级 tab / 筛选 chip 用实心 `button-primary-fill`, 输入框 `bg-base`、卡片 `bg-layer-2`、状态色走 `state-*` 令牌。 - **官方目录 1:1 还原**:宿主拉取官方前端端点 `https://openrouter.ai/api/frontend/v1/models/find?active=true&fmt=cards&order=newest`—— 分别以 `variant=standard` 与 `variant=free` 各取一次(两者都是一模型一卡、无分页), 并从响应信封的 `data.analytics` 联表出每个模型的**周调用量**(近 7 天 token 数)。 浏览器侧按官方筛选集做本地筛选:搜索、变体下拉、输出模态 tab(带计数)、排序 (周调用量 / 发布时间 / 上下文大小 / 输入价格 / 输出价格 / 模型名称),以及筛选组 (输入模态、有折扣、上下文长度档位、提示词价格档位、输出价格档位、系列、支持参数、 供应商、厂商)。卡片按官方版式:作者头像、名称、周用量、折扣徽标、描述两行截断、 底部规格行(by 厂商 / 上架日期 / 上下文 / $x/M input tokens / $x/M output tokens)。 - **卡片展示完整信息**:id(点击复制)、供应商、量化精度、上架时间、支持的请求参数、 内容审核 / 强制推理标记、上游警告文案、可展开的完整描述。排版适配窄面板与移动端。 - **「添加到模型列表」→ 一键创建模型配置**:仅当该平台**还没有**已存凭据时才要求输入 OpenRouter API Token(可现场「校验」,调 `GET /api/v1/key`)。确定后: 1. Token 经宿主 **credentials** 服务落 `.credentials.yaml`(`OPENROUTER_API_KEY`); 2. **把模型并入 `settings.yaml` 里的 OpenRouter 路由**(`llm-pi-ai` 命名空间——已有 `openrouter` / `openrouter-free` / baseURL 指向 openrouter.ai 的路由时并入,原样保留其 `apiKeyEnv` 与已有模型条目;否则新建 `openrouter-free` 路由,`openai-completions` 协议 + `https://openrouter.ai/api/v1`); 3. 可选勾选**设为新会话的默认模型**(`agent-default-model` 命名空间)。 完成后模型立即出现在对话框底部的模型选择器中,**无需重启宿主**。 - **稳健拉取**:所有 OpenRouter 请求失败自动重试,最多 **10 次**(指数退避 + 抖动); 刷新失败时降级展示旧缓存并提示。结果缓存到 `$DSH_HOME/dsh-model-list.json`(6 小时新鲜窗口)。 - 中英双语界面,无硬编码路径。 [English](README.md) ## 命令 | 脚本 | 作用 | | --- | --- | | `npm run check` | `tsc -b` 类型检查(host + client 两个 program) | | `npm run build` | 清理 `lib/`,`tsc -b`,tsdown 打包 `lib/index.js`(宿主,ESM)+ `lib/client.js`(浏览器,`__ModuleLoader__` 工厂) | | `npm run watch` | tsdown watch 模式 | | `npm run verify` | 模拟宿主模块加载器校验 `lib/client.js`(工厂形状 + seed 外部依赖) | | `npm run test:ops` | 宿主逻辑离线单测(find/cards 规范化、周用量联表、路由识别、配置写入编排、数据文件往返含 v1 旧缓存拒绝) | | `npm run smoke` | 直连真实 OpenRouter `models/find` 的冒烟测试 | | `npm run release` | `check` + `build` + `verify`,然后 `npm version patch` 并 `git push --follow-tags` | | `npm run publish:npm` | `check` + `build` + `verify`,然后 `npm publish --access public` | ## 安装 ```sh # 本地开发(link 工作区;lib/ 重新构建后自动生效) dsh plugin --profile web add ./dsh-model-list # 已发布:npm / tarball / GitHub dsh plugin --profile web add dsh-model-list dsh plugin --profile web add github:you/dsh-model-list# dsh --profile web --dump-config # 确认分层 dsh --profile web # 启动(宿主半边需要重启后加载) ``` > **本地开发依赖**:宿主经原生 Node ESM 加载 `lib/index.js`,因此插件目录必须能解析 > `@deepseek-ai/schemastery` 与 `@deepseek-ai/dsh-settings`。在插件目录里执行一次 > `pnpm install`,或直接使用仓库内已提交的 `lib/` 构建产物(本仓库提交 `lib/`,与 > dsh-jenkins 一致)。 ## 使用 1. 打开 **设置 → 模型聚合平台**(或在页面顶部开启**在菜单中显示**,使用宿主侧栏底部的 【模型聚合平台】按钮),选择平台 tab(OpenRouter)。 2. 目录在缓存新鲜时秒出(点**刷新**强制回源)。使用搜索框、变体下拉、输出模态 tab、 排序下拉(周调用量 / 发布时间 / 上下文大小 / 输入价格 / 输出价格 / 模型名称), 以及筛选组(输入模态、有折扣、上下文长度、提示词价格、输出价格、系列、支持参数、 供应商、厂商)——与官方 models 页一致的筛选集。 3. 点某张卡片的**添加到模型列表**: - 仅当平台还没有已存凭据时才出现 Token 输入框(已配置时直接沿用 `OPENROUTER_API_KEY`,弹框中有提示), - 需要的话勾选**设为新会话的默认模型**, - 确定——配置写入完成,弹框会显示该去哪里选这个模型。 ## 新增一个平台 1. `src/client/components/FreeModelsSection.tsx`:在 `PLATFORMS` 追加 `{ id, labelKey }`, 并在下方按 id 挂载面板组件。 2. 复制 `src/client/components/OpenRouterPanel.tsx` 作为起点;ops 请求带上 `platform: ''`。 3. `src/host/ops.ts`:扩展 `runOp` 的平台分发(按平台 API 实现拉取/过滤,映射到 `RouteModelProfile`,并在 `model-config.ts` 里补该平台的凭据引用与路由命名)。 ## 结构 ``` ├── src/host/*.ts # 宿主半边:/dsh-model-list/api 路由(按平台分发 op)、 │ # OpenRouter find/cards 拉取 + analytics 周用量联表(10 次重试)、 │ # 缓存存储、模型配置写入(settings + credentials 服务) ├── src/client/*.tsx # 浏览器半边:settings.section 平台 tab 外壳(含「在菜单中显示」 │ # 开关)、sidebar.footer.action 入口按钮、官方筛选/排序/卡片、 │ # 添加到模型列表弹框 ├── lib/index.js # 宿主半边构建产物(tsdown,ESM),已提交 ├── lib/client.js # 浏览器半边构建产物(tsdown → __ModuleLoader__ 工厂),已提交 ├── scripts/ # verify-client.mjs、test-ops.mts(离线单测)、smoke-openrouter.mts(真实冒烟) ├── tsdown.config.ts # node 半边 + client bundle 包装 ├── cordis.patch.yml # 组合包 patch:按包名引用的插件行 └── package.json # dsh.bundle + dsh.client(web) 清单 + peerDependencies ``` ## 说明 - OpenRouter 免费模型同样需要账号 Token(有每日限额),弹框里有提示。 - 写入走的是官方通道:`settings.update('llm-pi-ai', …)` 会经过 llm-pi-ai 的 schema 校验(不合法的配置在写入处被拒绝),适配器实时重注册路由——添加模型 后**无需重启宿主**。 - 对已有 OpenRouter 路由的手工修改(自定义 `apiKeyEnv`、额外字段)会被保留: 添加模型只重写该路由的 `models` 数组(按 id 合并,同 id 覆盖)。 - 插件从不直接改写 settings / credentials 文件——一切经宿主服务完成;插件自己写的 唯一文件是模型缓存(`dsh-model-list.json`)。