🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

插件双面构建器 janpauldahlke/dsh-build-tools

DeepSeek Harness
janpauldahlke16 天前有提交已提供中文文档扫描:含敏感能力

一次配置产出宿主与客户端双份插件包

29.9/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/10/16 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add janpauldahlke/dsh-build-tools
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:待复验
是什么
dsh 原生插件 · dev
装得上吗
曾在 dsh 0.2.0-rc.2 下真实安装成功,但当前 dsh 已更新至 0.2.1-rc.5,本站尚未在新版下复验
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 16 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

读写/删除本地文件
查看扫描证据(共 4 处,展示前 4 条)
  • 写入本地文件
    test/harness.mjs:155
    await mkdir(ev, { recursive: true })
  • 写入本地文件
    test/harness.mjs:156
    await writeFile(join(ev, evidenceName), `${log.join('\n')}\n`, 'utf8')
  • 读取本地文件
    bin/verify-plugin.js:48
    ;[candidate, baseline] = await Promise.all([readFile(candidatePath), readFile(baselinePath)])
  • 删除本地文件递归
    bin/verify-plugin.js:75
    await rm(tmp, { recursive: true, force: true })
扫描于 2026/10/31(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

README

由 DeepSeek 最新模型翻译生成
dsh-build-tools 为 DeepSeek Harness(dsh)插件准备的共享构建工具。一个包就能替代每个 dsh- 插件里那份复制粘贴的 build.mjs,让双面包契约只存在于一个地方。 问题所在 dsh 插件是一个 npm 包,却要同时服务两个截然不同的运行时: - 宿主面跑在 harness 内部的 node 里。它是 Cordis 插件(apply / class + inject),可以打开 ctx.webServer 路由,还会导入配置档在运行时解析的 harness 包。 - 客户端面跑在浏览器里。它不由打包器或 import map 作为模块加载——harness web 客户端会安装一个 window.__ModuleLoader__ 全局变量,每个插件脚本把自己注册进去,形式是闭包工厂:window.__ModuleLoader__.load({ id, factory })。在这个工厂里,require() 只能对着宿主冻结的模块表解析(React、cordis、dsh-client- UI 包)。其他一切都要内联进 bundle。 所以同一份源码树要产出两个产物,模块系统不同、目标不同、外部解析规则也不同——而且客户端那个必须包在精确的注册调用里,否则永远进不了 __DSH_BOOT__(往往还是静默失败)。
展开完整文档(README 剩余 1 节)
dsh-build-tools

为 DeepSeek Harness(dsh)插件准备的共享构建工具。一个包就能替代每个 dsh- 插件里那份复制粘贴的 build.mjs,让双面包契约只存在于一个地方。

问题所在

dsh 插件是一个 npm 包,却要同时服务两个截然不同的运行时:

- 宿主面跑在 harness 内部的 node 里。它是 Cordis 插件(apply / class + inject),可以打开 ctx.webServer 路由,还会导入配置档在运行时解析的 harness 包。
- 客户端面跑在浏览器里。它不由打包器或 import map 作为模块加载——harness web 客户端会安装一个 window.__ModuleLoader__ 全局变量,每个插件脚本把自己注册进去,形式是闭包工厂:window.__ModuleLoader__.load({ id, factory })。在这个工厂里,require() 只能对着宿主冻结的模块表解析(React、cordis、dsh-client- UI 包)。其他一切都要内联进 bundle。

所以同一份源码树要产出两个产物,模块系统不同、目标不同、外部解析规则也不同——而且客户端那个必须包在精确的注册调用里,否则永远进不了 __DSH_BOOT__(往往还是静默失败)。

这层包装不是这些插件发明的黑科技。它复刻的是 harness 对自己树内客户端包做的事:packages/client/tsdown.client.ts 产出的注册 banner、开头的 var module = { exports: {} }、结尾的 return module.exports; } }); 完全一样。树外插件只能手工复现这份契约,因为没有任何受支持的方式能让 harness 帮你打包。

真正的问题在于每个仓库都手工复现一遍:banner/footer 字符串、平台 externals 列表、宿主/客户端目标拆分,还有给单元测试用的额外独立 bundle,在七个插件之间复制粘贴。契约一改就要手动打七个补丁,而且任何一个都可能悄悄跑偏。

