openclaw/src/cli/nodes-cli/rpc.runtime.ts
Jason (Json) 65b460f234
fix(nodes): surface pending reapproval diagnostics (#92547)
* fix(nodes): surface pending reapproval diagnostics

* fix(nodes): harden reapproval diagnostics

* fix(nodes): scope pending diagnostics

* fix(nodes): request pairing diagnostics in cli

* fix(nodes): reuse stored auth for diagnostics

* fix(nodes): preserve selected diagnostics credentials

* fix(nodes): prefer approved diagnostics auth

* fix(nodes): narrow diagnostics fallbacks

* fix(nodes): recover from stale diagnostics auth

* fix(gateway): preserve connect error narrowing

* fix(nodes): isolate privileged diagnostics auth

* fix(nodes): constrain privileged diagnostics auth

* fix(nodes): close diagnostics review gaps

* fix(nodes): guard reapproval cleanup races

* fix(nodes): defer stale pairing cleanup

* fix(nodes): preserve reapproval on hello failure

* test(nodes): await post-handshake reapproval cleanup

* test(nodes): avoid unbound websocket send capture

* fix(nodes): allow local auth-none diagnostics

* fix(nodes): preserve overlapping reapproval

* fix(nodes): preserve pending node metadata

* fix(nodes): keep connection age with status

* fix(nodes): preserve reapproval during reconnect races

* fix(nodes): serialize reapproval cleanup

* fix(nodes): bound reapproval reconnect races

* test(nodes): satisfy cleanup claim lint

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-06-13 17:40:18 -07:00

88 lines
3.1 KiB
TypeScript

// Runtime gateway RPC helpers for node host and node pairing CLI commands.
import {
GATEWAY_CLIENT_MODES,
GATEWAY_CLIENT_NAMES,
} from "../../../packages/gateway-protocol/src/client-info.js";
import { callGateway } from "../../gateway/call.js";
import type { OperatorScope } from "../../gateway/method-scopes.js";
import { parseTimeoutMsWithFallback } from "../parse-timeout.js";
import { withProgress } from "../progress.js";
import type { NodesRpcOpts } from "./types.js";
const NODE_PAIR_APPROVAL_GATEWAY_METHODS = new Set<string>(["node.pair.list", "node.pair.approve"]);
const DEFAULT_NODES_RPC_TIMEOUT_MS = 10_000;
function resolveNodesTransportTimeoutMs(opts: NodesRpcOpts, overrideMs?: number): number {
return overrideMs ?? parseTimeoutMsWithFallback(opts.timeout, DEFAULT_NODES_RPC_TIMEOUT_MS);
}
export async function callGatewayCliRuntime(
method: string,
opts: NodesRpcOpts,
params?: unknown,
callOpts?: {
scopes?: OperatorScope[];
transportTimeoutMs?: number;
useStoredDeviceAuth?: boolean;
requiredStoredDeviceAuthScopes?: OperatorScope[];
useLocalBackendSharedAuth?: boolean;
},
) {
// Progress is suppressed for JSON callers so stdout remains structured.
return await withProgress(
{
label: `Nodes ${method}`,
indeterminate: true,
enabled: opts.json !== true,
},
async () =>
await callGateway({
url: opts.url,
token: opts.token,
method,
params,
scopes: callOpts?.scopes,
useStoredDeviceAuth: callOpts?.useStoredDeviceAuth,
requiredStoredDeviceAuthScopes: callOpts?.requiredStoredDeviceAuthScopes,
requireLocalBackendSharedAuth: callOpts?.useLocalBackendSharedAuth,
timeoutMs: resolveNodesTransportTimeoutMs(opts, callOpts?.transportTimeoutMs),
clientName: callOpts?.useLocalBackendSharedAuth
? GATEWAY_CLIENT_NAMES.GATEWAY_CLIENT
: GATEWAY_CLIENT_NAMES.CLI,
mode: callOpts?.useLocalBackendSharedAuth
? GATEWAY_CLIENT_MODES.BACKEND
: GATEWAY_CLIENT_MODES.CLI,
}),
);
}
export async function callNodePairApprovalGatewayCliRuntime(
method: "node.pair.list" | "node.pair.approve",
opts: NodesRpcOpts,
params: unknown,
callOpts: { scopes: OperatorScope[]; transportTimeoutMs?: number },
) {
if (!NODE_PAIR_APPROVAL_GATEWAY_METHODS.has(method)) {
throw new Error(`unsupported node pair approval gateway method: ${method}`);
}
// Node approval may need the local gateway's backend shared-auth authority
// before the CLI device has been granted the node's required operator scopes.
return await withProgress(
{
label: `Nodes ${method}`,
indeterminate: true,
enabled: opts.json !== true,
},
async () =>
await callGateway({
url: opts.url,
token: opts.token,
method,
params,
timeoutMs: resolveNodesTransportTimeoutMs(opts, callOpts.transportTimeoutMs),
clientName: GATEWAY_CLIENT_NAMES.GATEWAY_CLIENT,
mode: GATEWAY_CLIENT_MODES.BACKEND,
scopes: callOpts.scopes,
}),
);
}