依赖驱动加载
1// 文件路径:scratch-plugin/src/my-plugin.ts
2// 声明本插件依赖 tools 与 llm 两个服务
3export const inject = ['tools', 'llm']
4
5export function apply(ctx: Context) {
6 // 到这里 ctx.tools 与 ctx.llm 已经就绪,可以安全使用
7}
- 声明了 inject 的插件会等待所有必需服务就绪,才会进入 LOADING 执行 apply。
- 如果依赖的服务消失(例如提供方被替换时),插件会被自动卸载,从 ACTIVE 直接走向 DISPOSED。
- 当服务恢复后,插件会被重新加载,重新走一遍 PENDING → LOADING → ACTIVE。
自动重载的保障
自动重载之所以安全,是因为通过 ctx 做的所有注册都会在卸载时被撤销。旧实例的监听器、工具、适配器不会残留,新实例会重新注册。这防止了插件调用已不存在的服务,也防止了重复监听或重复注册。
嵌套上下文:ctx.plugin()
1// 文件路径:scratch-plugin/src/parent-plugin.ts
2export function apply(ctx: Context) {
3 // 注册一个子插件,childPlugin 可以是函数、对象或 Service 子类
4 ctx.plugin(childPlugin)
5
6 // 子插件拥有自己的 Fiber,随父插件一起卸载
7}
ctx.plugin()用来在当前插件内部注册一个子插件。- 子插件创建子 Fiber,它继承父上下文,但有独立的生命周期。
- 子 Fiber 会随父插件一起卸载。
- 嵌套上下文在插件组合里很常见:一个"聚合"插件把多个小插件组织到一起,统一管理它们的生命周期。
手动提前终止:fiber.dispose()
1// 文件路径:scratch-plugin/src/manual-dispose.ts
2import type { Context } from '@deepseek-ai/cordis'
3
4declare const ctx: Context
5declare function myPlugin(ctx: Context): void
6
7// 创建插件实例,拿到它的 Fiber
8const fiber = ctx.plugin(myPlugin)
9
10// 之后需要手动终止时调用 dispose
11// 注意:dispose 返回 Promise,需要 await 等待异步清理完成
12await fiber.dispose()
dispose 的三重保证
| 保证 | 说明 |
|---|---|
| 所有注册被移除 | 该插件拥有的监听、工具、适配器等注册全部撤销 |
| 子插件递归卸载 | 通过 ctx.plugin() 创建的子 Fiber 也会被一并清理 |
| 等待异步清理完成 | 返回的 Promise 在所有异步处置器结束后才兑现 |
换句话说,await fiber.dispose() 返回时,这个插件连同它的子树都完全停稳了。
自测
- inject 声明的服务没就绪时,插件停在哪个状态?→ PENDING
- ctx.plugin() 创建的子 Fiber 与父 Fiber 是什么关系?→ 继承父上下文,独立生命周期,随父卸载
- await fiber.dispose() 返回时,能保证哪些事情已经完成?→ 注册移除、子树卸载、异步清理完成