openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
555 lines (554 loc) • 25.2 kB
JavaScript
import { r as defaultRuntime } from "./runtime-BC29JSZp.mjs";
import { n as isRich, r as theme } from "./theme-DzaUZY4q.mjs";
import { n as sanitizeTerminalText } from "./safe-text-C6FhHCw7.mjs";
import { n as normalizeAgentId } from "./agent-id-D1-Q6vR4.mjs";
import { b as resolveSoleAgentId, m as resolveConfiguredAgentId, t as AgentSelectionRequiredError } from "./agent-scope-config-BU4qc9bA.mjs";
import { A as parseAgentSessionKey } from "./session-key-BC4m_Ly5.mjs";
import { i as listAgentIds } from "./agent-roster-B45JERT5.mjs";
import { a as normalizeExecMode, d as resolveExecModePolicy, l as resolveExactExecModeFromPolicy, o as normalizeExecSecurity, r as normalizeExecAsk, s as normalizeExecTarget } from "./exec-approvals-core-GflUelcW.mjs";
import { c as readConfigFileSnapshot } from "./io.runtime-FunTy0y1.mjs";
import { r as replaceConfigFile } from "./mutate-cU6JOWHQ.mjs";
import "./config-B-SAEPyK.mjs";
import { r as callGatewayFromCliWithTransport, s as resolveGatewayRpcOptionsWithLocalPort, t as addGatewayClientOptions } from "./gateway-rpc-BSJk6ho1.mjs";
import { c as readExecApprovalsSnapshot, d as restoreExecApprovalsSnapshotLocked, m as updateExecApprovals } from "./exec-approvals-store-B3Dzx_vP.mjs";
import { a as maxAsk, o as minSecurity } from "./exec-approvals-policy-CMlDajgd.mjs";
import { i as resolveExecApprovalsFromFile } from "./exec-approvals-BYWWK6_e.mjs";
import { n as sanitizeExecApprovalDisplayText } from "./exec-approval-text-sanitize-C5Sh27u3.mjs";
import { n as renderTable, t as getTerminalTableWidth } from "./table-BI11wcy5.mjs";
import { n as resolveConfiguredToolAccess } from "./tool-access-diagnostics-YMTcFPCf.mjs";
import { t as formatDocsHelp } from "./help-format-MN-qfst1.mjs";
import { n as collectExecPolicyScopeSnapshots, t as SESSION_EXEC_OVERRIDES_NOTE } from "./exec-approvals-effective-BhxcPbb0.mjs";
//#region src/cli/exec-policy-diagnostics.ts
const TERMINAL_TOOLS = ["exec", "process"];
async function buildExecPolicyToolAccess(config, options) {
const requestedAgent = options.agent?.trim();
const sessionKey = options.session?.trim();
if (options.agent !== void 0 && !requestedAgent) throw new Error("--agent must not be blank.");
if (options.session !== void 0 && !sessionKey) throw new Error("--session must not be blank.");
const sessionAgent = sessionKey ? parseAgentSessionKey(sessionKey)?.agentId : void 0;
const explicitAgent = requestedAgent ? normalizeAgentId(requestedAgent) : void 0;
if (explicitAgent && sessionAgent && explicitAgent !== sessionAgent) throw new Error(`Agent "${explicitAgent}" does not match session agent "${sessionAgent}".`);
const agentId = sessionKey ? explicitAgent ?? sessionAgent : resolveConfiguredAgentId(config, explicitAgent ?? resolveSoleAgentId(config, {
surface: "exec-policy show",
hint: "Pass --agent <id> or --session <agent session key>."
}));
const access = {
...agentId ? { agentId } : {},
...sessionKey ? { sessionKey } : {}
};
if (sessionKey) try {
const result = await callGatewayFromCliWithTransport("tools.effective", options, {
...agentId ? { agentId } : {},
sessionKey
}, {
progress: false,
sharedStateMode: "read-only"
});
if (agentId && result.agentId !== agentId) throw new Error("The Gateway returned tool access for a different agent.");
access.agentId = result.agentId;
access.live = {
status: "verified",
diagnostics: result.toolAccess ?? describeLegacySessionPreview(result)
};
} catch (error) {
access.live = {
status: "unverified",
error: error instanceof Error ? error.message : String(error)
};
}
if (access.agentId && listAgentIds(config).includes(access.agentId)) access.local = resolveConfiguredToolAccess({
config,
agentId: access.agentId,
toolNames: TERMINAL_TOOLS
});
return access;
}
function describeLegacySessionPreview(result) {
const tools = result.groups.flatMap((group) => group.tools);
return {
checked: "live-session",
profiles: [],
tools: TERMINAL_TOOLS.map((id) => {
const entry = tools.find((tool) => tool.id === id);
return {
id,
status: entry?.deniedBySession ? "excluded" : entry ? "available" : "unavailable",
reasons: entry?.deniedBySession ? [{
kind: "session",
label: "Denied by this session's tool restrictions"
}] : entry ? [] : [{
kind: "runtime",
label: "Not included in the session preview"
}]
};
})
};
}
function display(value) {
return sanitizeExecApprovalDisplayText(sanitizeTerminalText(value));
}
function terminalTools(diagnostics) {
return diagnostics.tools.filter((tool) => TERMINAL_TOOLS.some((id) => id === tool.id));
}
function isExcluded(tool) {
return tool.status === "excluded";
}
function formatExecPolicyCommandApprovals(params) {
const lines = params.scopes.map((approval) => {
const ask = approval.ask.effective === "on-miss" ? "ask on miss" : approval.ask.effective === "always" ? "always ask" : approval.ask.effective === "off" ? "no approval prompts" : "approval mode unknown";
return display(`${params.showScopeLabels && approval.scopeLabel ? `${approval.scopeLabel}: ` : ""}${approval.host.requested} · ${approval.security.effective} · ${ask} · fallback ${approval.askFallback.effective}`);
});
if (!params.approvalsExists) lines.push("Approvals State: defaults (no stored overrides)");
lines.push("Command approvals do not grant tool access.", SESSION_EXEC_OVERRIDES_NOTE);
return lines;
}
function renderExecPolicyToolAccess(params) {
const { access } = params;
const rich = isRich();
const heading = (value) => rich ? theme.heading(value) : value;
const preview = access.live?.status === "verified" ? access.live.diagnostics : void 0;
const diagnostics = preview ?? access.local;
const tools = diagnostics ? terminalTools(diagnostics) : [];
const excluded = tools.filter(isExcluded);
const missingFromPreview = tools.some((tool) => tool.status === "unavailable");
const status = access.live?.status === "unverified" ? "UNVERIFIED" : excluded.length === TERMINAL_TOOLS.length ? preview ? "BLOCKED" : "OFF" : excluded.length > 0 ? "PARTIAL" : preview ? "PREVIEW" : "UNVERIFIED";
const lines = [heading(`TERMINAL ACCESS · ${display(access.agentId ?? "unknown agent")} — ${status}`)];
if (access.sessionKey) lines.push(`Session: ${display(access.sessionKey)}`);
const section = (title) => {
lines.push("", heading(`── ${title} ${"─".repeat(Math.max(1, 43 - title.length))}`), "");
};
if (diagnostics && diagnostics.profiles.length > 0) {
section(preview ? "PROFILE (GATEWAY CONFIGURATION)" : "PROFILE");
const global = diagnostics.profiles.find((profile) => profile.source === "tools.profile");
const agent = diagnostics.profiles.find((profile) => profile.source.startsWith("agents.") && profile.source.endsWith(".tools.profile"));
const profileText = (profile) => `${display(profile.profile)}${profile.active ? " ← active" : ""}`;
if (global) lines.push(`Global: ${profileText(global)}`);
if (agent) lines.push(`${global ? "└─ " : ""}Agent: ${profileText(agent)}`);
for (const profile of diagnostics.profiles.filter((candidate) => candidate !== global && candidate !== agent)) lines.push(`${display(profile.source)}: ${profileText(profile)}`);
}
section("CHECK RESULTS");
if (!access.local) lines.push("Local tool policy: unavailable — no matching local agent.");
if (access.live?.status === "unverified") {
if (access.local) {
const localExcluded = terminalTools(access.local).filter(isExcluded);
lines.push(localExcluded.length > 0 ? `Local configuration excludes: ${localExcluded.map((tool) => tool.id).join(", ")}` : "Local configuration allows terminal tools; execution unverified");
}
lines.push(`Session preview: could not retrieve — ${display(access.live.error)}`);
}
for (const tool of tools) {
const label = tool.status === "available" ? "Included in session preview" : tool.status === "allowed" ? "Allowed by configuration; execution unverified" : isExcluded(tool) ? "Excluded by policy" : "Not included in session preview; execution unverified";
lines.push(`${display(tool.id)}: ${label}`);
for (const reason of tool.reasons) lines.push(` ${display(reason.label)}${reason.source ? ` (${display(reason.source)})` : ""}`);
}
if (diagnostics) lines.push("", `Based on: ${preview ? "session preview (saved settings)" : "local configuration"}`);
if (preview) lines.push("Session preview uses saved settings; active runs may differ.");
section("COMMAND APPROVALS (LOCAL)");
lines.push(...formatExecPolicyCommandApprovals({
scopes: params.approvals ? [params.approvals] : [],
approvalsExists: params.approvalsExists
}));
section("NEXT STEP");
if (access.live?.status === "unverified") lines.push("Check the Gateway connection and session key, then retry.");
else if (excluded.length > 0) {
const additions = /* @__PURE__ */ new Map();
if (!missingFromPreview && excluded.every((tool) => tool.alsoAllowPath)) {
for (const tool of excluded) {
const path = tool.alsoAllowPath;
additions.set(path, [...additions.get(path) ?? [], tool.id]);
}
lines.push("If terminal access is intended, append:");
for (const [path, ids] of additions) lines.push(` ${ids.join(", ")}`, "to:", ` ${display(path)}`);
lines.push("", "Preserve existing entries and command approvals.", "Inspect the session preview, then verify execution in a run.");
} else lines.push("Review the policy exclusions listed above before enabling terminal access.");
} else if (preview) lines.push(missingFromPreview ? "Missing preview tools are not necessarily disabled." : "Preview inclusion does not guarantee execution.", "Verify execution in a run; command approvals still apply.");
else {
lines.push("Inspect an existing session's tool preview:");
lines.push(" openclaw exec-policy show --session <session-key>");
lines.push("Verify execution in a run; command approvals still apply.");
}
if (preview && missingFromPreview && excluded.length > 0) lines.push("Missing preview tools are not necessarily disabled; verify execution in a run.");
if (params.verbose) {
section("TOOL POLICY SOURCES");
for (const profile of diagnostics?.profiles ?? []) lines.push(`${display(profile.source)} = ${display(profile.profile)}`);
} else lines.push("", "Use --verbose for policy sources and all command approval scopes.");
defaultRuntime.log(lines.join("\n"));
}
//#endregion
//#region src/cli/exec-policy-cli.ts
const EXEC_POLICY_PRESETS = {
yolo: {
host: "gateway",
security: "full",
ask: "off",
askFallback: "full"
},
cautious: {
host: "gateway",
security: "allowlist",
ask: "on-miss",
askFallback: "deny"
},
"deny-all": {
host: "gateway",
security: "deny",
ask: "off",
askFallback: "deny"
}
};
function failExecPolicy(message) {
throw new Error(message);
}
function formatExecPolicyError(err) {
return sanitizeExecPolicyMessage(err instanceof Error ? err.message : String(err));
}
async function runExecPolicyAction(action) {
try {
await action();
} catch (err) {
defaultRuntime.error(formatExecPolicyError(err));
defaultRuntime.exit(1);
}
}
function sanitizeExecPolicyTableCell(value) {
return sanitizeExecApprovalDisplayText(sanitizeTerminalText(value));
}
function sanitizeExecPolicyMessage(value) {
return sanitizeTerminalText(String(value));
}
function resolveExecPolicyInput(params) {
const resolved = {};
if (params.host !== void 0) {
const host = normalizeExecTarget(params.host);
if (!host) failExecPolicy(`Invalid exec host: ${sanitizeExecPolicyMessage(params.host)}`);
resolved.host = host;
}
if (params.security !== void 0) {
const security = normalizeExecSecurity(params.security);
if (!security) failExecPolicy(`Invalid exec security: ${sanitizeExecPolicyMessage(params.security)}`);
resolved.security = security;
}
if (params.ask !== void 0) {
const ask = normalizeExecAsk(params.ask);
if (!ask) failExecPolicy(`Invalid exec ask mode: ${sanitizeExecPolicyMessage(params.ask)}`);
resolved.ask = ask;
}
if (params.askFallback !== void 0) {
const askFallback = normalizeExecSecurity(params.askFallback);
if (!askFallback) failExecPolicy(`Invalid exec askFallback: ${sanitizeExecPolicyMessage(params.askFallback)}`);
resolved.askFallback = askFallback;
}
return resolved;
}
function applyConfigExecPolicy(root, policy) {
root.tools ??= {};
root.tools.exec ??= {};
if (policy.host !== void 0) root.tools.exec.host = policy.host;
if (policy.security !== void 0 || policy.ask !== void 0) {
const currentPolicy = resolveExecModePolicy({
mode: normalizeExecMode(root.tools.exec.mode),
security: root.tools.exec.security ?? "full",
ask: root.tools.exec.ask ?? "off"
});
const security = policy.security ?? currentPolicy.security;
const ask = policy.ask ?? currentPolicy.ask;
const mode = resolveExactExecModeFromPolicy({
security,
ask
});
if (mode) {
root.tools.exec.mode = mode;
delete root.tools.exec.security;
delete root.tools.exec.ask;
} else {
delete root.tools.exec.mode;
root.tools.exec.security = security;
root.tools.exec.ask = ask;
}
}
}
function applyApprovalsDefaults(file, policy) {
const next = structuredClone(file ?? { version: 1 });
next.version = 1;
next.defaults ??= {};
if (policy.security !== void 0) next.defaults.security = policy.security;
if (policy.ask !== void 0) next.defaults.ask = policy.ask;
if (policy.askFallback !== void 0) next.defaults.askFallback = policy.askFallback;
return next;
}
function buildExecPolicyApprovalsRollback(params) {
const fields = [
["security", params.policy.security],
["ask", params.policy.ask],
["askFallback", params.policy.askFallback]
];
const originalDefaults = resolveExecApprovalsFromFile({ file: params.original }).defaults;
const currentDefaults = resolveExecApprovalsFromFile({ file: params.current }).defaults;
const next = structuredClone(params.current);
let changed = false;
for (const [field, appliedValue] of fields) {
const currentValue = params.current.defaults?.[field];
const originalValue = params.original.defaults?.[field];
const rollbackDoesNotLoosen = field === "ask" ? maxAsk(originalDefaults.ask, currentDefaults.ask) === originalDefaults.ask : minSecurity(originalDefaults[field], currentDefaults[field]) === originalDefaults[field];
if (appliedValue !== void 0 && currentValue === params.written.defaults?.[field] && currentValue !== originalValue && rollbackDoesNotLoosen) {
next.defaults = {
...next.defaults,
[field]: originalValue
};
changed = true;
}
}
return changed ? next : null;
}
async function buildLocalExecPolicyShowPayload(options) {
const configSnapshot = await readConfigFileSnapshot();
const approvalsSnapshot = readExecApprovalsSnapshot();
const config = configSnapshot.config ?? {};
const scopes = collectExecPolicyScopeSnapshots({
cfg: config,
approvals: approvalsSnapshot.file,
hostPath: approvalsSnapshot.path
}).map(buildExecPolicyShowScope);
const baseNote = scopes.some((scope) => scope.runtimeApprovalsSource === "node-runtime") ? "Scopes requesting host=node are node-managed at runtime. Local approvals are shown only for local/gateway scopes." : "Effective exec policy is the host approvals policy intersected with requested tools.exec policy.";
const payload = {
configPath: configSnapshot.path,
approvalsPath: approvalsSnapshot.path,
approvalsExists: approvalsSnapshot.exists,
effectivePolicy: {
note: `${baseNote} ${SESSION_EXEC_OVERRIDES_NOTE}`,
scopes
}
};
if (!options) return payload;
const hasExplicitTarget = options.agent !== void 0 || options.session !== void 0;
if (!hasExplicitTarget && listAgentIds(config).length === 0) {
payload.toolAccessSelectionRequired = {
agentIds: [],
hint: "Configure an agent with openclaw agents add to inspect terminal tool access."
};
return payload;
}
try {
payload.toolAccess = await buildExecPolicyToolAccess(config, options);
} catch (error) {
if (hasExplicitTarget || !(error instanceof AgentSelectionRequiredError)) throw error;
payload.toolAccessSelectionRequired = {
agentIds: error.agentIds,
hint: error.hint
};
}
return payload;
}
function buildExecPolicyShowScope(snapshot) {
const { allowedDecisions: _allowedDecisions, ...baseScope } = snapshot;
if (snapshot.host.requested !== "node") return {
...baseScope,
runtimeApprovalsSource: "local-file"
};
return {
...baseScope,
runtimeApprovalsSource: "node-runtime",
security: {
requested: snapshot.security.requested,
requestedSource: snapshot.security.requestedSource,
host: "unknown",
hostSource: "node runtime approvals",
effective: "unknown",
note: "runtime policy resolved by node approvals"
},
ask: {
requested: snapshot.ask.requested,
requestedSource: snapshot.ask.requestedSource,
host: "unknown",
hostSource: "node runtime approvals",
effective: "unknown",
note: "runtime policy resolved by node approvals"
},
askFallback: {
effective: "unknown",
source: "node runtime approvals"
}
};
}
function renderExecPolicyShow(payload) {
const rich = isRich();
const heading = (text) => rich ? theme.heading(text) : text;
const muted = (text) => rich ? theme.muted(text) : text;
defaultRuntime.log(heading("Exec Policy"));
defaultRuntime.log(renderTable({
width: getTerminalTableWidth(),
columns: [{
key: "Field",
header: "Field",
minWidth: 14
}, {
key: "Value",
header: "Value",
minWidth: 24,
flex: true
}],
rows: [
{
Field: "Config",
Value: sanitizeExecPolicyTableCell(payload.configPath)
},
{
Field: "Approvals",
Value: sanitizeExecPolicyTableCell(payload.approvalsPath)
},
{
Field: "Approvals State",
Value: sanitizeExecPolicyTableCell(payload.approvalsExists ? "stored" : "defaults (no stored overrides)")
}
]
}).trimEnd());
defaultRuntime.log("");
defaultRuntime.log(heading("Effective Policy"));
defaultRuntime.log(renderTable({
width: getTerminalTableWidth(),
columns: [
{
key: "Scope",
header: "Scope",
minWidth: 12
},
{
key: "Requested",
header: "Requested",
minWidth: 24,
flex: true
},
{
key: "Host",
header: "Host",
minWidth: 24,
flex: true
},
{
key: "Effective",
header: "Effective",
minWidth: 16
}
],
rows: payload.effectivePolicy.scopes.map((scope) => ({
Scope: sanitizeExecPolicyTableCell(scope.scopeLabel),
Requested: sanitizeExecPolicyTableCell(`host=${scope.host.requested} (${scope.host.requestedSource})\nsecurity=${scope.security.requested} (${scope.security.requestedSource})\nask=${scope.ask.requested} (${scope.ask.requestedSource})`),
Host: sanitizeExecPolicyTableCell(`security=${scope.security.host} (${scope.security.hostSource})\nask=${scope.ask.host} (${scope.ask.hostSource})\naskFallback=${scope.askFallback.effective} (${scope.askFallback.source})`),
Effective: sanitizeExecPolicyTableCell(`security=${scope.security.effective}\nask=${scope.ask.effective}`)
}))
}).trimEnd());
defaultRuntime.log("");
defaultRuntime.log(muted(payload.effectivePolicy.note));
}
async function applyLocalExecPolicy(policy) {
const configSnapshot = await readConfigFileSnapshot();
const nextConfig = structuredClone(configSnapshot.config ?? {});
applyConfigExecPolicy(nextConfig, policy);
if (nextConfig.tools?.exec?.host === "node") failExecPolicy("Local exec-policy cannot synchronize host=node. Node approvals are fetched from the node at runtime.");
const approvalsSnapshot = readExecApprovalsSnapshot();
const nextApprovals = applyApprovalsDefaults(approvalsSnapshot.file, policy);
const writtenApprovals = await updateExecApprovals({
baseHash: approvalsSnapshot.hash,
update: () => nextApprovals
});
if (!writtenApprovals) throw new Error("Exec approvals changed; reload and retry.");
try {
await replaceConfigFile({
baseHash: configSnapshot.hash,
nextConfig
});
} catch (err) {
try {
if (!await restoreExecApprovalsSnapshotLocked(approvalsSnapshot, writtenApprovals.hash)) await updateExecApprovals({ update: (current) => buildExecPolicyApprovalsRollback({
current,
original: approvalsSnapshot.file,
written: writtenApprovals.file,
policy
}) });
} catch (rollbackError) {
throw new Error(`Config update failed: ${formatExecPolicyError(err)}; exec approvals rollback failed: ${formatExecPolicyError(rollbackError)}`, { cause: rollbackError });
}
throw err;
}
return await buildLocalExecPolicyShowPayload();
}
function registerExecPolicyCli(program) {
const execPolicy = program.command("exec-policy").description("Show or synchronize requested exec policy with host approvals").addHelpText("after", () => formatDocsHelp("/cli/approvals"));
addGatewayClientOptions(execPolicy.command("show").description("Explain terminal tool access and command approvals (offline unless --session)").option("--agent <id>", "Agent whose terminal tools to inspect (automatic for one agent)").option("--session <key>", "Fetch an existing session's tool preview from saved settings").option("--verbose", "Include policy sources and all command approval scopes", false).option("--json", "Output as JSON", false)).action(async (opts, command) => {
await runExecPolicyAction(async () => {
const payload = await buildLocalExecPolicyShowPayload(resolveGatewayRpcOptionsWithLocalPort(opts, command));
if (opts.json) {
defaultRuntime.writeJson(payload, 0);
return;
}
if (payload.toolAccess) {
const scope = payload.effectivePolicy.scopes.find((candidate) => candidate.scopeLabel === `agent:${payload.toolAccess?.agentId}`) ?? payload.effectivePolicy.scopes.find((candidate) => candidate.agentId === payload.toolAccess?.agentId) ?? payload.effectivePolicy.scopes[0];
renderExecPolicyToolAccess({
access: payload.toolAccess,
approvals: scope,
approvalsExists: payload.approvalsExists,
verbose: opts.verbose
});
} else if (payload.toolAccessSelectionRequired) {
const { agentIds, hint } = payload.toolAccessSelectionRequired;
const title = `TERMINAL ACCESS — ${agentIds.length ? "SELECT AGENT" : "NO AGENT CONFIGURED"}`;
defaultRuntime.log(isRich() ? theme.heading(title) : title);
if (agentIds.length > 0) defaultRuntime.log(`Configured agents: ${agentIds.map(sanitizeExecPolicyTableCell).join(", ")}`);
defaultRuntime.log("");
const approvalsTitle = "── COMMAND APPROVALS (LOCAL) ──────────────────";
defaultRuntime.log(isRich() ? theme.heading(approvalsTitle) : approvalsTitle);
defaultRuntime.log("");
defaultRuntime.log(formatExecPolicyCommandApprovals({
scopes: payload.effectivePolicy.scopes,
approvalsExists: payload.approvalsExists,
showScopeLabels: true
}).join("\n"));
defaultRuntime.log("");
const nextStep = "── NEXT STEP ─────────────────────────────────";
defaultRuntime.log(isRich() ? theme.heading(nextStep) : nextStep);
defaultRuntime.log("");
defaultRuntime.log(sanitizeExecPolicyTableCell(hint));
if (!opts.verbose) defaultRuntime.log("Use --verbose for policy sources and detailed command approval scopes.");
}
if (opts.verbose) {
defaultRuntime.log("");
renderExecPolicyShow(payload);
}
});
});
execPolicy.command("preset <name>").description("Apply a synchronized preset: \"yolo\", \"cautious\", or \"deny-all\"").option("--json", "Output as JSON", false).action(async (name, opts) => {
await runExecPolicyAction(async () => {
if (!Object.hasOwn(EXEC_POLICY_PRESETS, name)) failExecPolicy(`Unknown exec-policy preset: ${sanitizeExecPolicyMessage(name)}`);
const preset = EXEC_POLICY_PRESETS[name];
const payload = await applyLocalExecPolicy(preset);
if (opts.json) {
defaultRuntime.writeJson({
preset: name,
...payload
}, 0);
return;
}
defaultRuntime.log(`Applied exec-policy preset: ${sanitizeExecPolicyMessage(name)}`);
defaultRuntime.log("");
renderExecPolicyShow(payload);
});
});
execPolicy.command("set").description("Synchronize local config and host approvals using explicit values").option("--host <host>", "Exec host target: auto|sandbox|gateway|node").option("--security <mode>", "Exec security: deny|allowlist|full").option("--ask <mode>", "Exec ask mode: off|on-miss|always").option("--ask-fallback <mode>", "Host approvals fallback: deny|allowlist|full").option("--json", "Output as JSON", false).action(async (opts) => {
await runExecPolicyAction(async () => {
const policy = resolveExecPolicyInput(opts);
if (Object.keys(policy).length === 0) failExecPolicy("Provide at least one of --host, --security, --ask, or --ask-fallback.");
const payload = await applyLocalExecPolicy(policy);
if (opts.json) {
defaultRuntime.writeJson({
applied: policy,
...payload
}, 0);
return;
}
defaultRuntime.log("Synchronized local exec policy.");
defaultRuntime.log("");
renderExecPolicyShow(payload);
});
});
}
//#endregion
export { registerExecPolicyCli };