refactor(cli): dedupe allowlist command wiring

This commit is contained in:
Peter Steinberger
2026-02-21 21:45:50 +00:00
parent 7c9e1bada0
commit 266b3a356d

View File

@@ -295,11 +295,12 @@ async function loadWritableAllowlistAgent(opts: ExecApprovalsCliOpts): Promise<{
type WritableAllowlistAgentContext = Awaited<ReturnType<typeof loadWritableAllowlistAgent>> & { type WritableAllowlistAgentContext = Awaited<ReturnType<typeof loadWritableAllowlistAgent>> & {
trimmedPattern: string; trimmedPattern: string;
}; };
type AllowlistMutation = (context: WritableAllowlistAgentContext) => boolean | Promise<boolean>;
async function runAllowlistMutation( async function runAllowlistMutation(
pattern: string, pattern: string,
opts: ExecApprovalsCliOpts, opts: ExecApprovalsCliOpts,
mutate: (context: WritableAllowlistAgentContext) => boolean | Promise<boolean>, mutate: AllowlistMutation,
): Promise<void> { ): Promise<void> {
try { try {
const trimmedPattern = requireTrimmedNonEmpty(pattern, "Pattern required."); const trimmedPattern = requireTrimmedNonEmpty(pattern, "Pattern required.");
@@ -322,6 +323,25 @@ async function runAllowlistMutation(
} }
} }
function registerAllowlistMutationCommand(params: {
allowlist: Command;
name: "add" | "remove";
description: string;
mutate: AllowlistMutation;
}): Command {
const command = params.allowlist
.command(`${params.name} <pattern>`)
.description(params.description)
.option("--node <node>", "Target node id/name/IP")
.option("--gateway", "Force gateway approvals", false)
.option("--agent <id>", 'Agent id (defaults to "*")')
.action(async (pattern: string, opts: ExecApprovalsCliOpts) => {
await runAllowlistMutation(pattern, opts, params.mutate);
});
nodesCallOpts(command);
return command;
}
export function registerExecApprovalsCli(program: Command) { export function registerExecApprovalsCli(program: Command) {
const formatExample = (cmd: string, desc: string) => const formatExample = (cmd: string, desc: string) =>
` ${theme.command(cmd)}\n ${theme.muted(desc)}`; ` ${theme.command(cmd)}\n ${theme.muted(desc)}`;
@@ -416,63 +436,47 @@ export function registerExecApprovalsCli(program: Command) {
)}\n\n${theme.muted("Docs:")} ${formatDocsLink("/cli/approvals", "docs.openclaw.ai/cli/approvals")}\n`, )}\n\n${theme.muted("Docs:")} ${formatDocsLink("/cli/approvals", "docs.openclaw.ai/cli/approvals")}\n`,
); );
const allowlistAdd = allowlist registerAllowlistMutationCommand({
.command("add <pattern>") allowlist,
.description("Add a glob pattern to an allowlist") name: "add",
.option("--node <node>", "Target node id/name/IP") description: "Add a glob pattern to an allowlist",
.option("--gateway", "Force gateway approvals", false) mutate: ({ trimmedPattern, file, agent, agentKey, allowlistEntries }) => {
.option("--agent <id>", 'Agent id (defaults to "*")') if (allowlistEntries.some((entry) => normalizeAllowlistEntry(entry) === trimmedPattern)) {
.action(async (pattern: string, opts: ExecApprovalsCliOpts) => { defaultRuntime.log("Already allowlisted.");
await runAllowlistMutation( return false;
pattern, }
opts, allowlistEntries.push({ pattern: trimmedPattern, lastUsedAt: Date.now() });
({ trimmedPattern, file, agent, agentKey, allowlistEntries }) => { agent.allowlist = allowlistEntries;
if (allowlistEntries.some((entry) => normalizeAllowlistEntry(entry) === trimmedPattern)) { file.agents = { ...file.agents, [agentKey]: agent };
defaultRuntime.log("Already allowlisted."); return true;
return false; },
} });
allowlistEntries.push({ pattern: trimmedPattern, lastUsedAt: Date.now() });
agent.allowlist = allowlistEntries;
file.agents = { ...file.agents, [agentKey]: agent };
return true;
},
);
});
nodesCallOpts(allowlistAdd);
const allowlistRemove = allowlist registerAllowlistMutationCommand({
.command("remove <pattern>") allowlist,
.description("Remove a glob pattern from an allowlist") name: "remove",
.option("--node <node>", "Target node id/name/IP") description: "Remove a glob pattern from an allowlist",
.option("--gateway", "Force gateway approvals", false) mutate: ({ trimmedPattern, file, agent, agentKey, allowlistEntries }) => {
.option("--agent <id>", 'Agent id (defaults to "*")') const nextEntries = allowlistEntries.filter(
.action(async (pattern: string, opts: ExecApprovalsCliOpts) => { (entry) => normalizeAllowlistEntry(entry) !== trimmedPattern,
await runAllowlistMutation(
pattern,
opts,
({ trimmedPattern, file, agent, agentKey, allowlistEntries }) => {
const nextEntries = allowlistEntries.filter(
(entry) => normalizeAllowlistEntry(entry) !== trimmedPattern,
);
if (nextEntries.length === allowlistEntries.length) {
defaultRuntime.log("Pattern not found.");
return false;
}
if (nextEntries.length === 0) {
delete agent.allowlist;
} else {
agent.allowlist = nextEntries;
}
if (isEmptyAgent(agent)) {
const agents = { ...file.agents };
delete agents[agentKey];
file.agents = Object.keys(agents).length > 0 ? agents : undefined;
} else {
file.agents = { ...file.agents, [agentKey]: agent };
}
return true;
},
); );
}); if (nextEntries.length === allowlistEntries.length) {
nodesCallOpts(allowlistRemove); defaultRuntime.log("Pattern not found.");
return false;
}
if (nextEntries.length === 0) {
delete agent.allowlist;
} else {
agent.allowlist = nextEntries;
}
if (isEmptyAgent(agent)) {
const agents = { ...file.agents };
delete agents[agentKey];
file.agents = Object.keys(agents).length > 0 ? agents : undefined;
} else {
file.agents = { ...file.agents, [agentKey]: agent };
}
return true;
},
});
} }