
























@@ -365,8 +365,16 @@ export function runCommand(command, args, options = {}) {
365365 ? `timed out after ${timeoutMs}ms`
366366 : `failed with ${signal || status}`;
367367reject(
368-new Error(
369-`${command} ${args.join(" ")} ${failure}${detail ? `\n${tailText(detail)}` : ""}`,
368+Object.assign(
369+new Error(
370+`${command} ${args.join(" ")} ${failure}${detail ? `\n${tailText(detail)}` : ""}`,
371+),
372+{
373+ signal,
374+ status,
375+stderr: stderr.text,
376+stdout: stdout.text,
377+},
370378),
371379);
372380});
@@ -443,6 +451,38 @@ function parseJsonOutput(stdout) {
443451throw new Error(`JSON output was not parseable:\n${tailText(trimmed)}`);
444452}
445453454+export function parseGatewayCliRequestFailure(error) {
455+if (typeof error?.stdout !== "string" || !error.stdout.trim()) {
456+return null;
457+}
458+let payload;
459+try {
460+payload = parseJsonOutput(error.stdout);
461+} catch {
462+return null;
463+}
464+const requestError = payload?.ok === false ? payload.error : null;
465+if (
466+requestError?.type !== "gateway_request_error" ||
467+!isNonEmptyString(requestError.code) ||
468+!isNonEmptyString(requestError.message) ||
469+typeof requestError.retryable !== "boolean" ||
470+(requestError.retryAfterMs !== undefined &&
471+(typeof requestError.retryAfterMs !== "number" ||
472+!Number.isInteger(requestError.retryAfterMs) ||
473+requestError.retryAfterMs < 0))
474+) {
475+return null;
476+}
477+return Object.assign(new Error(requestError.message), {
478+name: "GatewayClientRequestError",
479+gatewayCode: requestError.code,
480+ ...(requestError.details !== undefined ? { details: requestError.details } : {}),
481+retryable: requestError.retryable,
482+ ...(requestError.retryAfterMs !== undefined ? { retryAfterMs: requestError.retryAfterMs } : {}),
483+});
484+}
485+446486function boundedJsonPreview(value, space) {
447487try {
448488return JSON.stringify(previewJsonValue(value), null, space) ?? String(value);
@@ -632,25 +672,30 @@ async function importCallGatewayModule() {
632672633673async function rpcCallViaCli(method, params, options) {
634674const config = resolveKitchenSinkRpcConfig(options.env);
635-const { stdout } = await runOpenClaw(
636-options.runner,
637-[
638-"gateway",
639-"call",
640-method,
641-"--url",
642-`ws://127.0.0.1:${options.port}`,
643-"--token",
644-TOKEN,
645-"--timeout",
646-String(config.rpcTimeoutMs),
647-"--json",
648-"--params",
649-JSON.stringify(params ?? {}),
650-],
651-options.env,
652-createRpcCliRunOptions(method, options),
653-);
675+let stdout;
676+try {
677+({ stdout } = await runOpenClaw(
678+options.runner,
679+[
680+"gateway",
681+"call",
682+method,
683+"--url",
684+`ws://127.0.0.1:${options.port}`,
685+"--token",
686+TOKEN,
687+"--timeout",
688+String(config.rpcTimeoutMs),
689+"--json",
690+"--params",
691+JSON.stringify(params ?? {}),
692+],
693+options.env,
694+createRpcCliRunOptions(method, options),
695+));
696+} catch (error) {
697+throw parseGatewayCliRequestFailure(error) ?? error;
698+}
654699return parseJsonOutput(stdout);
655700}
656701@@ -1399,10 +1444,7 @@ export async function assertOperatorRpcDenied(probe, call) {
13991444} catch (error) {
14001445const gatewayCode = error?.gatewayCode;
14011446const message = String(error?.message ?? "");
1402-if (
1403-(gatewayCode === undefined || gatewayCode === "INVALID_REQUEST") &&
1404-message.includes("unauthorized role: operator")
1405-) {
1447+if (gatewayCode === "INVALID_REQUEST" && message.includes("unauthorized role: operator")) {
14061448return;
14071449}
14081450throw error;
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。