Cordis 进阶
Service:用 ctx.get / inject 处理依赖
服务是插件通过 ctx 提供或消费的具名能力,ctx.tools、ctx.llm、ctx.agents 皆然。消费方只按名字(如 'tools')声明能力,不导入提供方,更换提供方无需改代码。
硬依赖用 inject:插件保持 PENDING 直到所需服务全部就绪,apply 内保证可用;运行期依赖消失(提供方被替换或卸载)时插件自动卸载,服务恢复后重新加载:
export const inject = ['tools']
export function apply(ctx: Context) {
ctx.tools.register(/* ... */)
}
可选依赖用 ctx.get:不声明 inject,在使用处探测,缺失时插件照常运行:
export function apply(ctx: Context) {
const greeter = ctx.get('greeter') // 未提供时返回 undefined
console.log(greeter?.greet('maybe') ?? 'no greeter available')
}
提供服务:继承 Service 基类,在构造函数里 super(ctx, 'myService') 注册;ctx.provide(name, value) 也可注册归当前 fiber 所有的实现。服务名是扁平命名空间,自有服务要加有辨识度的前缀。
Event:用 ctx.on 监听,五种分发模式
事件让插件无需知道谁在监听即可发出通知。ctx.on(name, listener) 注册监听器,它本身是 effect,卸载时自动移除,无需手动 removeListener:
export function apply(ctx: Context) {
ctx.on('stats/report', (name, count) => {
console.log(`[stats] ${name} -> ${count}`)
})
}
分发模式是事件的公开约定,决定监听器能否返回值、并发或短路:
| 模式 | 调用方法 | 语义 |
|---|---|---|
| emit | ctx.emit(name, ...args) | 同步广播,忽略返回值 |
| parallel | await ctx.parallel(name, ...args) | 所有监听器并发运行并一同等待 |
| serial | await ctx.serial(name, ...args) | 按顺序等待,第一个非 null/false/undefined 返回值胜出 |
| bail | ctx.bail(name, ...args) | serial 的同步版本 |
| waterfall | ctx.waterfall(name, ...args, next) | 环绕中间件,可转换结果或短路 |
waterfall 是拦截模式:监听器收到 next() 续接,调用它执行下游,不调用直接返回即短路。纪律:只做观察或标注的 waterfall 监听器必须调用 next(),否则会静默吞掉下游默认行为。
Slot:给 Web UI 挂界面
除 Host 侧能力外,浏览器端插件还能向 Web UI 注册界面。Web UI 把可扩展区域抽象为 Slot(侧边栏、消息卡片、设置面板等),客户端插件向 Slot 注册 React 组件渲染 UI。扩展工具提供只读查询(如 Slots.listSubTree)浏览可用 Slot 树与 props 契约;插件通过 host.call / harness.handle 等私有方法在浏览器与 Host 间传 JSON。
effect:生命周期清理
Cordis 管理的注册都会在卸载时撤销,需要手动管理的资源(定时器、连接、watcher)要包进 ctx.effect() 并返回 disposer(清理函数):
export function apply(ctx: Context) {
ctx.effect(() => {
const timer = setInterval(() => console.log('tick'), 200)
return () => clearInterval(timer) // 卸载时执行
})
}
已属于 effect 的操作:ctx.on() 监听器、ctx.plugin(child) 子插件、服务注册与 ctx.tools.register() 等注册表调用——都无需手动清理。fiber 状态机为 PENDING → LOADING → ACTIVE → UNLOADING → DISPOSED,apply 抛异常则进入 FAILED。两点注意:disposer 按注册顺序的逆序启动,多个异步 disposer 并发执行;若拆除步骤必须按顺序,就放进同一个 disposer 内依次 await。