fix: align exec approvals default agent
This commit is contained in:
@@ -149,6 +149,7 @@ struct ExecApprovalsResolvedDefaults {
|
|||||||
|
|
||||||
enum ExecApprovalsStore {
|
enum ExecApprovalsStore {
|
||||||
private static let logger = Logger(subsystem: "com.clawdbot", category: "exec-approvals")
|
private static let logger = Logger(subsystem: "com.clawdbot", category: "exec-approvals")
|
||||||
|
private static let defaultAgentId = "main"
|
||||||
private static let defaultSecurity: ExecSecurity = .deny
|
private static let defaultSecurity: ExecSecurity = .deny
|
||||||
private static let defaultAsk: ExecAsk = .onMiss
|
private static let defaultAsk: ExecAsk = .onMiss
|
||||||
private static let defaultAskFallback: ExecSecurity = .deny
|
private static let defaultAskFallback: ExecSecurity = .deny
|
||||||
@@ -165,13 +166,22 @@ enum ExecApprovalsStore {
|
|||||||
static func normalizeIncoming(_ file: ExecApprovalsFile) -> ExecApprovalsFile {
|
static func normalizeIncoming(_ file: ExecApprovalsFile) -> ExecApprovalsFile {
|
||||||
let socketPath = file.socket?.path?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
let socketPath = file.socket?.path?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||||
let token = file.socket?.token?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
let token = file.socket?.token?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||||
|
var agents = file.agents ?? [:]
|
||||||
|
if let legacyDefault = agents["default"] {
|
||||||
|
if let main = agents[self.defaultAgentId] {
|
||||||
|
agents[self.defaultAgentId] = self.mergeAgents(current: main, legacy: legacyDefault)
|
||||||
|
} else {
|
||||||
|
agents[self.defaultAgentId] = legacyDefault
|
||||||
|
}
|
||||||
|
agents.removeValue(forKey: "default")
|
||||||
|
}
|
||||||
return ExecApprovalsFile(
|
return ExecApprovalsFile(
|
||||||
version: 1,
|
version: 1,
|
||||||
socket: ExecApprovalsSocketConfig(
|
socket: ExecApprovalsSocketConfig(
|
||||||
path: socketPath.isEmpty ? nil : socketPath,
|
path: socketPath.isEmpty ? nil : socketPath,
|
||||||
token: token.isEmpty ? nil : token),
|
token: token.isEmpty ? nil : token),
|
||||||
defaults: file.defaults,
|
defaults: file.defaults,
|
||||||
agents: file.agents)
|
agents: agents)
|
||||||
}
|
}
|
||||||
|
|
||||||
static func readSnapshot() -> ExecApprovalsSnapshot {
|
static func readSnapshot() -> ExecApprovalsSnapshot {
|
||||||
@@ -272,9 +282,7 @@ enum ExecApprovalsStore {
|
|||||||
ask: defaults.ask ?? self.defaultAsk,
|
ask: defaults.ask ?? self.defaultAsk,
|
||||||
askFallback: defaults.askFallback ?? self.defaultAskFallback,
|
askFallback: defaults.askFallback ?? self.defaultAskFallback,
|
||||||
autoAllowSkills: defaults.autoAllowSkills ?? self.defaultAutoAllowSkills)
|
autoAllowSkills: defaults.autoAllowSkills ?? self.defaultAutoAllowSkills)
|
||||||
let key = (agentId?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == false)
|
let key = self.agentKey(agentId)
|
||||||
? agentId!.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
||||||
: "default"
|
|
||||||
let agentEntry = file.agents?[key] ?? ExecApprovalsAgent()
|
let agentEntry = file.agents?[key] ?? ExecApprovalsAgent()
|
||||||
let wildcardEntry = file.agents?["*"] ?? ExecApprovalsAgent()
|
let wildcardEntry = file.agents?["*"] ?? ExecApprovalsAgent()
|
||||||
let resolvedAgent = ExecApprovalsResolvedDefaults(
|
let resolvedAgent = ExecApprovalsResolvedDefaults(
|
||||||
@@ -457,7 +465,36 @@ enum ExecApprovalsStore {
|
|||||||
|
|
||||||
private static func agentKey(_ agentId: String?) -> String {
|
private static func agentKey(_ agentId: String?) -> String {
|
||||||
let trimmed = agentId?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
let trimmed = agentId?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||||
return trimmed.isEmpty ? "default" : trimmed
|
return trimmed.isEmpty ? self.defaultAgentId : trimmed
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func normalizedPattern(_ pattern: String?) -> String? {
|
||||||
|
let trimmed = pattern?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||||
|
return trimmed.isEmpty ? nil : trimmed.lowercased()
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func mergeAgents(
|
||||||
|
current: ExecApprovalsAgent,
|
||||||
|
legacy: ExecApprovalsAgent
|
||||||
|
) -> ExecApprovalsAgent {
|
||||||
|
var seen = Set<String>()
|
||||||
|
var allowlist: [ExecAllowlistEntry] = []
|
||||||
|
func append(_ entry: ExecAllowlistEntry) {
|
||||||
|
guard let key = self.normalizedPattern(entry.pattern), !seen.contains(key) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
seen.insert(key)
|
||||||
|
allowlist.append(entry)
|
||||||
|
}
|
||||||
|
for entry in current.allowlist ?? [] { append(entry) }
|
||||||
|
for entry in legacy.allowlist ?? [] { append(entry) }
|
||||||
|
|
||||||
|
return ExecApprovalsAgent(
|
||||||
|
security: current.security ?? legacy.security,
|
||||||
|
ask: current.ask ?? legacy.ask,
|
||||||
|
askFallback: current.askFallback ?? legacy.askFallback,
|
||||||
|
autoAllowSkills: current.autoAllowSkills ?? legacy.autoAllowSkills,
|
||||||
|
allowlist: allowlist.isEmpty ? nil : allowlist)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ If a prompt is required but no UI is reachable, fallback decides:
|
|||||||
Allowlists are **per agent**. If multiple agents exist, switch which agent you’re
|
Allowlists are **per agent**. If multiple agents exist, switch which agent you’re
|
||||||
editing in the macOS app. Patterns are **case-insensitive glob matches**.
|
editing in the macOS app. Patterns are **case-insensitive glob matches**.
|
||||||
Patterns should resolve to **binary paths** (basename-only entries are ignored).
|
Patterns should resolve to **binary paths** (basename-only entries are ignored).
|
||||||
|
Legacy `agents.default` entries are migrated to `agents.main` on load.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
- `~/Projects/**/bin/bird`
|
- `~/Projects/**/bin/bird`
|
||||||
|
|||||||
@@ -734,7 +734,7 @@ export function recordAllowlistUse(
|
|||||||
command: string,
|
command: string,
|
||||||
resolvedPath?: string,
|
resolvedPath?: string,
|
||||||
) {
|
) {
|
||||||
const target = agentId ?? "default";
|
const target = agentId ?? DEFAULT_AGENT_ID;
|
||||||
const agents = approvals.agents ?? {};
|
const agents = approvals.agents ?? {};
|
||||||
const existing = agents[target] ?? {};
|
const existing = agents[target] ?? {};
|
||||||
const allowlist = Array.isArray(existing.allowlist) ? existing.allowlist : [];
|
const allowlist = Array.isArray(existing.allowlist) ? existing.allowlist : [];
|
||||||
@@ -758,7 +758,7 @@ export function addAllowlistEntry(
|
|||||||
agentId: string | undefined,
|
agentId: string | undefined,
|
||||||
pattern: string,
|
pattern: string,
|
||||||
) {
|
) {
|
||||||
const target = agentId ?? "default";
|
const target = agentId ?? DEFAULT_AGENT_ID;
|
||||||
const agents = approvals.agents ?? {};
|
const agents = approvals.agents ?? {};
|
||||||
const existing = agents[target] ?? {};
|
const existing = agents[target] ?? {};
|
||||||
const allowlist = Array.isArray(existing.allowlist) ? existing.allowlist : [];
|
const allowlist = Array.isArray(existing.allowlist) ? existing.allowlist : [];
|
||||||
|
|||||||
Reference in New Issue
Block a user