Skip to content
dsh.fish
Bundle

@swyc520/dsh-plugin-model-whitelist

DeepSeek Harness plugin: whitelist which models appear in the composer model selector. 在 DSH 的模型选择器里按模型勾选显示/隐藏,未勾选的模型不再出现在列表中。

Source
swyc520
stars
1 stars
License
MIT
Updated
Updated 3 days ago

Readme

# dsh-plugin-model-whitelist

**简体中文** | [English](README.en.md)

给 DeepSeek Harness(dsh)的模型选择器加一道**白名单**:在「设置 → 模型」里逐个勾选,
没勾的模型就从输入框的模型菜单里消失。

- **默认不隐藏任何模型** —— 空白名单的行为与官方完全一致,装上去看不出区别。
- **随时可恢复** —— 隐藏的模型仍列在设置页里,勾回来即可。
- **不遮蔽官方界面** —— 模型菜单、推理等级、键盘操作、失败提示全部保持官方原样。

---

## 它解决什么问题

模型多了以后,选择器菜单会变得很长:多个 provider、几十个模型、还有地区受限或常年不可用的
条目夹杂其中。每次切换模型都要在一长串列表里翻找。

这个插件让你把不用的模型收起来,只留下常用的那几个。

它**不是**用来禁用或卸载模型的 —— 隐藏只影响选择器菜单的显示,模型本身、provider 配置和
调用链路都不受影响。

## 安装

### 命令行(纯 `dsh web`)

```bash
dsh plugin --profile web add @swyc520/dsh-plugin-model-whitelist
dsh --profile web --dump-config   # 确认插件行已进入配置树
```

安装后重启一次 `dsh web`。

### 桌面端 / DSHEAC AIO

桌面端的 profile 由 Electron 应用独占,命令行 `dsh plugin add` 无法写入。请在应用内的
插件管理器里安装;若手工安装,注意桌面端的 profile 闸门**不允许 `link:` 依赖或目录 junction**,
必须是真实目录。

## 使用

1. 打开 **设置 → 模型**。
2. 滚到页面底部,找到「**模型显示**」面板。
3. 取消勾选不想看到的模型。

面板顶部显示 `显示 N/M`,每个 provider 一张卡片。五个按钮:
**全部显示**、**全部隐藏**、**全部展开**、**全部折叠**、**重新读取**。

### 折叠分组

点 provider 卡片的**标题行**即可折叠 / 展开该组:箭头会转向,标题右侧显示该组的模型数
(有隐藏时改为显示已隐藏数量)。折叠状态会被记住,刷新后保持。

折叠纯粹是**阅读便利**:它不会隐藏任何模型,选择器菜单完全不受影响。provider 多的时候
用「**全部折叠**」一次收起来。

改完立刻生效,无需刷新。

> 如果面板里显示「还没有读到模型清单」,打开一次模型选择器即可 —— 模型目录由官方
> 模型选择插件按需加载,本插件读取的是同一份数据。

## 工作原理

这一节说明插件为什么这样实现,以及它在什么情况下会失效。

### 白名单存在浏览器本地存储里

键名 `dsh-model-whitelist/v1`,按「浏览器 + Web 地址」隔离。它**不写入** `settings.yaml`、
不改动任何 provider 配置,也不上传。清除浏览器数据会一并清掉白名单。

存两类状态:`hidden`(不进选择器的模型)与 `collapsed`(面板里折叠的 provider 卡片)。
两者刻意分开 —— 折叠是阅读便利,绝不能和「隐藏模型」混为一谈。

选择「隐藏集」而不是「显示集」来持久化:新模型(新版本、新接入的 provider)默认可见。
若持久化「显示集」,每个新模型都会被静默隐藏,那读起来像 bug。

### 过滤是「投影」,不是「遮蔽」

dsh 为每个 UI 扩展位(slot)都标注了 `replaceRisk`。官方的模型选择器位于
`conversation.input.model`,标注为 **`shadows-shipped-ui`** —— 注册到那里会**替换**官方控件,
连带它的菜单、推理等级子菜单、键盘处理和失败行一起接管。对一个「显示偏好」来说这是很差的
交换。

