插件契约 Plugin Contract

August 14, 2026 · View on GitHub

状态: approved | 更新时间: 2026-08-14 本契约定义 learning-dsh 作为 DeepSeek Harness (dsh) 插件 的接入接口。契约事实以 dsh 源码为准(v0.1.0-rc.5,参照 @deepseek-ai/dsh-tool-todo@deepseek-ai/dsh-host-webserver@deepseek-ai/dsh-host-frontend-static 等真实包)。

变更记录

版本日期变更
v1.22026-08-14契约修正:webserver 服务名双拼写自适应(httpServer npm 发布版 / webServer master 源码),插件不再静态 inject 单一名(Cordis inject 为 AND 语义会死锁),改运行时探测;新增 dsh.bundle 正式安装
v1.12026-08-14契约修正:npm 发布版 @deepseek-ai/dsh-host-webserver@0.0.1-rc.1 的 ctx key 为 httpServer/HttpServerService(master 源码拼写为 webServer/WebServer)。插件以 npm 可安装版本为准。

1. 定位

learning-dsh 是一个 dsh 插件(Cordis 插件),在 dsh 运行时内注册 /learning 路由,向 dsh 使用者提供学习页面(版本化源码阅读 + 设计决策讲解)。页面本身是 Vite 构建的静态产物,作为插件的资源被 serve。

形态结论(源码依据)

  • dsh 的 webserver 服务提供 register({ kind, path, handler }) 命名路由注册,匹配顺序 exact → 最长 prefix → fallback。服务名双拼写:npm 发布版 @deepseek-ai/dsh-host-webserver@0.0.1-rc.1ctx.httpServer/HttpServerService,本地 master 源码(v0.1.0-rc.5 开发线)为 ctx.webServer/WebServer——developer preview 破坏性变更的实例;插件运行时自适应两个服务名(v1.2),发布与源码环境均可激活。
  • fallback seat 是 single-owner(已被 dsh-host-frontend-static 占用),因此插件必须用 prefix/exact 路由而非 fallback;
  • 重复路径注册会 throw——路由路径是组合层契约,插件路径必须唯一。

2. 插件包格式

包名:@dylan/learning-dsh(第三方 scope;dsh 官方包为 @deepseek-ai/dsh-<name> 命名,第三方可用自有 scope)。

2.1 package.json 契约

字段要求依据
typemoduledsh 包约定
mainlib/index.js同上
typeslib/types/index.d.ts同上
exports["."]{ types, default }同上
exports["./package.json"]指向自身同上
fileslib/ 产物(lib/index.js + lib/types/**/*.d.ts)+ cordis.patch.yml(bundle 配置层),不发布 srcdsh 发布门禁
dsh.bundle{ patch: './cordis.patch.yml' }dsh bundle 声明:dsh plugin add 后自动加入 profile 的 dsh.profile.bundles
dependencies@deepseek-ai/schemastery(配置运行时验证)dsh 包约定
peerDependencies@deepseek-ai/cordis + 所用 dsh 包(本项目:@deepseek-ai/dsh-host-webserver 提供的 webServer 类型所在包)dsh 包约定
devDependencies镜像所有 peerDependenciesdsh 包约定
licenseMIT仓库 LICENSE

2.2 插件入口(Cordis 插件格式)

export const name = 'learning-dsh'       // 插件 id
export const inject: string[] = []        // 不静态 inject(见下方双拼写说明)
export const Config = z.object({          // schemastery 配置 schema
  // mountPath?: string  默认 '/learning'
  // ...
})
export async function apply(ctx: Context, config: Config) {
  // 运行时探测 webserver 服务,注册 /learning prefix route,serve 前端 dist
}

inject 契约(双拼写自适应):dsh webserver 服务名在发布版与 master 源码间不一致——npm 发布版 @deepseek-ai/dsh-host-webserver@0.0.1-rc.1 提供 ctx.httpServer(类 HttpServerService),master 源码提供 ctx.webServer(类 WebServer)。Cordis 的 inject 列表是 AND 语义(所有名字齐备才激活),静态声明任一名字都会在另一环境死锁;故插件 inject = [],在 apply 内用 ctx.reflect.get 预检 + internal/service 全局事件等待,解析宿主实际提供的服务名。两个变体的路由 API 一致:

方法签名说明
register(route: WebRoute) => () => void注册 exact/prefix 路由,返回 disposer
registerFallback(handler) => voidfallback seat(本项目不使用,single-owner)

WebRoute{ kind: 'exact' | 'prefix', path: string, handler: (req, res) => void }

2.3 前端 dist 解析(dsh-web-frontend 模式)

参照 @deepseek-ai/dsh-web-app:前端构建产物独立成包(packages/web),插件通过其 exports 的 require.resolve 解析 dist 路径,activation 时失败需 loud error(仿 dsh-host-frontend-static 的 "frontend dist must be built" 行为)。

3. 配置契约(Config)

字段类型默认说明
mountPathstring/learning路由挂载路径(prefix),须唯一
titlestringLearning dsh页面标题(进 index.html transform 可选)

配置经 schemastery 验证,patch 可覆盖(profile cordis.patch.yml 机制)。

4. 第三方插件加载机制(使用者视角)

机制说明
Profile$DSH_HOME/profiles/<name>/package.json 声明插件依赖 + dsh.profile.bundles
dsh plugin 命令dsh plugin --profile <name> add <pkg> 安装依赖;检测到 dsh.bundle.patch 声明则自动追加 bundle 到 dsh.profile.bundles
cordis.patch.yml按行 id 替换/插入配置(本项目插件的 Config 可被 patch 覆盖 mountPath 等)
--patch overlay不修改 profile 的临时覆盖层(快速试用:dsh --profile web --patch <file>

本项目插件的正式安装 = dsh plugin --profile web add <pkg>(bundle 自动入列);快速试用 = --patch overlay(见仓库根 README 中英双语使用说明)。

5. 契约边界

  • 在契约内:包格式(含 dsh.bundle)、插件入口(双拼写自适应 inject)、webserver 路由挂载、dist 解析、Config schema。
  • 不在契约内:前端页面内部实现(spec-ui.md)、内容数据结构(content-contract.md)、dsh 官方未来 web UI 嵌入通道(developer preview 期间无稳定通道,本插件不依赖)。