From f85806b6d78698fb6b6060d09e16cb09ae40131b Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 24 Apr 2026 07:31:59 +0100 Subject: [PATCH] fix: filter gateway node list locally --- src/gateway/server-plugins.test.ts | 23 +++++++++++++++++++++++ src/gateway/server-plugins.ts | 17 +++++++++++++---- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/gateway/server-plugins.test.ts b/src/gateway/server-plugins.test.ts index dc93df0bb26..a23500ce978 100644 --- a/src/gateway/server-plugins.test.ts +++ b/src/gateway/server-plugins.test.ts @@ -526,6 +526,29 @@ describe("loadGatewayPlugins", () => { expect(typeof subagent?.getSession).toBe("function"); }); + test("filters connected plugin nodes locally without sending unsupported node.list params", async () => { + loadOpenClawPlugins.mockReturnValue(createRegistry([])); + loadGatewayStartupPluginsForTest(); + serverPluginsModule.setFallbackGatewayContext(createTestContext("nodes-list-filter")); + handleGatewayRequest.mockImplementationOnce(async (opts: HandleGatewayRequestOptions) => { + expect(opts.req.method).toBe("node.list"); + opts.respond(true, { + nodes: [ + { nodeId: "connected", connected: true }, + { nodeId: "offline", connected: false }, + ], + }); + }); + + const runtime = runtimeModule.createPluginRuntime({ + allowGatewaySubagentBinding: true, + }); + const result = await runtime.nodes.list({ connected: true }); + + expect(getLastDispatchedParams()).toEqual({}); + expect(result.nodes).toEqual([{ nodeId: "connected", connected: true }]); + }); + test("forwards provider and model overrides when the request scope is authorized", async () => { const serverPlugins = serverPluginsModule; const runtime = await createSubagentRuntime(serverPlugins); diff --git a/src/gateway/server-plugins.ts b/src/gateway/server-plugins.ts index 92689d7ad49..e830ed68134 100644 --- a/src/gateway/server-plugins.ts +++ b/src/gateway/server-plugins.ts @@ -388,11 +388,20 @@ export function createGatewaySubagentRuntime(): PluginRuntime["subagent"] { export function createGatewayNodesRuntime(): PluginRuntime["nodes"] { return { async list(params) { - const payload = await dispatchGatewayMethod<{ nodes?: unknown[] }>("node.list", { - ...(params?.connected === true && { connected: true }), - }); + const payload = await dispatchGatewayMethod<{ nodes?: unknown[] }>("node.list", {}); const nodes = Array.isArray(payload?.nodes) ? payload.nodes : []; - return { nodes: nodes as Awaited>["nodes"] }; + const filteredNodes = + params?.connected === true + ? nodes.filter( + (node) => + node !== null && + typeof node === "object" && + (node as { connected?: unknown }).connected === true, + ) + : nodes; + return { + nodes: filteredNodes as Awaited>["nodes"], + }; }, async invoke(params) { const payload = await dispatchGatewayMethod("node.invoke", {