From 36c82827950fc216a238e540ab3d3e0a2e9df60d Mon Sep 17 00:00:00 2001 From: Shakker Date: Wed, 1 Apr 2026 21:08:14 +0100 Subject: [PATCH] refactor: lazy load cli gateway helper runtimes --- src/cli/logs-cli.runtime.ts | 1 + src/cli/logs-cli.ts | 24 +++++++++++++++++---- src/cli/nodes-cli/rpc.runtime.ts | 29 +++++++++++++++++++++++++ src/cli/nodes-cli/rpc.ts | 37 +++++++++++++------------------- 4 files changed, 65 insertions(+), 26 deletions(-) create mode 100644 src/cli/logs-cli.runtime.ts create mode 100644 src/cli/nodes-cli/rpc.runtime.ts diff --git a/src/cli/logs-cli.runtime.ts b/src/cli/logs-cli.runtime.ts new file mode 100644 index 00000000000..027ed533c2d --- /dev/null +++ b/src/cli/logs-cli.runtime.ts @@ -0,0 +1 @@ +export { buildGatewayConnectionDetails } from "../gateway/call.js"; diff --git a/src/cli/logs-cli.ts b/src/cli/logs-cli.ts index 69b4fdf2a9f..8af6a98c687 100644 --- a/src/cli/logs-cli.ts +++ b/src/cli/logs-cli.ts @@ -1,6 +1,5 @@ import { setTimeout as delay } from "node:timers/promises"; import type { Command } from "commander"; -import { buildGatewayConnectionDetails } from "../gateway/call.js"; import { parseLogLine } from "../logging/parse-log-line.js"; import { formatTimestamp, isValidTimeZone } from "../logging/timestamps.js"; import { formatDocsLink } from "../terminal/links.js"; @@ -10,6 +9,15 @@ import { colorize, isRich, theme } from "../terminal/theme.js"; import { formatCliCommand } from "./command-format.js"; import { addGatewayClientOptions, callGatewayFromCli } from "./gateway-rpc.js"; +type LogsCliRuntimeModule = typeof import("./logs-cli.runtime.js"); + +let logsCliRuntimePromise: Promise | undefined; + +async function loadLogsCliRuntime(): Promise { + logsCliRuntimePromise ??= import("./logs-cli.runtime.js"); + return logsCliRuntimePromise; +} + type LogsTailPayload = { file?: string; cursor?: number; @@ -149,7 +157,7 @@ function createLogWriters() { }; } -function emitGatewayError( +async function emitGatewayError( err: unknown, opts: LogsCliOptions, mode: "json" | "text", @@ -157,11 +165,12 @@ function emitGatewayError( emitJsonLine: (payload: Record, toStdErr?: boolean) => boolean, errorLine: (text: string) => boolean, ) { - const details = buildGatewayConnectionDetails({ url: opts.url }); + const runtime = await loadLogsCliRuntime(); const message = "Gateway not reachable. Is it running and accessible?"; const hint = `Hint: run \`${formatCliCommand("openclaw doctor")}\`.`; const errorText = err instanceof Error ? err.message : String(err); + const details = runtime.buildGatewayConnectionDetails({ url: opts.url }); if (mode === "json") { if ( !emitJsonLine( @@ -227,7 +236,14 @@ export function registerLogsCli(program: Command) { try { payload = await fetchLogs(opts, cursor, showProgress); } catch (err) { - emitGatewayError(err, opts, jsonMode ? "json" : "text", rich, emitJsonLine, errorLine); + await emitGatewayError( + err, + opts, + jsonMode ? "json" : "text", + rich, + emitJsonLine, + errorLine, + ); process.exit(1); return; } diff --git a/src/cli/nodes-cli/rpc.runtime.ts b/src/cli/nodes-cli/rpc.runtime.ts new file mode 100644 index 00000000000..fc3d1300447 --- /dev/null +++ b/src/cli/nodes-cli/rpc.runtime.ts @@ -0,0 +1,29 @@ +import { callGateway } from "../../gateway/call.js"; +import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../../utils/message-channel.js"; +import { withProgress } from "../progress.js"; +import type { NodesRpcOpts } from "./types.js"; + +export async function callGatewayCliRuntime( + method: string, + opts: NodesRpcOpts, + params?: unknown, + callOpts?: { transportTimeoutMs?: number }, +) { + return await withProgress( + { + label: `Nodes ${method}`, + indeterminate: true, + enabled: opts.json !== true, + }, + async () => + await callGateway({ + url: opts.url, + token: opts.token, + method, + params, + timeoutMs: callOpts?.transportTimeoutMs ?? Number(opts.timeout ?? 10_000), + clientName: GATEWAY_CLIENT_NAMES.CLI, + mode: GATEWAY_CLIENT_MODES.CLI, + }), + ); +} diff --git a/src/cli/nodes-cli/rpc.ts b/src/cli/nodes-cli/rpc.ts index e0ceebe2ba3..a3c71145861 100644 --- a/src/cli/nodes-cli/rpc.ts +++ b/src/cli/nodes-cli/rpc.ts @@ -1,11 +1,18 @@ +import { randomUUID } from "node:crypto"; import type { Command } from "commander"; -import { callGateway, randomIdempotencyKey } from "../../gateway/call.js"; import { resolveNodeFromNodeList } from "../../shared/node-resolve.js"; -import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../../utils/message-channel.js"; -import { withProgress } from "../progress.js"; import { parseNodeList, parsePairingList } from "./format.js"; import type { NodeListNode, NodesRpcOpts } from "./types.js"; +type NodesCliRpcRuntimeModule = typeof import("./rpc.runtime.js"); + +let nodesCliRpcRuntimePromise: Promise | undefined; + +async function loadNodesCliRpcRuntime(): Promise { + nodesCliRpcRuntimePromise ??= import("./rpc.runtime.js"); + return nodesCliRpcRuntimePromise; +} + export const nodesCallOpts = (cmd: Command, defaults?: { timeoutMs?: number }) => cmd .option("--url ", "Gateway WebSocket URL (defaults to gateway.remote.url when configured)") @@ -18,24 +25,10 @@ export const callGatewayCli = async ( opts: NodesRpcOpts, params?: unknown, callOpts?: { transportTimeoutMs?: number }, -) => - withProgress( - { - label: `Nodes ${method}`, - indeterminate: true, - enabled: opts.json !== true, - }, - async () => - await callGateway({ - url: opts.url, - token: opts.token, - method, - params, - timeoutMs: callOpts?.transportTimeoutMs ?? Number(opts.timeout ?? 10_000), - clientName: GATEWAY_CLIENT_NAMES.CLI, - mode: GATEWAY_CLIENT_MODES.CLI, - }), - ); +) => { + const runtime = await loadNodesCliRpcRuntime(); + return await runtime.callGatewayCliRuntime(method, opts, params, callOpts); +}; export function buildNodeInvokeParams(params: { nodeId: string; @@ -48,7 +41,7 @@ export function buildNodeInvokeParams(params: { nodeId: params.nodeId, command: params.command, params: params.params, - idempotencyKey: params.idempotencyKey ?? randomIdempotencyKey(), + idempotencyKey: params.idempotencyKey ?? randomUUID(), }; if (typeof params.timeoutMs === "number" && Number.isFinite(params.timeoutMs)) { invokeParams.timeoutMs = params.timeoutMs;