diff --git a/CHANGELOG.md b/CHANGELOG.md index c5f398ad9..11afa9ca0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ ### Fixes - Browser: add tests for snapshot labels/efficient query params and labeled image responses. +- macOS: ensure launchd log directory exists with a test-only override. (#909) — thanks @roshanasingh4. - Telegram: register dock native commands with underscores to avoid `BOT_COMMAND_INVALID` (#929, fixes #901) — thanks @grp06. - Google: downgrade unsigned thinking blocks before send to avoid missing signature errors. - Agents: make user time zone and 24-hour time explicit in the system prompt. (#859) — thanks @CashWilliams. diff --git a/apps/macos/Sources/Clawdbot/LogLocator.swift b/apps/macos/Sources/Clawdbot/LogLocator.swift index b49e58f9c..8714f7d0c 100644 --- a/apps/macos/Sources/Clawdbot/LogLocator.swift +++ b/apps/macos/Sources/Clawdbot/LogLocator.swift @@ -1,9 +1,25 @@ import Foundation enum LogLocator { - private static let logDir = URL(fileURLWithPath: "/tmp/clawdbot") - private static let stdoutLog = logDir.appendingPathComponent("clawdbot-stdout.log") - private static let gatewayLog = logDir.appendingPathComponent("clawdbot-gateway.log") + private static var logDir: URL { + if let override = ProcessInfo.processInfo.environment["CLAWDBOT_LOG_DIR"], !override.isEmpty { + return URL(fileURLWithPath: override) + } + + return URL(fileURLWithPath: "/tmp/clawdbot") + } + + private static var stdoutLog: URL { + logDir.appendingPathComponent("clawdbot-stdout.log") + } + + private static var gatewayLog: URL { + logDir.appendingPathComponent("clawdbot-gateway.log") + } + + private static func ensureLogDirExists() { + try? FileManager.default.createDirectory(at: self.logDir, withIntermediateDirectories: true) + } private static func modificationDate(for url: URL) -> Date { (try? url.resourceValues(forKeys: [.contentModificationDateKey]).contentModificationDate) ?? .distantPast @@ -11,6 +27,7 @@ enum LogLocator { /// Returns the newest log file under /tmp/clawdbot/ (rolling or stdout), or nil if none exist. static func bestLogFile() -> URL? { + self.ensureLogDirExists() let fm = FileManager.default let files = (try? fm.contentsOfDirectory( at: self.logDir, @@ -26,11 +43,13 @@ enum LogLocator { /// Path to use for launchd stdout/err. static var launchdLogPath: String { - stdoutLog.path + self.ensureLogDirExists() + return stdoutLog.path } /// Path to use for the Gateway launchd job stdout/err. static var launchdGatewayLogPath: String { - gatewayLog.path + self.ensureLogDirExists() + return gatewayLog.path } } diff --git a/apps/macos/Tests/ClawdbotIPCTests/LogLocatorTests.swift b/apps/macos/Tests/ClawdbotIPCTests/LogLocatorTests.swift new file mode 100644 index 000000000..bc29f9e2b --- /dev/null +++ b/apps/macos/Tests/ClawdbotIPCTests/LogLocatorTests.swift @@ -0,0 +1,24 @@ +import Darwin +import Foundation +import Testing +@testable import Clawdbot + +@Suite struct LogLocatorTests { + @Test func launchdGatewayLogPathEnsuresTmpDirExists() throws { + let fm = FileManager.default + let baseDir = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) + let logDir = baseDir.appendingPathComponent("clawdbot-tests-\(UUID().uuidString)") + + setenv("CLAWDBOT_LOG_DIR", logDir.path, 1) + defer { + unsetenv("CLAWDBOT_LOG_DIR") + try? fm.removeItem(at: logDir) + } + + _ = LogLocator.launchdGatewayLogPath + + var isDir: ObjCBool = false + #expect(fm.fileExists(atPath: logDir.path, isDirectory: &isDir)) + #expect(isDir.boolValue == true) + } +}