diff --git a/Sources/agentd/AgentdMCP.swift b/Sources/agentd/AgentdMCP.swift index b776635..0e6d3c7 100644 --- a/Sources/agentd/AgentdMCP.swift +++ b/Sources/agentd/AgentdMCP.swift @@ -110,16 +110,10 @@ struct SystemAgentdMCPRuntime: AgentdMCPRuntime { let permissions = await MainActor.run { PermissionSnapshot.current(promptForAccessibility: false) } - let submitter = try Submitter( - endpoint: config.endpoint, - localOnly: true, - authMode: .none, - maxBatchBytes: config.maxBatchBytes, - maxBatchAgeDays: config.maxBatchAgeDays, - deviceId: config.deviceId, - encryptLocalBatches: config.encryptLocalBatches + let batchStats = LocalBatchStats.collect( + in: FileManager.default.homeDirectoryForCurrentUser + .appendingPathComponent(".evalops/agentd/batches") ) - let batchStats = await submitter.localBatchStats() return AgentdMCPDeviceSnapshot( generatedAt: Date(), diff --git a/Sources/agentd/Submitter.swift b/Sources/agentd/Submitter.swift index 8760af3..10cae3a 100644 --- a/Sources/agentd/Submitter.swift +++ b/Sources/agentd/Submitter.swift @@ -416,11 +416,7 @@ actor Submitter { } func localBatchStats() async -> LocalBatchStats { - let files = localBatchFiles() - return LocalBatchStats( - fileCount: files.count, - bytes: files.reduce(Int64(0)) { $0 + $1.size } - ) + LocalBatchStats.collect(in: batchDirectory) } func localBatchSummaries() async -> [LocalBatchSummary] { @@ -458,6 +454,32 @@ actor Submitter { } private func localBatchFiles() -> [LocalBatchFile] { + LocalBatchFile.collect(in: batchDirectory) + } + + private func readLocalBatch(_ file: LocalBatchFile) throws -> Data { + let data = try Data(contentsOf: file.url) + guard file.encrypted else { + return data + } + guard let localBatchCryptor else { + throw LocalBatchCryptoError.invalidCiphertext + } + return try localBatchCryptor.decrypt(data) + } +} + +private struct LocalBatchFile { + let url: URL + let modified: Date + let size: Int64 + let encrypted: Bool + + var batchId: String { + url.deletingPathExtension().lastPathComponent + } + + static func collect(in batchDirectory: URL) -> [LocalBatchFile] { guard let urls = try? FileManager.default.contentsOfDirectory( at: batchDirectory, @@ -487,28 +509,6 @@ actor Submitter { } return files } - - private func readLocalBatch(_ file: LocalBatchFile) throws -> Data { - let data = try Data(contentsOf: file.url) - guard file.encrypted else { - return data - } - guard let localBatchCryptor else { - throw LocalBatchCryptoError.invalidCiphertext - } - return try localBatchCryptor.decrypt(data) - } -} - -private struct LocalBatchFile { - let url: URL - let modified: Date - let size: Int64 - let encrypted: Bool - - var batchId: String { - url.deletingPathExtension().lastPathComponent - } } struct LocalBatchReplayResult: Sendable, Equatable { @@ -519,6 +519,14 @@ struct LocalBatchReplayResult: Sendable, Equatable { struct LocalBatchStats: Sendable, Equatable { let fileCount: Int let bytes: Int64 + + static func collect(in batchDirectory: URL) -> LocalBatchStats { + let files = LocalBatchFile.collect(in: batchDirectory) + return LocalBatchStats( + fileCount: files.count, + bytes: files.reduce(Int64(0)) { $0 + $1.size } + ) + } } struct LocalBatchSummary: Sendable, Codable, Equatable { diff --git a/Tests/agentdTests/SubmitterTests.swift b/Tests/agentdTests/SubmitterTests.swift index a2cf981..10f3af9 100644 --- a/Tests/agentdTests/SubmitterTests.swift +++ b/Tests/agentdTests/SubmitterTests.swift @@ -945,6 +945,21 @@ final class SubmitterTests: XCTestCase { FileManager.default.fileExists(atPath: dir.appendingPathComponent("new.json").path)) } + func testLocalBatchStatsCollectsJSONAndEncryptedFilesWithoutKeychain() throws { + let dir = try makeTemporaryDirectory() + try writeBatchFile(dir.appendingPathComponent("plain.json"), bytes: 10, modified: Date()) + try writeBatchFile( + dir.appendingPathComponent("encrypted.\(LocalBatchCryptor.encryptedExtension)"), + bytes: 20, + modified: Date() + ) + try writeBatchFile(dir.appendingPathComponent("ignore.txt"), bytes: 30, modified: Date()) + + let stats = LocalBatchStats.collect(in: dir) + + XCTAssertEqual(stats, LocalBatchStats(fileCount: 2, bytes: 30)) + } + func testLocalBatchSweepRemovesOldestFilesOverByteBudget() async throws { let dir = try makeTemporaryDirectory() try writeBatchFile(