feat: add exec pathPrepend config
This commit is contained in:
@@ -11,6 +11,7 @@ describe("normalizeConfigPaths", () => {
|
||||
const { normalizeConfigPaths } = await import("./normalize-paths.js");
|
||||
|
||||
const cfg = normalizeConfigPaths({
|
||||
tools: { exec: { pathPrepend: ["~/bin"] } },
|
||||
plugins: { load: { paths: ["~/plugins/a"] } },
|
||||
logging: { file: "~/.clawdbot/logs/clawdbot.log" },
|
||||
hooks: {
|
||||
@@ -49,6 +50,7 @@ describe("normalizeConfigPaths", () => {
|
||||
expect(cfg.logging?.file).toBe(path.join(home, ".clawdbot", "logs", "clawdbot.log"));
|
||||
expect(cfg.hooks?.path).toBe(path.join(home, ".clawdbot", "hooks.json5"));
|
||||
expect(cfg.hooks?.transformsDir).toBe(path.join(home, "hooks-xform"));
|
||||
expect(cfg.tools?.exec?.pathPrepend?.[0]).toBe(path.join(home, "bin"));
|
||||
expect(cfg.channels?.telegram?.accounts?.personal?.tokenFile).toBe(
|
||||
path.join(home, ".clawdbot", "telegram.token"),
|
||||
);
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { ClawdbotConfig } from "./types.js";
|
||||
const PATH_VALUE_RE = /^~(?=$|[\\/])/;
|
||||
|
||||
const PATH_KEY_RE = /(dir|path|paths|file|root|workspace)$/i;
|
||||
const PATH_LIST_KEYS = new Set(["paths"]);
|
||||
const PATH_LIST_KEYS = new Set(["paths", "pathPrepend"]);
|
||||
|
||||
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
||||
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
||||
|
||||
@@ -146,6 +146,7 @@ const FIELD_LABELS: Record<string, string> = {
|
||||
"tools.exec.security": "Exec Security",
|
||||
"tools.exec.ask": "Exec Ask",
|
||||
"tools.exec.node": "Exec Node Binding",
|
||||
"tools.exec.pathPrepend": "Exec PATH Prepend",
|
||||
"tools.message.allowCrossContextSend": "Allow Cross-Context Messaging",
|
||||
"tools.message.crossContext.allowWithinProvider": "Allow Cross-Context (Same Provider)",
|
||||
"tools.message.crossContext.allowAcrossProviders": "Allow Cross-Context (Across Providers)",
|
||||
@@ -323,6 +324,8 @@ const FIELD_HELP: Record<string, string> = {
|
||||
'Optional allowlist of model ids (e.g. "gpt-5.2" or "openai/gpt-5.2").',
|
||||
"tools.exec.notifyOnExit":
|
||||
"When true (default), backgrounded exec sessions enqueue a system event and request a heartbeat on exit.",
|
||||
"tools.exec.pathPrepend":
|
||||
"Directories to prepend to PATH for exec runs (gateway/sandbox).",
|
||||
"tools.message.allowCrossContextSend":
|
||||
"Legacy override: allow cross-context sends across all providers.",
|
||||
"tools.message.crossContext.allowWithinProvider":
|
||||
|
||||
@@ -129,6 +129,8 @@ export type ExecToolConfig = {
|
||||
ask?: "off" | "on-miss" | "always";
|
||||
/** Default node binding for exec.host=node (node id/name). */
|
||||
node?: string;
|
||||
/** Directories to prepend to PATH when running exec (gateway/sandbox). */
|
||||
pathPrepend?: string[];
|
||||
/** Default time (ms) before an exec command auto-backgrounds. */
|
||||
backgroundMs?: number;
|
||||
/** Default timeout (seconds) before auto-killing exec commands. */
|
||||
|
||||
@@ -183,6 +183,25 @@ export const AgentToolsSchema = z
|
||||
allowFrom: ElevatedAllowFromSchema,
|
||||
})
|
||||
.optional(),
|
||||
exec: z
|
||||
.object({
|
||||
host: z.enum(["sandbox", "gateway", "node"]).optional(),
|
||||
security: z.enum(["deny", "allowlist", "full"]).optional(),
|
||||
ask: z.enum(["off", "on-miss", "always"]).optional(),
|
||||
node: z.string().optional(),
|
||||
pathPrepend: z.array(z.string()).optional(),
|
||||
backgroundMs: z.number().int().positive().optional(),
|
||||
timeoutSec: z.number().int().positive().optional(),
|
||||
cleanupMs: z.number().int().positive().optional(),
|
||||
notifyOnExit: z.boolean().optional(),
|
||||
applyPatch: z
|
||||
.object({
|
||||
enabled: z.boolean().optional(),
|
||||
allowModels: z.array(z.string()).optional(),
|
||||
})
|
||||
.optional(),
|
||||
})
|
||||
.optional(),
|
||||
sandbox: z
|
||||
.object({
|
||||
tools: ToolPolicySchema,
|
||||
@@ -362,6 +381,7 @@ export const ToolsSchema = z
|
||||
security: z.enum(["deny", "allowlist", "full"]).optional(),
|
||||
ask: z.enum(["off", "on-miss", "always"]).optional(),
|
||||
node: z.string().optional(),
|
||||
pathPrepend: z.array(z.string()).optional(),
|
||||
backgroundMs: z.number().int().positive().optional(),
|
||||
timeoutSec: z.number().int().positive().optional(),
|
||||
cleanupMs: z.number().int().positive().optional(),
|
||||
|
||||
Reference in New Issue
Block a user