Ir al contenido

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.

  • Acceso al código fuente de OpenClaw.
  • Node.js para la gestión de dependencias.
  • Conocimiento de la arquitectura actual de conectores.

Para empezar con esta nueva estructura, sigue estos pasos para limpiar tus integraciones:

  1. Adopta el Plugin SDK: Usa openclaw/plugin-sdk para acceder a tipos como ChannelPlugin y helpers de configuración como buildChannelConfigSchema.
  2. Usa el Runtime inyectado: No importes nada directamente de src/**. Accede a la lógica del core mediante OpenClawPluginApi.runtime.
  3. Limpia tus bridges: Si usas conectores como BlueBubbles o Zalo, reemplaza los archivos core-bridge.ts personalizados por las funciones disponibles en el Runtime.
  4. 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;
};
};
  • Error de importación directa: Si intentas importar desde src/** dentro de extensions/**, 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.version coincida 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.runtime sin alterar las claves de configuración.

¿Necesitas ayuda personalizada para configurar tu plugin? Consulta nuestro AI Setup Assistant.

  • 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

OpenClaw Expert

Sigues atascado?

Si esta pagina no resolvio tu caso, pregunta a OpenClaw Expert para pasos concretos.