DeepSeek Harness Plugin Hub

发布与管理完整 Harness Profiles,发现适合你的插件。

探索

插件目录环境预设文档中心动态

社区

发布插件联系我们报告问题

相关链接

Plugin Hub GitHubDeepSeek Harness 官方项目系统状态隐私说明
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

独立、非官方社区项目,与 DeepSeek 官方无隶属、授权或背书关系。

Dpk Manager — DeepSeek Harness 插件(DSH Plugin)
← Plugins

dsh-dpk-manager

Dpk Manager

用于 DeepSeek Harness 的 DPK 管理器——通过 Plugins 页面面板和会话内 dpk 工具打包、验证并安装本地 DSH 插件包(.dpk)

插件会安装到这里;不确定时保持 web。

npx -y @deepseek-ai/dsh plugin --profile web add dsh-dpk-manager@2.1.70
README兼容性版本

兼容性与来源证明

Dpk Manager 以 dsh-dpk-manager 发布,当前版本为 2.1.70。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

DSH 兼容范围
*
运行环境
web
发布来源
npm
Registry 更新时间
2026/10/16

版本

2.1.70stable
2026/10/8
2.1.69stable
2026/10/8
2.1.68stable
2026/10/7
查看其余 39 个版本收起版本
2.1.67stable
2026/10/7
2.1.65stable
2026/10/7
2.1.64stable
2026/10/7
2.1.63stable
2026/10/7
2.1.62stable
2026/10/6
2.1.61stable
2026/10/6
2.1.60stable
2026/10/6
2.1.59stable
2026/10/5
2.1.58stable
2026/10/5
2.1.57stable
2026/10/5
2.1.56stable
2026/10/5
2.1.55stable
2026/10/5
2.1.54stable
2026/10/5
2.1.53stable
2026/10/5
2.1.52stable
2026/10/5
2.1.51stable
2026/10/4
2.1.50stable
2026/10/4
2.1.24stable
2026/10/4
2.1.12stable
2026/10/3
2.1.11stable
2026/10/3
2.1.10stable
2026/10/2
2.1.9stable
2026/10/1
2.1.8stable
2026/10/1
2.1.7stable
2026/10/1
2.1.6stable
2026/10/1
2.1.5stable
2026/10/1
2.1.4stable
2026/10/1
2.1.3stable
2026/10/1
2.1.2stable
2026/10/1
2.1.1stable
2026/10/1
2.1.0stable
2026/10/1
2.0.3stable
2026/10/1
2.0.2stable
2026/10/1
2.0.1stable
2026/10/1
2.0.0stable
2026/9/30
1.1.8stable
2026/9/30
1.1.7stable
2026/9/30
1.1.6stable
2026/9/30
1.1.5stable
2026/9/30

相关插件

正在加载相关插件…

最新版
2.1.70
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
434.5 kB
文件数
30
Surface
web
许可证
MIT
发布源
npm
GitHub
★ 0
周下载
2,039
安全扫描
✓ v2.1.70 扫描通过
最近提交
2026/10/3
查看源码 ↗项目主页 ↗
README Badge

点击下方 Badge 复制 Markdown,粘贴到 README 即可。

这是你的 Plugin?认领权益 · 优先安全扫描

验证 package.json 声明的 GitHub 仓库,即可管理这个公开页面。认领后,Hub 会优先安排当前版本的安全扫描,并在通过后公开展示结果。

认领这个 Plugin →
报告问题
DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录

README

DSH 安装包管理助手(dpk)

dsh-dpk-manager —— 把一个标准 DSH 包目录打包成单个 .dpk 文件(本质是 zip), 并在本地可靠地安装它。它是一个标准 DSH 插件(dsh.bundle.patch + 中文标题的插件卡片 + 图标),按 DSH 桌面版插件的规范安装与更新;同时也提供一个独立的标准 Node dpk 命令, 用于本地包作者打包和验真:

形态入口用法
插件页面板src/client.js → 「本地 DPK」侧栏导入/导出 .dpk、迁移插件运行期数据(app+data)、卸载、按需打开 DSH Web 调试界面,人点按钮即可
官方插件页徽章src/client.js → plugins.detail.badge由本管理器装好的包,在官方插件页标题旁显示「该插件由 dpk 管理器安装和管理」
npm CLIsrc/cli.mjs → dpknpm install -g dsh-dpk-manager 后执行 dpk build ./my-plugin
会话内工具src/index.js → 注册 dpk 工具装进 profile 后,agent 可直接 dpk action=verify/install/…
dpk update                 检查已装的包有没有新版本。npm 包(官方插件页装的,profile 里是 ^range 行)经官方管理器问 registry;.dpk 文件包看来源目录。有新版就提醒,`upgrade` 逐个装
dpk upgrade                把那些更新装上
dpk install <文件.dpk>     验真 → 解到本地仓库 → 把 profile 写成 DSH 读得懂的样子(不跑 pnpm)
dpk remove name=<包>       从 profile 卸下(包名写 @local/x 或 x 都认),并回收无人引用的 store 副本;
                           该 profile 本来没装它时报错并列出它实际装了什么,不谎报成功
