dsh-comfyui-based-tools
Đã xác minh@liaozhi/dsh-comfyui-based-tools · v0.8.6 · MIT · Giao diện web
DeepSeek Harness plugin: 把 DSH 工具接到用户自备的 ComfyUI 实例(实例管理 + 工具分配 + MiniMax H3 视频 / Qwen-Image 图像工具)。
Cài đặt
dsh plugin add @liaozhi/dsh-comfyui-based-tools Xác nhận layer đã áp bằng dsh --profile default --dump-config — xem hướng dẫn cài plugin.
Mã nguồn
Phát hành lên npm mà không có repository công khai. Hãy kiểm tra nội dung package trước khi cài.
Thẻ
Readme
dsh-comfyui-based-tools
支持范围:本插件仅适配 DeepSeek 桌面版(desktop profile)的 Harness runtime。 当前支持的 DeepSeek 桌面版版本:
0.2.0-rc.1(版本门禁[MIN_HARNESS_VERSION, MAX_HARNESS_VERSION]=[0.2.0-rc.1, 0.2.0-rc.1]单点窗口,见src/version-gate.ts;桌面版应用与 runtime 锁步同版发版, 插件 peer 声明^0.2.0-rc.1)——桌面版版本不匹配时插件启动即软禁用, 设置里不再有本插件分区,改在「插件」页本插件详情页出现「已停用」说明面板 并给出版本指引,不影响 DSH 启动与其他插件。dsh web、自定义 profile、SDK/headless 等形态一律不支持——启动时 profile 门禁 (src/profile-gate.ts)会自动软禁用本插件。下文沿用的dsh web开发流示例是 插件模板的历史写法,实际部署与运行请以桌面版为准。
DeepSeek Harness 插件模板:双端(host + client)HMR 开发,开发完成后可直接发布到 npm 或推到 GitHub 供用户安装。
模板自带三个可删改的示例功能,覆盖两端与端间调用:
- Host half(
src/index.ts):注册模型可调用的greet工具,演示inject/Configschema / 事件监听; - Client half(
src/client/index.ts):在「插件」页本插件详情页注册配置面板(plugins.bundle.config槽位),演示 slot 组合、locale 词典(zh/en 双语键集)、CSS Modules 样式与浏览器原地热替换; - Host↔Client 调用(
src/remote.ts+src/client/api.ts):面板上的"调用 host"按钮经 Typert Gateway 调用 host 半的comfyui-tools/ping端点,演示第三方插件的远程调用形态(为什么是手写描述符而非官方@Remote装饰器,见src/remote.ts头注释)。
使用本模板
通过 GitHub 的 "Use this template" 创建新的插件仓库(仓库名随意),克隆到本地后, 先改名。改名前搞懂插件身份由三样东西组成,它们互相派生:
| 概念 | 本模板取值 | 怎么派生 |
|---|---|---|
| 短插件 id | dsh-comfyui-based-tools |
= 仓库名(use-this-template 建库时你起的名字;克隆到本地即根文件夹名) |
| npm 包名(scoped) | @liaozhi/dsh-comfyui-based-tools |
@<scope>/ + 短 id;@scope = 你的 npm 用户名 |
| 环境前缀 | DSH_COMFYUI_BASED_TOOLS |
短 id 全大写、-/. 转 _ |
@scope 即 npm 用户名(npm whoami 可查,未登录先 npm login)——本插件取 @liaozhi,
故包名为 @liaozhi/dsh-comfyui-based-tools。
改名 = 换这三样东西,各有固定落点。漏改不报错——这些标识只是注册 key,构建/加载 都不校验一致性,漏改只会让对应功能静默失效(面板不出现、HMR 不重载、RPC 404), 所以必须逐表核对。建议执行顺序:先全局换短 id(包名里的短 id 会一并变)→ 再换 3 处 scope → 再换前缀 → 最后核对顺手项。
① npm 包名(scoped)—— 只 3 处
| 位置 | 改成 |
|---|---|
package.json 的 name |
@<新scope>/<新短id> |
cordis.patch.yml 的 name: |
"@<新scope>/<新短id>"(必须加引号,YAML 里 @ 是保留指示符;按它解析到包 exports['.']) |
tsdown.config.ts banner 的 __ModuleLoader__ id |
同上(client 按包名发现) |
② 短插件 id —— 其余 15+ 处
| 位置 | 改成 |
|---|---|
cordis.patch.yml 的 id: |
<新短id> |
src/index.ts 的 export const name + Typert package: 字段 |
同上(cordis 插件名) |
src/client/index.ts 的 export const name + 两处 slot id |
同上 |
src/remote.ts 的端点描述符 id(<短id>#…) |
同上(Typert 关联) |
src/invariant.ts 的 PACKAGE_NAME / name |
同上(伴生插件名) |
tsdown.config.ts 的 CSS 虚拟模块前缀 + 插件名 |
同上 |
③ 环境前缀 —— 2 处 + 各文件错误/日志前缀
| 位置 | 改成 |
|---|---|
src/version-gate.ts 错误前缀、DSH_<前缀>_STRICT 变量名 |
<新前缀> + _STRICT |
src/shared/disabled-flag.ts 的 __DSH_<前缀>_DISABLED__ 变量名 |
__<新前缀>_DISABLED__ |
src/self-update.ts 的错误/日志前缀(dsh-comfyui-based-tools: / [dsh-comfyui-based-tools]) |
换成新短 id(包名/版本本身从自身 package.json 读,无需改) |
④ 顺手项
| 位置 | 改成 |
|---|---|
cordis.dev.yml 的 root 绝对路径(root 列表 + 顶部用法注释) |
你机器上实际的 lib/ 路径 |
src/client/locales.ts 的 title(配置页标题;插件页条目名与导航 label 由插件页按包清单自带,无需 locale key) |
你的 UI 标识(如 "Meme 生成") |
src/shared/update-contract.ts 端点 namespace('comfyui-tools/…')与 src/remote.ts 的 namespace / serviceKey / service |
与 Typert 服务名同源,改服务名时一并改 |
LICENSE 版权行 |
你的名字 |
README.md / AGENTS.md / 示例文案里出现的旧短 id |
同上 |
验证
pnpm install(自动跑prepare完成首次构建)pnpm typecheck && pnpm build通过git grep <旧短id>与git grep <旧包名>,全仓应无残留- 若该目录此前已装进 profile:重跑
dsh plugin --profile web add
开发(HMR)
一次安装进 profile(client half 的发现依赖包名解析,必须安装而非 --patch 插路径):
# 安装了 dsh CLI:
dsh plugin --profile web add <本模板目录的绝对路径>
# 或用 harness 源码仓库:
cd <deepseek-harness 仓库> && pnpm dsh plugin --profile web add <本模板目录的绝对路径>
每个开发会话两个终端:
# 终端 1:构建监视器(tsc --watch + tsdown --watch,持续重写 lib/)
pnpm dev
# 终端 2:harness(叠加开发 overlay,重新启用 HMR 并监听 lib/)
dsh web --patch <本模板目录>/cordis.dev.yml --port 3081
# 或源码仓库里:pnpm dsh web --patch ... --port 3081
然后:
- 改
src/index.ts(host 半) → tsc 重写lib/index.js→ cordis-plugin-hmr 重载插件 → 终端打印新的host half loaded日志。在 Web UI 让 agent 调用greet工具验证。 - 改
src/client/index.ts(client 半) → tsdown 重写lib/client.js(<100ms)→ host 的 client-hmr 轮询发现 → SSE 广播 → 浏览器不刷新页面就地更新。打开http://127.0.0.1:3081→ 插件页 → 本插件详情页 → 配置面板可见。
无浏览器验证 client 链:curl -N http://127.0.0.1:3081/plugins/events,改 client 源码会实时收到 data: {"type":"rebuilt","id":"dsh-comfyui-based-tools",...}。
安装版 dsh 注意:cordis-plugin-hmr 需要读 Node 内部模块加载器。若启动报
--expose-internals is required for HMR,用NODE_OPTIONS=--expose-internals dsh web ...启动即可(从源码运行的pnpm dsh自带 tsx,无此问题)。
依赖版本与追新
harness 处于 0.1 rc 快速迭代期,要点:
- peer 与 dev 写同一个
^<已实测的最低版本>:dev = 编译所用版本(typecheck 类型面),peer = 运行时声明。窗口内没有"下限/上限/并集"之分——不要用范围去"夹"多条 harness 线(预发布线内跨版本 API 破坏频繁,范围放宽救不了,取证见docs/dsh-0.1.5-rc.1-upgrade-report.md与docs/compat-plan-0.1.7-rc.1.md)。caret 在预发布线上只覆盖同一major.minor.patch的更高预发布号,所以^0.2.0-rc.1覆盖 0.2.0 线内更高预发布号——同线内追新(如 rc.1→rc.2)默认不需要改依赖,只抬 MAX;跨 minor 迁移(如 0.1.7→0.2.0)caret 不覆盖新线,MIN/MAX 一并上移 + 依赖下限同步改写(peer precheck 按 satisfies(includePrerelease) 校验,旧 caret 对新线 runtime 直接 false → 插件被声明面禁用);只有同线内 MIN 上移(目标 tag 被上游取代 / 旧 MIN 从未通过运行时验证)时才把依赖下限同步改写到新 MIN。 - harness 版本门禁:模板自带
src/version-gate.ts,要求 harness 版本落在[MIN_HARNESS_VERSION, MAX_HARNESS_VERSION]双边界窗口内(compat/0.2.0-rc.1 分支当前[0.2.0-rc.1, 0.2.0-rc.1]单点——跨 minor 迁移按 peer precheck 的 caret 硬约束一并上移;main 分支持已验证的[0.1.7-rc.2, 0.1.7-rc.2])。另有src/profile-gate.ts:无 profile 启动(SDK 嵌入等)或非 desktop profile 时插件软禁用——本插件仅支持 DeepSeek 桌面版(desktop-only 收敛,取证见该文件头注释)。0.1.7 起 harness 自身也做插件兼容性检查(peer 依赖 semver satisfies(includePrerelease) 校验,不满足的插件行在启动组装期直接 disabled),插件自身的精细窗口门禁仍在 apply 时执行,两者分工见 version-gate.ts 头注释。版本源 = runtime 内@deepseek-ai/dsh包 version(桌面版应用与 runtime 锁步同版发版,语义与dsh --version一致);【不能】读@deepseek-ai/dsh-tools等内嵌依赖当 harness 版本:caret 语义下实装版本可能高于 CLI 本体(实测@deepseek-ai/[email protected]内嵌的 dsh-tools 已解析到 0.1.5-rc.2,读它会误判软禁用)。解析锚点 = 桌面单锚:桌面版 host 子进程由 Electron spawn,process.argv[2]= runtimeDir(launcher 硬契约,apps/desktop/src/host-process.ts),读<argv[2]>/node_modules/@deepseek-ai/dsh/package.json并校验 name。落在窗口外默认软禁用:醒目错误日志 + 插件不注册任何业务能力,不影响 DSH 启动;DSH_COMFYUI_BASED_TOOLS_STRICT=1恢复抛错 fail-loud。软禁用时 host 半经webserver/index-inject向页面注入__DSH_COMFYUI_BASED_TOOLS_DISABLED__标记(fiber-bound,卸载即撤、无残留),client 半读到后改挂"已停用"说明面板并展示支持版本窗口(src/shared/disabled-flag.ts是两侧共享的标记契约)。
追新流程(手动,无脚本):
版本抬升刻意做成显式手改——目标线选择、破坏面评估、门禁窗口三件事本来就该人工过目(实测教训:0.1.x 线内 0.1.1 → 0.1.2 这种带破坏性变更的跨 patch 追新会被脚本静默扫进来)。追新的每一步都值得慢下来看一眼:
# ① 查目标线的最新版本(各 @deepseek-ai/dsh-* 与 harness 同步发版):
pnpm view @deepseek-ai/dsh versions --json
# ② 手改 package.json:全部 @deepseek-ai/dsh-* 的 peer 与 dev 改为同一个 ^<目标版本>
# (窗口内没有并集写法;新线才有、只 import type 的包只进 dev)
# 同 minor 线内追新(如 rc.1→rc.2)caret 已覆盖,默认无需改依赖,直接跳到 ④;
# 仅当 MIN 也要上移(旧 MIN 从未实测 / 已不是发布版)时才同步改写依赖下限
# ③ 同步与验证:
pnpm install
pnpm typecheck && pnpm build # 红 = 上游破坏性变更,按升级报告的取证清单修
# ④ 实测通过后手动上调 src/version-gate.ts 的 MAX_HARNESS_VERSION
# (MIN 保持已实测的最低版本;窗口保持双边界结构)
# ⑤ 同一轮同步指引:AGENTS.md / README.md / version-gate.ts 头注释里的版本表述
# ——留旧窗口 = 让后续 agent 照旧窗口写代码。
- 不用
pnpm update:它会剥掉 rc 包的^(实测),且不碰 peer 侧。 - 窗口 = 已验证承诺:抬 MAX 等于宣布「插件已在那个版本上验证过」,没实测过的版本不要抬;MIN 保持已实测的最低版本(compat/0.2.0-rc.1 分支当前
[0.2.0-rc.1, 0.2.0-rc.1])。MIN 上移 = 收缩承诺面:跨 minor 迁移必做(peer precheck 的 caret 不跨 minor 线,双窗口需 OR 范围夹线、被禁止);同线内只在旧 MIN 从未通过运行时验证或已不是发布版本(被 npm dist-tag 接任)时做,且须同轮把依赖 caret 下限改写到新 MIN(0.1.7-rc.2 追新即此情形)。 - 锁文件会钉住首次安装版本:想每次克隆都最新就删掉它,否则按上面流程主动追新。
- 注意 caret 语义会匹配同 tuple 的更高预发布(
^0.2.0-rc.1会解析到 0.2.0 线内更高的 rc/正式版)——这正是同线追新无需改依赖的理由:与门禁窗口重合。反之 caret 不跨 minor:^0.1.7-rc.2对 0.2.0-rc.1 runtime 为 false(实测 satisfies(includePrerelease)),跨线迁移必须改写依赖下限。
多线兼容(默认不做):
双线/多线支持(peer 并集 + dev 钉线 + 特征探测回退)是 0.1.1→0.1.2 时代的做法,本插件已收敛为单条线(main = 0.1.7 线、compat/0.2.0-rc.1 = 0.2.0 线;0.1.5 线已随 desktop-only 收敛放弃),不再维护。历史方法论仅存档于 docs/compat-guide-0.1.1-to-0.1.2.md(已加归档标注),仅在确需重新引入多线时参考,并须在 AGENTS.md 与迁移计划里明确标注为多线。
插件自更新
配置页(「插件」页本插件详情页)标题行右侧内置「检查更新 / 立即更新 / 取消」条(client 半 ComfySection.tsx 六态状态机 + host 半 src/self-update.ts,经 comfyui-tools/checkUpdate / comfyui-tools/updateSelf / comfyui-tools/cancelUpdateSelf RPC):
- 检查:当前版本 vs npm registry
/<pkg>/latest(严格 semver 比较,含预发布规则);候选源沿用官方 registryPlan 语义逐源降级(pluginManager 服务registries()计划,npm → npmmirror,registry 类失败转问下一候选);面板 mount 时静默查一次,离线不打扰、手动可重试; - 更新:一律走官方
pluginManager服务通道ctx.pluginManager.installBundle(<pkg>@<ver>)(harness 0.1.7 起 base bundle 内置的 cordis 服务,与dsh pluginCLI / Web「插件」页同源同锁;同 pnpm 通道 + profile manifest 写锁)。进程内直达、无子进程:白送 registry 回退计划、失败/取消自动还原package.json/pnpm-lock.yaml、安装期兼容性预检;成功后提示重启 harness 生效(对已装包做版本更新时服务明确不热重载,返回restart-required——与旧 CLI 通道同语义); - 取消 + 流式日志:安装中可点「取消」(服务端
cancelInstall硬中断 pnpm,退出并还原 profile 文件后才落定;预取阶段由协作检查点接住);pnpm 输出与阶段通告经plugin-manager/install-log/install-state事件(按 requestId 过滤)喂给 host 日志环形缓冲,面板实时展示,失败/被取消后保留供排查; - 前提:插件必须已发布到 npm。GitHub-only 分发(未发 npm)时 registry 检查失败——面板静默/手动重试,属预期行为;
- 软禁用态没有更新条:版本门禁软禁用时 host 半不注册任何端点(RPC 必然 404),停用面板只给 harness 版本修复引导——插件更新不了 harness,也不该试图更新;
- 并发与竞态:重复点击合并进同一次安装(host 半 in-flight 锁 + 相位单一写者);点更新时 latest 回落到 ≤ 当前则归位「已是最新」;更新只换磁盘,需重启 harness 加载新 host 半。
自更新测试桩(verdaccio 本地 registry)
npm 没有测试端点(版本号一次性,测试版本不能真发),自更新测试走本地 verdaccio(npm 官方文档推荐的本地 registry):
scripts/verdaccio.config.yaml:匿名 publish + npmmirror 上游代理(被测包的@deepseek-ai/*peer 依赖自动走代理);关键设计——被测 scope@liaozhi/*故意不配 proxy,版本空间完全本地化,上游真实已发布版本不会混进 dist-tags 干扰测试;scripts/update-stub.mjs:发布测试版本(临时改 version →pnpm pack(prepack 自动 build)→ 自动注册测试用户拿 token →npm publish --tag latest→ finally 恢复 package.json)。
PowerShell 测试步骤:
# ① 起服务(仓库根目录;storage 落在 .update-stub/,已 gitignore)
npx --yes verdaccio@6 --config scripts/verdaccio.config.yaml
# ② 发布测试版本(每次换新版本号——同版本重复 publish 会被拒,unpublish 也不释放)
node scripts/update-stub.mjs publish 0.5.2-test.1
# ③ 插件侧:设覆盖变量后同终端启动 harness
$env:DSH_COMFYUI_UPDATE_REGISTRY = "http://127.0.0.1:4873"
dsh web
面板「检查更新」应报测试版本 →「立即更新」(看日志面板 / 试「取消」按钮)→ 重启 harness 生效。测完 Remove-Item Env:DSH_COMFYUI_UPDATE_REGISTRY 恢复官方源。
两个前提:DSH_COMFYUI_UPDATE_REGISTRY 只被本仓库源码里的新版 self-update 读取——需以 --patch 开发 overlay 或 link 方式运行仓库源码才生效;私有源被 harness 单独询问、不回落公共源,桩挂了检查/安装就失败(不会静默装到真包,隔离性是刻意的)。测后切回 link 开发模式的标准姿势:先 dsh plugin --profile <name> remove @liaozhi/dsh-comfyui-based-tools 再 dsh plugin --profile <name> add C:\agentProjects\dsh-comfyui-based-tools\(先 remove 再 add,绕开 pnpm 的 junction 覆盖缺陷)。
发布到 npm
npm login # 首次
pnpm publish # prepack 会自动完成构建
files 白名单只带运行必需的产物(lib/index.js、lib/invariant.js、lib/client.js、cordis.patch.yml),发布前建议 pnpm pack 检查内容。用户安装:
dsh plugin --profile web add dsh-comfyui-based-tools
上传到 GitHub
直接 push 源码即可(不要提交 lib/,它在 .gitignore 里)。本模板带了 prepare 脚本:用户从 git 安装时 pnpm 会用它现场构建产物(自包含,只依赖 npm 上的公开包)。
用户侧安装分两步(pnpm ≥10 的构建许可要求):
# 1. 首次 add 会被 pnpm 拒绝构建脚本,按提示把包名加进 profile 的
# pnpm-workspace.yaml(~/.dsh/profiles/<profile>/pnpm-workspace.yaml):
# allowBuilds:
# dsh-comfyui-based-tools: true
# 2. 重新执行
dsh plugin --profile web add github:<owner>/dsh-comfyui-based-tools#<commit-sha>
构建许可 = 允许安装时执行该包的代码,提醒你的用户只对他们信任的包开启,并建议 pin commit。 不想让用户配许可的话,也可以发 npm 或提供
pnpm pack产物(tarball 安装不跑构建)。
原理速记
- 一切都是 Cordis 插件;通过
ctx注册的都是可逆 effect,卸载自动清理——这是 HMR 热替换的前提。 - Host 半 HMR:
@deepseek-ai/cordis-plugin-hmr(dsh-base 已挂载,web 模式默认禁用,cordis.dev.yml按 id 重启用)监听构建产物,沿 Node 模块图重载受影响的插件条目。 - Client 半 HMR:
dsh.client声明让modules服务把包扫进window.__DSH_BOOT__并以/plugins/<id>/client.js供给;client-hmrstat 轮询 bundle 变化 → SSE 广播 → 浏览器按"invalidate → prefetch → 卸载旧 fiber → 物化新工厂"原地热替换。触发源是任何重写lib/client.js的进程,本模板用tsdown --watch。 - 分发形态即开发形态:
exports['.']恒指向lib/index.js,npm 包、git 安装、本地开发走同一条加载路径,没有"源码能跑、装上就挂"的落差。 - 依赖规则:
@deepseek-ai/*一律进peerDependencies(运行时由 harness 安装环境提供;dsh profile 的 pnpm 工作区是autoInstallPeers: false+ 安装目录兜底解析,不会为你的 peer 链去 npm 拉包),普通第三方库进dependencies,构建工具进devDependencies。不要把@deepseek-ai/*放进dependencies。 - HMR 只覆盖 link 开发的场景:cordis-plugin-hmr 沿 Node 模块图追踪时排除路径含
/node_modules/的模块,所以 npm/git 安装进 profile 的实体副本不参与热重载(它们是给最终用户的不可变产物);dsh plugin add <本地目录>是符号链接,realpath 后跳出 node_modules,才能被追踪。 dsh.client声明是包元数据,扫描缓存永不过期:改它要重启 harness;lib/内容变化才走热替换。--patchoverlay 不在运行时监听列表里,改cordis.dev.yml需重启;profile 的cordis.patch.yml和$DSH_HOME/cordis.patch.yml才是热监听的。
已知坑(模板已绕过,建议反馈上游)
Windows 下 cordis-plugin-hmr 默认的 ignored 含 **/.*,而 hmr 用 picomatch 匹配 path.relative() 的反斜杠结果时反斜杠被当转义符,..\.. 前缀会误命中 **/.*——base 目录之外的监听 root 被静默吞掉。cordis.dev.yml 用 ignored: [] 绕行。上游正解:匹配前归一化分隔符(或 picomatch windows: true)。
文件清单
├── src/index.ts # host half 入口(版本门禁 + greet 工具示例 + Typert 端点注册)
├── src/profile-gate.ts # profile 启动门禁(profileContext 判空;无 profile 启动软禁用)
├── src/version-gate.ts # harness 版本门禁(软禁用语义 + CLI 锚点解析 + semver 比较)
├── src/self-update.ts # 插件自更新 host 半(registry 多源检查 + 官方 pluginManager 服务通道)
├── src/shared/disabled-flag.ts # host→client 的软禁用标记契约(全局变量名 + 载荷形状)
├── src/shared/update-contract.ts # 自更新 RPC 契约(端点名 + 请求/结果类型,两端共用)
├── src/remote.ts # host 半 Typert 远程端点(comfyui-tools/*,表驱动描述符 + 第三方安全形态)
├── src/invariant.ts # 官方 invariant 伴随件(每包必有)
├── src/css-modules.d.ts # CSS Modules 导入声明(*.module.css)
├── src/client/index.ts # client half 入口(词典/样式/槽位注册组装 + 停用标记分支,无 JSX)
├── src/client/locales.ts # zh/en 词典(所有 UI 文案走 locale key)
├── src/client/api.ts # 浏览器 → host 的 RPC 调用(OpResult 统一错误面)
├── src/client/ComfySection.tsx # 配置页组件(plugins.bundle.config 面板 + 插件更新条)
├── src/client/ComfyDisabledSection.tsx # 版本不兼容"已停用"说明面板
├── src/client/ComfySection.module.css # 配置页样式(CSS Modules + --dsw 设计令牌)
├── docs/compat-guide-0.1.1-to-0.1.2.md # 0.1.1↔0.1.2 兼容手册(两线差异取证 + 带代码方案)
├── docs/compat-plan-TEMPLATE.md # 兼容迁移执行记录骨架(每次追新 cp 一份填写)
├── docs/compat-plan-0.1.7-rc.1.md # 0.1.5-rc.3→0.1.7-rc.1 兼容迁移执行记录
├── docs/compat-plan-0.1.7-rc.2.md # 0.1.7-rc.1→0.1.7-rc.2 同线追新执行记录(main 窗口)
├── docs/compat-plan-0.2.0-rc.1.md # 0.1.7-rc.2→0.2.0-rc.1 跨 minor 迁移执行记录(当前窗口)
├── scripts/dev.mjs # pnpm dev:并行两个构建监视器
├── tsdown.config.ts # client bundle 构建(CJS 工厂 + 基线外部化 + CSS Modules 内联)
├── tsconfig.json # 全量类型检查(pnpm typecheck)
├── tsconfig.build.json # host 半构建(lib/index.js + lib/invariant.js)
├── cordis.patch.yml # bundle 层:安装时插入插件行
├── cordis.dev.yml # 开发 overlay:重启用 HMR 并监听 lib/
└── package.json # dsh.bundle + dsh.client 双清单,prepare/prepack 构建