Plugin SDK + Runtime 重構計畫:打造更穩定的插件架構
每次更新核心代碼,最怕的就是插件跟著一起掛掉。特別是當插件直接引用核心內部的代碼(src/**)時,任何微小的改動都可能引發連鎖反應,讓你的開發工作陷入無止盡的修復循環。
這種緊密耦合不僅讓升級變得脆弱,也讓開發外部插件變得很困難。為了徹底解決這個問題,我們正在推動一套全新的重構計畫,讓每個 messaging connector 都成為基於穩定 API 的插件。
需要準備的東西
Section titled “需要準備的東西”openclaw/plugin-sdk庫OpenClawPluginApi.runtime存取權限
如果你準備開始將現有的 connector 遷移到新的架構,可以參考這 4 個步驟:
- 更換依賴來源:停止從
src/**直接 import 任何內容。 - 接入 SDK:使用
openclaw/plugin-sdk提供的ChannelPlugin類型與配置工具(如buildChannelConfigSchema)。 - 改用 Runtime 呼叫:將原本直接調用的核心邏輯,改為透過
api.runtime呼叫(例如api.runtime.channel.text.chunkMarkdownText)。 - 聲明版本範圍:在插件配置中註明你需要的
openclawRuntime版本範圍,例如">=2026.2.0"。
技術架構:雙層解耦
Section titled “技術架構:雙層解耦”我們將架構拆分為兩個層級,確保插件與核心之間的邊界清晰:
1. Plugin SDK (編譯時)
Section titled “1. Plugin SDK (編譯時)”這是一個穩定且可發布的包,主要包含類型定義、Helper 函數與配置工具。它不包含任何運行時狀態。
- 類型:
ChannelPlugin,ChannelMeta,ChannelCapabilities - 配置輔助:
setAccountEnabledInConfigSection,deleteAccountFromConfigSection - 引導與工具:
promptChannelAccessConfig,readStringParam,formatDocsLink
2. Plugin Runtime (執行層)
Section titled “2. Plugin Runtime (執行層)”插件透過 OpenClawPluginApi.runtime 存取核心行為,這也是插件觸碰核心邏輯的唯一途徑。
export type PluginRuntime = { channel: { text: { chunkMarkdownText(text: string, limit: number): string[]; resolveTextChunkLimit(cfg: OpenClawConfig, channel: string, accountId?: string): number; hasControlCommand(text: string, cfg: OpenClawConfig): boolean; }; reply: { dispatchReplyWithBufferedBlockDispatcher(params: { ctx: unknown; cfg: unknown; dispatcherOptions: { deliver: (payload: { text?: string; mediaUrls?: string[]; mediaUrl?: string; }) => void | Promise<void>; onError?: (err: unknown, info: { kind: string }) => void; }; }): Promise<void>; createReplyDispatcherWithTyping?: unknown; // adapter for Teams-style flows }; routing: { resolveAgentRoute(params: { cfg: unknown; channel: string; accountId: string; peer: { kind: RoutePeerKind; id: string }; }): { sessionKey: string; accountId: string }; }; pairing: { buildPairingReply(params: { channel: string; idLine: string; code: string }): string; readAllowFromStore(channel: string): Promise<string[]>; upsertPairingRequest(params: { channel: string; id: string; meta?: { name?: string }; }): Promise<{ code: string; created: boolean }>; }; media: { fetchRemoteMedia(params: { url: string }): Promise<{ buffer: Buffer; contentType?: string }>; saveMediaBuffer( buffer: Uint8Array, contentType: string | undefined, direction: "inbound" | "outbound", maxBytes: number, ): Promise<{ path: string; contentType?: string }>; }; mentions: { buildMentionRegexes(cfg: OpenClawConfig, agentId?: string): RegExp[]; matchesMentionPatterns(text: string, regexes: RegExp[]): boolean; }; groups: { resolveGroupPolicy( cfg: OpenClawConfig, channel: string, accountId: string, groupId: string, ): { allowlistEnabled: boolean; allowed: boolean; groupConfig?: unknown; defaultConfig?: unknown; }; resolveRequireMention( cfg: OpenClawConfig, channel: string, accountId: string, groupId: string, override?: boolean, ): boolean; }; debounce: { createInboundDebouncer<T>(opts: { debounceMs: number; buildKey: (v: T) => string | null; shouldDebounce: (v: T) => boolean; onFlush: (entries: T[]) => Promise<void>; onError?: (err: unknown) => void; }): { push: (v: T) => void; flush: () => Promise<void> }; resolveInboundDebounceMs(cfg: OpenClawConfig, channel: string): number; }; commands: { resolveCommandAuthorizedFromAuthorizers(params: { useAccessGroups: boolean; authorizers: Array<{ configured: boolean; allowed: boolean }>; }): boolean; }; }; logging: { shouldLogVerbose(): boolean; getChildLogger(name: string): PluginLogger; }; state: { resolveStateDir(cfg: OpenClawConfig): string; };};如果你在遷移過程中遇到問題,請參考以下常見情境:
- 無法引用核心內部的 Helper:請檢查該 Helper 是否已遷移至
openclaw/plugin-sdk。如果沒有,代表該功能應透過api.runtime存取。 - 核心更新後插件失效:請檢查你的
openclawRuntime版本聲明,確保它涵蓋了當前核心的版本號。
我們預期在重構完成後達成以下目標:
- 所有 channel connector 均透過 SDK + Runtime 運作。
- 禁止從
src/**導入任何extensions/**的代碼。 - 新的連接器模板僅依賴 SDK 與 Runtime。
- 外部開發者無需存取核心原始碼即可開發與更新插件。
如果你在設定過程中遇到困難,可以隨時諮詢 AI Setup Assistant。
- Plugins:深入了解插件系統
- Channels:查看支援的通訊管道
- Configuration:配置你的 Gateway
OpenClaw Expert
還是卡住了?
如果這篇文件沒有解決你的情境,直接問 OpenClaw Expert,拿到可執行步驟。