dpk purge name=<包>        删除该包的全部受管数据卷
dpk autoremove             回收所有无人引用的 store 副本,以及被中断写入留下的暂存物
dpk list                   列出 dpk 装过什么、谁在用、能不能被 loader 解析(含 profile 引用但账本没记的)
dpk show <文件.dpk|包名>   一个动词回答"这是什么":清单,或版本/digest/store 路径/谁在用/数据卷
dpk verify <文件.dpk>      结构 + 完整性 + DSH 合规(装之前先用)
dpk build <目录>           校验 DSH 合规 → 写出 .dpk(同输入逐字节可复现)
dpk export name=<包>       导出该包的 `.dpk`(插件 + **app 卷**);不带 data 卷
dpk export all             全部 dpk 安装插件的 **app 卷** → `.dpks` 归档
dpk snap name=<包>         **快照**该包的 `.dpk`(插件 + **app + data 卷**)
dpk snap all               **整机快照**:全部插件的 **app + data 卷** → `.dpks` 归档
                           (归档 zip:`dpks.json` 清单只列路径,内容各在 `data/<包>/<卷路径>` 条目里一次)
                           默认名按动词分:`<包>-data.dpk` / `<包>-snap.dpk`、`dpks.dpks` / `snap.dpks`
dpk import <文件>          写回数据卷。按**内容**认 `.dpk` 数据区、旧版单包 JSON 与 `.dpks` 归档——改名也认、
                           也不问它出自 export 还是 snap;落位按包**当前声明**的类,未装的包跳过并提示
dpk pkg op=list            读一个数据文件里带了哪些包、每个包哪些卷(只读)
dpk pkg op=add name=<包>   把一个**已装**包并入该文件(已存在就刷新那条条目;默认 app+data,
                           `verb=export` 则仅 app)
dpk pkg op=remove name=<包>  从该文件里删掉一个包的条目
dpk pkg op=set name=<包> to=<新名>
                           **改包名**:把文件里那条记录改成新名字,卷的字节一动不动(归档里
                           承载卷的 `data/<包>/…` 条目随之改名);新名本机没装也可以
                           —— 形态随内容:1 个包的迁移使用 `.dpk`,≥2 个包 = `.dpks` 归档;
                           增到第二个包升为 `.dpks`,删回一个降回 `.json`,旧名一并移除

六个会改写 profile、持久数据或本地仓库的动作(install / upgrade / remove / purge / import / autoremove)都需要 danger-full-access 提权或批准;只读动作(update / list / show / verify / build / export / snap / pkg)不需要——pkg 只动你指定的那个文件。

导出/导入:两个动词 × 两个作用域

动词决定带哪些类,作用域决定装几个包——两条轴正交,四种组合各自只是一次选择:

一个包(name=<包>)全部包(all: true)
export(仅 app).dpk,含 dpk.json + dpk-data.json + data/app/….dpks 归档,dpks-data/1
snap(app + data).dpk,另外携带 data/data/….dpks 归档,dpks-data/1

单包 .dpk 的 dpk-data.json 与插件包共用一个归档;import 不问它出自 export 还是 snap,区分只在携带的类。默认文件名相同,同名导入不会 混淆,因为 .dpk 的 dpk.json/dpk-data.json 与归档清单都携带包名。

形态由内容决定。 一个包的迁移使用 .dpk,两个及以上仍使用 .dpks 归档;dpk pkg 处理旧版数据文件时仍可在这两个形态 之间迁移,扩展名跟着改(.json ↔ .dpks),旧名随即删除。所以读取端从不只信文件名或清单里的 format: 一份手写的、写着单包却列了多个包条目的文件照样能读(条目自带 package 时以条目为准,见 data-file.mjs)——拒绝一份完全可读的数据没有道理。

一个 data 卷和一个 app 卷共用同一条相对路径是合法的(两类目录各自成根),所以条目一律带 <class>/ 前缀:导入先按"类 + 路径"精确匹配,认不出时才按路径匹配(覆盖包在两个版本之间把某个卷换了类的情况)。

命令样式:对标 apt

dpk 是本地文件安装器,命令形状照抄 apt——高层动词、一个动词一件事——而不是 dpkg (低层工具、一堆 -i/-r/-P/-l 开关,还要分清 dpkg 与 dpkg-deb)。上一版曾按 dpkg 命名 (info/build/which/data),现已调回 apt 的说法,并把 which/data 并进 show。

dpkapt说明
updateapt update上游按包分两类:.dpk 文件包看来源目录;npm 包问 registry(经官方管理器的 inspect = profile 内的 pnpm view,registry/代理/认证全部沿用官方插件页的配置)。不缓存、不落状态,只报告
upgradeapt upgrade把已装的包升到可用最新版。npm 包逐个经官方 installBundle(name@version) 走 pnpm(registry 树只有 pnpm 装得了),.dpk 包由 dpk 自己装;每个引用了旧版的 profile 各装一次,并顺手回收被替换的 store 副本
install <文件>apt install ./x.deb同一形状:装一个本地文件;成功覆盖同一 profile 的同名 DPK 包后,按最后一次成功安装回收被替换的 store digest(其他 profile 仍引用时保留)
remove / purgeapt remove / apt purge卸下 / 连受管数据一起删
autoremoveapt autoremove删掉不再被任何 profile 引用的东西
listapt list --installed已装清单(含 broken 诊断,见下)
show <文件|包名>apt show <pkg> / apt show ./x.deb一个动词回答"这是什么";dpk 顺带列出 store 路径、引用它的 profile、受管数据卷
verify / buildapt 无对应apt 在下载时验哈希、也不打包;dpk 的对象是一个存档
export all → .dpksapt 无直接对应(形如把 /var/lib 整体打包)多包归档:全部已装插件的 app 卷;snap all 是同时带 data 卷的那种
reinstall: true--reinstall同一个 digest 也重新落一次 store 副本
deep: false--no-debsig 的性质跳过"再解一次并当 DSH 包校验"这一步
directory=无对应dpk 没有 sources.list:.dpk 包的 update/upgrade 默认看这些包上次从哪来(账本记着 source),需要时用这个参数再补一个目录;npm 包的来源就是 registry,不需要配置
via: "profile" | "service"

