spb/zyquo-agent Public MIT
The autonomous agent that actually operates your Mac — plans, runs real commands, verifies its own work.
Swift 94.7%
Shell 4.1%
Python 0.7%
Makefile 0.5%
1//2// AuditLog.swift3// Zyquo Agent4//5// Author: Simon-Pierre Boucher6// Mail: contact@spboucher.ai7//8// Append-only record of every executed action: timestamp, kind, exact9// payload, cwd, policy ruling, exit code, truncated output. Stored as JSONL10// next to the task so nothing the agent does is invisible; viewable in the11// Audit tab and exportable.12//1314import Foundation1516/// One audited action.17struct AuditEntry: Codable, Identifiable, Sendable {18 var id: UUID = UUID()19 var timestamp: Date = Date()20 var taskID: UUID?21 /// "bash", "osascript", "write_file", …22 var actionKind: String23 /// The exact command/script/path that ran.24 var payload: String25 var cwd: String26 /// How the gate cleared it (autoAllowed / approvedByUser / …).27 var ruling: String28 var exitCode: Int32?29 /// Output truncated to `AuditLog.outputLimit` characters.30 var outputExcerpt: String?31}3233/// Append-only JSONL writer. Actor: serializes file appends.34actor AuditLog {35 static let outputLimit = 20003637 private let fileURL: URL38 private let encoder: JSONEncoder3940 /// Default log lives in the app's data folder; tasks may pass a41 /// workspace-local URL instead.42 init(fileURL: URL) {43 self.fileURL = fileURL44 let encoder = JSONEncoder()45 encoder.dateEncodingStrategy = .iso860146 self.encoder = encoder47 }4849 /// Appends one entry; creates the file on first write. Failures are50 /// reported to stderr but never crash the agent — losing an audit line is51 /// bad, killing the run is worse.52 func append(_ entry: AuditEntry) {53 var entry = entry54 if let excerpt = entry.outputExcerpt, excerpt.count > Self.outputLimit {55 entry.outputExcerpt = String(excerpt.prefix(Self.outputLimit)) + "… [truncated]"56 }57 do {58 let data = try encoder.encode(entry)59 guard var line = String(data: data, encoding: .utf8) else { return }60 line += "\n"61 let dir = fileURL.deletingLastPathComponent()62 try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)63 if !FileManager.default.fileExists(atPath: fileURL.path) {64 try line.write(to: fileURL, atomically: true, encoding: .utf8)65 } else {66 let handle = try FileHandle(forWritingTo: fileURL)67 defer { try? handle.close() }68 try handle.seekToEnd()69 try handle.write(contentsOf: Data(line.utf8))70 }71 } catch {72 FileHandle.standardError.write(Data("AuditLog append failed: \(error)\n".utf8))73 }74 }7576 /// All entries, oldest first (for the Audit tab and export).77 func entries() -> [AuditEntry] {78 guard let content = try? String(contentsOf: fileURL, encoding: .utf8) else { return [] }79 let decoder = JSONDecoder()80 decoder.dateDecodingStrategy = .iso860181 return content.split(separator: "\n").compactMap { line in82 try? decoder.decode(AuditEntry.self, from: Data(line.utf8))83 }84 }85}86