Refactorización de Plugins: SDK y Runtime
¿Te ha pasado que intentas actualizar una integración y todo se rompe porque dependes de archivos internos que cambiaron de lugar? Mantener conectores de mensajería cuando el núcleo del sistema evoluciona rápido suele ser un dolor de cabeza constante.
Es frustrante ver cómo el código se vuelve frágil porque no hay una frontera clara entre lo que es el motor y lo que es el plugin. Queremos que cada conector sea un plugin independiente con una API estable, eliminando las importaciones directas desde src/** y moviendo todo hacia un SDK y un Runtime dedicado.
Requisitos previos
Sección titulada «Requisitos previos»- Acceso al código fuente de OpenClaw.
- Node.js para la gestión de dependencias.
- Conocimiento de la arquitectura actual de conectores.
Inicio rápido
Sección titulada «Inicio rápido»Para empezar con esta nueva estructura, sigue estos pasos para limpiar tus integraciones:
- Adopta el Plugin SDK: Usa
openclaw/plugin-sdkpara acceder a tipos comoChannelPluginy helpers de configuración comobuildChannelConfigSchema. - Usa el Runtime inyectado: No importes nada directamente de
src/**. Accede a la lógica del core medianteOpenClawPluginApi.runtime. - Limpia tus bridges: Si usas conectores como BlueBubbles o Zalo, reemplaza los archivos
core-bridge.tspersonalizados por las funciones disponibles en el Runtime. - Define la compatibilidad: Especifica el rango de versiones de runtime que soporta tu plugin en la configuración (ej.
openclawRuntime: ">=2026.2.0").
Aquí tienes la superficie completa del PluginRuntime que debes usar:
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; };};Solución de problemas
Sección titulada «Solución de problemas»- Error de importación directa: Si intentas importar desde
src/**dentro deextensions/**, el CI o las reglas de lint bloquearán el build. Usa el SDK. - Bridges duplicados: Si ves código repetido en BlueBubbles o Zalo, elimínalo. La lógica ahora vive en
api.runtime. - Fallo de carga del plugin: Verifica que la versión de
api.runtime.versioncoincida con el rango que definiste en tu plugin. - Comportamiento inconsistente en iMessage: Asegúrate de que las llamadas directas al core hayan sido reemplazadas por
api.runtimesin alterar las claves de configuración.
¿Necesitas ayuda personalizada para configurar tu plugin? Consulta nuestro AI Setup Assistant.
Próximos pasos
Sección titulada «Próximos pasos»- Plugins: Guía detallada sobre el sistema de extensiones.
- Channels: Lista de conectores disponibles.
- Configuration: Cómo gestionar el Gateway y sus parámetros.
OpenClaw Expert
Sigues atascado?
Si esta pagina no resolvio tu caso, pregunta a OpenClaw Expert para pasos concretos.