dpk 不借 apt 的仓库面:没有远端索引、sources.list、优先级/pinning,也没有 full-upgrade (dpk 没有依赖图,它与 upgrade 无从区分)。理由同一条:dpk 装的是本机文件。

也不留归档副本:.dpk 本来就在你手上,store 副本随时能 export 回一个 .dpk, 所以在 dpk 目录里再存一份只是重复(keepArchive 与 apt clean 的对应物因此都不存在)。

改用 apt 名字的对应关系:inspect→show、which/data→show、pack→build、 uninstall→remove、gc→autoremove、force→reinstall。旧名不再接受,避免两套词汇并存。

设计文档见 SPEC.md(格式规范 v1,逐条可判定);JSON Schema 见 schemas/dpk-1.schema.json(由 test/schema.test.mjs 钉住与校验器一致)。

安装这个助手本身

作为 DSH 插件使用时,它是普通 npm 包,就用 DSH 自己的插件页安装(与任何官方插件同一规范):

DSH 桌面版 / Web → 插件 → 添加插件 → 填 dsh-dpk-manager → 安装。 更新同样走插件页;插件页负责把它作为 DSH 插件加载,npm 安装则额外提供本地包作者使用的 dpk CLI。

作为本地包作者使用 CLI 时,也可以直接安装标准 Node 命令:

npm install -g dsh-dpk-manager
dpk build ./my-plugin
dpk verify ./my-plugin/dpk-dist/my-plugin@1.0.0.dpk

dpk build <目录> 会先按 DSH 插件规范校验目录,再在该目录的 dpk-dist/ 下生成 <name>@<version>.dpk;也可以用 --output <文件或目录> 指定输出位置。构建默认可复现, 不会执行插件代码。需要临时使用而不安装时,可运行 npx --package dsh-dpk-manager dpk build ./my-plugin。

CLI 是 src/cli.mjs 的薄入口,实际动作仍由 src/lib/cli.mjs 统一实现,因此 npm CLI、会话内工具和 Web Remote 不会产生三套命令逻辑。

生效条件:bundle 的 config 改动会热生效;插件的启停由官方服务的 setBundleEnabled 当场 reconcile。DPK 的安装、覆盖升级和卸载只接受 application: "applied"(相同 digest 的幂等导入除外);没有 live service、目标不是当前 profile,或官方返回 restart-required 时,操作失败并回滚,不留下“下次启动生效”的半成功状态。 第一次安装 dsh-dpk-manager 本身属于官方插件页的启动流程,不由 dpk 自己管理。

快速开始

  • 安装一个 .dpk:左侧栏 →「本地 DPK」→「导入 DPK 安装包」→ 选择 .dpk 文件。 面板与工具都默认由 dpk 自己把 profile 写好:依赖行、dsh.profile.bundles、 node_modules 链接、pnpm-lock.yaml 的 importer 行 —— 这四处正是 Harness loader 读取的全部内容。不跑 pnpm、不依赖官方管理器,所以它在一个 bundle 正在运行时也能装 (官方管理器此时会以 not-removable 拒绝)。写完再请官方服务 setBundleEnabled 当场应用,于是装完就能用;服务无法当场应用时直接失败并回滚。
  • 覆盖安装:自包含 DPK 包在当前 profile 里直接换行、换链接(同一个 store 目录就报 unchanged,见下面的耗时表); 需要官方 service 的运行时依赖包只有在 service 确认 live 应用时才会替换,否则保持旧版本。 导入同一个 digest 时,dpk 先读 profile 确认四处都已指向该目录,成立即不做任何写入。
  • 卸载:dpk remove name=…(面板上的卸载同理)先请官方服务 setBundleEnabled(name, false) 当场把它从运行中的 Harness 卸下,再删掉那四处并清理账本与无人引用的 store 副本; 同样不跑 pnpm —— 官方 removeBundle 会跑卸载脚本,dpk 只做 profile 与实时状态两件事。 装与卸因此是同一个行为的两个方向,不存在"装即时、卸要等重启"的缺口。
  • 需要官方机制时:via: "service" 会把安装交回官方安装器(它跑 pnpm、能处理 registry 依赖与原生编译),代价就是那一次 pnpm 启动。默认路径也会在包自己声明了 runtime dependencies 时自动走 service:链接一个 store 目录只能给出包本身,给不出它期望的 registry 依赖树,那是 pnpm 唯一不可替代的地方。SPEC 要求的自包含包(含本仓库的两个)都不声明 runtime 依赖,所以它们走的是零 pnpm 的那条路。
  • dsh web 调试模式:Desktop 内置 Web Host 默认运行在 19387,面板始终显示这个 端口。点击「开启 dsh web 调试模式」时,manager 使用 DSH 官方运行时额外启动独立的 dsh web --no-open --port 3080,并用它输出的认证 URL 打开浏览器;开启后面板同时显示 19387 与 3080。Electron 内嵌界面和 Desktop Host 不受影响。点击关闭只停止本次 manager 启动的独立 dsh web 子进程,不会停止 Desktop Host 或用户此前启动的 Web 服务。
  • 打包(对任意标准 DSH 包目录):dpk build directory=<包目录> —— 归档默认落在该包目录下的 dpk-dist/<name>-<version>.dpk(目录自动创建,与源在同一棵树里,不用管当前目录在哪)。 需要别的落点就写 output=<文件路径>;output 也可以写成目录(已存在的目录,或以分隔符结尾的路径), 此时同样按 <name>-<version>.dpk 命名写进去,目录同样会被自动创建。 归档留在包目录里就用 dpk-dist/ 或 dist/ —— 这四个目录(连同 node_modules/、.git/) 永不入包,所以刚生成的 .dpk 不会被当成包内容,也不会让下一次打包撞上"包内不允许归档"。 运行时文件进入 package/;源码、README、脚本和构建配置单独压成 source.tar.gz 额外打包进 .dpk 顶层 (test/、tests/ 和测试文件完全排除)。源码归档在安装释放时随包存放在 store 插件目录下,单纯存放备查, DSH 运行时不加载也不执行它。原本的 DPK 架构和处理方式不变。 也可以编程调用 dsh-dpk-manager/lib/pack.mjs 的 packDirectory()(库随包发布, 已导出)。

