From c6232347dcb2b32991ce23074c95cb0fbcd79719 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sun, 31 May 2026 23:42:57 +0200 Subject: [PATCH] refactor: share exec approvals node invoke --- src/gateway/server-methods/exec-approvals.ts | 117 +++++++++---------- 1 file changed, 53 insertions(+), 64 deletions(-) diff --git a/src/gateway/server-methods/exec-approvals.ts b/src/gateway/server-methods/exec-approvals.ts index de13a4ffa4f..d6f2311b4fd 100644 --- a/src/gateway/server-methods/exec-approvals.ts +++ b/src/gateway/server-methods/exec-approvals.ts @@ -21,8 +21,8 @@ import { respondUnavailableOnThrow, safeParseJson, } from "./nodes.helpers.js"; -import type { GatewayRequestHandlers, RespondFn } from "./types.js"; -import { assertValidParams } from "./validation.js"; +import type { GatewayRequestContext, GatewayRequestHandlers, RespondFn } from "./types.js"; +import { assertValidParams, type Validator } from "./validation.js"; function requireApprovalsBaseHash( params: unknown, @@ -90,13 +90,37 @@ function toExecApprovalsPayload(snapshot: ExecApprovalsSnapshot) { }; } -function resolveNodeIdOrRespond(nodeId: string, respond: RespondFn): string | null { - const id = nodeId.trim(); - if (!id) { - respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "nodeId required")); - return null; +async function respondWithExecApprovalsNodePayload(params: { + method: string; + rawParams: unknown; + validate: Validator; + context: GatewayRequestContext; + respond: RespondFn; + command: "system.execApprovals.get" | "system.execApprovals.set"; + commandParams: (parsedParams: TParams) => Record; + readPayload: (response: { payload?: unknown; payloadJSON?: string | null }) => unknown; +}): Promise { + const rawParams = params.rawParams; + if (!assertValidParams(rawParams, params.validate, params.method, params.respond)) { + return; } - return id; + const parsedParams = rawParams; + const nodeId = parsedParams.nodeId.trim(); + if (!nodeId) { + params.respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "nodeId required")); + return; + } + await respondUnavailableOnThrow(params.respond, async () => { + const res = await params.context.nodeRegistry.invoke({ + nodeId, + command: params.command, + params: params.commandParams(parsedParams), + }); + if (!respondUnavailableOnNodeInvokeError(params.respond, res)) { + return; + } + params.respond(true, params.readPayload(res), undefined); + }); } export const execApprovalsHandlers: GatewayRequestHandlers = { @@ -133,69 +157,34 @@ export const execApprovalsHandlers: GatewayRequestHandlers = { respond(true, toExecApprovalsPayload(nextSnapshot), undefined); }, "exec.approvals.node.get": async ({ params, respond, context }) => { - if ( - !assertValidParams( - params, - validateExecApprovalsNodeGetParams, - "exec.approvals.node.get", - respond, - ) - ) { - return; - } - const { nodeId } = params as { nodeId: string }; - const id = resolveNodeIdOrRespond(nodeId, respond); - if (!id) { - return; - } - await respondUnavailableOnThrow(respond, async () => { - const res = await context.nodeRegistry.invoke({ - nodeId: id, - command: "system.execApprovals.get", - params: {}, - }); - if (!respondUnavailableOnNodeInvokeError(respond, res)) { - return; - } + await respondWithExecApprovalsNodePayload({ + method: "exec.approvals.node.get", + rawParams: params, + validate: validateExecApprovalsNodeGetParams, + context, + respond, + command: "system.execApprovals.get", + commandParams: () => ({}), // Node invocations can return structured payloads or JSON strings // depending on the transport; normalize before echoing the RPC response. - const payload = res.payloadJSON ? safeParseJson(res.payloadJSON) : res.payload; - respond(true, payload, undefined); + readPayload: (res) => (res.payloadJSON ? safeParseJson(res.payloadJSON) : res.payload), }); }, "exec.approvals.node.set": async ({ params, respond, context }) => { - if ( - !assertValidParams( - params, - validateExecApprovalsNodeSetParams, - "exec.approvals.node.set", - respond, - ) - ) { - return; - } - const { nodeId, file, baseHash } = params as { - nodeId: string; - file: ExecApprovalsFile; - baseHash?: string; - }; - const id = resolveNodeIdOrRespond(nodeId, respond); - if (!id) { - return; - } - await respondUnavailableOnThrow(respond, async () => { - const res = await context.nodeRegistry.invoke({ - nodeId: id, - command: "system.execApprovals.set", - params: { file, baseHash }, - }); - if (!respondUnavailableOnNodeInvokeError(respond, res)) { - return; - } + await respondWithExecApprovalsNodePayload({ + method: "exec.approvals.node.set", + rawParams: params, + validate: validateExecApprovalsNodeSetParams, + context, + respond, + command: "system.execApprovals.set", + commandParams: (parsedParams) => ({ + file: parsedParams.file, + baseHash: parsedParams.baseHash, + }), // node.set returns JSON on the command channel; keep the gateway response // shape aligned with local exec.approvals.set. - const payload = safeParseJson(res.payloadJSON ?? null); - respond(true, payload, undefined); + readPayload: (res) => safeParseJson(res.payloadJSON ?? null), }); }, };