


























@@ -6,7 +6,7 @@ import type { OperatorScope } from "../../gateway/method-scopes.js";
66import { resolveNodePairApprovalScopes } from "../../infra/node-pairing-authz.js";
77import { defaultRuntime } from "../../runtime.js";
88import { formatCliCommand } from "../command-format.js";
9-import { getNodesTheme, runNodesCommand } from "./cli-utils.js";
9+import { formatConnectionFlagReminder, getNodesTheme, runNodesCommand } from "./cli-utils.js";
1010import { parsePairingList } from "./format.js";
1111import { renderPendingPairingRequestsTable } from "./pairing-render.js";
1212import {
@@ -44,25 +44,67 @@ function normalizeNodePairApproveScopes(scopes: unknown): OperatorScope[] {
4444async function resolveApproveScopesForRequest(
4545opts: NodesRpcOpts,
4646requestId: string,
47-): Promise<OperatorScope[]> {
47+): Promise<{ scopes: OperatorScope[] }> {
48+let pending: PendingRequest[];
4849try {
4950const result = await callNodePairApprovalGatewayCli(
5051"node.pair.list",
5152opts,
5253{},
5354{ scopes: DEFAULT_NODE_PAIR_APPROVE_SCOPES },
5455);
55-const { pending } = parsePairingList(result);
56-const request = pending.find((candidate: PendingRequest) => candidate.requestId === requestId);
57-const scopes = normalizeNodePairApproveScopes(request?.requiredApproveScopes);
58-if (scopes.length > DEFAULT_NODE_PAIR_APPROVE_SCOPES.length) {
59-return scopes;
60-}
61-// Older pending requests only list requested commands; derive approval scopes from them.
62-return resolveNodePairApprovalScopes(request?.commands) as OperatorScope[];
56+pending = parsePairingList(result).pending;
6357} catch {
64-return [...DEFAULT_NODE_PAIR_APPROVE_SCOPES];
58+return { scopes: [...DEFAULT_NODE_PAIR_APPROVE_SCOPES] };
59+}
60+const pendingRequestIds = pending
61+.map((request) => request.requestId)
62+.filter((id): id is string => typeof id === "string" && id.length > 0);
63+const request = pending.find((candidate) => candidate.requestId === requestId);
64+if (!request) {
65+throw new Error(buildUnknownNodePairRequestIdMessage(requestId, opts, pendingRequestIds));
6566}
67+const declaredScopes = normalizeNodePairApproveScopes(request.requiredApproveScopes);
68+if (declaredScopes.length > DEFAULT_NODE_PAIR_APPROVE_SCOPES.length) {
69+return { scopes: declaredScopes };
70+}
71+// Older pending requests only list requested commands; derive approval scopes from them.
72+return {
73+scopes: resolveNodePairApprovalScopes(request.commands) as OperatorScope[],
74+};
75+}
76+77+function isUnknownNodePairRequestIdError(
78+error: unknown,
79+): error is Error & { gatewayCode: "INVALID_REQUEST" } {
80+const requestError = error as (Error & { gatewayCode?: unknown }) | undefined;
81+return (
82+requestError instanceof Error &&
83+requestError.name === "GatewayClientRequestError" &&
84+requestError.gatewayCode === "INVALID_REQUEST" &&
85+requestError.message === "unknown requestId"
86+);
87+}
88+89+function buildUnknownNodePairRequestIdMessage(
90+requestId: string,
91+opts: NodesRpcOpts,
92+pendingRequestIds?: string[],
93+): string {
94+const lines = [`Unknown node pairing requestId: ${requestId}`];
95+if (pendingRequestIds !== undefined) {
96+if (pendingRequestIds.length > 0) {
97+lines.push(`Pending requestIds: ${pendingRequestIds.join(", ")}`);
98+} else {
99+lines.push("No pending node pairing requests are currently visible.");
100+}
101+}
102+lines.push(`Run ${formatCliCommand("openclaw nodes pending")} to inspect current requests.`);
103+const connectionReminder = formatConnectionFlagReminder(opts);
104+if (connectionReminder) {
105+lines.push(connectionReminder);
106+}
107+return lines.join("\n");
66108}
6710968110/** Register node pairing management commands. */
@@ -106,17 +148,28 @@ export function registerNodesPairingCommands(nodes: Command) {
106148.argument("<requestId>", "Pending request id")
107149.action(async (requestId: string, opts: NodesRpcOpts) => {
108150await runNodesCommand("approve", async () => {
109-const scopes = await resolveApproveScopesForRequest(opts, requestId);
110-const result = await callNodePairApprovalGatewayCli(
111-"node.pair.approve",
112-opts,
113-{
114- requestId,
115-},
116-{
117- scopes,
118-},
119-);
151+const { scopes } = await resolveApproveScopesForRequest(opts, requestId);
152+let result: unknown;
153+try {
154+result = await callNodePairApprovalGatewayCli(
155+"node.pair.approve",
156+opts,
157+{
158+ requestId,
159+},
160+{
161+ scopes,
162+},
163+);
164+} catch (error) {
165+if (!isUnknownNodePairRequestIdError(error)) {
166+throw error;
167+}
168+// Reuse the gateway error so generic formatting does not append its raw cause.
169+error.name = "Error";
170+error.message = buildUnknownNodePairRequestIdMessage(requestId, opts);
171+throw error;
172+}
120173defaultRuntime.writeJson(result);
121174});
122175}),
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。