install 的输出会明确告诉你三件事:解到哪、谁写的、账本记在哪:

package  @local/dpk-hello@1.0.0 (bundle)
store    C:\Users\…\.dsh\dpk\store\<digest>\package
profile  dpk-demo (written by dpk)
live     applied to the running Harness

会话内工具 dpk

十四个动作(见上表):update / upgrade(有更新可用吗 / 装上)、show(看清单或看已装的包)、verify(结构+完整性+DSH 合规)、build(打进 .dpk)、 install(装进当前 profile)、remove(从当前 profile 卸下)、list、autoremove (回收无人引用的 store 副本)、export / snap / import / pkg(数据卷的搬运:导出、快照、写回、增删改查)、purge(删除一个包的全部受管数据卷)。

  • install、upgrade、remove、import、purge 与 autoremove 是会改写 profile、持久数据或本地仓库的动作, 都先过沙箱提权判定(danger-full-access,与官方 plugin_manager 相同的请求与理由文本); 判定器不可用时一律拒绝执行。判定发生在 dpk 写任何东西之前,所以它没有因为不再调用官方服务而变松。
  • list 顺手做可加载性诊断:它像 dpkg --audit 一样回答"现在装的东西还能不能起来"。 对每个仍被 profile 引用的包,它按 loader 读的三处(依赖行 / dsh.profile.bundles / node_modules 链接)核对一遍,有分歧就打成 broken 并给出修复动作(重装它的 .dpk,或 dpk remove 掉这一行)。 这些事实全部可从 profile + store 推导,所以不新增状态文件、不新增命令。 被 profile 引用、账本却从没记过的包(写入被中断留下的那种状态)也会列出来并标 untracked: 清点读的是 profile 这份权威,不是 dpk 自己的账;账本只是来源记录,缺了它不该让一个真在跑的包从清单里消失。

面板的「需要重启」提示不再是标记文件:它由账本里的安装时间与本进程的启动时间比较得出 (账本里有比本次进程启动更新的安装 = 还没加载),因此既是只读判断,也永远不会过期。 官方服务当场应用过的安装会记上 live: true,这种条目不再计入该判断——它已经在跑着的 进程里了;而任何一次没有应用成功的安装都会清掉这个标记,所以"不需要重启"永远只对真正生效的 那一次成立。

历史账本或外部操作仍可能留下待处理记录,面板会点名显示对应包;DPK 自己的公共安装、升级和卸载入口 不会创建这类记录,实时应用失败时会直接报错并回滚。

dpk 会写哪些路径

<home> = DSH_HOME(默认 ~/.dsh)。这是全部写入面,没有别的目录:

