fix(nodes-tool): add missing 'run' action to execute commands on paired nodes with optional parameters as defined in CLI
This commit is contained in:
committed by
Peter Steinberger
parent
145fe1cec7
commit
b34fc0aaed
@@ -148,6 +148,18 @@ const NodesToolSchema = Type.Union([
|
||||
]),
|
||||
),
|
||||
}),
|
||||
Type.Object({
|
||||
action: Type.Literal("run"),
|
||||
gatewayUrl: Type.Optional(Type.String()),
|
||||
gatewayToken: Type.Optional(Type.String()),
|
||||
timeoutMs: Type.Optional(Type.Number()),
|
||||
node: Type.String(),
|
||||
command: Type.Array(Type.String()),
|
||||
cwd: Type.Optional(Type.String()),
|
||||
env: Type.Optional(Type.Array(Type.String())),
|
||||
commandTimeoutMs: Type.Optional(Type.Number()),
|
||||
needsScreenRecording: Type.Optional(Type.Boolean()),
|
||||
}),
|
||||
]);
|
||||
|
||||
export function createNodesTool(): AnyAgentTool {
|
||||
@@ -155,7 +167,7 @@ export function createNodesTool(): AnyAgentTool {
|
||||
label: "Nodes",
|
||||
name: "nodes",
|
||||
description:
|
||||
"Discover and control paired nodes (status/describe/pairing/notify/camera/screen/location).",
|
||||
"Discover and control paired nodes (status/describe/pairing/notify/camera/screen/location/run).",
|
||||
parameters: NodesToolSchema,
|
||||
execute: async (_toolCallId, args) => {
|
||||
const params = args as Record<string, unknown>;
|
||||
@@ -478,6 +490,68 @@ export function createNodesTool(): AnyAgentTool {
|
||||
})) as { payload?: unknown };
|
||||
return jsonResult(raw?.payload ?? {});
|
||||
}
|
||||
case "run": {
|
||||
const node = readStringParam(params, "node", { required: true });
|
||||
const nodeId = await resolveNodeId(gatewayOpts, node);
|
||||
const commandRaw = params.command;
|
||||
if (!commandRaw) {
|
||||
throw new Error(
|
||||
"command required (argv array, e.g. ['echo', 'Hello'])",
|
||||
);
|
||||
}
|
||||
if (!Array.isArray(commandRaw)) {
|
||||
throw new Error(
|
||||
"command must be an array of strings (argv), e.g. ['echo', 'Hello']",
|
||||
);
|
||||
}
|
||||
const command = commandRaw.map((c) => String(c));
|
||||
if (command.length === 0) {
|
||||
throw new Error("command must not be empty");
|
||||
}
|
||||
const cwd =
|
||||
typeof params.cwd === "string" && params.cwd.trim()
|
||||
? params.cwd.trim()
|
||||
: undefined;
|
||||
const envRaw = params.env;
|
||||
const env: Record<string, string> | undefined =
|
||||
Array.isArray(envRaw) && envRaw.length > 0
|
||||
? (() => {
|
||||
const parsed: Record<string, string> = {};
|
||||
for (const pair of envRaw) {
|
||||
if (typeof pair !== "string") continue;
|
||||
const idx = pair.indexOf("=");
|
||||
if (idx <= 0) continue;
|
||||
const key = pair.slice(0, idx).trim();
|
||||
const value = pair.slice(idx + 1);
|
||||
if (!key) continue;
|
||||
parsed[key] = value;
|
||||
}
|
||||
return Object.keys(parsed).length > 0 ? parsed : undefined;
|
||||
})()
|
||||
: undefined;
|
||||
const commandTimeoutMs =
|
||||
typeof params.commandTimeoutMs === "number" &&
|
||||
Number.isFinite(params.commandTimeoutMs)
|
||||
? params.commandTimeoutMs
|
||||
: undefined;
|
||||
const needsScreenRecording =
|
||||
typeof params.needsScreenRecording === "boolean"
|
||||
? params.needsScreenRecording
|
||||
: undefined;
|
||||
const raw = (await callGatewayTool("node.invoke", gatewayOpts, {
|
||||
nodeId,
|
||||
command: "system.run",
|
||||
params: {
|
||||
command,
|
||||
cwd,
|
||||
env,
|
||||
timeoutMs: commandTimeoutMs,
|
||||
needsScreenRecording,
|
||||
},
|
||||
idempotencyKey: crypto.randomUUID(),
|
||||
})) as { payload?: unknown };
|
||||
return jsonResult(raw?.payload ?? {});
|
||||
}
|
||||
default:
|
||||
throw new Error(`Unknown action: ${action}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user