官方选择器读取模型目录的链路是公开的:

```
ctx.modelDirectories.catalog.store.getSnapshot()
  -> { value: { groups: [{ id, name, models: [...] }], default, failures },
       status, error }
```

`createSnapshotStore` 返回的是普通对象(`{ getSnapshot, subscribe, update, set }`),
`getSnapshot` 是普通方法。本插件因此**包装这一个方法**,返回过滤后的快照:

- 官方组件照常渲染它**自己**的界面,只是它读到的列表少了被隐藏的模型;
- 真实 store 从不被写入,`subscribe` 仍然报告真实变化,所以目录更新与白名单改动都会触发重渲染;
- 投影是幂等且全函数式的:只复制 `groups` 数组,且仅在确实隐藏了东西时才复制。

插件在注销时恢复原方法。

### 因此它不会拖垮启动

客户端插件若在激活阶段抛异常,宿主会以 `web boot: 1 entry did not activate` 中止启动。
本插件对此做了两件事:

1. 导出 cordis 需要的 `inject` 数组(`['locale', 'slots']`),让宿主等到依赖就绪再调用 `apply`;
2. 每一处可能失败的读取(`require` 官方组件库、取服务、读本地存储、投影快照)都包在
   `try/catch` 内并降级:拿不到官方组件库就退回普通元素,拿不到服务就显示空状态,
   读存储失败就当作空白名单,投影出错就返回未过滤的快照。

最坏情况是「不隐藏任何模型」,而不是打不开应用。

## 兼容性

在 **dsh `0.2.0-rc.2`**(桌面端 @deepseek-ai/dsh-desktop 0.2.0-rc.2,Electron 44,
Node 24.18.1)上实测通过:面板正常显示、勾选后选择器菜单立即少掉对应模型、勾回后恢复。

插件依赖的公开面是 `ctx.modelDirectories.catalog.store` 的快照形状与
`settings.models.footer` 座位。前者是官方模型选择插件对外的服务字段,后者由 dsh 的
slot 契约文档化并标注 `replaceRisk: none`。两者若在未来版本改变形状,插件会退化为
「面板为空」或「不过滤」,不会中断启动。

未在 0.1.x 系列上验证。

## 已知限制

- **白名单只影响选择器菜单的显示**。已选中的模型若被隐藏,它仍会继续工作;
  当前会话正在使用的模型你仍可在设置页里隐藏,不影响正在进行的对话。
- 面板在打开时读取一次目录,并订阅后续变化;若某个 provider 的模型清单是懒加载的,
  需先触发一次加载(打开一次模型选择器)才会出现在面板里。
- 白名单与折叠状态按浏览器与地址隔离。换端口、换浏览器或清空站点数据都会丢。
- 没有「按 provider 整组隐藏」的批量操作(只有全部显示 / 全部隐藏)。**折叠**可以整组收起,
  但它不改变哪些模型可见。
- 折叠功能只做在设置面板里。模型选择器菜单本身由官方组件渲染,往那里加折叠需要遮蔽官方
  界面(`replaceRisk: shadows-shipped-ui`),本插件刻意不这么做。

## 开发

```bash
node scripts/verify-client.mjs
```

该脚本用一个打桩的模块表和小型渲染器载入 `lib/client.js`,然后断言:工厂与导出形态正确、
`inject` 数组存在、座位注册命中 `settings.models.footer`、**投影确实过滤掉了被隐藏的模型**、
**折叠只影响渲染而不影响 hidden 集或选择器**,同时覆盖空/畸形快照、服务缺失、本地存储被拒
等降级路径。

## 许可证

MIT。见 [LICENSE](LICENSE) 与 [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md)。

版本变更见 [CHANGELOG.md](CHANGELOG.md)。

Install

dsh plugin --profile web add github:swyc520/dsh-plugin-model-whitelist

Profile: web

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source