fix(cli): clarify nodes invoke params errors (#101838)

This commit is contained in:
qingminlong 2026-07-09 15:33:47 +08:00 committed by GitHub
parent a522e4309c
commit b40e5b974f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 25 additions and 3 deletions

View file

@ -208,6 +208,19 @@ describe("nodes-cli coverage", () => {
expect(runtimeErrors.at(-1)).toContain('command "system.run" is reserved for shell execution');
});
it("rejects malformed nodes invoke params before opening a gateway call", async () => {
await expect(
sharedProgram.parseAsync(
["nodes", "invoke", "--node", "mac-1", "--command", "canvas.eval", "--params", "not-json"],
{ from: "user" },
),
).rejects.toThrow("__exit__:1");
expect(runtimeErrors.at(-1)).toContain("--params must be valid JSON.");
expect(callGateway).not.toHaveBeenCalled();
expect(lastNodeInvokeCall).toBeNull();
});
it("invokes system.notify with provided fields", async () => {
const invoke = await runNodesCommand([
"nodes",

View file

@ -17,6 +17,14 @@ import type { NodesRpcOpts } from "./types.js";
const BLOCKED_NODE_INVOKE_COMMANDS = new Set(["system.run", "system.run.prepare"]);
function parseNodeInvokeParams(value = "{}"): unknown {
try {
return JSON.parse(value) as unknown;
} catch {
throw new Error("--params must be valid JSON.");
}
}
/** Register direct node command invocation. */
export function registerNodesInvokeCommands(nodes: Command) {
nodesCallOpts(
@ -30,9 +38,9 @@ export function registerNodesInvokeCommands(nodes: Command) {
.option("--idempotency-key <key>", "Idempotency key (optional)")
.action(async (opts: NodesRpcOpts) => {
await runNodesCommand("invoke", async () => {
const nodeId = await resolveNodeId(opts, normalizeOptionalString(opts.node) ?? "");
const nodeQuery = normalizeOptionalString(opts.node) ?? "";
const command = normalizeOptionalString(opts.command) ?? "";
if (!nodeId || !command) {
if (!nodeQuery || !command) {
const { error } = getNodesTheme();
defaultRuntime.error(error("--node and --command required"));
defaultRuntime.exit(1);
@ -43,7 +51,8 @@ export function registerNodesInvokeCommands(nodes: Command) {
`command "${command}" is reserved for shell execution; use the exec tool with host=node instead`,
);
}
const params = JSON.parse(opts.params ?? "{}") as unknown;
const params = parseNodeInvokeParams(opts.params);
const nodeId = await resolveNodeId(opts, nodeQuery);
const timeoutMs = parseOptionalNodePositiveInteger(
opts.invokeTimeout,
"--invoke-timeout",