feat: add exec approvals allowlists
This commit is contained in:
@@ -74,7 +74,10 @@ export function createClawdbotTools(options?: {
|
||||
allowedControlPorts: options?.allowedControlPorts,
|
||||
}),
|
||||
createCanvasTool(),
|
||||
createNodesTool(),
|
||||
createNodesTool({
|
||||
agentSessionKey: options?.agentSessionKey,
|
||||
config: options?.config,
|
||||
}),
|
||||
createCronTool({
|
||||
agentSessionKey: options?.agentSessionKey,
|
||||
}),
|
||||
|
||||
@@ -17,12 +17,14 @@ import {
|
||||
writeScreenRecordToFile,
|
||||
} from "../../cli/nodes-screen.js";
|
||||
import { parseDurationMs } from "../../cli/parse-duration.js";
|
||||
import type { ClawdbotConfig } from "../../config/config.js";
|
||||
import { imageMimeFromFormat } from "../../media/mime.js";
|
||||
import { resolveSessionAgentId } from "../agent-scope.js";
|
||||
import { optionalStringEnum, stringEnum } from "../schema/typebox.js";
|
||||
import { sanitizeToolResultImages } from "../tool-images.js";
|
||||
import { type AnyAgentTool, jsonResult, readStringParam } from "./common.js";
|
||||
import { callGatewayTool, type GatewayCallOptions } from "./gateway.js";
|
||||
import { resolveNodeId } from "./nodes-utils.js";
|
||||
import { listNodes, resolveNodeIdFromList, resolveNodeId } from "./nodes-utils.js";
|
||||
|
||||
const NODES_TOOL_ACTIONS = [
|
||||
"status",
|
||||
@@ -86,7 +88,14 @@ const NodesToolSchema = Type.Object({
|
||||
needsScreenRecording: Type.Optional(Type.Boolean()),
|
||||
});
|
||||
|
||||
export function createNodesTool(): AnyAgentTool {
|
||||
export function createNodesTool(options?: {
|
||||
agentSessionKey?: string;
|
||||
config?: ClawdbotConfig;
|
||||
}): AnyAgentTool {
|
||||
const agentId = resolveSessionAgentId({
|
||||
sessionKey: options?.agentSessionKey,
|
||||
config: options?.config,
|
||||
});
|
||||
return {
|
||||
label: "Nodes",
|
||||
name: "nodes",
|
||||
@@ -375,7 +384,22 @@ export function createNodesTool(): AnyAgentTool {
|
||||
}
|
||||
case "run": {
|
||||
const node = readStringParam(params, "node", { required: true });
|
||||
const nodeId = await resolveNodeId(gatewayOpts, node);
|
||||
const nodes = await listNodes(gatewayOpts);
|
||||
if (nodes.length === 0) {
|
||||
throw new Error(
|
||||
"system.run requires a paired macOS companion app (no nodes available).",
|
||||
);
|
||||
}
|
||||
const nodeId = resolveNodeIdFromList(nodes, node);
|
||||
const nodeInfo = nodes.find((entry) => entry.nodeId === nodeId);
|
||||
const supportsSystemRun = Array.isArray(nodeInfo?.commands)
|
||||
? nodeInfo?.commands?.includes("system.run")
|
||||
: false;
|
||||
if (!supportsSystemRun) {
|
||||
throw new Error(
|
||||
"system.run requires the macOS companion app; the selected node does not support system.run.",
|
||||
);
|
||||
}
|
||||
const commandRaw = params.command;
|
||||
if (!commandRaw) {
|
||||
throw new Error("command required (argv array, e.g. ['echo', 'Hello'])");
|
||||
@@ -405,6 +429,7 @@ export function createNodesTool(): AnyAgentTool {
|
||||
env,
|
||||
timeoutMs: commandTimeoutMs,
|
||||
needsScreenRecording,
|
||||
agentId,
|
||||
},
|
||||
timeoutMs: invokeTimeoutMs,
|
||||
idempotencyKey: crypto.randomUUID(),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { callGatewayTool, type GatewayCallOptions } from "./gateway.js";
|
||||
|
||||
type NodeListNode = {
|
||||
export type NodeListNode = {
|
||||
nodeId: string;
|
||||
displayName?: string;
|
||||
platform?: string;
|
||||
@@ -99,12 +99,15 @@ function pickDefaultNode(nodes: NodeListNode[]): NodeListNode | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function resolveNodeId(
|
||||
opts: GatewayCallOptions,
|
||||
export async function listNodes(opts: GatewayCallOptions): Promise<NodeListNode[]> {
|
||||
return loadNodes(opts);
|
||||
}
|
||||
|
||||
export function resolveNodeIdFromList(
|
||||
nodes: NodeListNode[],
|
||||
query?: string,
|
||||
allowDefault = false,
|
||||
) {
|
||||
const nodes = await loadNodes(opts);
|
||||
): string {
|
||||
const q = String(query ?? "").trim();
|
||||
if (!q) {
|
||||
if (allowDefault) {
|
||||
@@ -138,3 +141,12 @@ export async function resolveNodeId(
|
||||
.join(", ")})`,
|
||||
);
|
||||
}
|
||||
|
||||
export async function resolveNodeId(
|
||||
opts: GatewayCallOptions,
|
||||
query?: string,
|
||||
allowDefault = false,
|
||||
) {
|
||||
const nodes = await loadNodes(opts);
|
||||
return resolveNodeIdFromList(nodes, query, allowDefault);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user