dev/2026-04-08 #1

Merged
hzhang merged 24 commits from dev/2026-04-08 into main 2026-04-13 09:34:01 +00:00
Showing only changes of commit 4adb187331 - Show all commits

View File

@@ -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) => {