dsh-plugin-suite
Verified@hualiong/dsh-plugin-suite · v1.2.1 · MIT
One installable DSH bundle, three independent plugins: the MCP Manager panel, the OAuth-enabled Streamable HTTP MCP client bridge, and the Session Delete UI.
Install
dsh plugin add @hualiong/dsh-plugin-suite Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Tags
Readme
@hualiong/dsh-plugin-suite
一个安装单元 + 一层补丁:本包自身不含任何代码,只声明三个原本独立的 DSH 插件为依赖,并把它们各自的行插入 profile 的组合树。
| 行 id | 组件包 | 宿主 | 浏览器 UI |
|---|---|---|---|
mcp-manager |
@hualiong/dsh-mcp-manager |
MCP 面板的 HTTP/SSE 载体,读写 profile patch 文件 | 设置面板 “MCP” 小节 |
chameleon-session-delete |
@hualiong/dsh-session-delete |
POST /__chameleon/session/delete 及其删除链路 |
侧栏会话菜单项 + 确认弹窗 |
oauth-mcp-cloudflare(默认 disabled: true) |
@hualiong/dsh-oauth-mcp-client |
OAuth 授权的 Streamable HTTP MCP 客户端桥,每个服务器一行 | 无 |
三个组件仍然是各自独立的包、各自拥有自己的 ./client 浏览器产物、各自的 fiber:可单独启停、删除或重排,互不影响。
为什么是「聚合包」而不是「单包合并」
DSH 插件详细页的组件行元数据是按该行声明的模块标识去读那个模块自己的 package.json / locale/*.json 的
(declaredRows() → metaOf(row.name);客户端渲染 title = row.meta?.title ?? row.moduleName)。
所以三行都写同一个包名时,三行必然显示同一个标题和描述,只有行 id 不同(旧版就是这样)。让每行指向各自的组件包后:
- 每行显示各自的名称/描述(来自组件包自己的
locale/<lang>.json); - 每个组件包的
./client各自成为一个独立浏览器模块(旧版是三个 UI 挤在一个lib/client.js里整包加载)。
目录
package.json 依赖三个组件包;dsh.bundle.patch = ./cordis.patch.yml;无 icon(用默认图标)
cordis.patch.yml bundle 层:一个 insert,插入三条行(oauth 行默认 disabled)
locale/en.json 套件卡片的显示名(英文 meta.title / meta.description;锚点文件,必须存在)
locale/zh.json 套件卡片的显示名(中文)
README.md
安装
profile 的 package.json:
{
"dependencies": {
"@hualiong/dsh-plugin-suite": "link:C:/Users/64261/Desktop/Workspace/dsh-plugin-suite"
},
"dsh": { "profile": { "bundles": [ /* …, */ "@hualiong/dsh-plugin-suite" ] } }
}
# 三个组件包由本包声明为 dependencies,先装进本包自己的 node_modules
pnpm install --dir C:/Users/64261/Desktop/Workspace/dsh-plugin-suite
dsh plugin --profile desktop install # 或 pnpm install(profile 目录内)
bundles 里的顺序即组合顺序;本包自带 cordis.patch.yml 会在其中插入下面三条行。
profile 只依赖本包即可,不要再单独依赖三个组件包:
- 运行时按「已选中 bundle 的依赖闭包」解析组件(
@deepseek-ai/dsh-app-boot/lib/index.js:819-822的collectProfileScopePackages;请求按声明者目录查找,见profile-resolution-bootstrap.js:205-211的packageSearchPaths),所以本包node_modules里的三份副本就够用。 - 插件页的「已安装」列表 =
dsh.profile.bundles∪ profile 的dependencies∪ 安装自带依赖(@deepseek-ai/dsh-plugin-manager/lib/index.js:1459-1469)。凡是声明了dsh.bundle.patch的依赖都会占一张卡 —— 在 profile 里再依赖三个组件包,插件页就会多出三张卡(且开关处于「未选中」状态)。 - 改动 profile 的
dependencies后必须重启 DSH:运行时解析器拒绝热替换,会报profile resolution: removing local package … requires a process restart(profile-resolution-bootstrap.js:258)。 - 想让三个组件的本地源码改动即时生效时,把三条
link:加回 profile 的dependencies(插件页会重新出现那三张卡),改完再删掉。
行长什么样
# 包自带层(cordis.patch.yml)
- insert:
- id: mcp-manager
name: '@hualiong/dsh-mcp-manager'
- id: chameleon-session-delete
name: '@hualiong/dsh-session-delete'
# 每个 OAuth 服务器一行;默认禁用,绝不因「装了套件」而触发授权流程
- id: oauth-mcp-cloudflare
name: '@hualiong/dsh-oauth-mcp-client'
disabled: true
config:
serverName: Cloudflare
url: https://mcp.cloudflare.com/mcp
credentialRef: CLOUDFLARE_MCP_OAUTH
callbackPort: 45871
authorizationTimeoutMs: 600000
failOnStartupError: false
profile 自己的 patch 文件按 id 覆盖这些行来补配置 / 打开开关(patch 会替换该行的整个 config,所以要把它拥有的键写全):
- id: mcp-manager
name: '@hualiong/dsh-mcp-manager'
config:
patchFile: C:\Users\64261\.dsh\profiles\desktop\cordis.patch.yml
disabled: false
- id: oauth-mcp-cloudflare
name: '@hualiong/dsh-oauth-mcp-client'
disabled: false # 本 profile 打开这个 OAuth 服务器
config: { serverName: Cloudflare, url: …, credentialRef: …, callbackPort: 45871, authorizationTimeoutMs: 600000, failOnStartupError: false }
显示名称(中英双语)
DSH 读取显示元数据的方式是:先把 ${包名}/locale/en.json 当作锚点解析,再扫描该文件所在目录下所有 *.json 作为各语言词典;
显示时 ctx.locale.resolveText(text) 以 en 键作为兜底种子,再沿当前语言的回退链查找。
所以四个包都提供规范的 locale/en.json(英文)+ locale/zh.json(中文),并按当前界面语言显示:
| 包 | 详细页标题(zh / en) | 图标 |
|---|---|---|
@hualiong/dsh-plugin-suite |
DSH 插件套件 / DSH Plugin Suite | 无(默认图标) |
@hualiong/dsh-mcp-manager |
MCP 管理器 / MCP Manager | 无(默认图标) |
@hualiong/dsh-oauth-mcp-client |
OAuth MCP 客户端 / OAuth MCP Client | 无(默认图标) |
@hualiong/dsh-session-delete |
会话删除 / Session Delete | 无(默认图标) |
- 改标题/描述:编辑对应包的
locale/en.json(英文)与locale/zh.json(中文)的meta.title/meta.description; 新增语言 = 在同目录再加一份<lang>.json(文件名必须是合法语言 id),en必须始终存在; - 这些文件必须能被解析到 —— 三个组件包导出
"./package.json"、"./locale/en.json"、"./locale/zh.json"; 套件包有意不声明exports,因此${包名}/package.json、${包名}/locale/*.json与${包名}/cordis.patch.yml都能按路径解析; - 四个包都不声明
icon,所以卡片与每个组件行都用默认图标。
单独启停 / 卸载
- 停用某个组件:在 profile patch 里给该行加/改最后一条匹配覆盖项的
disabled: true,或直接删掉该行。 - 停用整包:把
@hualiong/dsh-plugin-suite从dsh.profile.bundles移除(依赖可保留,官方推荐的“关闭保留依赖”语义)。 本机 desktop profile 就是这么停用原来那三个插件的:三个旧包仍在dependencies,但不在bundles里。 - 卸载:先移出
bundles,再pnpm remove。
发布(npm)
四个包的 registry 现状(查询于 2026-10-11):
| 包 | registry | 发布方式 |
|---|---|---|
@hualiong/dsh-mcp-manager |
已发布 0.1.6(0.1.5 为历史版本) |
publish.yml 的 npm trusted publishing(OIDC);CI run 38121187747 成功 |
@hualiong/dsh-oauth-mcp-client |
已发布 0.1.1(0.1.0 为历史版本) |
publish.yml 的 npm trusted publishing(OIDC);CI run 38121182396 成功 |
@hualiong/dsh-session-delete |
已发布 0.3.6(0.3.3、0.3.4、0.3.5 为历史版本) |
publish.yml 的 npm trusted publishing(OIDC);CI run 38123210607 成功 |
@hualiong/dsh-plugin-suite |
已发布 1.1.1(1.1.0 为历史版本) |
publish.yml 的 npm trusted publishing(OIDC);CI run 38123393991 成功 |
四个包的 workflow 现在都是 OIDC 路线(无任何 secret,四个仓库的 gh secret list 均为空):
on:
push:
tags: ['v*']
permissions:
contents: read
id-token: write
# setup-node 故意不写 registry-url(它会往 .npmrc 写空的 _authToken 占位符,遮蔽 OIDC 凭据)
npm install -g npm@latest # trusted publishing 需要 npm >= 11.5.1
npm publish --access public --provenance
发布姿势:版本号写进 package.json → 打同名 tag → 推 tag 触发 CI → 用 gh 观察(不建 GitHub Release):
gh auth setup-git # 让 git push 用 gh 的凭据
git -C dsh-mcp-manager tag v0.1.6 # tag 必须等于 package.json 的 version,CI 会校验
git -C dsh-mcp-manager push origin fix/dsh-0.2.0-rc.2 v0.1.6
git -C dsh-oauth-mcp-client tag v0.1.1
git -C dsh-oauth-mcp-client push origin main v0.1.1
git -C dsh-plugin-suite tag v1.1.0
git -C dsh-plugin-suite push origin main v1.1.0
gh run watch --repo hualiong/dsh-mcp-manager $(gh run list --repo hualiong/dsh-mcp-manager --limit 1 --json databaseId -q '.[0].databaseId')
每个包在 npm 网页上各有一次性的 trusted publisher 绑定(Settings → Trusted Publisher → GitHub Actions):
organization/user: hualiong、repository: <仓库名>、workflow filename: publish.yml、Environment name 留空、勾 Allow npm publish。
命令行等价物 npm trust github <包名> --file publish.yml --repo hualiong/<repo> --allow-publish 也可,
但它属于账号变更操作、必须交互式 2FA(bypass-2FA token 会被 npm 拒绝),只能由账号主人执行一次。
绑定好之后不要再配 NPM_TOKEN secret,也不要把 bypass-2FA token 交给 CI。
(四个包都已绑定 trusted publisher:dsh-plugin-suite 的 1.1.0 是先用一次性 token 发布的,
1.1.1 起改由 OIDC 发布(CI run 38123393991,带 provenance),四个仓库的 gh secret list 均为空。)
0.3.6 / 1.1.1 修掉的问题:
dsh-session-delete的 host half 在src/index.js里import '@deepseek-ai/dsh-home-paths', 但该包此前只把它写在devDependencies。源码 checkout(link:安装)能跑,从 registry 安装时这个依赖不会被装, Cordis 于是报1 entry did not activate chameleon-session-delete (@hualiong/dsh-session-delete): failed to import(ERR_MODULE_NOT_FOUND)。0.3.6把它移进dependencies(0.2.0-rc.2),套件的依赖下限同步提到^0.3.6。 这个坑只在组件作为套件依赖从 registry 装时才暴露:link:到工作区目录时,工作区自己的node_modules恰好能解析它。dsh-mcp-manager/dsh-oauth-mcp-client不 import 该包,所以一直正常(它们的客户端 half 用的@deepseek-ai/dsh-client-*、@deepseek-ai/cordis、react由 DSH 运行时的解析表提供)。
0.1.6 / 0.1.1 顺带修掉的小瑕疵:
dsh-mcp-manager:删掉指向从未构建的lib/types/的types与exports[].types; 补keywords;删掉pnpm.onlyBuiltDependencies(pnpm 11 已不读package.json的pnpm字段,改用pnpm-workspace.yaml的allowBuilds);删掉项目.npmrc(它的 pnpm-only 键让 npm 打npm warn Unknown project config "autoInstallPeers",而 npm 12 会直接报错)。dsh-oauth-mcp-client:补上缺的author。- 两个
publish.yml都加了pnpm test(oauth 另加pnpm typecheck)作为发布前置门槛。
首次发布的包在 registry 上会留下一个 0.0.0-stage 版本,那是 npm 处理首包时写入的占位版本,
dist-tags.latest 指向真实版本,不影响安装。
发布内容已用 npm pack --dry-run --json --ignore-scripts 逐包核对:
mcp-manager 10 项(lib/index.js、lib/client.js + 两个 sourcemap、cordis.patch.yml、locale/{en,zh}.json、LICENSE/README/package.json)、
oauth 25 项(lib/** 的 js/d.ts/map、dsh-oauth-mcp.cordis.yml、locale/{en,zh}.json、README)、
session-delete 7 项(src/{index,client}.js、cordis.patch.yml、locale/{en,zh}.json,已无 icon.svg)、
suite 6 项(cordis.patch.yml、package.json、README.md、LICENSE、locale/{en,zh}.json)。
一手小坑:带 token 的 workflow 里 npm publish --provenance 同样需要 id-token: write,否则 CI 会以
npm error Provenance generation in GitHub Actions requires "write" access to the "id-token" permission 直接失败。
发布后别人的 profile 只需
{
"dependencies": { "@hualiong/dsh-plugin-suite": "1.1.0" },
"dsh": { "profile": { "bundles": [ /* …, */ "@hualiong/dsh-plugin-suite" ] } }
}
即可 —— 三个组件包会被套件的 dependencies 自动拉进来,不必逐个安装。
从原来的三个插件迁移
| 原行 | 新行 |
|---|---|
id: mcp-manager, name: '@hualiong/dsh-mcp-manager' |
同名 id、同一个 name(现在由套件插入),profile 覆盖行只提供 config.patchFile |
id: chameleon-session-delete, name: '@hualiong/dsh-session-delete' |
同名 id、同一个 name(现在由套件插入,原先由该包自己的 patch 插入) |
id: oauth-mcp-cloudflare, name: '@hualiong/dsh-oauth-mcp-client' |
同名 id、同一个 name(原先写在 profile 的 insert: 里,现在由套件插入并在本 profile 覆盖为 disabled: false) |
验证记录(本机 desktop profile)
- 元数据:四个包的
package.json+locale/en.json+locale/zh.json均可按 profile 路径解析, 词典为en+zh、标题与描述在两种语言下都成立(脚本模拟readPluginMeta的锚点/扫描/兜底逻辑,全部通过); 四个包的icon均为空(用默认图标);套件v1.1.0的dsh.client不存在(纯补丁层,合法;官方@deepseek-ai/dsh-web-app同理)、dsh.bundle.patch = ./cordis.patch.yml。 - 组合层:一次性探针 profile(
bundles = dsh-base + dsh-web-app + 本包)--dump-config退出码 0,逐行确认三条行 (mcp-manager、chameleon-session-delete、oauth-mcp-cloudflare)均来自本包,且oauth-mcp-cloudflare为disabled。 Electron 独占的 desktop profile 本身不允许--dump-config。 - 真机启动(探针 profile,
dsh <profile> --no-open --port 51410):无 “did not activate” 警告;- 浏览器清单(
__DSH_BOOT__,67 条)里@hualiong/dsh-mcp-manager与@hualiong/dsh-session-delete各自是一个模块, 且不含@hualiong/dsh-plugin-suite⇒ 两个 UI 各自独立加载; POST /mcp-manager/list(body{"payload":{}})→200 {"ok":true,…},列出id: oauth-mcp-cloudflare、ownerPackage: '@hualiong/dsh-oauth-mcp-client'、enabled: false(= 套件层的disabled: true生效);POST /__chameleon/session/delete→400 {"error":"invalid session id: …"}⇒ 组件路由已挂载,无重复注册。
- 浏览器清单(
- 迁移期间发现并修复:
@hualiong/dsh-session-delete的源码import { dshHomePath } from '@deepseek-ai/dsh-home-paths'从未在package.json里声明(该包原本零依赖),一旦从 npm 安装改为link:指向工作区目录就会ERR_MODULE_NOT_FOUND。已在dsh-session-delete/package.json的devDependencies里补上"@deepseek-ai/dsh-home-paths": "0.2.0-rc.2"并pnpm install(与dsh-mcp-manager的既有约定一致)。
已知限制
- OAuth 桥是「每个服务器一行」,套件里只提供一条默认禁用的示例行;启用它需要在 profile patch 里覆盖,或由 MCP 面板写入。
- 三个组件的 UI 不再共享一个浏览器产物,但
dsh.client的排序只作用于单个包内部:跨组件的加载顺序由各自的inject决定。 - 组件内部的日志前缀沿用各组件包自己的前缀(
dsh-mcp-manager(...)等)。 @hualiong/dsh-mcp-manager的lib/里只有index.js/client.js与两个 sourcemap,没有lib/types/, 因此它不再对外声明类型(0.1.6起删掉了指向不存在文件的types/exports[].types,而不是留一个坏指针)。 运行时不受影响(DSH Loader 只按 ESM importlib/index.js/lib/client.js);要让 TypeScript 使用者拿到声明, 得先让scripts/build.mjs产出.d.ts(其tsconfig.json的paths指向 DSH checkout,tsc --emitDeclarationOnly目前无法直接跑通)。