@@ -64,6 +64,10 @@ actor MacNodeRuntime {
6464 return try await self . handleSystemWhich ( req)
6565 case ClawdbotSystemCommand . notify. rawValue:
6666 return try await self . handleSystemNotify ( req)
67+ case ClawdbotSystemCommand . execApprovalsGet. rawValue:
68+ return try await self . handleSystemExecApprovalsGet ( req)
69+ case ClawdbotSystemCommand . execApprovalsSet. rawValue:
70+ return try await self . handleSystemExecApprovalsSet ( req)
6771 default :
6872 return Self . errorResponse ( req, code: . invalidRequest, message: " INVALID_REQUEST: unknown command " )
6973 }
@@ -676,6 +680,72 @@ actor MacNodeRuntime {
676680 return BridgeInvokeResponse ( id: req. id, ok: true , payloadJSON: payload)
677681 }
678682
683+ private func handleSystemExecApprovalsGet( _ req: BridgeInvokeRequest ) async throws -> BridgeInvokeResponse {
684+ _ = ExecApprovalsStore . ensureFile ( )
685+ let snapshot = ExecApprovalsStore . readSnapshot ( )
686+ let redacted = ExecApprovalsSnapshot (
687+ path: snapshot. path,
688+ exists: snapshot. exists,
689+ hash: snapshot. hash,
690+ file: ExecApprovalsStore . redactForSnapshot ( snapshot. file) )
691+ let payload = try Self . encodePayload ( redacted)
692+ return BridgeInvokeResponse ( id: req. id, ok: true , payloadJSON: payload)
693+ }
694+
695+ private func handleSystemExecApprovalsSet( _ req: BridgeInvokeRequest ) async throws -> BridgeInvokeResponse {
696+ struct SetParams : Decodable {
697+ var file : ExecApprovalsFile
698+ var baseHash : String ?
699+ }
700+
701+ let params = try Self . decodeParams ( SetParams . self, from: req. paramsJSON)
702+ let current = ExecApprovalsStore . ensureFile ( )
703+ let snapshot = ExecApprovalsStore . readSnapshot ( )
704+ if snapshot. exists {
705+ if snapshot. hash. isEmpty {
706+ return Self . errorResponse (
707+ req,
708+ code: . invalidRequest,
709+ message: " INVALID_REQUEST: exec approvals base hash unavailable; reload and retry " )
710+ }
711+ let baseHash = params. baseHash? . trimmingCharacters ( in: . whitespacesAndNewlines) ?? " "
712+ if baseHash. isEmpty {
713+ return Self . errorResponse (
714+ req,
715+ code: . invalidRequest,
716+ message: " INVALID_REQUEST: exec approvals base hash required; reload and retry " )
717+ }
718+ if baseHash != snapshot. hash {
719+ return Self . errorResponse (
720+ req,
721+ code: . invalidRequest,
722+ message: " INVALID_REQUEST: exec approvals changed; reload and retry " )
723+ }
724+ }
725+
726+ var normalized = ExecApprovalsStore . normalizeIncoming ( params. file)
727+ let socketPath = normalized. socket? . path? . trimmingCharacters ( in: . whitespacesAndNewlines)
728+ let token = normalized. socket? . token? . trimmingCharacters ( in: . whitespacesAndNewlines)
729+ let resolvedPath = ( socketPath? . isEmpty == false )
730+ ? socketPath!
731+ : current. socket? . path? . trimmingCharacters ( in: . whitespacesAndNewlines) ??
732+ ExecApprovalsStore . socketPath ( )
733+ let resolvedToken = ( token? . isEmpty == false )
734+ ? token!
735+ : current. socket? . token? . trimmingCharacters ( in: . whitespacesAndNewlines) ?? " "
736+ normalized. socket = ExecApprovalsSocketConfig ( path: resolvedPath, token: resolvedToken)
737+
738+ ExecApprovalsStore . saveFile ( normalized)
739+ let nextSnapshot = ExecApprovalsStore . readSnapshot ( )
740+ let redacted = ExecApprovalsSnapshot (
741+ path: nextSnapshot. path,
742+ exists: nextSnapshot. exists,
743+ hash: nextSnapshot. hash,
744+ file: ExecApprovalsStore . redactForSnapshot ( nextSnapshot. file) )
745+ let payload = try Self . encodePayload ( redacted)
746+ return BridgeInvokeResponse ( id: req. id, ok: true , payloadJSON: payload)
747+ }
748+
679749 private func emitExecEvent( _ event: String , payload: ExecEventPayload ) async {
680750 guard let sender = self . eventSender else { return }
681751 guard let data = try ? JSONEncoder ( ) . encode ( payload) ,
0 commit comments