这个包就是那次整合。 它把契约编码一次,藏在选项后面,并用字节级 diff 门禁证明等价,而不是靠肉眼比对。

构建产物

| 产物 | 格式 | 作用 |
| lib/index.js | ESM, node, es2024 | 宿主半边(Cordis 插件);@deepseek-ai/ 保持 external |
| lib/client.js | CJS, browser, es2022 | 客户端半边;包在 __ModuleLoader__ 注册里 |
| lib/<name>.mjs | ESM, node | 可选的独立模块,构建时不带 harness externals |

第三行让逻辑变得可测:你的 store/config/tool 代码拿到自己的 bundle,里面没有 cordis,所以 node --test 可以直接导入它,不用去 mock harness。

安装

npm install --save-dev dsh-build-tools esbuild

esbuild 是 peer 依赖——插件自带锁定版本,和以前一样。

使用

插件的 build.mjs 会变成这样:

import { buildDshPlugin } from 'dsh-build-tools'

await buildDshPlugin()

默认配置覆盖常见形态(src/host/index.ts → lib/index.js,src/client/index.tsx → lib/client.js)。其他情况都走选项:

import { buildDshPlugin } from 'dsh-build-tools'

await buildDshPlugin({
// extra pure-logic bundles for node --test
modules: [
{ entry: 'src/host/store.ts', out: 'store.mjs' },
{ entry: 'src/host/config.ts', out: 'config.mjs' },
],
// a native dep that must stay external in the host half
host: { externals: ['node-nvml', '@deepseek-ai/'] },
})

完整选项参考:types/index.d.ts。每一面都留了逃生通道(externals、loader、esbuildOverrides)——插件遇到特殊需求,不必去 fork 这个包。

prepare 脚本

这些插件以前用的写法是:

"prepare": "node build.mjs || node -e \"console.warn('... relying on prebuilt lib/')\""

它会吞掉构建失败,然后悄悄发布仓库里那份过期的 lib/——esbuild 版本不匹配或类型错误,最后都变成一个加载着昨天代码、还报告自己健康的插件。这里的策略正好相反:大声失败。用最朴素的写法:

"prepare": "node build.mjs"

没有软回退。 这个包曾经提供过一个(dsh-prepare-soft),后来删掉了,因为它根本不可能生效:node build.mjs 在没有工具链的安装里失败,恰恰是因为 dsh-build-tools 解析不到,而回退用的二进制又由同一个解析不到的包提供。安装于是以 command not found(退出码 127)失败,而不是给出清晰的构建错误——这比严格 prepare 更糟,不是更软。

构建失败,安装就失败。这是有意为之:插件悄悄加载昨天的 lib/ 还报告健康,比安装直接停下更糟。

注意,npm 不会为从 registry 安装的包运行 prepare——发布的 tarball 里已经带了构建好的 lib/。所以已发布的插件在没有工具链的机器上也能正常安装。prepare 只在 git 安装和 clone 时运行;那些场景需要工具链。

测试

dsh-build-tools/test 导出一个极小的零依赖测试框架——describe / it / assertEqual / assertThrows / runAll——专为在纯 node --test 下运行而设计。不用 jest,不用 vitest,不用配置文件。

实在的建议: 这个框架约 120 行。把它复制进你的仓库(比如 scripts/test/harness.mjs)是推荐的默认做法——零耦合,测试栈归你自己。子路径导入是给那些更愿意从单一上游拉取修复的仓库准备的:

import { describe, it, assertEqual } from 'dsh-build-tools/test'
为什么不选 Vitest?node --test 已经自带 runner、断言和 mock,零依赖,现有插件也都在用它。Vitest 能带来 watch 模式、快照和覆盖率——这些确实能提升开发体验,但代价是每个仓库都要多一个依赖和一份配置,只为这点语法糖,而这份代码库并不需要。如果你想要这些,harness 就是那个接缝:把 runAll 换成你选的 runner 即可。

验证迁移

把现有插件迁移到这个包上时,产出的构建产物必须保持一致。dsh-verify-plugin 就是这道关卡:

npx dsh-verify-plugin /path/to/plugin /path/to/config.mjs

它会构建到一个临时目录,然后把每个产物与已提交的 lib/ 逐字节比对。JS 必须完全一致;source map 只要 mappings/names 相同就算通过(临时目录里的 source 路径不同是正常的)。退出码非零 = 有漂移 = 不要发布。

