feat: wire rule registry and authenticated callback into client runtime

- Add ruleRegistry and onAuthenticated options to YonexusClientRuntime
- Dispatch non-builtin messages to rule registry
- Fire onAuthenticated callback on auth_success
- Reload persisted state on reconnect so externally-written secrets are picked up
- Re-send hello on auth_failed("not_paired") when client has a valid secret
- Always enter waiting_pair_confirm after pair_request regardless of notification status
- Expose __yonexusClient on globalThis for cross-plugin communication
- Wire onStateChange in transport creation (previously missing, prevented connection)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
h z
2026-04-10 20:14:57 +01:00
parent 57b53fc122
commit 8824e768fb
5 changed files with 105 additions and 19 deletions

View File

@@ -39,30 +39,82 @@ export {
type ClientRuleProcessor
} from "./core/rules.js";
import path from "node:path";
import { validateYonexusClientConfig } from "./core/config.js";
import { createYonexusClientStateStore } from "./core/state.js";
import { createClientTransport } from "./core/transport.js";
import { createYonexusClientRuntime } from "./core/runtime.js";
import { createClientRuleRegistry } from "./core/rules.js";
export interface YonexusClientPluginManifest {
readonly name: "Yonexus.Client";
readonly version: string;
readonly description: string;
}
export interface YonexusClientPluginRuntime {
readonly hooks: readonly [];
readonly commands: readonly [];
readonly tools: readonly [];
}
const manifest: YonexusClientPluginManifest = {
name: "Yonexus.Client",
version: "0.1.0",
description: "Yonexus client plugin for cross-instance OpenClaw communication"
};
export function createYonexusClientPlugin(): YonexusClientPluginRuntime {
return {
hooks: [],
commands: [],
tools: []
let _clientStarted = false;
export function createYonexusClientPlugin(api: { rootDir: string; pluginConfig: unknown }): void {
if (_clientStarted) return;
_clientStarted = true;
const config = validateYonexusClientConfig(api.pluginConfig);
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({
config,
onMessage: (msg) => {
runtimeRef?.handleMessage(msg).catch((err: unknown) => {
console.error("[yonexus-client] message handler error:", err);
});
},
onStateChange: (state) => {
runtimeRef?.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({
config,
transport,
stateStore,
ruleRegistry,
onAuthenticated: () => {
for (const cb of onAuthenticatedCallbacks) cb();
}
});
runtimeRef = runtime;
const shutdown = (): void => {
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;