dev/2026-04-08 #1
@@ -43,8 +43,14 @@ import path from "node:path";
|
|||||||
import { validateYonexusClientConfig } from "./core/config.js";
|
import { validateYonexusClientConfig } from "./core/config.js";
|
||||||
import { createYonexusClientStateStore } from "./core/state.js";
|
import { createYonexusClientStateStore } from "./core/state.js";
|
||||||
import { createClientTransport } from "./core/transport.js";
|
import { createClientTransport } from "./core/transport.js";
|
||||||
import { createYonexusClientRuntime } from "./core/runtime.js";
|
import { createYonexusClientRuntime, type YonexusClientRuntime } from "./core/runtime.js";
|
||||||
import { createClientRuleRegistry } from "./core/rules.js";
|
import { createClientRuleRegistry, YonexusClientRuleRegistry } from "./core/rules.js";
|
||||||
|
|
||||||
|
const _G = globalThis as Record<string, unknown>;
|
||||||
|
const _STARTED_KEY = "_yonexusClientStarted";
|
||||||
|
const _RUNTIME_KEY = "_yonexusClientRuntime";
|
||||||
|
const _REGISTRY_KEY = "_yonexusClientRegistry";
|
||||||
|
const _CALLBACKS_KEY = "_yonexusClientOnAuthCallbacks";
|
||||||
|
|
||||||
export interface YonexusClientPluginManifest {
|
export interface YonexusClientPluginManifest {
|
||||||
readonly name: "Yonexus.Client";
|
readonly name: "Yonexus.Client";
|
||||||
@@ -58,41 +64,48 @@ const manifest: YonexusClientPluginManifest = {
|
|||||||
description: "Yonexus client plugin for cross-instance OpenClaw communication"
|
description: "Yonexus client plugin for cross-instance OpenClaw communication"
|
||||||
};
|
};
|
||||||
|
|
||||||
let _clientStarted = false;
|
|
||||||
|
|
||||||
export function createYonexusClientPlugin(api: { rootDir: string; pluginConfig: unknown }): void {
|
export function createYonexusClientPlugin(api: { rootDir: string; pluginConfig: unknown }): void {
|
||||||
if (_clientStarted) return;
|
// 1. Ensure shared state survives hot-reload — only initialise when absent
|
||||||
_clientStarted = true;
|
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. Start the runtime only once — the globalThis flag survives hot-reload
|
||||||
|
if (_G[_STARTED_KEY]) return;
|
||||||
|
_G[_STARTED_KEY] = true;
|
||||||
|
|
||||||
const config = validateYonexusClientConfig(api.pluginConfig);
|
const config = validateYonexusClientConfig(api.pluginConfig);
|
||||||
const stateStore = createYonexusClientStateStore(path.join(api.rootDir, "state.json"));
|
const stateStore = createYonexusClientStateStore(path.join(api.rootDir, "state.json"));
|
||||||
|
|
||||||
const ruleRegistry = createClientRuleRegistry();
|
|
||||||
const onAuthenticatedCallbacks: Array<() => void> = [];
|
|
||||||
|
|
||||||
let runtimeRef: ReturnType<typeof createYonexusClientRuntime> | null = null;
|
|
||||||
const transport = createClientTransport({
|
const transport = createClientTransport({
|
||||||
config,
|
config,
|
||||||
onMessage: (msg) => {
|
onMessage: (msg) => {
|
||||||
runtimeRef?.handleMessage(msg).catch((err: unknown) => {
|
(_G[_RUNTIME_KEY] as YonexusClientRuntime | undefined)?.handleMessage(msg).catch((err: unknown) => {
|
||||||
console.error("[yonexus-client] message handler error:", err);
|
console.error("[yonexus-client] message handler error:", err);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onStateChange: (state) => {
|
onStateChange: (state) => {
|
||||||
runtimeRef?.handleTransportStateChange(state);
|
(_G[_RUNTIME_KEY] as YonexusClientRuntime | undefined)?.handleTransportStateChange(state);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Expose registry and helpers for other plugins loaded in the same process
|
|
||||||
(globalThis as Record<string, unknown>)["__yonexusClient"] = {
|
|
||||||
ruleRegistry,
|
|
||||||
sendRule: (ruleId: string, content: string): boolean =>
|
|
||||||
runtimeRef?.sendRuleMessage(ruleId, content) ?? false,
|
|
||||||
submitPairingCode: (code: string): boolean =>
|
|
||||||
runtimeRef?.submitPairingCode(code) ?? false,
|
|
||||||
onAuthenticated: onAuthenticatedCallbacks
|
|
||||||
};
|
|
||||||
|
|
||||||
const runtime = createYonexusClientRuntime({
|
const runtime = createYonexusClientRuntime({
|
||||||
config,
|
config,
|
||||||
transport,
|
transport,
|
||||||
@@ -102,7 +115,7 @@ export function createYonexusClientPlugin(api: { rootDir: string; pluginConfig:
|
|||||||
for (const cb of onAuthenticatedCallbacks) cb();
|
for (const cb of onAuthenticatedCallbacks) cb();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
runtimeRef = runtime;
|
_G[_RUNTIME_KEY] = runtime;
|
||||||
|
|
||||||
const shutdown = (): void => {
|
const shutdown = (): void => {
|
||||||
runtime.stop().catch((err: unknown) => {
|
runtime.stop().catch((err: unknown) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user