保证与诚实的边界

逐字节一致的输出就是契约。 用默认选项时,产出的 lib/index.js / lib/client.js 与它所取代的手写 build.mjs 文件逐字节相同。现有七个插件目前全部验证通过。如果哪天必须改动,那就一次性、有意地改遍所有插件——而不是在某个仓库里意外改掉。

客户端 wrapper 与 harness 耦合,这是有意为之。 它跟随 packages/client/tsdown.client.ts,而后者并不是已发布的稳定 API。这个包并没有消除这种耦合——它把耦合集中到一个文件,并用测试锁住字节,这样 harness 一改就是一次协同编辑,而不是七次悄无声息的改动。

防御性守卫默认关闭——下面是诚实的理由。 如果 window.__ModuleLoader__ 缺失或被改名,当前 wrapper 会在 bundle 顶层抛出一个 ReferenceError,位置在宿主的 UI 线程里。client.guard: true 会把注册包进一次存在性检查加一层 try/catch,这样插件就降级为一条 console.warn,而不是把整个 UI 拖垮。

它仍然是关闭的,原因有两点:

1. 在受支持的启动路径里,harness 会在插件脚本运行之前装好 loader facade,所以守卫永远不会触发。它只在环境损坏时才会生效——而在那种环境里,控制台里一声响亮的 ReferenceError 恐怕比一条没人看的警告更有用。
2. 打开它会让所有插件同时改变产出的字节,这与迁移期间的字节一致性关卡冲突。

所以它已经实现、已经测试,只差一个开关——但要在所有插件上启用它,是一个需要深思熟虑的决定,而不是默认行为。优雅降级与大声失败之间的张力真实存在,这里也没有定论;如果你有站得住脚的立场,欢迎贡献(见 AGENTS.md)。

不是通用打包器。 它只编码了一个插件生态的一套打包约定。如果你的插件看起来不像 dsh 插件,那它不适合你。
仅构建期使用。 插件把它作为 devDependency 依赖;发布出去的插件产物里不含它的任何内容。这里发一个坏版本,不会弄坏已经发布的插件。

还没有 CSS 流水线。 harness 预设用 lightningcss 编译 .module.css;目前没有插件用到它。等有插件用了,那时再加——不是现在。

开发

npm install
npm test # node --test, includes real-esbuild integration tests
npm run typecheck
npx dsh-verify-plugin /path/to/plugin /path/to/config.mjs

AGENTS.md 是自包含的背景资料,写给智能体和从 npm 或 clone 过来的人看——双面契约、这个包为什么存在,以及那些踩过坑才学会的失败模式。

dsh 插件动物园

这个包之所以存在,是因为下面这些插件各自都手抄了一份相同的构建脚本。它们是这里所编码契约的消费者——也是契约必须支持什么的证据。每个插件都在用不同的逃生舱口,正因如此才有这些逃生舱口,而不是一个写死的构建。

| 包 | 仓库 | 它验证了什么 |
| dsh-gpu-monitor-nvml | source | 原生运行时依赖(node-nvml),通过 host.externals 保持外部化。 |
| dsh-slot-health | source | 最长的 modules 列表——六个独立可测项,横跨 host 和 client 两棵源码树,这样逻辑不用 cordis 就能做单元测试。 |
| dsh-local-long-horizon | source | 可测项由自定义 runner(scripts/run-tests.mjs)消费,而不是直接用 node --test。 |
| dsh-agent-processes | source | 共享逻辑只打包一次,两个面都导入它。带一个 ambient src/types.d.ts shim,让 tsc --noEmit 能解析 @deepseek-ai/*。 |
| dsh-local-memento | source | loader: { '.md': 'text' }——Markdown 在构建期作为字符串内联,client 和独立模块都如此。 |

如果你维护的插件用了这个包,又撞上了选项覆盖不到的情况,那正是来这里开 issue 的信号,而不是把构建脚本 fork 回自己仓库。新增一个逃生舱口,上面每个插件都受益;在单个插件里打一次性补丁,恰恰是这个包要终结的漂移。

License

MIT
预览分享卡片长什么样 ↓janpauldahlke/dsh-build-tools
分享 · DeepSeek Harness Hub
janpauldahlke/dsh-build-tools

一次配置产出宿主与客户端双份插件包

回到顶部