Web 演示的客户端插件产物构建:demo:web 为何必须先执行完整 build)
DeepSeek HarnessdshWeb 演示的客户端插件产物构建demo:web 为何必须先执行完整 build【免费下载链接】deepseek-harnessDeepSeek Harness: Everything is a Plugin.项目地址: https://gitcode.com/gh_mirrors/de/deepseek-harness在 DeepSeek Harnessdsh中dsh web启动的 Web UI 并不是一个单体前端而是前端外壳 按插件动态加载的浏览器端 bundle的组合体。本文围绕一个真实缺陷修复记录展开从干净检出的代码树运行 Web 演示时所有/plugins/id/client.js端点返回 404启动界面显示 Failed to load plugins根因是演示脚本只构建了 Vite 前端外壳而没有构建插件产物。读完后你将理解pnpm run build与pnpm run build:web各自产出什么、dsh web如何解析并分发每个插件的lib/client.js产物以及dev:web三阶段 watch 循环如何保证源码改动同步到浏览器。问题背景Web 插件的产物由谁生产、由谁分发dsh web的架构可以概括为两条独立的产物链前端外壳frontend shellapps/web下的 Vite 项目deepseek-ai/dsh-web-frontend由build:web步骤构建产出apps/web/dist。Web 客户端插件 bundle每个声明了dsh.client且platform: web的包其浏览器端入口由exports[./client]指向的lib/client.js提供这些文件由根目录完整构建pnpm run build中的tsc -b加各包tsdown.client.ts配置生成。分发侧的路由协议在 客户端模块子系统文档 中有完整描述GET/HEAD /plugins/??package-a/client.js,package-b/client.jsrevrev提供一个精确生成的组合脚本单资源请求走同一形式同时是 HMR 路径Source Map 也通过并行改写资源后缀得到/plugins/??package-a/client.js.map,...。哪些包是客户端插件在仓库里是通过声明发现的而不是手工清单。scripts/dev-web.ts 中的discoverPluginDirs扫描packages/*/*/package.json凡携带dsh.client且platform web的包即为客户端插件 bundle 产出者docs/subsystems/client-modules.md 与 packages/client/AGENTS.md 进一步说明插件包骨架必须包含./clientexport扫描器在没有该 export 时会直接抛错并需要tsconfig.client.json聚合引用、packages/bundle/web-app/cordis.patch.yml中的dsh.client行、以及packages/bundle/web-app/package.json中的依赖声明这三个注册面。产物路径的解析逻辑同样以包声明为准。测试装配代码 apps/web/tests/assembled-boot.ts 展示了dsh web使用的同一套解析规则读取pkg.exports?.[./client]字符串或default形式从包目录解析出lib/client.js的绝对路径再按组合 URL 形式/plugins/??id/client.jsrevrev广播给浏览器端加载器。缺陷表现外壳构建成功掩盖了缺失的插件产物在修复前的行为链路上demo:web与 README 的 Web UI 说明只执行了build:web。后果是在一个未预先完整构建的检出上每个插件的lib/client.js都不存在dsh web提供/plugins/id/client.js时全部返回 404浏览器端客户端加载器将所有插件标记为失败启动界面显示Failed to load plugins。这个 bug 的隐蔽点在于前端外壳能正常构建——apps/web/dist一切如常服务器正常启动、页面能打开直到浏览器运行时逐个加载插件才暴露产物缺失。换句话说构建期没有任何报错失败被推迟到了浏览器端运行时且错误形态插件加载失败与真实原因产物从未被构建相隔甚远。这正是产物链中缺了某一环时静默展示旧产物这一类故障的典型形态——scripts/dev-web.ts 的头部注释也明确承认缺一个阶段不会失败——它会静默展示上一份产物于是编辑看起来毫无效果。修复决策demo:web 先跑完整 build再跑 build:web修复方案只做了两件事demo:web在npm run build:web之前先运行npm run build确保dsh web开始分发之前各插件的lib/client.js打包产物已经存在。README 的 Web UI 小节改为针对已安装的~/.dsh/source检出执行pnpm run build pnpm run build:web因为安装器从不会构建这份检出。这里的顺序有严格的技术含义需要把仓库的构建脚本拆解开来看见 package.jsonbuild: tsx scripts/build.ts, build:lib: npm run build:lib:host npm run build:lib:client, build:lib:host: node --max-old-space-size4096 ./node_modules/typescript/bin/tsc -b tsconfig.host.json tsdown --env.DSH_BUILD_FACE host, build:lib:client: tsc -b tsconfig.client.json tsdown --env.DSH_BUILD_FACE client, build:web: pnpm --filter deepseek-ai/dsh-web-frontend run build完整链路由 scripts/build.ts 串联先执行build:libhost 面 client 面的tsc -b类型发射与tsdown打包再执行build:webVite 前端外壳构建最后记录本次构建的客户端产物清单与环境值。其中对插件 bundle 至关重要的一步是build:lib:clienttsc -b tsconfig.client.json发射出lib/types——tsdown 的 lib entry 就是这个发射产物而不是src随后tsdown --env.DSH_BUILD_FACE client按各包的tsdown.client.ts配置如clientBundle(id, [lib/types/index.js, lib/types/invariant.js])形式打包出lib/index.js与lib/client.js。所以先build再build:web本质上是在保证 tsdown 的 client 打包趟先于 Vite 外壳构建完成——外壳的模块图会引用这些已构建的 lib 产物。scripts/dev-web.ts 对此的表述是编译外壳链接的是构建后的 lib 产物而非源码。验证方式八个端点全部 200无头浏览器渲染正常修复记录的验证步骤是完整构建后全部八个/plugins/id/client.js端点均返回 200用无头 Chromium 加载http://127.0.0.1:3080即 README.md 中说明的dsh web默认地址能够渲染出外壳不再出现 Failed to load plugins 状态。这一验证路径在仓库的测试设施中也有对应物apps/web/tests/下的 e2e 与 expected 套件如 smoke-real.e2e.ts正是通过 ModuleLoader 路径加载packages/*/*/lib/client.js真实产物运行的根package.json中的test:web、test:web:perf、test:web:stress也都先执行npm run build再跑浏览器侧测试package.json与本次修复遵循同一前提——浏览器侧的一切都假定插件产物已由根 build 产出。被否决的替代方案及原因修复记录列出了两个考虑过但未采纳的方案它们的取舍逻辑本身就说明了 dsh 的源码/产物边界设计方案一在dsh web启动时构建打包产物。被否决。该应用通过 tsx 从源码运行对应 package.json 中dsh: node --import tsx/esm apps/cli/src/bin.ts本身不拥有构建步骤把产物构建塞进服务器启动流程会越过源码与产物的分离边界并且拖慢每一次启动。方案二扩大 tsdown 根配置让pnpm run build:web也产出客户端打包产物。被否决。build:web是 Vite 前端构建客户端打包产物是对lib/types的另一趟独立 tsdown 处理。把两者合并会混淆外壳构建与包构建两条流水线而且无论怎么合并根目录的build仍然是唯一完整的产出者合并并不能改变这一点只会让产物归属变得模糊。后果与代价从干净树跑 Web 演示需要付出完整构建成本这个决策的直接代价记录在修复文档的 Consequences 部分demo:web现在每次调用都要付出完整的tsc -b tsdown代价而不再只是 Vite 构建。这是从干净代码树运行 web 演示的固有成本已经完成构建的调用方可以直接调用dsh web——这与 README.md 中Run from source小节的说明一致pnpm run build准备仓库产物pnpm dsh web使用这些已构建产物且不重新构建。对于日常开发循环仓库提供了专门的 watch 版本 scripts/dev-web.ts对应根脚本dev:web: tsx scripts/dev-web.ts --pollpackage.json它维护浏览器会读取的所有产物三阶段流水线tsc -b tsconfig.client.json --watch持续发射lib/typestsdownworkspace watch 模式通过 API 级内联配置覆盖插件包与静态链接库包重打包lib/index.js与lib/client.jsvite build --watch经deepseek-ai/dsh-web-frontend包自身的watch脚本执行重写apps/web/dist脚本明确要求先做过一次pnpm run build因为每个阶段都是相对上一阶段输出的增量构建没有一个阶段能为缺失的树做引导不得与pnpm run build并发运行两者写同一棵lib/与apps/web/dist/树重载信号不归它管——宿主 web server 自己 stat 轮询所分发的 bundle 文件并广播rebuilt帧所以任何重写了lib/client.js的进程都会触发浏览器重载。排查要点小结症状是 Failed to load plugins 且所有/plugins/id/client.js返回 404 时首先检查检出上是否存在各packages/*/*/lib/client.js文件而不是先怀疑插件声明或 bundle 补丁确认执行顺序完整链路是tsc -b tsconfig.client.json→tsdownclient 面→build:webVite 外壳缺任何一环都会在运行时以插件 404 的形式暴露已构建的树直接dsh web它不重新构建干净树则先pnpm run build开发期用pnpm run dev:web并保持与根build互斥相关实现与协议入口客户端模块子系统、插件包开发约定、Web 服务器实现、客户端 bundle 测试。【免费下载链接】deepseek-harnessDeepSeek Harness: Everything is a Plugin.项目地址: https://gitcode.com/gh_mirrors/de/deepseek-harness创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考