十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

deepseek-harness 中 dsh 的 Node 原生 TypeScript 源码启动链路:设计、门禁与演进

deepseek-harness 中 dsh 的 Node 原生 TypeScript 源码启动链路:设计、门禁与演进 deepseek-harness 中 dsh 的 Node 原生 TypeScript 源码启动链路设计、门禁与演进【免费下载链接】deepseek-harnessDeepSeek Harness: Everything is a Plugin.项目地址: https://gitcode.com/gh_mirrors/de/deepseek-harness本篇技术文章解读 deepseek-harness下称 dshEverything is a Plugin 的 Agent 运行时仓库中一份关键的架构决策笔记dsh的 TUI、Web 与无头模式如何在不经过tsx/esbuild、不预构建lib/产物的前提下用 Node 原生能力直接启动 TypeScript 源码。读完后你会掌握node --experimental-transform-types启动链路的设计动机与边界、只做 resolve 钩子的tspath-loader的解析规则、verify-cordis-config静态门禁与 app-boot 的 fail-loud 插件诊断如何防止退出码 0 的残缺应用以及该方案被 Node 26 移除特性取代后仓库的演进路径。一、背景为什么必须重建源码启动链路dsh 是 monorepo 形态apps/cli是 CLI 应用入口dsh命令packages/下是一两百个以 Cordis 插件为单元的 workspace 包vendor/内嵌 Cordis、Loader、Include、HMR、Schemastery 等框架源码。开发时希望pnpm dsh一类的命令能零构建直接跑源码这条链路原本由tsx承担存在两个隐性耦合TypeScript 转换与路径解析都由同一个第三方 loader 隐式处理。tsx同时负责把.ts变成可执行 JS并应用根tsconfig.json的paths映射把 workspace 裸包名如deepseek-ai/dsh-session解析到.ts源文件。这个能力是顺带的没有显式契约。改用 Node 原生处理 TypeScript 后这条隐式契约断裂。Node 的 transform-types 模式不会应用 tsconfig 路径映射如果退而通过包exports解析源码启动就会混入可能陈旧甚至不存在的lib/构建产物——构建好的开发树能启动、干净 checkout 反而失败正是这类问题的典型症状。文档还指出了 Node 转换层的两个语义坑.agents/notes/archived/architecture/2026-07-28-dsh-native-typescript-source-launch.zh.md问题一节Node 的转换不做类型分析。通过普通值 import 导入的类型会保留为运行时 ESM 请求即类型导入会在运行时真的去解析模块而 TypeScript 的export 会被转成 CommonJS 赋值而不是 ESM default export。因此源码图必须显式使用仅类型导入import type和原生 ESM 导出且 resolve hook 无法修复不兼容的源码语法——契约只能写在源码里。Cordis 配置引入了第二条解析边界。cordis.yml中的 bare 插件包不经过 TypeScript import 分析其解析方 manifestpackage.json可能漏掉所需依赖。Cordis Loader 对插件 import 失败只是记录日志、留下一个没有 fiber 的 entry不会让启动本身失败——配置里一个拼写错误就能产出退出码 0 的残缺应用。二、核心决策node --experimental-transform-types统一启动链路决策部分笔记 决策一节规定dsh的TUI、Web 与无头源码启动统一使用node --experimental-transform-types由 Node 完成 TypeScript 转换启动链路上不加载tsx或 esbuild。bin/dsh、根级dsh/TUI/Web demo 以及 Code Mode TUI 全部进入同一条apps/cli/src/bin.ts启动链路避免多入口分叉。测试与 e2e 启动器保留各自现有策略构建后的lib/bin.js继续由普通 Node 运行这一点可从apps/cli/package.json得到印证bin: { dsh: lib/bin.js }发布产物与源码启动是两个平面。这里有一个刻意的范围约束该原生源码 loader 只覆盖dshCLI 应用链路。CI 的lib模式、测试/E2E 启动器和其他示例启动器均不受影响避免一个启动向量改动波及整个仓库的验证矩阵。适用前提值得强调根package.json声明engines: { node: ^22.19.0 || 24.0.0 }。--experimental-transform-types是 Node 22.18/24 引入的实验性能力这条链路天然依赖引擎版本支持——这一假设后来正是导致整个方案被取代的原因见第六节。三、tspath-loader只注册一个 resolve 钩子的源码路径解析器Node 原生启动后最大的缺口是 tsconfigpaths失效。仓库的解法是scripts/tspath-loader.ts该文件已随后续演进删除此处按决策笔记还原其设计——它只注册一个模块解析resolve钩子不做任何代码转换代码转换始终只由 Node 负责。其解析规则配置文件选择设置了TSX_TSCONFIG_PATH环境变量时使用该路径相对路径从调用方的 cwd解析否则读取根tsconfig.json。沿extends链解析TsconfigPathsResolver复用仓库已有的 TypeScript 开发工具沿配置的extends链读取按 tsconfig 规则选择精确exact或 wildcard 的paths条目。这与根tsconfig.json的注释相呼应——该 solution 文件刻意保持files: []且通过extends携带 base paths正是为了让从仓库根启动、没有就近 tsconfig的脚本当时是 tsx 启动的 scripts/也能解析 workspace import。命中即映射到源文件命中的 workspace bare specifier 被映射到.ts/.mts/.cts源文件或目录 index 文件。未命中一律回退未命中 tsconfig paths、引用未声明依赖、或根本不是 bare specifier 的说明符全部交回 Node 默认解析。两条设计边界值得注意该 loader 不属于构建后的 CLI。它是源码专用的使用 checkout 根目录的开发依赖apps/cli/package.json的dependencies中没有typescripttypescript只出现在根级开发依赖中保证发布产物的运行时依赖面不被开发期能力污染。钩子只管 URL不碰源码。这与在 loader 内转换 import的备选方案直接对撞见第四节感知类型的源码改写会让 loader 重新变成事实上的 TypeScript 编译器。四、最近一层 manifest 持有依赖运行时声明门禁paths映射是无条件的但源码启动不应无条件如果 tsconfig paths 兜底一切未声明的跨包 import 和 Cordis 插件将继续成功解析manifest 与实际运行图之间的不一致就被永久掩盖。因此源码 import 的重定向受一条显式规则约束只有当目标包是最近一层包 manifest 的自身名称或该 manifest 已声明的运行时依赖时才允许重定向到 workspace 源码。规则的两个关键场景插件源码内的 import解析方包的package.json自身声明了这个依赖才允许从lib/兜底中改道到.ts源文件。Cordis 插件的解析 parentCordis Loader 使用配置目录的 URL作为 import parentresolver 此时向上查找声明该插件的 workspace manifest。于是随附的apps/cli/config/base.cordis.yml及其界面覆盖层所需依赖由apps/cli/package.json持有——可以从该文件的dependencies清单验证deepseek-ai/dsh-app-boot、deepseek-ai/dsh-tool-bash、deepseek-ai/cordis-plugin-loader等插件包都以workspace:^形式显式列出与配置行一一对应。这条运行时规则确实抓到过真实缺陷取代它的后续决策笔记.agents/notes/implemented/architecture/2026-07-29-dsh-source-launch-tsx-esm.zh.md记录了dsh-plan-mode与dsh-tool-jobs导入deepseek-ai/dsh-llm却只声明在 devDependencies 的问题后已修复。运行时强制不是摆设。五、静态门禁与 fail-loud 诊断配置与启动不再静默残缺运行时规则管住源码 import一侧另一侧靠两道静态/应用层门禁补齐1.verify-cordis-config的单向完整性检查。配置中的每个 bare plugin 包都必须出现在对应 manifest 的dependencies中反向不要求manifest 可以包含该配置未引用的额外依赖多出不算错。当前仓库中该门禁已扩展为更完整的 Loader 元数据与包解析校验器scripts/verify-cordis-config.tsmissingPluginDependenciesscripts/verify-cordis-config.ts实现上述单向检查收集所有name行引用的包名凡不在依赖面dependencies测试配置可含devDependencies中即报错... must be declared in ownervalidateSourcePlaneResolutionscripts/verify-cordis-config.ts进一步保证每个本地 workspace 包都能通过 tsconfig.base.json 的 paths 解析到.ts/.tsx源文件——注释明确说明没有 paths 命中就会回退到包exports抵达构建lib/在构建过的开发树能启动、在干净 checkout 上炸掉同一脚本还覆盖 bundle patch 行、包级 Loader fixture、目录选择器chooser后端包等扩展面。根AGENTS.md把同步更新配置和依赖定为常驻规则改配置必须同时改 manifest门禁防止配置先于依赖落地。2. app-boot 的 fail-loud 插件诊断。Loader 完全停稳后共享的dsh-app-boot检查每个已启用但没有 fiber 的 entry并拒绝启动报错为plugin(s) failed to load: ...; Cordis startup failed because these plugin(s) could not be resolved同时列出全部加载失败的插件。该诊断位于应用层packages/boot/app-boot/src/index.ts中可见此错误字符串不改变 vendor 中 Loader 自身的启动行为——Loader 依旧只是记录错误并留下空 entry让失败显形的责任上移到应用启动层。结果是插件 import 失败不再留下退出码 0 的残缺应用最终错误同时说明 Cordis 启动失败原因与具体插件名Loader 的原始错误仍保留在更早的日志中。六、Node 兼容 TypeScript 契约vendor 源码的显式标注决策笔记把Node-compatible TypeScript列为源码启动契约的一部分落到 vendor 源码上的具体约定Cordis、Loader、Include、HMR、Schemastery对会被擦除的类型导入统一使用import type标记——避免 Node transform 把类型当作运行时导出去请求。Schemastery源码使用原生 ESM default export 并声明type: module其.mjs与.cjs构建产物分别保留现有的 ESM default export 行为和require()返回可调用值的行为。这些 vendor 与上游的差异记录在vendor/README.md中其中第 10 条即Vendored Node-compatible TypeScript与笔记一一对应且没有为 vendor 框架新增任何运行时行为——只是把既有行为对齐到 Node 的模块语义。后果面上CLI 源码图中的 vendor 源码必须持续兼容 Node transform-types 的模块语义vendor 的本地修改记录local-modification log明确了上游同步义务。七、被否决的四个备选方案笔记 曾考虑的替代方案 一节给出了完整的取舍记录是理解整套设计约束的好材料备选方案否决理由继续使用tsxtsx/esbuild 会继续负责 TypeScript 转换本链路无法证明 Node 原生转换可用——这正是一次启动向量改造要达成的验证目标源码入口通过包导出加载构建后的lib/混淆 source plane 与 artifact plane零构建的开发启动可能读到陈旧产物或直接失败无条件应用根 tsconfigpaths未声明的跨包 import 和 Cordis 插件继续成功解析掩盖 manifest 与实际运行图之间的不一致在自定义 loader 内转换 import感知类型的源码改写重新引入编译器式转换让 loader 而非 Node 负责执行 TypeScript使签入仓库的源码兼容 Node才能让启动边界保持显式四个否决项共同指向同一个原则转换归 Node解析归显式规则声明归 manifest诊断归应用层——每一层各管一段边界可静态验证。八、后果与后续演进从原生链到 tsx ESM 钩子决策笔记 后果 一节的五条结论TUI/无头界面保留零构建源码回路Web 仍在启动 CLI 源码入口前构建前端产物。TypeScript 语法只经过 Node 原生转换仅处理 URL 的 loader 使用 checkout 根目录的开发依赖不增加 CLI 运行时依赖。workspace package import 和 Cordis 配置依赖都必须在解析方 manifest 中明确声明静态门禁防止配置先于依赖落地额外依赖不构成错误。插件 import 失败不再留下退出码 0 的残缺应用最终错误同时说明 Cordis 启动失败及具体插件名。CLI 源码图中的 vendor 源码必须与 Node transform-types 模块语义兼容本地修改记录明确上游同步义务。CI 的lib模式、测试/E2E 启动器和其他示例启动器保留各自现有策略。重要演进提示截至当前仓库状态该笔记状态为implemented且已归档Archived: 2026-08-07。笔记开头即声明Node 26.0.0 移除了--experimental-transform-types进程以bad option拒绝该 flag本方案描述的 paths loaderscripts/tspath-loader.ts、apps/cli/src/tsconfig-paths-loader.ts已被删除dsh 源码启动改由 dsh 通过 tsx ESM hook 源码启动 的决策接管新启动向量为node --import tsx/esm由 tsx 的 ESM-only 钩子同时负责转换与 tsconfigpaths投影CLI 源码图是纯 ESM故 CJS 钩子保持关闭随之消失的是 tspath-loader 的运行时依赖声明强制声明完整性仅由静态门禁保障配置的裸插件走verify-cordis-configmanifest 走 workspace constraints仓库新增dsh-source-launch-smoke门禁apps/cli/tests/source-launch.compat.spec.ts在 Node 22.19 与 26 的 node-compat CI 矩阵中强制执行用精确的生产运行时启动向量做 keyless 管道 stdio 启动断言进程会因 TTY 拒绝而以非零状态退出——未来 Node 对模块钩子或 TypeScript 处理的任何改动会让该门禁变红而不是悄悄破坏开发者的启动体验。而本笔记确立的三块资产仍然有效verify-cordis-config配置声明门禁、app-boot 的显式失败插件诊断、以及 vendor 中的import type标注。换言之Node 原生转换这条路被换掉了但配置必须声明、失败必须响亮、源码必须显式类型导入这套契约经受住了版本更替成为 dsh 源码启动可持续演进的底盘。九、可复用的工程结论把这份 ADR 的机制抽象出来对任何Node 上直接跑 TypeScript monorepo 源码的项目都有参考价值启动向量的显式化与其让某个 loader 隐式兜底转换解析不如把转换Node 或 tsx、路径解析tsconfigpaths投影、依赖声明manifest分成三个显式可验证的环节每个环节有独立的静态门禁。单向完整性检查是低成本高收益的门禁形态配置引用的包必须在 manifest 中声明manifest 多出不报错——既挡住配置先于依赖落地又不惩罚合理的依赖预留。fail-loud 诊断放在应用启动层而非改动底层 Loader底层保持记录错误、不中断的宽容语义由app-boot在停稳后统一裁决错误信息聚合全部失败项。实验性 flag 的依赖要可观测本方案的终结不是代码腐化而是 Node 26 移除一个引擎 flag 且没有任何 CI 任务执行过真实启动向量。仓库的补救——按生产启动向量做冒烟断言、纳入多版本 CI 矩阵——值得所有依赖引擎实验特性的项目对照。【免费下载链接】deepseek-harnessDeepSeek Harness: Everything is a Plugin.项目地址: https://gitcode.com/gh_mirrors/de/deepseek-harness创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表