From dc6ac472dbb46accbd931a8d5e60eba679a05211 Mon Sep 17 00:00:00 2001 From: Shakker Date: Mon, 27 Apr 2026 07:22:57 +0100 Subject: [PATCH] refactor: use plugin lookup table for gateway load fallback --- src/gateway/server-plugins.test.ts | 42 +++++++++++++++++++++--------- src/gateway/server-plugins.ts | 6 ++--- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/src/gateway/server-plugins.test.ts b/src/gateway/server-plugins.test.ts index 00e50107428..b0c6e639bdb 100644 --- a/src/gateway/server-plugins.test.ts +++ b/src/gateway/server-plugins.test.ts @@ -6,7 +6,13 @@ import type { PluginDiagnostic } from "../plugins/types.js"; import type { GatewayRequestContext, GatewayRequestOptions } from "./server-methods/types.js"; const loadOpenClawPlugins = vi.hoisted(() => vi.fn()); -const resolveGatewayStartupPluginIds = vi.hoisted(() => vi.fn(() => ["discord", "telegram"])); +const loadPluginLookUpTable = vi.hoisted(() => + vi.fn(() => ({ + startup: { + pluginIds: ["discord", "telegram"], + }, + })), +); const applyPluginAutoEnable = vi.hoisted(() => vi.fn(({ config }) => ({ config, changes: [], autoEnabledReasons: {} })), ); @@ -34,8 +40,8 @@ vi.mock("../plugins/runtime/load-context.js", () => ({ createPluginRuntimeLoaderLogger: () => pluginRuntimeLoaderLogger, })); -vi.mock("../plugins/channel-plugin-ids.js", () => ({ - resolveGatewayStartupPluginIds, +vi.mock("../plugins/plugin-lookup-table.js", () => ({ + loadPluginLookUpTable, })); vi.mock("../config/plugin-auto-enable.js", () => ({ @@ -243,7 +249,11 @@ beforeAll(async () => { beforeEach(() => { loadOpenClawPlugins.mockReset(); - resolveGatewayStartupPluginIds.mockReset().mockReturnValue(["discord", "telegram"]); + loadPluginLookUpTable.mockReset().mockReturnValue({ + startup: { + pluginIds: ["discord", "telegram"], + }, + }); applyPluginAutoEnable .mockReset() .mockImplementation(({ config }) => ({ config, changes: [], autoEnabledReasons: {} })); @@ -306,7 +316,7 @@ describe("loadGatewayPlugins", () => { config: {}, env: process.env, }); - expect(resolveGatewayStartupPluginIds).toHaveBeenCalledWith({ + expect(loadPluginLookUpTable).toHaveBeenCalledWith({ config: {}, activationSourceConfig: undefined, workspaceDir: "/tmp", @@ -354,7 +364,7 @@ describe("loadGatewayPlugins", () => { pluginIds: ["browser"], }); - expect(resolveGatewayStartupPluginIds).not.toHaveBeenCalled(); + expect(loadPluginLookUpTable).not.toHaveBeenCalled(); expect(loadOpenClawPlugins).toHaveBeenCalledWith( expect.objectContaining({ onlyPluginIds: ["browser"], @@ -397,7 +407,7 @@ describe("loadGatewayPlugins", () => { pluginIds: ["slack"], }); - expect(resolveGatewayStartupPluginIds).not.toHaveBeenCalled(); + expect(loadPluginLookUpTable).not.toHaveBeenCalled(); expect(applyPluginAutoEnable).toHaveBeenCalledWith({ config: rawConfig, env: process.env, @@ -415,7 +425,11 @@ describe("loadGatewayPlugins", () => { }); test("treats an empty startup scope as no plugin load instead of an unscoped load", async () => { - resolveGatewayStartupPluginIds.mockReturnValue([]); + loadPluginLookUpTable.mockReturnValue({ + startup: { + pluginIds: [], + }, + }); const result = serverPluginsModule.loadGatewayPlugins({ cfg: {}, @@ -431,7 +445,11 @@ describe("loadGatewayPlugins", () => { }); test("stores workspaceDir on the active registry when startup scope is empty", () => { - resolveGatewayStartupPluginIds.mockReturnValue([]); + loadPluginLookUpTable.mockReturnValue({ + startup: { + pluginIds: [], + }, + }); serverPluginsModule.loadGatewayPlugins({ cfg: {}, @@ -457,7 +475,7 @@ describe("loadGatewayPlugins", () => { loadGatewayPluginsForTest(); - expect(resolveGatewayStartupPluginIds).toHaveBeenCalledWith({ + expect(loadPluginLookUpTable).toHaveBeenCalledWith({ config: autoEnabledConfig, activationSourceConfig: undefined, workspaceDir: "/tmp", @@ -495,7 +513,7 @@ describe("loadGatewayPlugins", () => { config: rawConfig, env: process.env, }); - expect(resolveGatewayStartupPluginIds).toHaveBeenCalledWith({ + expect(loadPluginLookUpTable).toHaveBeenCalledWith({ config: resolvedConfig, activationSourceConfig: rawConfig, workspaceDir: "/tmp", @@ -979,7 +997,7 @@ describe("loadGatewayPlugins", () => { logDiagnostics: false, }); - expect(resolveGatewayStartupPluginIds).not.toHaveBeenCalled(); + expect(loadPluginLookUpTable).not.toHaveBeenCalled(); expect(loadOpenClawPlugins).toHaveBeenCalledWith( expect.objectContaining({ onlyPluginIds: ["discord"], diff --git a/src/gateway/server-plugins.ts b/src/gateway/server-plugins.ts index 0737bfed62e..c04a2d6bd50 100644 --- a/src/gateway/server-plugins.ts +++ b/src/gateway/server-plugins.ts @@ -3,9 +3,9 @@ import { normalizeModelRef, parseModelRef } from "../agents/model-selection.js"; import { applyPluginAutoEnable } from "../config/plugin-auto-enable.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import type { BundledRuntimeDepsInstallParams } from "../plugins/bundled-runtime-deps.js"; -import { resolveGatewayStartupPluginIds } from "../plugins/channel-plugin-ids.js"; import { normalizePluginsConfig } from "../plugins/config-state.js"; import { loadOpenClawPlugins } from "../plugins/loader.js"; +import { loadPluginLookUpTable } from "../plugins/plugin-lookup-table.js"; import { createEmptyPluginRegistry } from "../plugins/registry-empty.js"; import { setActivePluginRegistry } from "../plugins/runtime.js"; import { getPluginRuntimeGatewayRequestScope } from "../plugins/runtime/gateway-request-scope.js"; @@ -479,12 +479,12 @@ export function loadGatewayPlugins(params: { const resolvedConfig = autoEnabled.config; const pluginIds = params.pluginIds ?? - resolveGatewayStartupPluginIds({ + loadPluginLookUpTable({ config: resolvedConfig, activationSourceConfig: params.activationSourceConfig, workspaceDir: params.workspaceDir, env: process.env, - }); + }).startup.pluginIds; if (pluginIds.length === 0) { const pluginRegistry = createEmptyPluginRegistry(); setActivePluginRegistry(pluginRegistry, undefined, "gateway-bindable", params.workspaceDir);