diff --git a/package.json b/package.json index 67609a0..c205547 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "devDependencies": { "@types/node": "^25.5.2", "typescript": "^5.6.3", - "vitest": "^4.1.3" + "vitest": "^4.1.3", + "openclaw": "file:/usr/lib/node_modules/openclaw" } } diff --git a/plugin/index.ts b/plugin/index.ts index 7054776..451d459 100644 --- a/plugin/index.ts +++ b/plugin/index.ts @@ -39,7 +39,11 @@ export { 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"; @@ -52,6 +56,8 @@ 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; @@ -64,7 +70,7 @@ const manifest: YonexusClientPluginManifest = { description: "Yonexus client plugin for cross-instance OpenClaw communication" }; -export function createYonexusClientPlugin(api: { rootDir: string; pluginConfig: unknown }): void { +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(); @@ -87,48 +93,59 @@ export function createYonexusClientPlugin(api: { rootDir: string; pluginConfig: onAuthenticated: onAuthenticatedCallbacks }; - // 3. Start the runtime only once — the globalThis flag survives hot-reload - if (_G[_STARTED_KEY]) return; - _G[_STARTED_KEY] = true; + // 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; - const config = validateYonexusClientConfig(api.pluginConfig); - const stateStore = createYonexusClientStateStore(path.join(api.rootDir, "state.json")); + fs.mkdirSync(PLUGIN_DATA_DIR, { recursive: true }); - 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 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); + }); }); - const runtime = createYonexusClientRuntime({ - config, - transport, - stateStore, - ruleRegistry, - onAuthenticated: () => { - for (const cb of onAuthenticatedCallbacks) cb(); - } - }); - _G[_RUNTIME_KEY] = runtime; - - const shutdown = (): void => { - runtime.stop().catch((err: unknown) => { + api.on("gateway_stop", () => { + const runtime = _G[_RUNTIME_KEY] as YonexusClientRuntime | undefined; + runtime?.stop().catch((err: unknown) => { console.error("[yonexus-client] shutdown error:", err); }); - }; - process.once("SIGTERM", shutdown); - process.once("SIGINT", shutdown); - - runtime.start().catch((err: unknown) => { - console.error("[yonexus-client] failed to start:", err); }); } -export default createYonexusClientPlugin; +export default definePluginEntry({ + id: "yonexus-client", + name: "Yonexus.Client", + description: "Yonexus client plugin for cross-instance OpenClaw communication", + register: createYonexusClientPlugin, +}); export { manifest }; diff --git a/plugin/openclaw.plugin.json b/plugin/openclaw.plugin.json index ec65507..fd5c52e 100644 --- a/plugin/openclaw.plugin.json +++ b/plugin/openclaw.plugin.json @@ -1,10 +1,10 @@ { "id": "yonexus-client", "name": "Yonexus.Client", - "version": "0.1.0", "description": "Yonexus client plugin for cross-instance OpenClaw communication", - "entry": "./dist/Yonexus.Client/plugin/index.js", - "permissions": [], + "activation": { + "onStartup": true + }, "configSchema": { "type": "object", "additionalProperties": false,