路径内容何时
<home>/dpk/store/<digest>/package/解包后的包本体(profile link: 的目标),只有 digest 目录install
<home>/dpk/cache/唯一的事务暂存区:store 落位、reinstall 让位、账本、profile 清单的暂存物都在这里;写入者改名成功后若它已空就删掉它每次写入期间;中断留下的由 autoremove 回收
<home>/dpk/index.json溯源账本:name/version/digest/installedAt/source(外加 live: true = 已应用到本进程)install / upgrade / remove / autoremove
<home>/profiles/<p>/package.json依赖行 link:<store 目录> + dsh.profile.bundlesinstall / upgrade / remove
<home>/profiles/<p>/node_modules/<name>指向 store 目录的链接(引用判定的权威来源之一)install / upgrade / remove
<home>/profiles/<p>/pnpm-lock.yaml. importer 的那一行install / upgrade / remove
<home>/profiles/<p>/pnpm-workspace.yamlminimumReleaseAge: 0仅 via: "service"
<home>/data/<scope>/<name>/{data,app}/…、.dpk/*.seed.json受管数据卷(data 由声明/种子管,app 随 export 走)与种子标记;旧类名(config/state/cache)声明报错,旧类名目录不读也不搬迁install / upgrade / import / purge
系统临时目录 dpk-verify-*、dpk-incoming-*、dpk-outgoing-*独立验真的解压目录、面板上传的中转、面板下载文件的暂存用完即删

暂存物(写一半的目录/文件)由写入方在失败时立即删除;进程被杀而留下的,由 dpk autoremove 按"名字里的 pid 已不存在"回收——活着的那次写入绝不动。store/ 里若还有旧世代在自己目标旁边留下的暂存残留 (dpk ≤ 2.1.43 的写法),那是见到即删:那些世代不在服务范围内。

dpk 只管理自己的路径:dpk 目录或 profile 里出现别人的文件,它既不删也不报错(autoremove 只认暂存命名约定)。

不向后兼容:旧世代的归档、布局与残留一律拒绝或直接删除,不做静默迁移(详见 SPEC §9)。

不写:DSH 应用安装目录、<home>/storages、settings、会话数据、别的插件的 node_modules; 不做全局安装、不写 PATH、不写注册表。via: "service" 时 node_modules/.pnpm、.modules.yaml 这些是 pnpm 写的,不算 dpk 的写入面(那也正是默认路径不跑 pnpm 的原因之一)。

store 只保留 profile 真正引用的 digest:判据永远是 profile 的依赖行,不是账本。 install、remove 与 upgrade 各自只回收自己替换掉的 digest(只读得到全部 profile 时才动手,读不到就保留并说明)。DPK 的覆盖安装不比较 SemVer:安装成功的最后一次结果就是当前结果;旧 digest 只有在没有任何 profile 引用时才回收。 把 store 收敛到"只剩被引用的"是 dpk autoremove 的事——它的整个语义就是回收,所以只有它会做全量清扫。

与 apt/dpkg 的对照

dpk 是这条链里 dpkg 那一半(装一个本地文件、记清楚自己做过什么),不是 apt 那一半(仓库、解析依赖)。 用 apt 的清单逐条对过,结论是:能借的都已经是现在的形状,剩下的借过来只会变成仪式。

apt/dpkgdpk 对应状态
control 与 data.tar 分离dpk.json 与 package/已有
apt show ./x.deb / dpkg-deb -I(装前看清单)dpk show <文件>(读清单,不解包)/ dpk verify(深校验)已有
md5sums 与 Release 完整性files[].sha256 + integrity.digest已有
dpkg -i 先解到临时处再原子就位store/<digest>.tmp-* → 改名已有
conffile 三方合并(.dpkg-dist).dpk/*.seed.json + .dpk-new,用户改过的不覆盖已有
dpkg --purge 与 -rdpk purge 与 dpk remove已有
apt-get autoremovedpk autoremove(store 副本、孤儿目录、遗留文件)已有
dpkg --audit(装的东西还能不能起来)dpk list 的 broken 诊断已有
Depends 解析声明了 runtime dependencies 就交给 pnpm(via: "service")已有(边界明确:dpk 不做解析)
/var/lib/dpkg/status 状态库不带刻意不借:dpkg 需要它是因为一次装几千个文件、要跑维护者脚本;dpk 每次只创建三处、全部由名字与 digest 推出,重跑即幂等收敛。再存一份"装到哪一步"就是第二份真相
info/<pkg>.list 文件清单不带刻意不借:同上,写入面可推导
两阶段 unpack / configure单阶段刻意不借:dpk 不执行包内任何代码,没有 configure 阶段;半装状态由"链接先于依赖行"的写入顺序变得无害
dpkg --verify / debsums 全量校验不带刻意不借:安装时已验过一次归档;store 副本会被 dpk 改名(作用域),且没有任何东西会消费"跑起来之后又变了"这个答案
Conflicts / Breaks不带刻意不借:行级冲突归 DSH loader;dpk 不实现 YAML 语义,靠正则猜行 id 只会制造假警报
apt update 的索引不缓存:dpk 的源是本机目录,扫一遍是毫秒级

受管数据卷(dpkg 语义,SPEC §13)

包可以用 package.json 的 dsh.data.volumes 声明自己的持久文件,dpk 负责它们的整个生命周期 (声明校验、首装种子、conffile 式升级、remove 保留 / purge 清除、数据卷导出导入)。布局:

<home>/data/<scope>/<name>/{data,app}/…      卷内容(插件读写)
<home>/data/<scope>/<name>/.dpk/*.seed.json          dpk 私有种子标记
  • 升级规则:用户没改过的种子卷原地刷新;改过的新种子落到旁边 .dpk-new,本地文件永不 被静默覆盖;无种子标记的既有文件按用户数据收编。实测:重装同一 DPK,改过的 providers=kept-local、没改的 prompt-overrides=refreshed。
  • 卷分类是 data/app 两个名字,差别只在迁移(旧类名 config/state/cache 声明直接报错, 其目录不读也不搬迁):data 是包声明的设置(换机由声明重建,不随 export 走), app 是包运行期自己写下的东西(会话、凭证、派生数据,export 携带)。换机迁移 = 装好包 + 导入 .dpks,新机接着现在跑。
  • 安装期不执行包内任何代码(与 verify 同一红线);卷先校验、再写入,且都在 profile 指向该包之前完成, 所以卷装不上时 profile 一个字不改,上一版继续可用。
  • 本插件不 import 任何 Harness 包:tools、sandboxPolicy、approval、pluginManager 全部经 ctx 服务拿;工具定义按 Harness 自己的参数 schema 形式手写,提权判定按 @deepseek-ai/dsh-sandbox#approveEscalation 的规则与措辞镜像实现。这样一个 profile bundle 在宿主自己的模块加载器下不会因为裸导入解析不到而失效,也让本包能作为 .dpk 自包含地分发。

为什么这样设计:它严格贴着 DSH 的标准

DSH 的既有事实出处DPK 的对应设计
安装 spec 只认 registry / path / git / tarball(仅 .tgz、.tar.gz);zip 会被判 invalid-specpackages/boot/plugin-manager/src/install-spec.ts:29,68-90.dpk 不冒充 spec:验真后解包成目录,让 profile 的依赖行指向它(via: "service" 时把绝对路径交给官方安装器,kind: 'path')
本地路径 spec 必须是绝对路径同上 :72-76store 用绝对路径寻址($DSH_HOME/dpk/store/<digest>/package)
loader 只读三处:dependencies 行、dsh.profile.bundles、以及从 profile 出发的 Node 解析(node_modules/<name>)packages/boot/app-boot/src/profile.ts:630-641、:655-692默认安装就写这三处(外加 pnpm 会写的那条 pnpm-lock.yaml importer 行),不跑 pnpm、不依赖官方管理器
官方 installBundle 会跑 pnpm(联网解析 registry 依赖、每次启动进程)operations.ts:284-460、:118,160via: "service" 保留这条路;默认路径既然只是写那四处,就没有让 pnpm 介入的理由
官方 removeBundle 对任何已启动的 bundle 答 not-removable(startedBundles → management-required)packages/boot/plugin-manager/src/index.ts:188-210,291-293,605uninstall 自己删那四处;bundle 的行在下次启动时不再加载
pnpm 的供应链冷却期(minimumReleaseAge)只在 pnpm 跑起来时才有意义pnpm 11 minimumReleaseAge/minimumReleaseAgeStrict默认路径不跑 pnpm,因此不需要豁免;via: "service" 时导入前仍把 profile 的 pnpm-workspace.yaml 写上 minimumReleaseAge: 0(用户显式配置过的值永不改写)
bundle = package.json 里声明 dsh.bundle.patchpackages/util/package-manifest/src/types.ts:69-72pack 校验补丁存在、在包内、是 .yml/.yaml,并把它复制进 dpk.json;安装时也用它决定要不要写进 dsh.profile.bundles(与官方 reconcile 同一判据)
展示元数据读 locale/<lang>.json 的 meta.title/description;图标必须相对路径、允许的扩展名、realpath 在包内、≤256 KiB

一句话:**DPK 只负责运输、验真,以及把 profile 写成 DSH 读得懂的样子。**安装完成后 profile 里躺着的是一条普通的 link: 依赖,DSH 完全不知道 DPK 存在;升级、卸载、list_bundles、HMR 全部沿用现成机制。

格式一页速览

<name>-<version>.dpk          (zip;store + deflate,UTF-8 名,无 zip64,无加密)
├── dpk.json                  格式版本、身份、每个文件的 size+sha256、整体摘要
├── source.tar.gz             可选:额外打包的源码归档(单纯带着,不参与运行时处理)
├── dpk-data.json             可选:迁移数据清单
├── data/app/…                可选:app 卷
├── data/data/…               可选:data 卷
└── package/                  一个标准 DSH 运行时包目录
    ├── package.json          (必需)
    ├── cordis.patch.yml      bundle 角色的加载器补丁
    └── lib/ locale/ icon.svg …
  • 归档层限制:≤20000 条目、单文件 ≤64 MiB、总量 ≤512 MiB、压缩比 >200:1 拒绝、拒绝符号链接、 拒绝仅大小写不同的路径、拒绝盘符/绝对路径/..(SPEC §5)。
  • 完整性:运行时文件逐文件 sha256 + integrity.digest = sha256(files.map(f => ${path}\0${size}\0${sha256}).join("\n"))(SPEC §7)。源码归档单纯携带,不参与 DPK 运行时校验或导入。
  • 可复现:同目录两次 pack 产出逐字节相同的文件(zip 时间戳与 createdAt 都取固定时刻)。
  • 严格性:dpk.json 顶层与 files[] 的未知字段一律拒绝,不做静默兼容(SPEC §9)。

本地仓库布局

$DSH_HOME/dpk/
  index.json                    dpk 的溯源账本(DSH 不读它;谁在用读 profile)
  store/<digest>/package/       profile 的 link: 目标(只保留被引用的 digest)

内容寻址(目录名 = integrity.digest)带来的性质:同一 DPK 重复安装天然幂等;不同构建不会互相覆盖; index.json 损坏也不影响已解包内容。

安装耗时(本机实测,Windows)

安装在 Windows 上的成本几乎都在文件创建上,所以默认路径只做三件事:解压一次、写一次 store、 写四处 profile 记录。不启动 pnpm,也不经过官方管理器:

场景本地耗时pnpm
冷安装(首次/新 digest)~0.35 s(506 KB / 104 文件)不跑
温安装(同 digest 重复导入)~0.03 s不跑
对照:dpk verify(深校验)~0.35 s—
对照:via: "service"同上 + 官方管理器开销跑 1 次(~0.5–1.1 s)

三处优化:

  1. 单次解压校验:verifyArchive(buffer, { extractTo }) 在一次解压里同时校验每个条目的 size+sha256 并把它写进 store 的暂存目录,随后就地对这棵树做 DSH 一致性校验。此前是"解压到临时 目录校验 → 丢掉 → 再解压进 store",同一批文件写两遍。
  2. 同 digest 复用:digest 已在 store 里时,只需重新哈希归档(内容寻址已保证内容同一), 深校验由当初落库时那次负责。
  3. 不跑 pnpm:src/lib/profile-install.mjs 直接写 Harness loader 读的那四处。installedState() 先确认四处都已指向同一个目录,成立就一个字节都不写;否则写依赖行、node_modules 链接与 lockfile 行。官方服务保留为 via: "service"。

pnpm 那条路真正的问题不是慢,而是在运行时不可用:官方 removeBundle 对任何已启动的 bundle 答 not-removable(startedBundles → management-required),于是"覆盖安装/卸载一个正在跑的 插件"在官方路径上只能靠重启绕过;自洽写入没有这个限制,改完下次启动生效。

基准脚本:node scripts/bench-install.mjs [dpk 路径] --runs 3(用临时 DSH home,不碰真实 store/profile)。

安全模型

  • 完整性有,真实性没有:DPK 能证明"运行时内容自打包后未被改动",不能证明"是谁打的包"。 v1 没有签名(dpk.json 顶层出现 signatures 会被拒绝,字段名留给 v2)。
  • 解包前逐路径校验 + 解包后 realpath 复核,双重防 zip slip。
  • 四重炸弹护栏(条目数/单文件/总量/压缩比)。
  • verify 不执行包内代码、不加载任何模块。

已知边界(都是刻意的,写清楚比藏着好)

  1. 归档永不嵌套:包内出现任何 .dpk 文件都会被拒绝打包(PACKAGE_NESTED_ARCHIVE)——构建产物 必须放在 dist/、dpk-dist/(这两个目录永不入包)或包外。历史归档里的嵌套杂物在导入时被自动剥离,store 只落一个干净包。
  2. 本地分发的包在安装期归入 @local/ 作用域(如 @local/example-provider、@local/dsh-reverse-skill)。 归档携带包的本名,@local/ 由 dpk 在安装动作里补到 store 副本、patch 行名、账本与数据卷根上; 源目录与归档清单都不写入作用域。loader 行 id 与模块自身的注册 id 不在自动改写之列——带 client 半区的包请让这些 id 直接使用 scoped 名(@local/example-provider 是完整范例)。
  3. 一个 DPK 只装一个包。 套件请打成多个 DPK。多包归档(packages/<name>)留给 v2。
  4. 带本地 link: 依赖的包不能单独分发。 归档只携带一个包,其 dependencies 里 link:../sibling 这样的相对目标在目标机器上不存在。要分发这类包,二选一: 把它做成自包含(把共享代码并进包内,参见 examples/hello-bundle), 或者把被依赖的包也各自打成 DPK 并按顺序安装。
  5. 补丁文件只做结构校验:DPK 不实现 YAML 解析器,只证明 cordis.patch.yml 看起来是顶层数组; 完整语义由 DSH 在挂载时校验(verify 输出里标为 note)。
  6. 可执行位不承诺保留:包内文件以 0644 写入 zip,Windows 源码树本来也没有 POSIX 权限。
  7. Node ESM 模块缓存的既有约束:安装/替换包后要让新的 JS 代次生效,仍需重启 harness—— 这是 DSH 侧行为(见 harness 文档),DPK 不绕过。
  8. 归档与读取器必须版本对齐:dpk.json 的 generator 记录写出归档的实现版本。同一个 dpk: 1 之下语义改过不止一次(数据卷声明的落点、@local 的写入时机),而读取端对未知字段一律拒绝, 于是"在别的机器上导入失败"多数是两端版本不同,而不是包坏了。报错末行会写明 archive written by …; this reader is …:两侧不同就先对齐工具版本,再怀疑包本身。 打包器与校验器现在跑同一套交叉校验,所以本机 pack 成功即意味着导入方过校验(前提是导入方 也已是同一代实现)。

目录

dpk/
  package.json                 npm/DSH 包清单,入口与导出均指向 src/
  src/index.js                 DSH 插件入口:注册会话内 `dpk` 工具(零 harness 导入)
  src/client.js                「本地 DPK」侧栏面板(调试模式 / 导入 / 导出 / 迁移 / 卸载)
  src/host-service.js          面板后端:Typert Remote 服务(dpk.* 七个方法)
  cordis.patch.yml             bundle 层:插入 Loader 行 dsh-dpk-manager
  locale/{en,zh}.json          插件卡片文案(zh 标题即「DSH 安装包管理助手」)
  icon.svg                     插件卡片图标
  SPEC.md                      格式规范 v1(设计正本)
  README.md                    本文件
  src/lib/zip.mjs              纯 Node zip 读写(store+deflate、CRC32、路径与限额校验)
  src/lib/dsh-package.mjs      DSH 包严格合规(逐条镜像 DSH 规则,带出处)
  src/lib/dpk-manifest.mjs     dpk.json 构造/校验 + 声明交叉校验 + 完整性摘要
  src/lib/pack.mjs             pack
  src/lib/verify.mjs           verify(含深度合规复检;extractTo 时校验与解压合成一次)
  src/lib/store.mjs            内容寻址仓库 + 账本
  src/lib/install.mjs          安装编排:解包(单次解压校验)+ profile 写入/官方服务交接 + 记账
  src/lib/profile-install.mjs  自洽 profile 写入:依赖行、dsh.profile.bundles、node_modules 链接、lockfile importer 行
  src/lib/profile-policy.mjs   profile 的 pnpm-workspace.yaml 冷却期豁免写入(仅 via: "service" 需要)
  src/cli.mjs                  npm 安装后的 `dpk` 命令行入口(薄适配层)
  src/lib/cli.mjs              命令核心:工具、面板与 CLI 共用的动作编排
  src/lib/actions.mjs          兼容导出:转发到 cli.mjs,不重复实现命令逻辑
  schemas/dpk-1.schema.json    dpk.json 的 JSON Schema
  examples/hello-bundle/       自包含示例包(发布时用 `dpk build` 打包成 dpk-dist/dpk-hello@1.0.0.dpk)
  scripts/deprecate-previous.mjs  npm 发布后自动废弃旧版本
  test/                        测试(一致性清单 + 动作层 + 自洽写入 + autoremove + cache 生命周期 + schema 对齐)

测试

npm test

npm 发布规则

仓库使用 npm 的 postpublish 生命周期自动收敛版本:每次 npm publish 成功后, scripts/deprecate-previous.mjs 会执行 npm deprecate name@<current-version, 将所有低于当前版本的旧版本标记为废弃,并提示用户升级到刚发布的版本。当前版本及未来版本不会被匹配。 脚本沿用 publishConfig.registry,因此发布和废弃操作使用同一个 registry;若废弃操作失败,发布命令会报告失败, 便于立即补处理。发布时不要使用 --ignore-scripts。

覆盖 zip 往返/可复现/CRC 篡改/路径攻击/加密/zip64/未知方法/炸弹护栏、DSH 合规的每一类拒绝、 清单严格性与摘要重算、安装的幂等、失败不留账本、 --force 重建、--keep-archive 留存,以及 pnpm 冷却期豁免的写入/保留/幂等。 自洽写入一侧另有专门一组:依赖行/dsh.profile.bundles/node_modules 链接/lockfile importer 行四处是否落对、 重复安装是否零写入、非 bundle 依赖不进 bundles 列表、链接指向别处时是否判否、以及拒绝替换一个真实目录。 数据卷一侧另有两条不变式:同一份声明的不同书写形式(键序、卷的列表顺序、路径尾斜杠)必须同判, 且打包器的产出必然通过校验器的交叉校验。

无对应
dpk 自有选择:谁去写 profile(自己写,还是交给官方管理器跑 pnpm)
exports
  • 验真:dpk 工具 action=verify file=…,不需要执行包内任何代码。
  • 已有(update 只报告)
    apt-mark hold / pinning不带刻意不借:autoremove 之后重装一个 .dpk 约 0.3 秒,为"留住旧版本"加一份状态不值
    仓库索引 / sources.list / 优先级不带不需要:dpk 装的是本地文件,没有要解析的远端
    packages/boot/app-boot/src/package-meta.ts:10,15-19,24-39,101-122
    pack/verify 逐条镜像这些规则,不满足直接拒绝
    包名文法与长度上限install-spec.ts:31-32dpk.json.name 与 package.json.name 必须一致且通过同一文法
    DSH 读元数据时不加载插件代码package-meta.ts 模块注释verify/show 同样不执行包内任何代码

    相关插件

    继续浏览 developer-tools 分类下经过校验的插件。

    Client Ui Git Graph@linxin666/dsh-client-ui-git-graph外部 dsh Web GUI 插件:空会话 Git 分支选择器和 Git 图,包含实际的主机端 Git 操作与防护,作为 dsh 配置文件包Ssh@linxin666/dsh-sshdsh Web GUI 的远程 SSH 操作:主机配置存储(~/.dsh/dsh-ssh.json,可从 ~/.ssh/config 导入)、支持跳板主机的持久化 ssh2 连接池、exec / PTY Web 终端 / SFTP 传输 / 本地端口转发隧道 / 集群执行Update@linxin666/dsh-update用于 dsh Web GUI 的自更新插件:探测 npm 上是否有更新的系列版本,并在原地运行所属配置的 pnpm 更新;可通过设置图标旁侧边栏中的触发器启动,并显示发布说明面板Cc Safety Netcc-safety-net编码代理 CLI 钩子——阻止破坏性命令和访问机密文件