
字节笔记本
2026年10月6日 · 约 9 分钟读完
vendored 包改名:一份映射表与四条命令
DeepSeek Harness 是 DeepSeek 开源的 agent 框架,以「一切皆插件」的架构构建在 Cordis 框架之上。Cordis 框架及其基础库不走普通 npm 依赖,而是以源码形式 vendored 在仓库的 vendor/ 目录下,发布时统一冠以 @deepseek-ai scope。这样做有很实际的原因:每个 harness 包都把框架声明为 peer dependency,发布 harness 就会连带发布这一层框架;如果沿用上游原名发布,等于在 npm registry 上占用别人的包名。于是这个仓库完成了一次系统性的改名(rescope),把九个 vendored 包全部迁到自有 scope 之下。本文整理这次改名沉淀下来的一份映射表、七类刻意保持不变的标识、代码需要修改的四处位置,以及施加、核验与回退的完整命令链。

一份映射表:九个包的新名字
改名覆盖框架核心、基础工具和七个 Cordis 官方插件,规律很统一:上游叫什么,发布名就换成 @deepseek-ai 加上语义化的前缀。
| 上游名 | 发布名 | 版本 | 角色 |
|---|---|---|---|
cordis | @deepseek-ai/cordis | 4.0.0-rc.7 | 框架核心:Context、Service、Fiber 与事件 |
cosmokit | @deepseek-ai/cosmokit | 1.8.1 | 框架与 Schemastery 共用的基础工具 |
schemastery | @deepseek-ai/schemastery | 3.18.0 | 配置 schema,每个插件的 Config 都基于它 |
@cordisjs/plugin-loader | @deepseek-ai/cordis-plugin-loader | 1.0.0-rc.5 | cordis.yml 装载、插件解析、repository 缓存 |
@cordisjs/plugin-include | @deepseek-ai/cordis-plugin-include | 1.0.4 | 配置包含与 patch 叠加 |
@cordisjs/plugin-group | @deepseek-ai/cordis-plugin-group | 1.0.0 | 嵌套插件分组 |
@cordisjs/plugin-timer | @deepseek-ai/cordis-plugin-timer | 1.1.2 | ctx 上随 disposal 回收的定时器 |
@cordisjs/plugin-hmr | @deepseek-ai/cordis-plugin-hmr | 1.0.15 | 插件与配置的热替换 |
@cordisjs/plugin-logger-console | @deepseek-ai/cordis-plugin-logger-console | 1.0.0 | 控制台日志导出 |
子路径导出同样保持原路径结构,例如 @cordisjs/plugin-loader/repository 对应改成 @deepseek-ai/cordis-plugin-loader/repository,只换包名,不动路径。
七类刻意保持不变的东西
改名只动包名,下面这些一概不碰:
- 目录名与版本号。
vendor/hmr/仍然是vendor/hmr/,每个包保留映射表里记录的上游版本,vendored 目录树依旧读作一份上游快照,随时可以和上游逐行对照。 - 依赖 range。 依赖条目只换键、不换范围:
"cordis": "^4.0.0-rc.7"变成"@deepseek-ai/cordis": "^4.0.0-rc.7"。linkWorkspacePackages正是靠这些保留下来的范围,把依赖解析到仓库内固定的 workspace。 cordis:内建前缀。cordis:include、cordis:group是 loader 的协议前缀,不是包名,不在改名范围内。cordis.yml配置文件家族。 包括*.cordis.yml、*.cordis.snapshot.yml、cordis.patch.yml,文件名一个不改。- 名字里带这个词的 harness 包。 例如
@deepseek-ai/dsh-tool-cordis,它的 cordis 指的是技术栈归属,不是那个框架包。 - 上游运行时标识符。 例如 Schemastery 的
Symbol.for('schemastery')及其vendor:元数据字段,这类标识参与运行时识别,动了会出问题。 docs/之外的散文。 各 vendored 包自己的 README 保留写作当时的名字,那里的裸cordis也可能是 Python SDK 的选项名或某个 agent preset 的 id;而在docs/之内,散文与所有 Markdown 代码围栏则全部跟着改。
代码要改的四处
| 位置 | 改前 | 改后 |
|---|---|---|
| 模块 import | import { Context } from 'cordis' | import { Context } from '@deepseek-ai/cordis' |
| 类型事件声明合并 | declare module 'cordis' | declare module '@deepseek-ai/cordis' |
package.json 依赖键 | "@cordisjs/plugin-hmr": "^1.0.15" | "@deepseek-ai/cordis-plugin-hmr": "^1.0.15" |
cordis.yml 插件条目 | name: '@cordisjs/plugin-include' | name: '@deepseek-ai/cordis-plugin-include' |

施加、核验与回退
整份映射由仓库里的 rescope-vendor 脚本承载并执行,所有引用的改写都不靠手改。四条命令各管一段:
pnpm run rescope-vendor # 先报告将改什么
pnpm run rescope-vendor --apply # 改写全部引用
pnpm run rescope-vendor:check # 断言后置状态,挂在 hygiene gate
pnpm run rescope-vendor --apply --reverse # 回退到上游名不带参数先跑一遍,得到的是即将发生的改动报告,此时什么都没写盘;加 --apply 才真正落盘;rescope-vendor:check 在卫生闸门(hygiene gate)里断言改名后的状态,一旦有人手工改动引入漂移就会被拦下;--reverse 则把整棵 vendored 树还原成上游名字,改错了有后悔药。
上游同步之后要重跑一遍改名,并接上脚本打印的再生成步骤:pnpm install 重生成 lockfile,pnpm run gen-third-party-notices 更新第三方声明,再对触及的双语文件对跑 pnpm run verify-translation-pairing --write,保证文档与代码不脱节。
这套做法的价值不止于改名本身:映射收敛在一个脚本里,不变量交给 check 命令守卫,改名这种牵一发动全身的全局操作,就变成了可施加、可核验、可回退的普通工程步骤。维护任何带 vendored 依赖的 monorepo,都值得照这个样子搭一套自己的机制。



