export { validateYonexusClientConfig, YonexusClientConfigError } from "./core/config.js"; export type { YonexusClientConfig } from "./core/config.js"; export { CLIENT_STATE_VERSION, YonexusClientStateError, YonexusClientStateCorruptionError, createYonexusClientStateStore, loadYonexusClientState, saveYonexusClientState, createInitialClientState, hasClientSecret, hasClientKeyPair, type YonexusClientState, type YonexusClientStateFile, type YonexusClientStateStore } from "./core/state.js"; export { createClientTransport, YonexusClientTransport, type ClientTransport, type ClientTransportOptions, type ClientConnectionState, type ClientMessageHandler, type ClientStateChangeHandler, type ClientErrorHandler } from "./core/transport.js"; export { createYonexusClientRuntime, YonexusClientRuntime, type YonexusClientRuntimeOptions, type YonexusClientRuntimeState, type YonexusClientPhase } from "./core/runtime.js"; export { createClientRuleRegistry, YonexusClientRuleRegistry, ClientRuleRegistryError, type ClientRuleRegistry, type ClientRuleProcessor } from "./core/rules.js"; import os from "node:os"; import path from "node:path"; import fs from "node:fs"; import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry"; import type { OpenClawPluginApi } from "openclaw/plugin-sdk/core"; import { validateYonexusClientConfig } from "./core/config.js"; import { createYonexusClientStateStore } from "./core/state.js"; import { createClientTransport } from "./core/transport.js"; import { createYonexusClientRuntime, type YonexusClientRuntime } from "./core/runtime.js"; import { createClientRuleRegistry, YonexusClientRuleRegistry } from "./core/rules.js"; const _G = globalThis as Record; const _STARTED_KEY = "_yonexusClientStarted"; const _RUNTIME_KEY = "_yonexusClientRuntime"; const _REGISTRY_KEY = "_yonexusClientRegistry"; const _CALLBACKS_KEY = "_yonexusClientOnAuthCallbacks"; const PLUGIN_DATA_DIR = path.join(os.homedir(), ".openclaw", "yonexus-client"); export interface YonexusClientPluginManifest { readonly name: "Yonexus.Client"; readonly version: string; readonly description: string; } const manifest: YonexusClientPluginManifest = { name: "Yonexus.Client", version: "0.1.0", description: "Yonexus client plugin for cross-instance OpenClaw communication" }; export function createYonexusClientPlugin(api: OpenClawPluginApi): void { // 1. Ensure shared state survives hot-reload — only initialise when absent if (!(_G[_REGISTRY_KEY] instanceof YonexusClientRuleRegistry)) { _G[_REGISTRY_KEY] = createClientRuleRegistry(); } if (!Array.isArray(_G[_CALLBACKS_KEY])) { _G[_CALLBACKS_KEY] = []; } const ruleRegistry = _G[_REGISTRY_KEY] as YonexusClientRuleRegistry; const onAuthenticatedCallbacks = _G[_CALLBACKS_KEY] as Array<() => void>; // 2. Refresh the cross-plugin API object every call so that sendRule / submitPairingCode // closures always read the live runtime from globalThis. _G["__yonexusClient"] = { ruleRegistry, sendRule: (ruleId: string, content: string): boolean => (_G[_RUNTIME_KEY] as YonexusClientRuntime | undefined)?.sendRuleMessage(ruleId, content) ?? false, submitPairingCode: (code: string): boolean => (_G[_RUNTIME_KEY] as YonexusClientRuntime | undefined)?.submitPairingCode(code) ?? false, onAuthenticated: onAuthenticatedCallbacks }; // 3. Runtime startup — only fire when the gateway boots, not eagerly during // register() inside one-shot CLI subprocesses (e.g. `openclaw completion`). // Without this gate, every CLI invocation that loads plugins would open // a WebSocket to the Yonexus server. api.on("gateway_start", () => { if (_G[_STARTED_KEY]) return; _G[_STARTED_KEY] = true; fs.mkdirSync(PLUGIN_DATA_DIR, { recursive: true }); const config = validateYonexusClientConfig(api.pluginConfig); const stateStore = createYonexusClientStateStore(path.join(PLUGIN_DATA_DIR, "state.json")); const transport = createClientTransport({ config, onMessage: (msg) => { (_G[_RUNTIME_KEY] as YonexusClientRuntime | undefined)?.handleMessage(msg).catch((err: unknown) => { console.error("[yonexus-client] message handler error:", err); }); }, onStateChange: (state) => { (_G[_RUNTIME_KEY] as YonexusClientRuntime | undefined)?.handleTransportStateChange(state); } }); const runtime = createYonexusClientRuntime({ config, transport, stateStore, ruleRegistry, onAuthenticated: () => { for (const cb of onAuthenticatedCallbacks) cb(); } }); _G[_RUNTIME_KEY] = runtime; runtime.start().catch((err: unknown) => { console.error("[yonexus-client] failed to start:", err); }); }); api.on("gateway_stop", () => { const runtime = _G[_RUNTIME_KEY] as YonexusClientRuntime | undefined; runtime?.stop().catch((err: unknown) => { console.error("[yonexus-client] shutdown error:", err); }); }); } export default definePluginEntry({ id: "yonexus-client", name: "Yonexus.Client", description: "Yonexus client plugin for cross-instance OpenClaw communication", register: createYonexusClientPlugin, }); export { manifest };