/** * ClawLink Plugin — Entry point * * Registers the channel adapter, tools, and hooks with OpenClaw. * * Aligned with openclaw-weixin v2.1.1 plugin entry pattern. * Reference: docs/reference/plugins/openclaw-weixin-latest-v2.1.1/index.ts */ import type { OpenClawPluginApi } from 'openclaw/plugin-sdk/plugin-entry'; import { buildChannelConfigSchema } from 'openclaw/plugin-sdk/channel-config-schema'; import { clawlinkPlugin } from './src/channel.js'; import { assertHostCompatibility } from './src/compat.js'; import { ClawlinkConfigSchema } from './src/config/config-schema.js'; import { registerTools, registerHooks } from './src/app/index.js'; import { installClawlinkVerbosePreflight } from './src/app/verbose-preflight.js'; // guard.ts removed — unhandled rejections fixed at source in ws-frame.ts send() import { setClawlinkPluginRuntime } from './src/runtime/plugin-runtime.js'; import { injectControlUiOverride } from './src/plugin-ui/index.js'; import { createAuthProxyHandler } from './src/proxy/auth-proxy.js'; import { logger } from './src/util/logger.js'; // ── Runtime reference ───────────────────────────────────────────────────── // Saved from api.runtime during register(), accessible by other modules. let _runtime: OpenClawPluginApi['runtime'] | undefined; export function getClawlinkRuntime(): OpenClawPluginApi['runtime'] | undefined { return _runtime; } export function setClawlinkRuntime(runtime: OpenClawPluginApi['runtime']): void { _runtime = runtime; } // ── Plugin entry ────────────────────────────────────────────────────────── export default { id: 'openclaw-clawlink', name: 'ClawLink', description: 'ClawLink agent network channel plugin — real-time messaging between AI agents via Tencent IM', configSchema: buildChannelConfigSchema(ClawlinkConfigSchema), register(api: OpenClawPluginApi): void { logger.info('[entry] register() called'); // P1-1: Fail-fast — reject incompatible host versions before any side-effects. assertHostCompatibility(api.runtime?.version); logger.info(`[entry] Host version: ${api.runtime?.version ?? 'unknown'}`); // P0: TIM rejection guard removed — root cause fixed in ws-frame.ts send() with p.catch(() => {}). // P1-3: Save runtime reference for use by other modules (logging, config, etc.) if (api.runtime) { setClawlinkRuntime(api.runtime); setClawlinkPluginRuntime(api.runtime as import('openclaw/plugin-sdk/core').PluginRuntime); installClawlinkVerbosePreflight(api); logger.info('[entry] Runtime reference saved'); } // Always register the channel adapter (needed even in setup-only mode). api.registerChannel({ plugin: clawlinkPlugin }); logger.info('[entry] Channel adapter registered'); // P1-2: registrationMode exists in 2026.3.22+; skip heavy registrations in // setup-only mode (e.g. `openclaw channels list`). const mode = (api as { registrationMode?: string }).registrationMode; if (mode && mode !== 'full') { logger.info(`[entry] Setup-only mode (registrationMode=${mode}), skipping tools/hooks`); return; } // Full mode — register tools and hooks. registerTools(api); registerHooks(api); // Auth proxy — same-origin reverse proxy to bypass CSP connect-src. // Browser fetches /plugins/clawlink/* → Gateway proxies to auth.ai-talk.live. // Covers both API calls (/api/agents) and static assets (/avatars/oc1.png). // Replaces the fragile patchCsp() approach (see docs/audit/022-A §4.6–4.10). try { if (typeof api.registerHttpRoute !== 'function') { logger.warn('[entry] api.registerHttpRoute is NOT a function — type: ' + typeof api.registerHttpRoute); } else { api.registerHttpRoute({ path: '/plugins/clawlink', auth: 'plugin', match: 'prefix', handler: createAuthProxyHandler(), }); logger.info('[entry] Auth proxy route registered (prefix: /plugins/clawlink)'); } } catch (routeErr) { logger.warn(`[entry] registerHttpRoute failed: ${(routeErr as Error).message}`); } // Auto-inject Control UI override (copies clawlink-override.js + patches) injectControlUiOverride().catch(err => { logger.warn(`[entry] Control UI injection failed: ${(err as Error).message}`); }); logger.info('[entry] Tools + hooks registered (full mode)'); }, }; // Named exports for direct imports export { clawlinkPlugin } from './src/channel.js'; export { registerTools, registerHooks } from './src/app/index.js'; export { registry } from './src/runtime/registry.js';