mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 21:07:01 -06:00
fix(cli): render QR JSON failures (#126884)
This commit is contained in:
committed by
GitHub
parent
a0786cf741
commit
a73166b49d
+51
-10
@@ -7,6 +7,8 @@ import {
|
||||
PAIRING_SETUP_BOOTSTRAP_PROFILE,
|
||||
VOICE_NODE_PAIRING_SETUP_BOOTSTRAP_PROFILE,
|
||||
} from "../shared/device-bootstrap-profile.js";
|
||||
import { formatCliJsonFailure } from "./failure-output.js";
|
||||
import { runCliWithExitFinalization } from "./one-shot-exit.js";
|
||||
import { createCliRuntimeCapture, mockRuntimeModule } from "./test-runtime-capture.js";
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
@@ -264,17 +266,56 @@ describe("registerQrCli", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("rejects combining --limited with --voice-node", async () => {
|
||||
loadConfig.mockReturnValue({
|
||||
gateway: {
|
||||
bind: "custom",
|
||||
customBindHost: "127.0.0.1",
|
||||
auth: { mode: "token", token: "tok" },
|
||||
},
|
||||
});
|
||||
const conflictingQrOptions = [
|
||||
{
|
||||
name: "access profiles",
|
||||
args: ["--limited", "--voice-node"],
|
||||
message: "Use either --limited or --voice-node, not both.",
|
||||
},
|
||||
{
|
||||
name: "authentication overrides",
|
||||
args: ["--token", "test-token", "--password", "test-password"],
|
||||
message: "Use either --token or --password, not both.",
|
||||
},
|
||||
];
|
||||
|
||||
await expect(runQr(["--setup-code-only", "--limited", "--voice-node"])).rejects.toThrow("exit");
|
||||
expect(runtime.error).toHaveBeenCalledWith("Use either --limited or --voice-node, not both.");
|
||||
it.each(conflictingQrOptions)("rejects conflicting $name in human mode", async (testCase) => {
|
||||
await expect(runQr(["--setup-code-only", ...testCase.args])).rejects.toThrow("exit");
|
||||
|
||||
expect(runtimeError).toHaveBeenCalledExactlyOnceWith(testCase.message);
|
||||
expect(runtimeExit).toHaveBeenCalledExactlyOnceWith(1);
|
||||
expect(loadConfig).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.each(conflictingQrOptions)("renders conflicting $name as canonical JSON", async (testCase) => {
|
||||
const args = ["--json", ...testCase.args];
|
||||
const originalArgv = process.argv;
|
||||
let exitCode: number | undefined;
|
||||
process.argv = ["node", "openclaw", "qr", ...args];
|
||||
try {
|
||||
await runCliWithExitFinalization({
|
||||
runtime,
|
||||
run: async () => await runQr(args),
|
||||
onError: (error) => {
|
||||
runtime.writeJson(formatCliJsonFailure(error));
|
||||
exitCode = 1;
|
||||
},
|
||||
});
|
||||
} finally {
|
||||
process.argv = originalArgv;
|
||||
}
|
||||
|
||||
const expected = {
|
||||
ok: false,
|
||||
error: { type: "cli_error", message: testCase.message },
|
||||
};
|
||||
expect(exitCode).toBe(1);
|
||||
expect(runtime.writeJson).toHaveBeenCalledExactlyOnceWith(expected);
|
||||
expect(runtimeLog).toHaveBeenCalledOnce();
|
||||
expect(JSON.parse(readRuntimeCallText(runtimeLog.mock.calls[0]))).toEqual(expected);
|
||||
expect(runtimeError).not.toHaveBeenCalled();
|
||||
expect(runtimeExit).not.toHaveBeenCalled();
|
||||
expect(loadConfig).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("renders ASCII QR by default", async () => {
|
||||
|
||||
+3
-6
@@ -7,7 +7,6 @@ import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { hasConfiguredSecretInput } from "../config/types.secrets.js";
|
||||
import { trimToUndefined } from "../gateway/credentials.js";
|
||||
import { resolveRequiredConfiguredSecretRefInputString } from "../gateway/resolve-configured-secret-input-string.js";
|
||||
import { formatErrorMessage } from "../infra/errors.js";
|
||||
import { loadGatewayTlsRuntime } from "../infra/tls/gateway.js";
|
||||
import { renderQrTerminal } from "../media/qr-terminal.ts";
|
||||
import { resolvePairingSetupFromConfig, encodePairingSetupCode } from "../pairing/setup-code.js";
|
||||
@@ -17,6 +16,7 @@ import {
|
||||
PAIRING_SETUP_BOOTSTRAP_PROFILE,
|
||||
VOICE_NODE_PAIRING_SETUP_BOOTSTRAP_PROFILE,
|
||||
} from "../shared/device-bootstrap-profile.js";
|
||||
import { runCommandWithRuntime } from "./cli-utils.js";
|
||||
import { resolveCommandSecretRefsViaGateway } from "./command-secret-gateway.js";
|
||||
import { getQrRemoteCommandSecretTargetIds } from "./command-secret-targets.js";
|
||||
|
||||
@@ -125,7 +125,7 @@ export function registerQrCli(program: Command) {
|
||||
.option("--no-ascii", "Skip ASCII QR rendering")
|
||||
.option("--json", "Output JSON", false)
|
||||
.action(async (opts: QrCliOptions) => {
|
||||
try {
|
||||
await runCommandWithRuntime(defaultRuntime, async () => {
|
||||
if (opts.token && opts.password) {
|
||||
throw new Error("Use either --token or --password, not both.");
|
||||
}
|
||||
@@ -284,9 +284,6 @@ export function registerQrCli(program: Command) {
|
||||
);
|
||||
|
||||
defaultRuntime.log(lines.join("\n"));
|
||||
} catch (err) {
|
||||
defaultRuntime.error(formatErrorMessage(err));
|
||||
defaultRuntime.exit(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -295,6 +295,40 @@ describe("cli json stdout contract", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ name: "qr", command: ["qr"] },
|
||||
{ name: "clawbot qr", command: ["clawbot", "qr"] },
|
||||
])("renders conflicting $name options as one canonical JSON document", async (testCase) => {
|
||||
await withTempHome(
|
||||
async (tempHome) => {
|
||||
for (const conflict of [
|
||||
{
|
||||
args: ["--limited", "--voice-node"],
|
||||
message: "Use either --limited or --voice-node, not both.",
|
||||
},
|
||||
{
|
||||
args: ["--token", "test-token", "--password", "test-password"],
|
||||
message: "Use either --token or --password, not both.",
|
||||
},
|
||||
]) {
|
||||
const result = runBuiltCli(tempHome, [...testCase.command, "--json", ...conflict.args], {
|
||||
OPENCLAW_CONFIG_PATH: path.join(tempHome, "missing-openclaw.json"),
|
||||
OPENCLAW_STATE_DIR: path.join(tempHome, "isolated-state"),
|
||||
});
|
||||
|
||||
expect(result.status, result.stderr).toBe(1);
|
||||
expect(JSON.parse(result.stdout)).toEqual({
|
||||
ok: false,
|
||||
error: { type: "cli_error", message: conflict.message },
|
||||
});
|
||||
expect(result.stdout).not.toContain("[openclaw]");
|
||||
expect(result.stderr).toContain(conflict.message);
|
||||
}
|
||||
},
|
||||
{ prefix: "openclaw-qr-json-failure-e2e-" },
|
||||
);
|
||||
});
|
||||
|
||||
it("returns one canonical document when docs search fails", async () => {
|
||||
await withTempHome(
|
||||
async (tempHome) => {
|
||||
|
||||
Reference in New Issue
Block a user