mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-26 20:35:39 -06:00
fix(canvas): emit successful control results in JSON mode (#117297)
Co-authored-by: Peter Steinberger <steipete@macos.shared>
This commit is contained in:
committed by
GitHub
parent
338a7ccb2f
commit
b0b708d919
@@ -71,6 +71,34 @@ function createCanvasCliDepsWithDefaultParsers() {
|
||||
};
|
||||
}
|
||||
|
||||
const canvasAcknowledgementCommands = [
|
||||
{
|
||||
args: ["present"],
|
||||
command: "canvas.present",
|
||||
message: "canvas present ok",
|
||||
},
|
||||
{
|
||||
args: ["hide"],
|
||||
command: "canvas.hide",
|
||||
message: "canvas hide ok",
|
||||
},
|
||||
{
|
||||
args: ["navigate", "https://example.com"],
|
||||
command: "canvas.navigate",
|
||||
message: "canvas navigate ok",
|
||||
},
|
||||
{
|
||||
args: ["a2ui", "push", "--text", "hello"],
|
||||
command: "canvas.a2ui.pushJSONL",
|
||||
message: "canvas a2ui push ok (v0.8, 2 messages)",
|
||||
},
|
||||
{
|
||||
args: ["a2ui", "reset"],
|
||||
command: "canvas.a2ui.reset",
|
||||
message: "canvas a2ui reset ok",
|
||||
},
|
||||
] as const;
|
||||
|
||||
describe("canvas CLI", () => {
|
||||
it("registers under nodes and captures a snapshot media path", async () => {
|
||||
const program = new Command();
|
||||
@@ -194,6 +222,69 @@ describe("canvas CLI", () => {
|
||||
expect(runtime.log).not.toHaveBeenCalledWith("canvas eval ok");
|
||||
});
|
||||
|
||||
it.each(canvasAcknowledgementCommands)(
|
||||
"prints the full successful $command Gateway response with --json",
|
||||
async ({ args, command }) => {
|
||||
const program = new Command();
|
||||
program.exitOverride();
|
||||
const nodes = program.command("nodes");
|
||||
const { deps, runtime } = createCanvasCliDeps();
|
||||
const response = {
|
||||
ok: true,
|
||||
nodeId: "ios-node",
|
||||
command,
|
||||
payload: { acknowledged: true },
|
||||
payloadJSON: '{"acknowledged":true}',
|
||||
};
|
||||
vi.mocked(deps.callGatewayCli).mockResolvedValueOnce(response);
|
||||
|
||||
registerNodesCanvasCommands(nodes, deps);
|
||||
await program.parseAsync(["nodes", "canvas", ...args, "--node", "ios-node", "--json"], {
|
||||
from: "user",
|
||||
});
|
||||
|
||||
expect(deps.callGatewayCli).toHaveBeenCalledTimes(1);
|
||||
expect(runtime.writeJson).toHaveBeenCalledExactlyOnceWith(response);
|
||||
expect(runtime.log).not.toHaveBeenCalled();
|
||||
},
|
||||
);
|
||||
|
||||
it.each(canvasAcknowledgementCommands)(
|
||||
"preserves the human-readable $command acknowledgement",
|
||||
async ({ args, message }) => {
|
||||
const program = new Command();
|
||||
program.exitOverride();
|
||||
const nodes = program.command("nodes");
|
||||
const { deps, runtime } = createCanvasCliDeps();
|
||||
|
||||
registerNodesCanvasCommands(nodes, deps);
|
||||
await program.parseAsync(["nodes", "canvas", ...args, "--node", "ios-node"], {
|
||||
from: "user",
|
||||
});
|
||||
|
||||
expect(runtime.log).toHaveBeenCalledExactlyOnceWith(message);
|
||||
expect(runtime.writeJson).not.toHaveBeenCalled();
|
||||
},
|
||||
);
|
||||
|
||||
it("does not print a machine-readable success response when a Canvas invocation fails", async () => {
|
||||
const program = new Command();
|
||||
program.exitOverride();
|
||||
const nodes = program.command("nodes");
|
||||
const { deps, runtime } = createCanvasCliDeps();
|
||||
vi.mocked(deps.callGatewayCli).mockRejectedValueOnce(new Error("node disconnected"));
|
||||
|
||||
registerNodesCanvasCommands(nodes, deps);
|
||||
|
||||
await expect(
|
||||
program.parseAsync(["nodes", "canvas", "present", "--node", "ios-node", "--json"], {
|
||||
from: "user",
|
||||
}),
|
||||
).rejects.toThrow("node disconnected");
|
||||
expect(runtime.writeJson).not.toHaveBeenCalled();
|
||||
expect(runtime.log).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.each([
|
||||
["--max-width", "640px", "--max-width must be a positive integer."],
|
||||
["--quality", "0.8x", "--quality must be a number."],
|
||||
|
||||
@@ -271,6 +271,21 @@ async function invokeCanvas(
|
||||
);
|
||||
}
|
||||
|
||||
/** Prints the complete invocation response for machines or the existing human acknowledgement. */
|
||||
function writeCanvasInvokeResult(
|
||||
deps: CanvasCliDependencies,
|
||||
opts: CanvasNodesRpcOpts,
|
||||
result: unknown,
|
||||
message: string,
|
||||
): void {
|
||||
if (opts.json) {
|
||||
deps.defaultRuntime.writeJson(result);
|
||||
return;
|
||||
}
|
||||
const { ok } = deps.getNodesTheme();
|
||||
deps.defaultRuntime.log(ok(message));
|
||||
}
|
||||
|
||||
/** Registers Canvas subcommands under the nodes CLI command group. */
|
||||
export function registerNodesCanvasCommands(nodes: Command, deps: CanvasCliDependencies) {
|
||||
const canvas = nodes
|
||||
@@ -344,11 +359,8 @@ export function registerNodesCanvasCommands(nodes: Command, deps: CanvasCliDepen
|
||||
) {
|
||||
params.placement = placement;
|
||||
}
|
||||
await invokeCanvas(deps, opts, "canvas.present", params);
|
||||
if (!opts.json) {
|
||||
const { ok } = deps.getNodesTheme();
|
||||
deps.defaultRuntime.log(ok("canvas present ok"));
|
||||
}
|
||||
const result = await invokeCanvas(deps, opts, "canvas.present", params);
|
||||
writeCanvasInvokeResult(deps, opts, result, "canvas present ok");
|
||||
});
|
||||
}),
|
||||
);
|
||||
@@ -361,11 +373,8 @@ export function registerNodesCanvasCommands(nodes: Command, deps: CanvasCliDepen
|
||||
.option("--invoke-timeout <ms>", "Node invoke timeout in ms")
|
||||
.action(async (opts: CanvasNodesRpcOpts) => {
|
||||
await deps.runNodesCommand("canvas hide", async () => {
|
||||
await invokeCanvas(deps, opts, "canvas.hide", undefined);
|
||||
if (!opts.json) {
|
||||
const { ok } = deps.getNodesTheme();
|
||||
deps.defaultRuntime.log(ok("canvas hide ok"));
|
||||
}
|
||||
const result = await invokeCanvas(deps, opts, "canvas.hide", undefined);
|
||||
writeCanvasInvokeResult(deps, opts, result, "canvas hide ok");
|
||||
});
|
||||
}),
|
||||
);
|
||||
@@ -379,11 +388,8 @@ export function registerNodesCanvasCommands(nodes: Command, deps: CanvasCliDepen
|
||||
.option("--invoke-timeout <ms>", "Node invoke timeout in ms")
|
||||
.action(async (url: string, opts: CanvasNodesRpcOpts) => {
|
||||
await deps.runNodesCommand("canvas navigate", async () => {
|
||||
await invokeCanvas(deps, opts, "canvas.navigate", { url });
|
||||
if (!opts.json) {
|
||||
const { ok } = deps.getNodesTheme();
|
||||
deps.defaultRuntime.log(ok("canvas navigate ok"));
|
||||
}
|
||||
const result = await invokeCanvas(deps, opts, "canvas.navigate", { url });
|
||||
writeCanvasInvokeResult(deps, opts, result, "canvas navigate ok");
|
||||
});
|
||||
}),
|
||||
);
|
||||
@@ -445,15 +451,13 @@ export function registerNodesCanvasCommands(nodes: Command, deps: CanvasCliDepen
|
||||
? buildA2UITextJsonl(opts.text ?? "")
|
||||
: await fs.readFile(String(opts.jsonl), "utf8");
|
||||
const { messageCount } = validateSupportedA2UIJsonl(jsonl);
|
||||
await invokeCanvas(deps, opts, "canvas.a2ui.pushJSONL", { jsonl });
|
||||
if (!opts.json) {
|
||||
const { ok } = deps.getNodesTheme();
|
||||
deps.defaultRuntime.log(
|
||||
ok(
|
||||
`canvas a2ui push ok (v0.8, ${messageCount} message${messageCount === 1 ? "" : "s"})`,
|
||||
),
|
||||
);
|
||||
}
|
||||
const result = await invokeCanvas(deps, opts, "canvas.a2ui.pushJSONL", { jsonl });
|
||||
writeCanvasInvokeResult(
|
||||
deps,
|
||||
opts,
|
||||
result,
|
||||
`canvas a2ui push ok (v0.8, ${messageCount} message${messageCount === 1 ? "" : "s"})`,
|
||||
);
|
||||
});
|
||||
}),
|
||||
);
|
||||
@@ -466,11 +470,8 @@ export function registerNodesCanvasCommands(nodes: Command, deps: CanvasCliDepen
|
||||
.option("--invoke-timeout <ms>", "Node invoke timeout in ms")
|
||||
.action(async (opts: CanvasNodesRpcOpts) => {
|
||||
await deps.runNodesCommand("canvas a2ui reset", async () => {
|
||||
await invokeCanvas(deps, opts, "canvas.a2ui.reset", undefined);
|
||||
if (!opts.json) {
|
||||
const { ok } = deps.getNodesTheme();
|
||||
deps.defaultRuntime.log(ok("canvas a2ui reset ok"));
|
||||
}
|
||||
const result = await invokeCanvas(deps, opts, "canvas.a2ui.reset", undefined);
|
||||
writeCanvasInvokeResult(deps, opts, result, "canvas a2ui reset ok");
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user