packages/dsh-plugin-manager
zhu1090093659/dsh-web设置 → 插件 分区里的插件管理 Tab:从 npm/git 安装带进度、下次启动生效的启停开关、安装冲突对账可撤销、失败一键转交修复会话。
DSH 新人引导插件:按八个领域推荐插件(GitHub Star 与 npm 近月下载加权排序),先出计划再一键安装,并可导出/导入记录 profile 插件清单、精确版本与启用状态的环境码。
给第一次用 dsh 的人一个「十分钟内把环境配好」的入口。它只做四件事,不多做:
不做的事:不改插件运行时、不代理模型调用、不动 profile 之外的全局配置。
English — An onboarding plugin for DSH. It (1) recommends plugins across eight domains, ranked by a weight on GitHub stars and npm monthly downloads, (2) installs the whole set in one click behind a reviewable plan, (3) exports a compact environment code for the current profile, and (4) imports someone else's code to clone it — or just to flip the enabled set. Install:
dsh plugin --profile <profile> add dsh-bootstrap. Panel: a floating avatar in the bottom-right corner opens a drawer with four tabs. CLI:dsh-bootstrap recommend | install-all | env export | env import <code> | env switch <code> --only-toggle. No network calls are made at startup, every request times out after 8 s, and a failure only ever costs you the panel, never the host.
# 装进指定 profile(把 desktop 换成你的 profile 名)
dsh plugin --profile desktop add dsh-bootstrap
# 装完重启 dsh,让插件层重新加载
本地开发时可以直接装目录:
dsh plugin --profile desktop add file:C:/path/to/dsh-bootstrap
具体子命令以你所用 dsh 版本的
dsh plugin --help为准;本插件只依赖 profile 层的dsh.profile.bundles语义,不依赖某个特定 CLI 参数。
装好后,web profile 会在右下角出现一个圆形按钮(🚀),点开就是四标签面板。非 web profile 请用命令行。
面板的交互约定:
×,按 Esc 也可以收起;抽屉展开时悬浮球会挪到抽屉左侧(窄屏下隐藏),仍然可以点它切回来。http://127.0.0.1:19387/dsh-bootstrap/panel,适合没有注入权限的环境(例如桌面壳的注入表是启动时快照,插件后装需要重启一次才会出现悬浮球)。recommenddsh-bootstrap recommend
dsh-bootstrap recommend --json --per-domain 3
dsh-bootstrap recommend --star-weight 0.7 --download-weight 0.3
dsh-bootstrap recommend --offline # 只用本地快照与内置种子,不联网
输出按领域分组,每个插件给:npm 包名、一句话用途、仓库 Star、npm 近月下载量、兼容的 dsh 版本范围、推荐理由。
注意文案区分了两个概念:
两者按权重加权:默认 starWeight: 0.4、downloadWeight: 0.6(都会自动归一化到和为 1)。覆盖的 8 个领域:
| 领域 | 说明 |
|---|---|
| 插件市场与发现 | 先有市场,才知道还有什么可装 |
| 计费与用量 | 看清 token 与花费 |
| 上下文压缩 | 长任务的前提 |
| 界面工作台 | 侧栏、面板、多面板布局 |
| 多 Agent 协作 | 子代理、任务板、编排 |
| 视觉 / 多模态 | 让 Agent 看得懂图 |
| 文件与终端 | 远程主机、终端、文件浏览 |
| 安全与审计 | 权限规则、自动复核 |
候选池优先取 dshmarket / @ace-zone/dsh-market 使用的同一份官方 registry(https://awesome-dsh-plugin.com/plugins.json)。市场插件不是硬依赖:没装市场时,插件用内置种子清单 + npm/GitHub 实时查询照样能推荐。
面板的「推荐」页会提示「建议先安装 dshmarket」——这只是引导,不是强制。
install-alldsh-bootstrap install-all # 打印计划 → 交互确认 → 执行
dsh-bootstrap install-all --yes # 非交互环境必须显式确认
dsh-bootstrap install-all --all # 装完把全部推荐都启用(默认只启用核心推荐)
dsh-bootstrap install-all --json # 只输出计划,不执行
执行前一定会先给出计划,逐条列出:包名、精确版本、所属领域、「安装 / 已安装跳过 / 版本没锁定而跳过」、「装完是否启用」。
硬性行为:
dsh.profile.bundles 里已有的包不会重复 add。lock,环境码复刻时还原同一版本,而不是重新解析 latest。package.json,并发必然互相覆盖。执行完输出一份报告:成功 / 跳过 / 失败 三类,各自的包名与错误原因。
env exportdsh-bootstrap env export # 打印环境码
dsh-bootstrap env export --out env.txt
dsh-bootstrap env export --json # 同时给出 manifest.yaml + lock.json
面板的「我的环境」页有复制按钮,并把历史环境码存在浏览器本地(不上传)。
env import 与 env switch# 粘贴环境码或给文件路径,先看差异,再决定
dsh-bootstrap env import DSHB1z.pdJBT8MgFMDx...
dsh-bootstrap env import env.txt --yes
# 已经装好全部插件,只想切开关
dsh-bootstrap env switch env.txt --only-toggle
env import 的流程固定为:解析 → 差异预览 → 确认 → 执行 → 收敛报告。
差异预览会分成五栏:需要启用、需要停用、需要安装、已经是启用状态(无需改动)、环境码未包含,将保持现状(不删除用户已有的插件)。
--only-toggle(面板上的「仅切换开关、不安装」)的语义是:只改 bundle 可见性,缺的包只报告、不安装也不启用。
环境码是一个自包含字符串,不要求上传文件:
DSHB1.<base64url(JSON)> # 明文版
DSHB1z.<base64url(deflateRaw(JSON))> # 压缩版(默认选更短的那个)
解码后的 JSON 就是 dshenv 风格的声明式清单:
{
"v": 1,
"createdAt": "2026-10-16T00:00:00.000Z",
"profile": { "name": "desktop", "dshVersion": "1.0.0", "dshRange": "^1.0.0" },
"plugins": [
{ "name": "dshmarket", "version": "1.66.27", "enabled": true },
{ "name": "dsh-cost-meter", "version": "1.8.29", "enabled": false }
],
"digest": "d63144265c9adbfb"
}
plugins[].enabled 对应 dsh.profile.bundles 是否包含该名字;false 表示「装了但停用」。digest 是规范化 JSON(键排序)的 SHA-256 前 16 个十六进制字符。导入时校验,不一致会拒绝应用并提示内容可能被改过。manifest.yaml + lock.json 导出;两者内容等价,环境码始终是受支持的主路径,短链/文件只是补充。增量语义(导入时):
改 cordis.patch.yml 里本插件的 config 即可(宿主若把插件 schema 展示到设置页,也会出现在那里):
| 键 | 默认 | 说明 |
|---|---|---|
starWeight | 0.4 | GitHub Star 权重 |
downloadWeight | 0.6 | npm 近月下载量权重 |
requestTimeoutMs | 8000 | 外部请求超时,超时即降级 |
perDomain | 2 | 每个领域推荐几个(1–5) |
autoRefresh | true | 为 false 时只在用户手动刷新时才重新拉目录(默认 5 分钟内复用内存缓存) |
profile | "" | 目标 profile,空表示当前 profile |
catalogUrl | 官方 registry | 插件目录地址 |
downloadsApi / npmRegistry / githubApi | npm / GitHub 官方 | 指标来源 |
offline | false | 强制离线 |
coreOnlyDefault | true | 一键安装后只启用核心推荐 |
maxEnvCodeLength | 6000 | 环境码长度上限 |
panel | auto | auto 按 profile 判断 / always / off |
latest。pluginManager 也没有 dsh CLI,插件会退化为直接改 profile 清单。这种模式下不会更新 cordis.patch.yml,可能导致市场页与官方插件页状态不一致(这是 dsh 已知的坑)。插件会在 UI 与报告里显著提示,并建议重启 dsh 后复查。有 pluginManager 时优先走 setBundleEnabled / addProfileBundle / removeBundle。manifestVersion: 1 的 profile 布局(package.json 的 dsh.profile.bundles + cordis.patch.yml)。peer 依赖声明为 @deepseek-ai/cordis ^4.0.1、@deepseek-ai/schemastery ^3.18.1(后者可选)。环境码里会写入 dshRange,导入时只做提示、不做强制阻断;环境码格式版本固定为 v1,将来不兼容时提升 v。file: 依赖)只能人工处理。dsh-bootstrap/
├── package.json # dsh.bundle.patch 指向 cordis.patch.yml
├── cordis.patch.yml # bundle 层:把本插件 insert 进 profile 的层栈
├── bin/
│ └── dsh-bootstrap.mjs # 命令行:recommend / install-all / env export|import|switch
├── lib/
│ ├── index.js # 插件入口(apply 全程 fail-soft)
│ ├── config.js # schemastery 配置 schema
│ ├── defaults.js # 默认值 + 归一化(纯 JS,CLI 也能用)
│ ├── sources.js # 带超时的取数:registry / npm / GitHub
│ ├── cache.js # 本地快照缓存(只存指标,不存凭据)
│ ├── seed-catalog.js # 8 个领域 + 16 个种子插件(离线兜底)
│ ├── catalog.js # 候选池加载:live → cache → seed
│ ├── recommend.js # 加权打分、领域归属、推荐理由
│ ├── install.js # 计划构建 + 串行执行 + 失败隔离
│ ├── envcode.js # 环境码编解码、摘要、敏感扫描
│ ├── switch.js # 差异计算 + 开关收敛
│ ├── backend.js # pluginManager → CLI → 文件 三层降级
│ ├── profile.js # profile 清单读写(原子写)
│ ├── service.js # 服务层:把上面这些装配起来,面板与 CLI 共用
│ ├── routes.js # HTTP 路由(exact 匹配 + 同源校验)
│ ├── client/
│ │ ├── bootstrap.js # 面板(四标签,纯 DOM,无构建步骤)
│ │ └── bootstrap.css
│ └── types/index.d.ts # 类型定义
└── test/
├── bootstrap.test.mjs # 核心行为(离线降级、失败隔离、收敛、幂等、敏感拦截、同源校验)
├── load.test.mjs # 真加载:import 入口 + 假宿主 ctx(需要宿主提供的 schemastery)
└── panel.smoke.mjs # 面板脚本在最小 DOM 桩上挂载 → 点开 → 渲染
设计上的两个关键取舍:
lib/service.js,所以「命令行里确认过的计划」和「面板里确认过的计划」是同一份逻辑,不会出现两套语义。backend:能拿到 pluginManager 就用它的 setBundleEnabled / addProfileBundle / removeBundle;拿不到才退到 dsh plugin CLI;最后才是直接改清单(并显著告警)。npm test
四个必需用例都已覆盖:
ENOTFOUND 时回退到缓存/种子,并标记 offline: true;offline: true 时确认不发生网络调用。failed。converged: true,且环境码之外的 C 保持启用。另外还有五条,共 13 个用例:
test/load.test.mjs 真的 import 插件入口,用一个假的宿主 ctx 走一遍 apply(),断言会注册 10 条路由、把面板脚本塞进索引注入表、并且在残缺宿主(没有 inject、没有 webServer、panel: 'off')下安静降级而不抛。同时校验 schemastery 的链式调用(.min/.max/.step/.default/.description)在这一版上确实存在 —— 这类拼错在 import 期就会炸,普通单测抓不到。test/panel.smoke.mjs 在最小 DOM 桩上跑面板脚本,验证「挂载 → 悬浮按钮 → 点开抽屉 → 四个标签 → 推荐卡片渲染出真实的 Star/下载量千分位」。
load.test.mjs需要@deepseek-ai/schemastery(dsh 宿主自带)。直接npm install装不到它, 所以该组用例在拿不到依赖时会整组 skip 并说明原因,而不是假装通过。想在本地跑全: 把宿主 profile 的node_modules/@deepseek-ai/schemastery链到本包的node_modules/@deepseek-ai/schemastery即可。
面板是「悬浮球 + 右侧抽屉」:右下角一颗 56px 的圆形头像按钮(抽屉展开时让到抽屉左侧,窄屏隐藏),点开后是品牌头部(头像 + 标题 + 副标题)、分段式四个标签(推荐 / 一键安装 / 我的环境 / 导入环境)、可滚动主体、底部一条版本信息栏。
lib/client/bootstrap.js 里。这样不额外发请求,也不受「插件路由是否已注册」影响(本插件新增路由必须重启宿主进程才生效,而面板脚本每次请求都从磁盘现读)。fornarwhal/deepseek-whale-girl-icon(GitHub),角色形象出自 上善无形(OC「溟月」)的 DeepSeek 二创,另经 ZipZipPipe、QYQCAMIAO 修复。原图授权为 CC BY-NC-SA 4.0:仅非商业使用、需署名、衍生作品同协议。若你要把 dsh-bootstrap 用于商业场景,请自行替换 AVATAR 常量为其他图标。light-dark() 与 --dsb-* 变量,跟随宿主明暗主题;选择器一律挂在 #dsh-bootstrap-root 下,不污染宿主页面。kind: 'exact',会越过宿主的 /api 前缀栅栏,因此路由内部自己做同源校验:拒绝 Sec-Fetch-Site: cross-site,Origin 与 Host 不同源直接 403。apply() 内任何异常只记录、不抛出;面板脚本加载失败也只是面板不出现,不影响 dsh 本身。docs/ART-CREDITS.md(CC BY-NC-SA 4.0)。商业场景请替换
lib/client/bootstrap.js 里的 AVATAR 常量为自有图标,或直接删掉 <img>。