refactor: simplify plugin runtime singletons

This commit is contained in:
Peter Steinberger
2026-03-22 17:57:57 +00:00
parent 9428b38452
commit f095bbd7b0
4 changed files with 26 additions and 36 deletions

View File

@@ -118,13 +118,17 @@ type PluginBindingGlobalState = {
};
const pluginBindingGlobalStateKey = Symbol.for("openclaw.plugins.binding.global-state");
function getPluginBindingGlobalState(): PluginBindingGlobalState {
return resolveGlobalSingleton<PluginBindingGlobalState>(pluginBindingGlobalStateKey, () => ({
const pluginBindingGlobalState = resolveGlobalSingleton<PluginBindingGlobalState>(
pluginBindingGlobalStateKey,
() => ({
fallbackNoticeBindingIds: new Set<string>(),
approvalsCache: null,
approvalsLoaded: false,
}));
}),
);
function getPluginBindingGlobalState(): PluginBindingGlobalState {
return pluginBindingGlobalState;
}
function resolveApprovalsPath(): string {

View File

@@ -6,6 +6,7 @@
*/
import { createSubsystemLogger } from "../logging/subsystem.js";
import { resolveGlobalSingleton } from "../shared/global-singleton.js";
import { createHookRunner, type HookRunner } from "./hooks.js";
import type { PluginRegistry } from "./registry.js";
import type { PluginHookGatewayContext, PluginHookGatewayStopEvent } from "./types.js";
@@ -18,23 +19,16 @@ type HookRunnerGlobalState = {
};
const hookRunnerGlobalStateKey = Symbol.for("openclaw.plugins.hook-runner-global-state");
function getHookRunnerGlobalState(): HookRunnerGlobalState {
const globalStore = globalThis as typeof globalThis & {
[hookRunnerGlobalStateKey]?: HookRunnerGlobalState;
};
return (globalStore[hookRunnerGlobalStateKey] ??= {
hookRunner: null,
registry: null,
});
}
const state = resolveGlobalSingleton<HookRunnerGlobalState>(hookRunnerGlobalStateKey, () => ({
hookRunner: null,
registry: null,
}));
/**
* Initialize the global hook runner with a plugin registry.
* Called once when plugins are loaded during gateway startup.
*/
export function initializeGlobalHookRunner(registry: PluginRegistry): void {
const state = getHookRunnerGlobalState();
state.registry = registry;
state.hookRunner = createHookRunner(registry, {
logger: {
@@ -56,7 +50,7 @@ export function initializeGlobalHookRunner(registry: PluginRegistry): void {
* Returns null if plugins haven't been loaded yet.
*/
export function getGlobalHookRunner(): HookRunner | null {
return getHookRunnerGlobalState().hookRunner;
return state.hookRunner;
}
/**
@@ -64,14 +58,14 @@ export function getGlobalHookRunner(): HookRunner | null {
* Returns null if plugins haven't been loaded yet.
*/
export function getGlobalPluginRegistry(): PluginRegistry | null {
return getHookRunnerGlobalState().registry;
return state.registry;
}
/**
* Check if any hooks are registered for a given hook name.
*/
export function hasGlobalHooks(hookName: Parameters<HookRunner["hasHooks"]>[0]): boolean {
return getHookRunnerGlobalState().hookRunner?.hasHooks(hookName) ?? false;
return state.hookRunner?.hasHooks(hookName) ?? false;
}
export async function runGlobalGatewayStopSafely(params: {
@@ -98,7 +92,6 @@ export async function runGlobalGatewayStopSafely(params: {
* Reset the global hook runner (for testing).
*/
export function resetGlobalHookRunner(): void {
const state = getHookRunnerGlobalState();
state.hookRunner = null;
state.registry = null;
}

View File

@@ -1,4 +1,4 @@
import { createDedupeCache } from "../infra/dedupe.js";
import { createDedupeCache, resolveGlobalDedupeCache } from "../infra/dedupe.js";
import { resolveGlobalSingleton } from "../shared/global-singleton.js";
import {
dispatchDiscordInteractiveHandler,
@@ -43,7 +43,7 @@ const PLUGIN_INTERACTIVE_STATE_KEY = Symbol.for("openclaw.pluginInteractiveState
const state = resolveGlobalSingleton<InteractiveState>(PLUGIN_INTERACTIVE_STATE_KEY, () => ({
interactiveHandlers: new Map<string, RegisteredInteractiveHandler>(),
callbackDedupe: createDedupeCache({
callbackDedupe: resolveGlobalDedupeCache(Symbol.for("openclaw.pluginInteractiveCallbackDedupe"), {
ttlMs: 5 * 60_000,
maxSize: 4096,
}),

View File

@@ -1,3 +1,4 @@
import { resolveGlobalSingleton } from "../shared/global-singleton.js";
import { createEmptyPluginRegistry } from "./registry-empty.js";
import type { PluginRegistry } from "./registry.js";
@@ -11,21 +12,13 @@ type RegistryState = {
version: number;
};
const state: RegistryState = (() => {
const globalState = globalThis as typeof globalThis & {
[REGISTRY_STATE]?: RegistryState;
};
if (!globalState[REGISTRY_STATE]) {
globalState[REGISTRY_STATE] = {
registry: createEmptyPluginRegistry(),
httpRouteRegistry: null,
httpRouteRegistryPinned: false,
key: null,
version: 0,
};
}
return globalState[REGISTRY_STATE];
})();
const state = resolveGlobalSingleton<RegistryState>(REGISTRY_STATE, () => ({
registry: createEmptyPluginRegistry(),
httpRouteRegistry: null,
httpRouteRegistryPinned: false,
key: null,
version: 0,
}));
export function setActivePluginRegistry(registry: PluginRegistry, cacheKey?: string) {
state.registry = registry;