refactor(macos): replace FileManager.default for Swift 6

This commit is contained in:
Ryan Lisse
2026-01-18 20:00:26 +01:00
committed by Peter Steinberger
parent 44d55667de
commit 87d995bcde
51 changed files with 197 additions and 213 deletions

View File

@@ -23,7 +23,7 @@ enum AgentWorkspace {
} }
static func displayPath(for url: URL) -> String { static func displayPath(for url: URL) -> String {
let home = FileManager.default.homeDirectoryForCurrentUser.path let home = FileManager().homeDirectoryForCurrentUser.path
let path = url.path let path = url.path
if path == home { return "~" } if path == home { return "~" }
if path.hasPrefix(home + "/") { if path.hasPrefix(home + "/") {
@@ -44,12 +44,12 @@ enum AgentWorkspace {
} }
static func workspaceEntries(workspaceURL: URL) throws -> [String] { static func workspaceEntries(workspaceURL: URL) throws -> [String] {
let contents = try FileManager.default.contentsOfDirectory(atPath: workspaceURL.path) let contents = try FileManager().contentsOfDirectory(atPath: workspaceURL.path)
return contents.filter { !self.ignoredEntries.contains($0) } return contents.filter { !self.ignoredEntries.contains($0) }
} }
static func isWorkspaceEmpty(workspaceURL: URL) -> Bool { static func isWorkspaceEmpty(workspaceURL: URL) -> Bool {
let fm = FileManager.default let fm = FileManager()
var isDir: ObjCBool = false var isDir: ObjCBool = false
if !fm.fileExists(atPath: workspaceURL.path, isDirectory: &isDir) { if !fm.fileExists(atPath: workspaceURL.path, isDirectory: &isDir) {
return true return true
@@ -66,7 +66,7 @@ enum AgentWorkspace {
} }
static func bootstrapSafety(for workspaceURL: URL) -> BootstrapSafety { static func bootstrapSafety(for workspaceURL: URL) -> BootstrapSafety {
let fm = FileManager.default let fm = FileManager()
var isDir: ObjCBool = false var isDir: ObjCBool = false
if !fm.fileExists(atPath: workspaceURL.path, isDirectory: &isDir) { if !fm.fileExists(atPath: workspaceURL.path, isDirectory: &isDir) {
return .safe return .safe
@@ -90,29 +90,29 @@ enum AgentWorkspace {
static func bootstrap(workspaceURL: URL) throws -> URL { static func bootstrap(workspaceURL: URL) throws -> URL {
let shouldSeedBootstrap = self.isWorkspaceEmpty(workspaceURL: workspaceURL) let shouldSeedBootstrap = self.isWorkspaceEmpty(workspaceURL: workspaceURL)
try FileManager.default.createDirectory(at: workspaceURL, withIntermediateDirectories: true) try FileManager().createDirectory(at: workspaceURL, withIntermediateDirectories: true)
let agentsURL = self.agentsURL(workspaceURL: workspaceURL) let agentsURL = self.agentsURL(workspaceURL: workspaceURL)
if !FileManager.default.fileExists(atPath: agentsURL.path) { if !FileManager().fileExists(atPath: agentsURL.path) {
try self.defaultTemplate().write(to: agentsURL, atomically: true, encoding: .utf8) try self.defaultTemplate().write(to: agentsURL, atomically: true, encoding: .utf8)
self.logger.info("Created AGENTS.md at \(agentsURL.path, privacy: .public)") self.logger.info("Created AGENTS.md at \(agentsURL.path, privacy: .public)")
} }
let soulURL = workspaceURL.appendingPathComponent(self.soulFilename) let soulURL = workspaceURL.appendingPathComponent(self.soulFilename)
if !FileManager.default.fileExists(atPath: soulURL.path) { if !FileManager().fileExists(atPath: soulURL.path) {
try self.defaultSoulTemplate().write(to: soulURL, atomically: true, encoding: .utf8) try self.defaultSoulTemplate().write(to: soulURL, atomically: true, encoding: .utf8)
self.logger.info("Created SOUL.md at \(soulURL.path, privacy: .public)") self.logger.info("Created SOUL.md at \(soulURL.path, privacy: .public)")
} }
let identityURL = workspaceURL.appendingPathComponent(self.identityFilename) let identityURL = workspaceURL.appendingPathComponent(self.identityFilename)
if !FileManager.default.fileExists(atPath: identityURL.path) { if !FileManager().fileExists(atPath: identityURL.path) {
try self.defaultIdentityTemplate().write(to: identityURL, atomically: true, encoding: .utf8) try self.defaultIdentityTemplate().write(to: identityURL, atomically: true, encoding: .utf8)
self.logger.info("Created IDENTITY.md at \(identityURL.path, privacy: .public)") self.logger.info("Created IDENTITY.md at \(identityURL.path, privacy: .public)")
} }
let userURL = workspaceURL.appendingPathComponent(self.userFilename) let userURL = workspaceURL.appendingPathComponent(self.userFilename)
if !FileManager.default.fileExists(atPath: userURL.path) { if !FileManager().fileExists(atPath: userURL.path) {
try self.defaultUserTemplate().write(to: userURL, atomically: true, encoding: .utf8) try self.defaultUserTemplate().write(to: userURL, atomically: true, encoding: .utf8)
self.logger.info("Created USER.md at \(userURL.path, privacy: .public)") self.logger.info("Created USER.md at \(userURL.path, privacy: .public)")
} }
let bootstrapURL = workspaceURL.appendingPathComponent(self.bootstrapFilename) let bootstrapURL = workspaceURL.appendingPathComponent(self.bootstrapFilename)
if shouldSeedBootstrap, !FileManager.default.fileExists(atPath: bootstrapURL.path) { if shouldSeedBootstrap, !FileManager().fileExists(atPath: bootstrapURL.path) {
try self.defaultBootstrapTemplate().write(to: bootstrapURL, atomically: true, encoding: .utf8) try self.defaultBootstrapTemplate().write(to: bootstrapURL, atomically: true, encoding: .utf8)
self.logger.info("Created BOOTSTRAP.md at \(bootstrapURL.path, privacy: .public)") self.logger.info("Created BOOTSTRAP.md at \(bootstrapURL.path, privacy: .public)")
} }
@@ -120,7 +120,7 @@ enum AgentWorkspace {
} }
static func needsBootstrap(workspaceURL: URL) -> Bool { static func needsBootstrap(workspaceURL: URL) -> Bool {
let fm = FileManager.default let fm = FileManager()
var isDir: ObjCBool = false var isDir: ObjCBool = false
if !fm.fileExists(atPath: workspaceURL.path, isDirectory: &isDir) { if !fm.fileExists(atPath: workspaceURL.path, isDirectory: &isDir) {
return true return true
@@ -305,7 +305,7 @@ enum AgentWorkspace {
if let dev = self.devTemplateURL(named: named) { if let dev = self.devTemplateURL(named: named) {
urls.append(dev) urls.append(dev)
} }
let cwd = URL(fileURLWithPath: FileManager.default.currentDirectoryPath) let cwd = URL(fileURLWithPath: FileManager().currentDirectoryPath)
urls.append(cwd.appendingPathComponent("docs") urls.append(cwd.appendingPathComponent("docs")
.appendingPathComponent(self.templateDirname) .appendingPathComponent(self.templateDirname)
.appendingPathComponent(named)) .appendingPathComponent(named))

View File

@@ -45,7 +45,7 @@ struct AnthropicAuthControls: View {
NSWorkspace.shared.activateFileViewerSelecting([ClawdbotOAuthStore.oauthURL()]) NSWorkspace.shared.activateFileViewerSelecting([ClawdbotOAuthStore.oauthURL()])
} }
.buttonStyle(.bordered) .buttonStyle(.bordered)
.disabled(!FileManager.default.fileExists(atPath: ClawdbotOAuthStore.oauthURL().path)) .disabled(!FileManager().fileExists(atPath: ClawdbotOAuthStore.oauthURL().path))
Button("Refresh") { Button("Refresh") {
self.refresh() self.refresh()

View File

@@ -234,7 +234,7 @@ enum ClawdbotOAuthStore {
return URL(fileURLWithPath: expanded, isDirectory: true) return URL(fileURLWithPath: expanded, isDirectory: true)
} }
return FileManager.default.homeDirectoryForCurrentUser return FileManager().homeDirectoryForCurrentUser
.appendingPathComponent(".clawdbot", isDirectory: true) .appendingPathComponent(".clawdbot", isDirectory: true)
.appendingPathComponent("credentials", isDirectory: true) .appendingPathComponent("credentials", isDirectory: true)
} }
@@ -253,7 +253,7 @@ enum ClawdbotOAuthStore {
urls.append(URL(fileURLWithPath: expanded, isDirectory: true).appendingPathComponent(self.oauthFilename)) urls.append(URL(fileURLWithPath: expanded, isDirectory: true).appendingPathComponent(self.oauthFilename))
} }
let home = FileManager.default.homeDirectoryForCurrentUser let home = FileManager().homeDirectoryForCurrentUser
urls.append(home.appendingPathComponent(".pi/agent/\(self.oauthFilename)")) urls.append(home.appendingPathComponent(".pi/agent/\(self.oauthFilename)"))
urls.append(home.appendingPathComponent(".claude/\(self.oauthFilename)")) urls.append(home.appendingPathComponent(".claude/\(self.oauthFilename)"))
urls.append(home.appendingPathComponent(".config/claude/\(self.oauthFilename)")) urls.append(home.appendingPathComponent(".config/claude/\(self.oauthFilename)"))
@@ -270,10 +270,10 @@ enum ClawdbotOAuthStore {
static func importLegacyAnthropicOAuthIfNeeded() -> URL? { static func importLegacyAnthropicOAuthIfNeeded() -> URL? {
let dest = self.oauthURL() let dest = self.oauthURL()
guard !FileManager.default.fileExists(atPath: dest.path) else { return nil } guard !FileManager().fileExists(atPath: dest.path) else { return nil }
for url in self.legacyOAuthURLs() { for url in self.legacyOAuthURLs() {
guard FileManager.default.fileExists(atPath: url.path) else { continue } guard FileManager().fileExists(atPath: url.path) else { continue }
guard self.anthropicOAuthStatus(at: url).isConnected else { continue } guard self.anthropicOAuthStatus(at: url).isConnected else { continue }
guard let storage = self.loadStorage(at: url) else { continue } guard let storage = self.loadStorage(at: url) else { continue }
do { do {
@@ -296,7 +296,7 @@ enum ClawdbotOAuthStore {
} }
static func anthropicOAuthStatus(at url: URL) -> AnthropicOAuthStatus { static func anthropicOAuthStatus(at url: URL) -> AnthropicOAuthStatus {
guard FileManager.default.fileExists(atPath: url.path) else { return .missingFile } guard FileManager().fileExists(atPath: url.path) else { return .missingFile }
guard let data = try? Data(contentsOf: url) else { return .unreadableFile } guard let data = try? Data(contentsOf: url) else { return .unreadableFile }
guard let json = try? JSONSerialization.jsonObject(with: data, options: []) else { return .invalidJSON } guard let json = try? JSONSerialization.jsonObject(with: data, options: []) else { return .invalidJSON }
@@ -360,7 +360,7 @@ enum ClawdbotOAuthStore {
private static func saveStorage(_ storage: [String: Any]) throws { private static func saveStorage(_ storage: [String: Any]) throws {
let dir = self.oauthDir() let dir = self.oauthDir()
try FileManager.default.createDirectory( try FileManager().createDirectory(
at: dir, at: dir,
withIntermediateDirectories: true, withIntermediateDirectories: true,
attributes: [.posixPermissions: 0o700]) attributes: [.posixPermissions: 0o700])
@@ -370,7 +370,7 @@ enum ClawdbotOAuthStore {
withJSONObject: storage, withJSONObject: storage,
options: [.prettyPrinted, .sortedKeys]) options: [.prettyPrinted, .sortedKeys])
try data.write(to: url, options: [.atomic]) try data.write(to: url, options: [.atomic])
try FileManager.default.setAttributes([.posixPermissions: 0o600], ofItemAtPath: url.path) try FileManager().setAttributes([.posixPermissions: 0o600], ofItemAtPath: url.path)
} }
} }

View File

@@ -61,7 +61,7 @@ enum CLIInstaller {
} }
private static func installPrefix() -> String { private static func installPrefix() -> String {
FileManager.default.homeDirectoryForCurrentUser FileManager().homeDirectoryForCurrentUser
.appendingPathComponent(".clawdbot") .appendingPathComponent(".clawdbot")
.path .path
} }

View File

@@ -167,20 +167,20 @@ actor CameraCaptureService {
defer { session.stopRunning() } defer { session.stopRunning() }
await Self.warmUpCaptureSession() await Self.warmUpCaptureSession()
let tmpMovURL = FileManager.default.temporaryDirectory let tmpMovURL = FileManager().temporaryDirectory
.appendingPathComponent("clawdbot-camera-\(UUID().uuidString).mov") .appendingPathComponent("clawdbot-camera-\(UUID().uuidString).mov")
defer { try? FileManager.default.removeItem(at: tmpMovURL) } defer { try? FileManager().removeItem(at: tmpMovURL) }
let outputURL: URL = { let outputURL: URL = {
if let outPath, !outPath.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { if let outPath, !outPath.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
return URL(fileURLWithPath: outPath) return URL(fileURLWithPath: outPath)
} }
return FileManager.default.temporaryDirectory return FileManager().temporaryDirectory
.appendingPathComponent("clawdbot-camera-\(UUID().uuidString).mp4") .appendingPathComponent("clawdbot-camera-\(UUID().uuidString).mp4")
}() }()
// Ensure we don't fail exporting due to an existing file. // Ensure we don't fail exporting due to an existing file.
try? FileManager.default.removeItem(at: outputURL) try? FileManager().removeItem(at: outputURL)
let logger = self.logger let logger = self.logger
var delegate: MovieFileDelegate? var delegate: MovieFileDelegate?

View File

@@ -25,7 +25,7 @@ final class CanvasManager {
var defaultAnchorProvider: (() -> NSRect?)? var defaultAnchorProvider: (() -> NSRect?)?
private nonisolated static let canvasRoot: URL = { private nonisolated static let canvasRoot: URL = {
let base = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first! let base = FileManager().urls(for: .applicationSupportDirectory, in: .userDomainMask).first!
return base.appendingPathComponent("Clawdbot/canvas", isDirectory: true) return base.appendingPathComponent("Clawdbot/canvas", isDirectory: true)
}() }()
@@ -83,7 +83,7 @@ final class CanvasManager {
self.panelSessionKey = nil self.panelSessionKey = nil
Self.logger.debug("showDetailed ensure canvas root dir") Self.logger.debug("showDetailed ensure canvas root dir")
try FileManager.default.createDirectory(at: Self.canvasRoot, withIntermediateDirectories: true) try FileManager().createDirectory(at: Self.canvasRoot, withIntermediateDirectories: true)
Self.logger.debug("showDetailed init CanvasWindowController") Self.logger.debug("showDetailed init CanvasWindowController")
let controller = try CanvasWindowController( let controller = try CanvasWindowController(
sessionKey: session, sessionKey: session,
@@ -258,7 +258,7 @@ final class CanvasManager {
// (Avoid treating Canvas routes like "/" as filesystem paths.) // (Avoid treating Canvas routes like "/" as filesystem paths.)
if trimmed.hasPrefix("/") { if trimmed.hasPrefix("/") {
var isDir: ObjCBool = false var isDir: ObjCBool = false
if FileManager.default.fileExists(atPath: trimmed, isDirectory: &isDir), !isDir.boolValue { if FileManager().fileExists(atPath: trimmed, isDirectory: &isDir), !isDir.boolValue {
return URL(fileURLWithPath: trimmed) return URL(fileURLWithPath: trimmed)
} }
} }
@@ -293,7 +293,7 @@ final class CanvasManager {
} }
private static func localStatus(sessionDir: URL, target: String) -> CanvasShowStatus { private static func localStatus(sessionDir: URL, target: String) -> CanvasShowStatus {
let fm = FileManager.default let fm = FileManager()
let trimmed = target.trimmingCharacters(in: .whitespacesAndNewlines) let trimmed = target.trimmingCharacters(in: .whitespacesAndNewlines)
let withoutQuery = trimmed.split(separator: "?", maxSplits: 1, omittingEmptySubsequences: false).first let withoutQuery = trimmed.split(separator: "?", maxSplits: 1, omittingEmptySubsequences: false).first
.map(String.init) ?? trimmed .map(String.init) ?? trimmed
@@ -331,7 +331,7 @@ final class CanvasManager {
} }
private static func indexExists(in dir: URL) -> Bool { private static func indexExists(in dir: URL) -> Bool {
let fm = FileManager.default let fm = FileManager()
let a = dir.appendingPathComponent("index.html", isDirectory: false) let a = dir.appendingPathComponent("index.html", isDirectory: false)
if fm.fileExists(atPath: a.path) { return true } if fm.fileExists(atPath: a.path) { return true }
let b = dir.appendingPathComponent("index.htm", isDirectory: false) let b = dir.appendingPathComponent("index.htm", isDirectory: false)

View File

@@ -69,8 +69,8 @@ final class CanvasSchemeHandler: NSObject, WKURLSchemeHandler {
if path.isEmpty { if path.isEmpty {
let indexA = sessionRoot.appendingPathComponent("index.html", isDirectory: false) let indexA = sessionRoot.appendingPathComponent("index.html", isDirectory: false)
let indexB = sessionRoot.appendingPathComponent("index.htm", isDirectory: false) let indexB = sessionRoot.appendingPathComponent("index.htm", isDirectory: false)
if !FileManager.default.fileExists(atPath: indexA.path), if !FileManager().fileExists(atPath: indexA.path),
!FileManager.default.fileExists(atPath: indexB.path) !FileManager().fileExists(atPath: indexB.path)
{ {
return self.scaffoldPage(sessionRoot: sessionRoot) return self.scaffoldPage(sessionRoot: sessionRoot)
} }
@@ -106,7 +106,7 @@ final class CanvasSchemeHandler: NSObject, WKURLSchemeHandler {
} }
private func resolveFileURL(sessionRoot: URL, requestPath: String) -> URL? { private func resolveFileURL(sessionRoot: URL, requestPath: String) -> URL? {
let fm = FileManager.default let fm = FileManager()
var candidate = sessionRoot.appendingPathComponent(requestPath, isDirectory: false) var candidate = sessionRoot.appendingPathComponent(requestPath, isDirectory: false)
var isDir: ObjCBool = false var isDir: ObjCBool = false
@@ -137,7 +137,7 @@ final class CanvasSchemeHandler: NSObject, WKURLSchemeHandler {
} }
private func resolveIndex(in dir: URL) -> URL? { private func resolveIndex(in dir: URL) -> URL? {
let fm = FileManager.default let fm = FileManager()
let a = dir.appendingPathComponent("index.html", isDirectory: false) let a = dir.appendingPathComponent("index.html", isDirectory: false)
if fm.fileExists(atPath: a.path) { return a } if fm.fileExists(atPath: a.path) { return a }
let b = dir.appendingPathComponent("index.htm", isDirectory: false) let b = dir.appendingPathComponent("index.htm", isDirectory: false)

View File

@@ -32,7 +32,7 @@ final class CanvasWindowController: NSWindowController, WKNavigationDelegate, NS
let safeSessionKey = CanvasWindowController.sanitizeSessionKey(sessionKey) let safeSessionKey = CanvasWindowController.sanitizeSessionKey(sessionKey)
canvasWindowLogger.debug("CanvasWindowController init sanitized session=\(safeSessionKey, privacy: .public)") canvasWindowLogger.debug("CanvasWindowController init sanitized session=\(safeSessionKey, privacy: .public)")
self.sessionDir = root.appendingPathComponent(safeSessionKey, isDirectory: true) self.sessionDir = root.appendingPathComponent(safeSessionKey, isDirectory: true)
try FileManager.default.createDirectory(at: self.sessionDir, withIntermediateDirectories: true) try FileManager().createDirectory(at: self.sessionDir, withIntermediateDirectories: true)
canvasWindowLogger.debug("CanvasWindowController init session dir ready") canvasWindowLogger.debug("CanvasWindowController init session dir ready")
self.schemeHandler = CanvasSchemeHandler(root: root) self.schemeHandler = CanvasSchemeHandler(root: root)
@@ -143,8 +143,8 @@ final class CanvasWindowController: NSWindowController, WKNavigationDelegate, NS
if path == "/" || path.isEmpty { if path == "/" || path.isEmpty {
let indexA = sessionDir.appendingPathComponent("index.html", isDirectory: false) let indexA = sessionDir.appendingPathComponent("index.html", isDirectory: false)
let indexB = sessionDir.appendingPathComponent("index.htm", isDirectory: false) let indexB = sessionDir.appendingPathComponent("index.htm", isDirectory: false)
if !FileManager.default.fileExists(atPath: indexA.path), if !FileManager().fileExists(atPath: indexA.path),
!FileManager.default.fileExists(atPath: indexB.path) !FileManager().fileExists(atPath: indexB.path)
{ {
return return
} }
@@ -233,7 +233,7 @@ final class CanvasWindowController: NSWindowController, WKNavigationDelegate, NS
// (Avoid treating Canvas routes like "/" as filesystem paths.) // (Avoid treating Canvas routes like "/" as filesystem paths.)
if trimmed.hasPrefix("/") { if trimmed.hasPrefix("/") {
var isDir: ObjCBool = false var isDir: ObjCBool = false
if FileManager.default.fileExists(atPath: trimmed, isDirectory: &isDir), !isDir.boolValue { if FileManager().fileExists(atPath: trimmed, isDirectory: &isDir), !isDir.boolValue {
let url = URL(fileURLWithPath: trimmed) let url = URL(fileURLWithPath: trimmed)
canvasWindowLogger.debug("canvas load file \(url.absoluteString, privacy: .public)") canvasWindowLogger.debug("canvas load file \(url.absoluteString, privacy: .public)")
self.loadFile(url) self.loadFile(url)

View File

@@ -18,7 +18,7 @@ enum ClawdbotConfigFile {
static func loadDict() -> [String: Any] { static func loadDict() -> [String: Any] {
let url = self.url() let url = self.url()
guard FileManager.default.fileExists(atPath: url.path) else { return [:] } guard FileManager().fileExists(atPath: url.path) else { return [:] }
do { do {
let data = try Data(contentsOf: url) let data = try Data(contentsOf: url)
guard let root = self.parseConfigData(data) else { guard let root = self.parseConfigData(data) else {
@@ -38,7 +38,7 @@ enum ClawdbotConfigFile {
do { do {
let data = try JSONSerialization.data(withJSONObject: dict, options: [.prettyPrinted, .sortedKeys]) let data = try JSONSerialization.data(withJSONObject: dict, options: [.prettyPrinted, .sortedKeys])
let url = self.url() let url = self.url()
try FileManager.default.createDirectory( try FileManager().createDirectory(
at: url.deletingLastPathComponent(), at: url.deletingLastPathComponent(),
withIntermediateDirectories: true) withIntermediateDirectories: true)
try data.write(to: url, options: [.atomic]) try data.write(to: url, options: [.atomic])

View File

@@ -21,7 +21,7 @@ enum ClawdbotPaths {
if let override = ClawdbotEnv.path(self.stateDirEnv) { if let override = ClawdbotEnv.path(self.stateDirEnv) {
return URL(fileURLWithPath: override, isDirectory: true) return URL(fileURLWithPath: override, isDirectory: true)
} }
return FileManager.default.homeDirectoryForCurrentUser return FileManager().homeDirectoryForCurrentUser
.appendingPathComponent(".clawdbot", isDirectory: true) .appendingPathComponent(".clawdbot", isDirectory: true)
} }

View File

@@ -6,9 +6,9 @@ enum CommandResolver {
static func gatewayEntrypoint(in root: URL) -> String? { static func gatewayEntrypoint(in root: URL) -> String? {
let distEntry = root.appendingPathComponent("dist/index.js").path let distEntry = root.appendingPathComponent("dist/index.js").path
if FileManager.default.isReadableFile(atPath: distEntry) { return distEntry } if FileManager().isReadableFile(atPath: distEntry) { return distEntry }
let binEntry = root.appendingPathComponent("bin/clawdbot.js").path let binEntry = root.appendingPathComponent("bin/clawdbot.js").path
if FileManager.default.isReadableFile(atPath: binEntry) { return binEntry } if FileManager().isReadableFile(atPath: binEntry) { return binEntry }
return nil return nil
} }
@@ -47,16 +47,16 @@ enum CommandResolver {
static func projectRoot() -> URL { static func projectRoot() -> URL {
if let stored = UserDefaults.standard.string(forKey: self.projectRootDefaultsKey), if let stored = UserDefaults.standard.string(forKey: self.projectRootDefaultsKey),
let url = self.expandPath(stored), let url = self.expandPath(stored),
FileManager.default.fileExists(atPath: url.path) FileManager().fileExists(atPath: url.path)
{ {
return url return url
} }
let fallback = FileManager.default.homeDirectoryForCurrentUser let fallback = FileManager().homeDirectoryForCurrentUser
.appendingPathComponent("Projects/clawdbot") .appendingPathComponent("Projects/clawdbot")
if FileManager.default.fileExists(atPath: fallback.path) { if FileManager().fileExists(atPath: fallback.path) {
return fallback return fallback
} }
return FileManager.default.homeDirectoryForCurrentUser return FileManager().homeDirectoryForCurrentUser
} }
static func setProjectRoot(_ path: String) { static func setProjectRoot(_ path: String) {
@@ -70,7 +70,7 @@ enum CommandResolver {
static func preferredPaths() -> [String] { static func preferredPaths() -> [String] {
let current = ProcessInfo.processInfo.environment["PATH"]? let current = ProcessInfo.processInfo.environment["PATH"]?
.split(separator: ":").map(String.init) ?? [] .split(separator: ":").map(String.init) ?? []
let home = FileManager.default.homeDirectoryForCurrentUser let home = FileManager().homeDirectoryForCurrentUser
let projectRoot = self.projectRoot() let projectRoot = self.projectRoot()
return self.preferredPaths(home: home, current: current, projectRoot: projectRoot) return self.preferredPaths(home: home, current: current, projectRoot: projectRoot)
} }
@@ -99,10 +99,10 @@ enum CommandResolver {
let bin = base.appendingPathComponent("bin") let bin = base.appendingPathComponent("bin")
let nodeBin = base.appendingPathComponent("tools/node/bin") let nodeBin = base.appendingPathComponent("tools/node/bin")
var paths: [String] = [] var paths: [String] = []
if FileManager.default.fileExists(atPath: bin.path) { if FileManager().fileExists(atPath: bin.path) {
paths.append(bin.path) paths.append(bin.path)
} }
if FileManager.default.fileExists(atPath: nodeBin.path) { if FileManager().fileExists(atPath: nodeBin.path) {
paths.append(nodeBin.path) paths.append(nodeBin.path)
} }
return paths return paths
@@ -113,13 +113,13 @@ enum CommandResolver {
// Volta // Volta
let volta = home.appendingPathComponent(".volta/bin") let volta = home.appendingPathComponent(".volta/bin")
if FileManager.default.fileExists(atPath: volta.path) { if FileManager().fileExists(atPath: volta.path) {
bins.append(volta.path) bins.append(volta.path)
} }
// asdf // asdf
let asdf = home.appendingPathComponent(".asdf/shims") let asdf = home.appendingPathComponent(".asdf/shims")
if FileManager.default.fileExists(atPath: asdf.path) { if FileManager().fileExists(atPath: asdf.path) {
bins.append(asdf.path) bins.append(asdf.path)
} }
@@ -137,10 +137,10 @@ enum CommandResolver {
} }
private static func versionedNodeBinPaths(base: URL, suffix: String) -> [String] { private static func versionedNodeBinPaths(base: URL, suffix: String) -> [String] {
guard FileManager.default.fileExists(atPath: base.path) else { return [] } guard FileManager().fileExists(atPath: base.path) else { return [] }
let entries: [String] let entries: [String]
do { do {
entries = try FileManager.default.contentsOfDirectory(atPath: base.path) entries = try FileManager().contentsOfDirectory(atPath: base.path)
} catch { } catch {
return [] return []
} }
@@ -167,7 +167,7 @@ enum CommandResolver {
for entry in sorted { for entry in sorted {
let binDir = base.appendingPathComponent(entry).appendingPathComponent(suffix) let binDir = base.appendingPathComponent(entry).appendingPathComponent(suffix)
let node = binDir.appendingPathComponent("node") let node = binDir.appendingPathComponent("node")
if FileManager.default.isExecutableFile(atPath: node.path) { if FileManager().isExecutableFile(atPath: node.path) {
paths.append(binDir.path) paths.append(binDir.path)
} }
} }
@@ -177,7 +177,7 @@ enum CommandResolver {
static func findExecutable(named name: String, searchPaths: [String]? = nil) -> String? { static func findExecutable(named name: String, searchPaths: [String]? = nil) -> String? {
for dir in searchPaths ?? self.preferredPaths() { for dir in searchPaths ?? self.preferredPaths() {
let candidate = (dir as NSString).appendingPathComponent(name) let candidate = (dir as NSString).appendingPathComponent(name)
if FileManager.default.isExecutableFile(atPath: candidate) { if FileManager().isExecutableFile(atPath: candidate) {
return candidate return candidate
} }
} }
@@ -191,12 +191,12 @@ enum CommandResolver {
static func projectClawdbotExecutable(projectRoot: URL? = nil) -> String? { static func projectClawdbotExecutable(projectRoot: URL? = nil) -> String? {
let root = projectRoot ?? self.projectRoot() let root = projectRoot ?? self.projectRoot()
let candidate = root.appendingPathComponent("node_modules/.bin").appendingPathComponent(self.helperName).path let candidate = root.appendingPathComponent("node_modules/.bin").appendingPathComponent(self.helperName).path
return FileManager.default.isExecutableFile(atPath: candidate) ? candidate : nil return FileManager().isExecutableFile(atPath: candidate) ? candidate : nil
} }
static func nodeCliPath() -> String? { static func nodeCliPath() -> String? {
let candidate = self.projectRoot().appendingPathComponent("bin/clawdbot.js").path let candidate = self.projectRoot().appendingPathComponent("bin/clawdbot.js").path
return FileManager.default.isReadableFile(atPath: candidate) ? candidate : nil return FileManager().isReadableFile(atPath: candidate) ? candidate : nil
} }
static func hasAnyClawdbotInvoker(searchPaths: [String]? = nil) -> Bool { static func hasAnyClawdbotInvoker(searchPaths: [String]? = nil) -> Bool {
@@ -459,7 +459,7 @@ enum CommandResolver {
private static func expandPath(_ path: String) -> URL? { private static func expandPath(_ path: String) -> URL? {
var expanded = path var expanded = path
if expanded.hasPrefix("~") { if expanded.hasPrefix("~") {
let home = FileManager.default.homeDirectoryForCurrentUser.path let home = FileManager().homeDirectoryForCurrentUser.path
expanded.replaceSubrange(expanded.startIndex...expanded.startIndex, with: home) expanded.replaceSubrange(expanded.startIndex...expanded.startIndex, with: home)
} }
return URL(fileURLWithPath: expanded) return URL(fileURLWithPath: expanded)

View File

@@ -26,7 +26,7 @@ enum DebugActions {
static func openLog() { static func openLog() {
let path = self.pinoLogPath() let path = self.pinoLogPath()
let url = URL(fileURLWithPath: path) let url = URL(fileURLWithPath: path)
guard FileManager.default.fileExists(atPath: path) else { guard FileManager().fileExists(atPath: path) else {
let alert = NSAlert() let alert = NSAlert()
alert.messageText = "Log file not found" alert.messageText = "Log file not found"
alert.informativeText = path alert.informativeText = path
@@ -38,7 +38,7 @@ enum DebugActions {
@MainActor @MainActor
static func openConfigFolder() { static func openConfigFolder() {
let url = FileManager.default let url = FileManager()
.homeDirectoryForCurrentUser .homeDirectoryForCurrentUser
.appendingPathComponent(".clawdbot", isDirectory: true) .appendingPathComponent(".clawdbot", isDirectory: true)
NSWorkspace.shared.activateFileViewerSelecting([url]) NSWorkspace.shared.activateFileViewerSelecting([url])
@@ -55,7 +55,7 @@ enum DebugActions {
} }
let path = self.resolveSessionStorePath() let path = self.resolveSessionStorePath()
let url = URL(fileURLWithPath: path) let url = URL(fileURLWithPath: path)
if FileManager.default.fileExists(atPath: path) { if FileManager().fileExists(atPath: path) {
NSWorkspace.shared.activateFileViewerSelecting([url]) NSWorkspace.shared.activateFileViewerSelecting([url])
} else { } else {
NSWorkspace.shared.open(url.deletingLastPathComponent()) NSWorkspace.shared.open(url.deletingLastPathComponent())
@@ -195,7 +195,7 @@ enum DebugActions {
@MainActor @MainActor
private static func resolveSessionStorePath() -> String { private static func resolveSessionStorePath() -> String {
let defaultPath = SessionLoader.defaultStorePath let defaultPath = SessionLoader.defaultStorePath
let configURL = FileManager.default.homeDirectoryForCurrentUser let configURL = FileManager().homeDirectoryForCurrentUser
.appendingPathComponent(".clawdbot/clawdbot.json") .appendingPathComponent(".clawdbot/clawdbot.json")
guard guard
let data = try? Data(contentsOf: configURL), let data = try? Data(contentsOf: configURL),

View File

@@ -354,7 +354,7 @@ struct DebugSettings: View {
Button("Save") { self.saveRelayRoot() } Button("Save") { self.saveRelayRoot() }
.buttonStyle(.borderedProminent) .buttonStyle(.borderedProminent)
Button("Reset") { Button("Reset") {
let def = FileManager.default.homeDirectoryForCurrentUser let def = FileManager().homeDirectoryForCurrentUser
.appendingPathComponent("Projects/clawdbot").path .appendingPathComponent("Projects/clawdbot").path
self.gatewayRootInput = def self.gatewayRootInput = def
self.saveRelayRoot() self.saveRelayRoot()
@@ -743,7 +743,7 @@ struct DebugSettings: View {
do { do {
let data = try JSONSerialization.data(withJSONObject: root, options: [.prettyPrinted, .sortedKeys]) let data = try JSONSerialization.data(withJSONObject: root, options: [.prettyPrinted, .sortedKeys])
try FileManager.default.createDirectory( try FileManager().createDirectory(
at: url.deletingLastPathComponent(), at: url.deletingLastPathComponent(),
withIntermediateDirectories: true) withIntermediateDirectories: true)
try data.write(to: url, options: [.atomic]) try data.write(to: url, options: [.atomic])
@@ -776,7 +776,7 @@ struct DebugSettings: View {
} }
private func configURL() -> URL { private func configURL() -> URL {
FileManager.default.homeDirectoryForCurrentUser FileManager().homeDirectoryForCurrentUser
.appendingPathComponent(".clawdbot") .appendingPathComponent(".clawdbot")
.appendingPathComponent("clawdbot.json") .appendingPathComponent("clawdbot.json")
} }

View File

@@ -20,8 +20,8 @@ actor DiagnosticsFileLog {
} }
nonisolated static func logDirectoryURL() -> URL { nonisolated static func logDirectoryURL() -> URL {
let library = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first let library = FileManager().urls(for: .libraryDirectory, in: .userDomainMask).first
?? FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent("Library", isDirectory: true) ?? FileManager().homeDirectoryForCurrentUser.appendingPathComponent("Library", isDirectory: true)
return library return library
.appendingPathComponent("Logs", isDirectory: true) .appendingPathComponent("Logs", isDirectory: true)
.appendingPathComponent("Clawdbot", isDirectory: true) .appendingPathComponent("Clawdbot", isDirectory: true)
@@ -43,7 +43,7 @@ actor DiagnosticsFileLog {
} }
func clear() throws { func clear() throws {
let fm = FileManager.default let fm = FileManager()
let base = Self.logFileURL() let base = Self.logFileURL()
if fm.fileExists(atPath: base.path) { if fm.fileExists(atPath: base.path) {
try fm.removeItem(at: base) try fm.removeItem(at: base)
@@ -67,7 +67,7 @@ actor DiagnosticsFileLog {
} }
private func ensureDirectory() throws { private func ensureDirectory() throws {
try FileManager.default.createDirectory( try FileManager().createDirectory(
at: Self.logDirectoryURL(), at: Self.logDirectoryURL(),
withIntermediateDirectories: true) withIntermediateDirectories: true)
} }
@@ -79,7 +79,7 @@ actor DiagnosticsFileLog {
line.append(data) line.append(data)
line.append(0x0A) // newline line.append(0x0A) // newline
let fm = FileManager.default let fm = FileManager()
if !fm.fileExists(atPath: url.path) { if !fm.fileExists(atPath: url.path) {
fm.createFile(atPath: url.path, contents: nil) fm.createFile(atPath: url.path, contents: nil)
} }
@@ -92,13 +92,13 @@ actor DiagnosticsFileLog {
private func rotateIfNeeded() throws { private func rotateIfNeeded() throws {
let url = Self.logFileURL() let url = Self.logFileURL()
guard let attrs = try? FileManager.default.attributesOfItem(atPath: url.path), guard let attrs = try? FileManager().attributesOfItem(atPath: url.path),
let size = attrs[.size] as? NSNumber let size = attrs[.size] as? NSNumber
else { return } else { return }
if size.int64Value < self.maxBytes { return } if size.int64Value < self.maxBytes { return }
let fm = FileManager.default let fm = FileManager()
let oldest = self.rotatedURL(index: self.maxBackups) let oldest = self.rotatedURL(index: self.maxBackups)
if fm.fileExists(atPath: oldest.path) { if fm.fileExists(atPath: oldest.path) {

View File

@@ -176,7 +176,7 @@ enum ExecApprovalsStore {
static func readSnapshot() -> ExecApprovalsSnapshot { static func readSnapshot() -> ExecApprovalsSnapshot {
let url = self.fileURL() let url = self.fileURL()
guard FileManager.default.fileExists(atPath: url.path) else { guard FileManager().fileExists(atPath: url.path) else {
return ExecApprovalsSnapshot( return ExecApprovalsSnapshot(
path: url.path, path: url.path,
exists: false, exists: false,
@@ -216,7 +216,7 @@ enum ExecApprovalsStore {
static func loadFile() -> ExecApprovalsFile { static func loadFile() -> ExecApprovalsFile {
let url = self.fileURL() let url = self.fileURL()
guard FileManager.default.fileExists(atPath: url.path) else { guard FileManager().fileExists(atPath: url.path) else {
return ExecApprovalsFile(version: 1, socket: nil, defaults: nil, agents: [:]) return ExecApprovalsFile(version: 1, socket: nil, defaults: nil, agents: [:])
} }
do { do {
@@ -238,11 +238,11 @@ enum ExecApprovalsStore {
encoder.outputFormatting = [.prettyPrinted, .sortedKeys] encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
let data = try encoder.encode(file) let data = try encoder.encode(file)
let url = self.fileURL() let url = self.fileURL()
try FileManager.default.createDirectory( try FileManager().createDirectory(
at: url.deletingLastPathComponent(), at: url.deletingLastPathComponent(),
withIntermediateDirectories: true) withIntermediateDirectories: true)
try data.write(to: url, options: [.atomic]) try data.write(to: url, options: [.atomic])
try? FileManager.default.setAttributes([.posixPermissions: 0o600], ofItemAtPath: url.path) try? FileManager().setAttributes([.posixPermissions: 0o600], ofItemAtPath: url.path)
} catch { } catch {
self.logger.error("exec approvals save failed: \(error.localizedDescription, privacy: .public)") self.logger.error("exec approvals save failed: \(error.localizedDescription, privacy: .public)")
} }
@@ -442,11 +442,11 @@ enum ExecApprovalsStore {
private static func expandPath(_ raw: String) -> String { private static func expandPath(_ raw: String) -> String {
let trimmed = raw.trimmingCharacters(in: .whitespacesAndNewlines) let trimmed = raw.trimmingCharacters(in: .whitespacesAndNewlines)
if trimmed == "~" { if trimmed == "~" {
return FileManager.default.homeDirectoryForCurrentUser.path return FileManager().homeDirectoryForCurrentUser.path
} }
if trimmed.hasPrefix("~/") { if trimmed.hasPrefix("~/") {
let suffix = trimmed.dropFirst(2) let suffix = trimmed.dropFirst(2)
return FileManager.default.homeDirectoryForCurrentUser return FileManager().homeDirectoryForCurrentUser
.appendingPathComponent(String(suffix)).path .appendingPathComponent(String(suffix)).path
} }
return trimmed return trimmed
@@ -497,7 +497,7 @@ struct ExecCommandResolution: Sendable {
return expanded return expanded
} }
let base = cwd?.trimmingCharacters(in: .whitespacesAndNewlines) let base = cwd?.trimmingCharacters(in: .whitespacesAndNewlines)
let root = (base?.isEmpty == false) ? base! : FileManager.default.currentDirectoryPath let root = (base?.isEmpty == false) ? base! : FileManager().currentDirectoryPath
return URL(fileURLWithPath: root).appendingPathComponent(expanded).path return URL(fileURLWithPath: root).appendingPathComponent(expanded).path
} }
let searchPaths = self.searchPaths(from: env) let searchPaths = self.searchPaths(from: env)

View File

@@ -155,7 +155,8 @@ actor GatewayEndpointStore {
let trimmed = raw.trimmingCharacters(in: .whitespacesAndNewlines) let trimmed = raw.trimmingCharacters(in: .whitespacesAndNewlines)
if !trimmed.isEmpty { if !trimmed.isEmpty {
if let configToken = self.resolveConfigToken(isRemote: isRemote, root: root), if let configToken = self.resolveConfigToken(isRemote: isRemote, root: root),
!configToken.isEmpty !configToken.isEmpty,
configToken != trimmed
{ {
self.warnEnvOverrideOnce( self.warnEnvOverrideOnce(
kind: .token, kind: .token,
@@ -164,32 +165,19 @@ actor GatewayEndpointStore {
} }
return trimmed return trimmed
} }
if isRemote {
if let gateway = root["gateway"] as? [String: Any], if let configToken = self.resolveConfigToken(isRemote: isRemote, root: root),
let remote = gateway["remote"] as? [String: Any], !configToken.isEmpty
let token = remote["token"] as? String
{
let value = token.trimmingCharacters(in: .whitespacesAndNewlines)
if !value.isEmpty {
return value
}
}
return nil
}
if let gateway = root["gateway"] as? [String: Any],
let auth = gateway["auth"] as? [String: Any],
let token = auth["token"] as? String
{ {
let value = token.trimmingCharacters(in: .whitespacesAndNewlines) return configToken
if !value.isEmpty {
return value
}
} }
if let token = launchdSnapshot?.token?.trimmingCharacters(in: .whitespacesAndNewlines), if let token = launchdSnapshot?.token?.trimmingCharacters(in: .whitespacesAndNewlines),
!token.isEmpty !token.isEmpty
{ {
return token return token
} }
return nil return nil
} }

View File

@@ -5,7 +5,7 @@ enum GatewayLaunchAgentManager {
private static let disableLaunchAgentMarker = ".clawdbot/disable-launchagent" private static let disableLaunchAgentMarker = ".clawdbot/disable-launchagent"
private static var plistURL: URL { private static var plistURL: URL {
FileManager.default.homeDirectoryForCurrentUser FileManager().homeDirectoryForCurrentUser
.appendingPathComponent("Library/LaunchAgents/\(gatewayLaunchdLabel).plist") .appendingPathComponent("Library/LaunchAgents/\(gatewayLaunchdLabel).plist")
} }
@@ -67,9 +67,9 @@ enum GatewayLaunchAgentManager {
extension GatewayLaunchAgentManager { extension GatewayLaunchAgentManager {
private static func isLaunchAgentWriteDisabled() -> Bool { private static func isLaunchAgentWriteDisabled() -> Bool {
let marker = FileManager.default.homeDirectoryForCurrentUser let marker = FileManager().homeDirectoryForCurrentUser
.appendingPathComponent(self.disableLaunchAgentMarker) .appendingPathComponent(self.disableLaunchAgentMarker)
return FileManager.default.fileExists(atPath: marker.path) return FileManager().fileExists(atPath: marker.path)
} }
private static func readDaemonLoaded() async -> Bool? { private static func readDaemonLoaded() async -> Bool? {

View File

@@ -365,7 +365,7 @@ final class GatewayProcessManager {
func clearLog() { func clearLog() {
self.log = "" self.log = ""
try? FileManager.default.removeItem(atPath: GatewayLaunchAgentManager.launchdGatewayLogPath()) try? FileManager().removeItem(atPath: GatewayLaunchAgentManager.launchdGatewayLogPath())
self.logger.debug("gateway log cleared") self.logger.debug("gateway log cleared")
} }
@@ -378,7 +378,7 @@ final class GatewayProcessManager {
} }
private nonisolated static func readGatewayLog(path: String, limit: Int) -> String { private nonisolated static func readGatewayLog(path: String, limit: Int) -> String {
guard FileManager.default.fileExists(atPath: path) else { return "" } guard FileManager().fileExists(atPath: path) else { return "" }
guard let data = try? Data(contentsOf: URL(fileURLWithPath: path)) else { return "" } guard let data = try? Data(contentsOf: URL(fileURLWithPath: path)) else { return "" }
let text = String(data: data, encoding: .utf8) ?? "" let text = String(data: data, encoding: .utf8) ?? ""
if text.count <= limit { return text } if text.count <= limit { return text }

View File

@@ -3,17 +3,17 @@ import Foundation
enum LaunchAgentManager { enum LaunchAgentManager {
private static let legacyLaunchdLabel = "com.steipete.clawdbot" private static let legacyLaunchdLabel = "com.steipete.clawdbot"
private static var plistURL: URL { private static var plistURL: URL {
FileManager.default.homeDirectoryForCurrentUser FileManager().homeDirectoryForCurrentUser
.appendingPathComponent("Library/LaunchAgents/com.clawdbot.mac.plist") .appendingPathComponent("Library/LaunchAgents/com.clawdbot.mac.plist")
} }
private static var legacyPlistURL: URL { private static var legacyPlistURL: URL {
FileManager.default.homeDirectoryForCurrentUser FileManager().homeDirectoryForCurrentUser
.appendingPathComponent("Library/LaunchAgents/\(legacyLaunchdLabel).plist") .appendingPathComponent("Library/LaunchAgents/\(legacyLaunchdLabel).plist")
} }
static func status() async -> Bool { static func status() async -> Bool {
guard FileManager.default.fileExists(atPath: self.plistURL.path) else { return false } guard FileManager().fileExists(atPath: self.plistURL.path) else { return false }
let result = await self.runLaunchctl(["print", "gui/\(getuid())/\(launchdLabel)"]) let result = await self.runLaunchctl(["print", "gui/\(getuid())/\(launchdLabel)"])
return result == 0 return result == 0
} }
@@ -21,7 +21,7 @@ enum LaunchAgentManager {
static func set(enabled: Bool, bundlePath: String) async { static func set(enabled: Bool, bundlePath: String) async {
if enabled { if enabled {
_ = await self.runLaunchctl(["bootout", "gui/\(getuid())/\(self.legacyLaunchdLabel)"]) _ = await self.runLaunchctl(["bootout", "gui/\(getuid())/\(self.legacyLaunchdLabel)"])
try? FileManager.default.removeItem(at: self.legacyPlistURL) try? FileManager().removeItem(at: self.legacyPlistURL)
self.writePlist(bundlePath: bundlePath) self.writePlist(bundlePath: bundlePath)
_ = await self.runLaunchctl(["bootout", "gui/\(getuid())/\(launchdLabel)"]) _ = await self.runLaunchctl(["bootout", "gui/\(getuid())/\(launchdLabel)"])
_ = await self.runLaunchctl(["bootstrap", "gui/\(getuid())", self.plistURL.path]) _ = await self.runLaunchctl(["bootstrap", "gui/\(getuid())", self.plistURL.path])
@@ -29,7 +29,7 @@ enum LaunchAgentManager {
} else { } else {
// Disable autostart going forward but leave the current app running. // Disable autostart going forward but leave the current app running.
// bootout would terminate the launchd job immediately (and crash the app if launched via agent). // bootout would terminate the launchd job immediately (and crash the app if launched via agent).
try? FileManager.default.removeItem(at: self.plistURL) try? FileManager().removeItem(at: self.plistURL)
} }
} }
@@ -46,7 +46,7 @@ enum LaunchAgentManager {
<string>\(bundlePath)/Contents/MacOS/Clawdbot</string> <string>\(bundlePath)/Contents/MacOS/Clawdbot</string>
</array> </array>
<key>WorkingDirectory</key> <key>WorkingDirectory</key>
<string>\(FileManager.default.homeDirectoryForCurrentUser.path)</string> <string>\(FileManager().homeDirectoryForCurrentUser.path)</string>
<key>RunAtLoad</key> <key>RunAtLoad</key>
<true/> <true/>
<key>KeepAlive</key> <key>KeepAlive</key>

View File

@@ -18,7 +18,7 @@ enum LogLocator {
} }
private static func ensureLogDirExists() { private static func ensureLogDirExists() {
try? FileManager.default.createDirectory(at: self.logDir, withIntermediateDirectories: true) try? FileManager().createDirectory(at: self.logDir, withIntermediateDirectories: true)
} }
private static func modificationDate(for url: URL) -> Date { private static func modificationDate(for url: URL) -> Date {
@@ -28,7 +28,7 @@ enum LogLocator {
/// Returns the newest log file under /tmp/clawdbot/ (rolling or stdout), or nil if none exist. /// Returns the newest log file under /tmp/clawdbot/ (rolling or stdout), or nil if none exist.
static func bestLogFile() -> URL? { static func bestLogFile() -> URL? {
self.ensureLogDirExists() self.ensureLogDirExists()
let fm = FileManager.default let fm = FileManager()
let files = (try? fm.contentsOfDirectory( let files = (try? fm.contentsOfDirectory(
at: self.logDir, at: self.logDir,
includingPropertiesForKeys: [.contentModificationDateKey], includingPropertiesForKeys: [.contentModificationDateKey],

View File

@@ -2,7 +2,7 @@ import Foundation
import JavaScriptCore import JavaScriptCore
enum ModelCatalogLoader { enum ModelCatalogLoader {
static let defaultPath: String = FileManager.default.homeDirectoryForCurrentUser static let defaultPath: String = FileManager().homeDirectoryForCurrentUser
.appendingPathComponent("Projects/pi-mono/packages/ai/src/models.generated.ts").path .appendingPathComponent("Projects/pi-mono/packages/ai/src/models.generated.ts").path
private static let logger = Logger(subsystem: "com.clawdbot", category: "models") private static let logger = Logger(subsystem: "com.clawdbot", category: "models")

View File

@@ -133,7 +133,7 @@ actor MacNodeRuntime {
let sessionKey = self.mainSessionKey let sessionKey = self.mainSessionKey
let path = try await CanvasManager.shared.snapshot(sessionKey: sessionKey, outPath: nil) let path = try await CanvasManager.shared.snapshot(sessionKey: sessionKey, outPath: nil)
defer { try? FileManager.default.removeItem(atPath: path) } defer { try? FileManager().removeItem(atPath: path) }
let data = try Data(contentsOf: URL(fileURLWithPath: path)) let data = try Data(contentsOf: URL(fileURLWithPath: path))
guard let image = NSImage(data: data) else { guard let image = NSImage(data: data) else {
return Self.errorResponse(req, code: .unavailable, message: "canvas snapshot decode failed") return Self.errorResponse(req, code: .unavailable, message: "canvas snapshot decode failed")
@@ -206,7 +206,7 @@ actor MacNodeRuntime {
includeAudio: params.includeAudio ?? true, includeAudio: params.includeAudio ?? true,
deviceId: params.deviceId, deviceId: params.deviceId,
outPath: nil) outPath: nil)
defer { try? FileManager.default.removeItem(atPath: res.path) } defer { try? FileManager().removeItem(atPath: res.path) }
let data = try Data(contentsOf: URL(fileURLWithPath: res.path)) let data = try Data(contentsOf: URL(fileURLWithPath: res.path))
struct ClipPayload: Encodable { struct ClipPayload: Encodable {
var format: String var format: String
@@ -312,7 +312,7 @@ actor MacNodeRuntime {
fps: params.fps, fps: params.fps,
includeAudio: params.includeAudio, includeAudio: params.includeAudio,
outPath: nil) outPath: nil)
defer { try? FileManager.default.removeItem(atPath: res.path) } defer { try? FileManager().removeItem(atPath: res.path) }
let data = try Data(contentsOf: URL(fileURLWithPath: res.path)) let data = try Data(contentsOf: URL(fileURLWithPath: res.path))
struct ScreenPayload: Encodable { struct ScreenPayload: Encodable {
var format: String var format: String

View File

@@ -24,7 +24,7 @@ actor PortGuardian {
private var records: [Record] = [] private var records: [Record] = []
private let logger = Logger(subsystem: "com.clawdbot", category: "portguard") private let logger = Logger(subsystem: "com.clawdbot", category: "portguard")
private nonisolated static let appSupportDir: URL = { private nonisolated static let appSupportDir: URL = {
let base = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first! let base = FileManager().urls(for: .applicationSupportDirectory, in: .userDomainMask).first!
return base.appendingPathComponent("Clawdbot", isDirectory: true) return base.appendingPathComponent("Clawdbot", isDirectory: true)
}() }()
@@ -71,7 +71,7 @@ actor PortGuardian {
} }
func record(port: Int, pid: Int32, command: String, mode: AppState.ConnectionMode) async { func record(port: Int, pid: Int32, command: String, mode: AppState.ConnectionMode) async {
try? FileManager.default.createDirectory(at: Self.appSupportDir, withIntermediateDirectories: true) try? FileManager().createDirectory(at: Self.appSupportDir, withIntermediateDirectories: true)
self.records.removeAll { $0.pid == pid } self.records.removeAll { $0.pid == pid }
self.records.append( self.records.append(
Record( Record(

View File

@@ -111,7 +111,7 @@ enum RuntimeLocator {
// MARK: - Internals // MARK: - Internals
private static func findExecutable(named name: String, searchPaths: [String]) -> String? { private static func findExecutable(named name: String, searchPaths: [String]) -> String? {
let fm = FileManager.default let fm = FileManager()
for dir in searchPaths { for dir in searchPaths {
let candidate = (dir as NSString).appendingPathComponent(name) let candidate = (dir as NSString).appendingPathComponent(name)
if fm.isExecutableFile(atPath: candidate) { if fm.isExecutableFile(atPath: candidate) {

View File

@@ -42,10 +42,10 @@ final class ScreenRecordService {
if let outPath, !outPath.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { if let outPath, !outPath.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
return URL(fileURLWithPath: outPath) return URL(fileURLWithPath: outPath)
} }
return FileManager.default.temporaryDirectory return FileManager().temporaryDirectory
.appendingPathComponent("clawdbot-screen-record-\(UUID().uuidString).mp4") .appendingPathComponent("clawdbot-screen-record-\(UUID().uuidString).mp4")
}() }()
try? FileManager.default.removeItem(at: outURL) try? FileManager().removeItem(at: outURL)
let content = try await SCShareableContent.current let content = try await SCShareableContent.current
let displays = content.displays.sorted { $0.displayID < $1.displayID } let displays = content.displays.sorted { $0.displayID < $1.displayID }

View File

@@ -66,12 +66,12 @@ enum SessionActions {
let dir = URL(fileURLWithPath: storePath).deletingLastPathComponent() let dir = URL(fileURLWithPath: storePath).deletingLastPathComponent()
urls.append(dir.appendingPathComponent("\(sessionId).jsonl")) urls.append(dir.appendingPathComponent("\(sessionId).jsonl"))
} }
let home = FileManager.default.homeDirectoryForCurrentUser let home = FileManager().homeDirectoryForCurrentUser
urls.append(home.appendingPathComponent(".clawdbot/sessions/\(sessionId).jsonl")) urls.append(home.appendingPathComponent(".clawdbot/sessions/\(sessionId).jsonl"))
return urls return urls
}() }()
let existing = candidates.first(where: { FileManager.default.fileExists(atPath: $0.path) }) let existing = candidates.first(where: { FileManager().fileExists(atPath: $0.path) })
guard let url = existing else { guard let url = existing else {
let alert = NSAlert() let alert = NSAlert()
alert.messageText = "Session log not found" alert.messageText = "Session log not found"

View File

@@ -246,7 +246,7 @@ enum SessionLoader {
static let fallbackContextTokens = 200_000 static let fallbackContextTokens = 200_000
static let defaultStorePath = standardize( static let defaultStorePath = standardize(
FileManager.default.homeDirectoryForCurrentUser FileManager().homeDirectoryForCurrentUser
.appendingPathComponent(".clawdbot/sessions/sessions.json").path) .appendingPathComponent(".clawdbot/sessions/sessions.json").path)
static func loadSnapshot( static func loadSnapshot(

View File

@@ -44,7 +44,7 @@ enum SoundEffectCatalog {
] ]
private static let searchRoots: [URL] = [ private static let searchRoots: [URL] = [
FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent("Library/Sounds"), FileManager().homeDirectoryForCurrentUser.appendingPathComponent("Library/Sounds"),
URL(fileURLWithPath: "/Library/Sounds"), URL(fileURLWithPath: "/Library/Sounds"),
URL(fileURLWithPath: "/System/Applications/Mail.app/Contents/Resources"), // Mail swoosh URL(fileURLWithPath: "/System/Applications/Mail.app/Contents/Resources"), // Mail swoosh
URL(fileURLWithPath: "/System/Library/Sounds"), URL(fileURLWithPath: "/System/Library/Sounds"),
@@ -53,7 +53,7 @@ enum SoundEffectCatalog {
private static let discoveredSoundMap: [String: URL] = { private static let discoveredSoundMap: [String: URL] = {
var map: [String: URL] = [:] var map: [String: URL] = [:]
for root in Self.searchRoots { for root in Self.searchRoots {
guard let contents = try? FileManager.default.contentsOfDirectory( guard let contents = try? FileManager().contentsOfDirectory(
at: root, at: root,
includingPropertiesForKeys: nil, includingPropertiesForKeys: nil,
options: [.skipsHiddenFiles]) options: [.skipsHiddenFiles])

View File

@@ -53,7 +53,7 @@ final class TailscaleService {
#endif #endif
func checkAppInstallation() -> Bool { func checkAppInstallation() -> Bool {
let installed = FileManager.default.fileExists(atPath: "/Applications/Tailscale.app") let installed = FileManager().fileExists(atPath: "/Applications/Tailscale.app")
self.logger.info("Tailscale app installed: \(installed)") self.logger.info("Tailscale app installed: \(installed)")
return installed return installed
} }

View File

@@ -37,7 +37,7 @@ final class VoicePushToTalkHotkey: @unchecked Sendable {
} }
private func startMonitoring() { private func startMonitoring() {
assert(Thread.isMainThread) // assert(Thread.isMainThread) - Removed for Swift 6
guard self.globalMonitor == nil, self.localMonitor == nil else { return } guard self.globalMonitor == nil, self.localMonitor == nil else { return }
// Listen-only global monitor; we rely on Input Monitoring permission to receive events. // Listen-only global monitor; we rely on Input Monitoring permission to receive events.
self.globalMonitor = NSEvent.addGlobalMonitorForEvents(matching: .flagsChanged) { [weak self] event in self.globalMonitor = NSEvent.addGlobalMonitorForEvents(matching: .flagsChanged) { [weak self] event in
@@ -55,7 +55,7 @@ final class VoicePushToTalkHotkey: @unchecked Sendable {
} }
private func stopMonitoring() { private func stopMonitoring() {
assert(Thread.isMainThread) // assert(Thread.isMainThread) - Removed for Swift 6
if let globalMonitor { if let globalMonitor {
NSEvent.removeMonitor(globalMonitor) NSEvent.removeMonitor(globalMonitor)
self.globalMonitor = nil self.globalMonitor = nil
@@ -75,15 +75,11 @@ final class VoicePushToTalkHotkey: @unchecked Sendable {
} }
private func withMainThread(_ block: @escaping @Sendable () -> Void) { private func withMainThread(_ block: @escaping @Sendable () -> Void) {
if Thread.isMainThread { DispatchQueue.main.async(execute: block)
block()
} else {
DispatchQueue.main.async(execute: block)
}
} }
private func updateModifierState(keyCode: UInt16, modifierFlags: NSEvent.ModifierFlags) { private func updateModifierState(keyCode: UInt16, modifierFlags: NSEvent.ModifierFlags) {
assert(Thread.isMainThread) // assert(Thread.isMainThread) - Removed for Swift 6
// Right Option (keyCode 61) acts as a hold-to-talk modifier. // Right Option (keyCode 61) acts as a hold-to-talk modifier.
if keyCode == 61 { if keyCode == 61 {
self.optionDown = modifierFlags.contains(.option) self.optionDown = modifierFlags.contains(.option)

View File

@@ -409,7 +409,7 @@ extension Request: Codable {
// Shared transport settings // Shared transport settings
public let controlSocketPath = public let controlSocketPath =
FileManager.default FileManager()
.homeDirectoryForCurrentUser .homeDirectoryForCurrentUser
.appendingPathComponent("Library/Application Support/clawdbot/control.sock") .appendingPathComponent("Library/Application Support/clawdbot/control.sock")
.path .path

View File

@@ -187,7 +187,7 @@ private func resolvedPassword(opts: WizardCliOptions, config: GatewayConfig) ->
} }
private func loadGatewayConfig() -> GatewayConfig { private func loadGatewayConfig() -> GatewayConfig {
let url = FileManager.default.homeDirectoryForCurrentUser let url = FileManager().homeDirectoryForCurrentUser
.appendingPathComponent(".clawdbot") .appendingPathComponent(".clawdbot")
.appendingPathComponent("clawdbot.json") .appendingPathComponent("clawdbot.json")
guard let data = try? Data(contentsOf: url) else { return GatewayConfig() } guard let data = try? Data(contentsOf: url) else { return GatewayConfig() }

View File

@@ -6,7 +6,7 @@ import Testing
struct AgentWorkspaceTests { struct AgentWorkspaceTests {
@Test @Test
func displayPathUsesTildeForHome() { func displayPathUsesTildeForHome() {
let home = FileManager.default.homeDirectoryForCurrentUser let home = FileManager().homeDirectoryForCurrentUser
#expect(AgentWorkspace.displayPath(for: home) == "~") #expect(AgentWorkspace.displayPath(for: home) == "~")
let inside = home.appendingPathComponent("Projects", isDirectory: true) let inside = home.appendingPathComponent("Projects", isDirectory: true)
@@ -28,12 +28,12 @@ struct AgentWorkspaceTests {
@Test @Test
func bootstrapCreatesAgentsFileWhenMissing() throws { func bootstrapCreatesAgentsFileWhenMissing() throws {
let tmp = FileManager.default.temporaryDirectory let tmp = FileManager().temporaryDirectory
.appendingPathComponent("clawdbot-ws-\(UUID().uuidString)", isDirectory: true) .appendingPathComponent("clawdbot-ws-\(UUID().uuidString)", isDirectory: true)
defer { try? FileManager.default.removeItem(at: tmp) } defer { try? FileManager().removeItem(at: tmp) }
let agentsURL = try AgentWorkspace.bootstrap(workspaceURL: tmp) let agentsURL = try AgentWorkspace.bootstrap(workspaceURL: tmp)
#expect(FileManager.default.fileExists(atPath: agentsURL.path)) #expect(FileManager().fileExists(atPath: agentsURL.path))
let contents = try String(contentsOf: agentsURL, encoding: .utf8) let contents = try String(contentsOf: agentsURL, encoding: .utf8)
#expect(contents.contains("# AGENTS.md")) #expect(contents.contains("# AGENTS.md"))
@@ -41,9 +41,9 @@ struct AgentWorkspaceTests {
let identityURL = tmp.appendingPathComponent(AgentWorkspace.identityFilename) let identityURL = tmp.appendingPathComponent(AgentWorkspace.identityFilename)
let userURL = tmp.appendingPathComponent(AgentWorkspace.userFilename) let userURL = tmp.appendingPathComponent(AgentWorkspace.userFilename)
let bootstrapURL = tmp.appendingPathComponent(AgentWorkspace.bootstrapFilename) let bootstrapURL = tmp.appendingPathComponent(AgentWorkspace.bootstrapFilename)
#expect(FileManager.default.fileExists(atPath: identityURL.path)) #expect(FileManager().fileExists(atPath: identityURL.path))
#expect(FileManager.default.fileExists(atPath: userURL.path)) #expect(FileManager().fileExists(atPath: userURL.path))
#expect(FileManager.default.fileExists(atPath: bootstrapURL.path)) #expect(FileManager().fileExists(atPath: bootstrapURL.path))
let second = try AgentWorkspace.bootstrap(workspaceURL: tmp) let second = try AgentWorkspace.bootstrap(workspaceURL: tmp)
#expect(second == agentsURL) #expect(second == agentsURL)
@@ -51,10 +51,10 @@ struct AgentWorkspaceTests {
@Test @Test
func bootstrapSafetyRejectsNonEmptyFolderWithoutAgents() throws { func bootstrapSafetyRejectsNonEmptyFolderWithoutAgents() throws {
let tmp = FileManager.default.temporaryDirectory let tmp = FileManager().temporaryDirectory
.appendingPathComponent("clawdbot-ws-\(UUID().uuidString)", isDirectory: true) .appendingPathComponent("clawdbot-ws-\(UUID().uuidString)", isDirectory: true)
defer { try? FileManager.default.removeItem(at: tmp) } defer { try? FileManager().removeItem(at: tmp) }
try FileManager.default.createDirectory(at: tmp, withIntermediateDirectories: true) try FileManager().createDirectory(at: tmp, withIntermediateDirectories: true)
let marker = tmp.appendingPathComponent("notes.txt") let marker = tmp.appendingPathComponent("notes.txt")
try "hello".write(to: marker, atomically: true, encoding: .utf8) try "hello".write(to: marker, atomically: true, encoding: .utf8)
@@ -69,10 +69,10 @@ struct AgentWorkspaceTests {
@Test @Test
func bootstrapSafetyAllowsExistingAgentsFile() throws { func bootstrapSafetyAllowsExistingAgentsFile() throws {
let tmp = FileManager.default.temporaryDirectory let tmp = FileManager().temporaryDirectory
.appendingPathComponent("clawdbot-ws-\(UUID().uuidString)", isDirectory: true) .appendingPathComponent("clawdbot-ws-\(UUID().uuidString)", isDirectory: true)
defer { try? FileManager.default.removeItem(at: tmp) } defer { try? FileManager().removeItem(at: tmp) }
try FileManager.default.createDirectory(at: tmp, withIntermediateDirectories: true) try FileManager().createDirectory(at: tmp, withIntermediateDirectories: true)
let agents = tmp.appendingPathComponent(AgentWorkspace.agentsFilename) let agents = tmp.appendingPathComponent(AgentWorkspace.agentsFilename)
try "# AGENTS.md".write(to: agents, atomically: true, encoding: .utf8) try "# AGENTS.md".write(to: agents, atomically: true, encoding: .utf8)
@@ -87,25 +87,25 @@ struct AgentWorkspaceTests {
@Test @Test
func bootstrapSkipsBootstrapFileWhenWorkspaceHasContent() throws { func bootstrapSkipsBootstrapFileWhenWorkspaceHasContent() throws {
let tmp = FileManager.default.temporaryDirectory let tmp = FileManager().temporaryDirectory
.appendingPathComponent("clawdbot-ws-\(UUID().uuidString)", isDirectory: true) .appendingPathComponent("clawdbot-ws-\(UUID().uuidString)", isDirectory: true)
defer { try? FileManager.default.removeItem(at: tmp) } defer { try? FileManager().removeItem(at: tmp) }
try FileManager.default.createDirectory(at: tmp, withIntermediateDirectories: true) try FileManager().createDirectory(at: tmp, withIntermediateDirectories: true)
let marker = tmp.appendingPathComponent("notes.txt") let marker = tmp.appendingPathComponent("notes.txt")
try "hello".write(to: marker, atomically: true, encoding: .utf8) try "hello".write(to: marker, atomically: true, encoding: .utf8)
_ = try AgentWorkspace.bootstrap(workspaceURL: tmp) _ = try AgentWorkspace.bootstrap(workspaceURL: tmp)
let bootstrapURL = tmp.appendingPathComponent(AgentWorkspace.bootstrapFilename) let bootstrapURL = tmp.appendingPathComponent(AgentWorkspace.bootstrapFilename)
#expect(!FileManager.default.fileExists(atPath: bootstrapURL.path)) #expect(!FileManager().fileExists(atPath: bootstrapURL.path))
} }
@Test @Test
func needsBootstrapFalseWhenIdentityAlreadySet() throws { func needsBootstrapFalseWhenIdentityAlreadySet() throws {
let tmp = FileManager.default.temporaryDirectory let tmp = FileManager().temporaryDirectory
.appendingPathComponent("clawdbot-ws-\(UUID().uuidString)", isDirectory: true) .appendingPathComponent("clawdbot-ws-\(UUID().uuidString)", isDirectory: true)
defer { try? FileManager.default.removeItem(at: tmp) } defer { try? FileManager().removeItem(at: tmp) }
try FileManager.default.createDirectory(at: tmp, withIntermediateDirectories: true) try FileManager().createDirectory(at: tmp, withIntermediateDirectories: true)
let identityURL = tmp.appendingPathComponent(AgentWorkspace.identityFilename) let identityURL = tmp.appendingPathComponent(AgentWorkspace.identityFilename)
try """ try """
# IDENTITY.md - Agent Identity # IDENTITY.md - Agent Identity

View File

@@ -6,9 +6,9 @@ import Testing
struct AnthropicAuthResolverTests { struct AnthropicAuthResolverTests {
@Test @Test
func prefersOAuthFileOverEnv() throws { func prefersOAuthFileOverEnv() throws {
let dir = FileManager.default.temporaryDirectory let dir = FileManager().temporaryDirectory
.appendingPathComponent("clawdbot-oauth-\(UUID().uuidString)", isDirectory: true) .appendingPathComponent("clawdbot-oauth-\(UUID().uuidString)", isDirectory: true)
try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true) try FileManager().createDirectory(at: dir, withIntermediateDirectories: true)
let oauthFile = dir.appendingPathComponent("oauth.json") let oauthFile = dir.appendingPathComponent("oauth.json")
let payload = [ let payload = [
"anthropic": [ "anthropic": [

View File

@@ -6,7 +6,7 @@ import Testing
@MainActor @MainActor
struct CLIInstallerTests { struct CLIInstallerTests {
@Test func installedLocationFindsExecutable() throws { @Test func installedLocationFindsExecutable() throws {
let fm = FileManager.default let fm = FileManager()
let root = fm.temporaryDirectory.appendingPathComponent( let root = fm.temporaryDirectory.appendingPathComponent(
"clawdbot-cli-installer-\(UUID().uuidString)") "clawdbot-cli-installer-\(UUID().uuidString)")
defer { try? fm.removeItem(at: root) } defer { try? fm.removeItem(at: root) }

View File

@@ -7,13 +7,13 @@ import Testing
private func makeTempDir() throws -> URL { private func makeTempDir() throws -> URL {
let base = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) let base = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
let dir = base.appendingPathComponent("clawdbot-canvaswatch-\(UUID().uuidString)", isDirectory: true) let dir = base.appendingPathComponent("clawdbot-canvaswatch-\(UUID().uuidString)", isDirectory: true)
try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true) try FileManager().createDirectory(at: dir, withIntermediateDirectories: true)
return dir return dir
} }
@Test func detectsInPlaceFileWrites() async throws { @Test func detectsInPlaceFileWrites() async throws {
let dir = try self.makeTempDir() let dir = try self.makeTempDir()
defer { try? FileManager.default.removeItem(at: dir) } defer { try? FileManager().removeItem(at: dir) }
let file = dir.appendingPathComponent("index.html") let file = dir.appendingPathComponent("index.html")
try "hello".write(to: file, atomically: false, encoding: .utf8) try "hello".write(to: file, atomically: false, encoding: .utf8)

View File

@@ -8,10 +8,10 @@ import Testing
@MainActor @MainActor
struct CanvasWindowSmokeTests { struct CanvasWindowSmokeTests {
@Test func panelControllerShowsAndHides() async throws { @Test func panelControllerShowsAndHides() async throws {
let root = FileManager.default.temporaryDirectory let root = FileManager().temporaryDirectory
.appendingPathComponent("clawdbot-canvas-test-\(UUID().uuidString)") .appendingPathComponent("clawdbot-canvas-test-\(UUID().uuidString)")
try FileManager.default.createDirectory(at: root, withIntermediateDirectories: true) try FileManager().createDirectory(at: root, withIntermediateDirectories: true)
defer { try? FileManager.default.removeItem(at: root) } defer { try? FileManager().removeItem(at: root) }
let anchor = { NSRect(x: 200, y: 400, width: 40, height: 40) } let anchor = { NSRect(x: 200, y: 400, width: 40, height: 40) }
let controller = try CanvasWindowController( let controller = try CanvasWindowController(
@@ -31,10 +31,10 @@ struct CanvasWindowSmokeTests {
} }
@Test func windowControllerShowsAndCloses() async throws { @Test func windowControllerShowsAndCloses() async throws {
let root = FileManager.default.temporaryDirectory let root = FileManager().temporaryDirectory
.appendingPathComponent("clawdbot-canvas-test-\(UUID().uuidString)") .appendingPathComponent("clawdbot-canvas-test-\(UUID().uuidString)")
try FileManager.default.createDirectory(at: root, withIntermediateDirectories: true) try FileManager().createDirectory(at: root, withIntermediateDirectories: true)
defer { try? FileManager.default.removeItem(at: root) } defer { try? FileManager().removeItem(at: root) }
let controller = try CanvasWindowController( let controller = try CanvasWindowController(
sessionKey: "main", sessionKey: "main",

View File

@@ -6,7 +6,7 @@ import Testing
struct ClawdbotConfigFileTests { struct ClawdbotConfigFileTests {
@Test @Test
func configPathRespectsEnvOverride() async { func configPathRespectsEnvOverride() async {
let override = FileManager.default.temporaryDirectory let override = FileManager().temporaryDirectory
.appendingPathComponent("clawdbot-config-\(UUID().uuidString)") .appendingPathComponent("clawdbot-config-\(UUID().uuidString)")
.appendingPathComponent("clawdbot.json") .appendingPathComponent("clawdbot.json")
.path .path
@@ -19,7 +19,7 @@ struct ClawdbotConfigFileTests {
@MainActor @MainActor
@Test @Test
func remoteGatewayPortParsesAndMatchesHost() async { func remoteGatewayPortParsesAndMatchesHost() async {
let override = FileManager.default.temporaryDirectory let override = FileManager().temporaryDirectory
.appendingPathComponent("clawdbot-config-\(UUID().uuidString)") .appendingPathComponent("clawdbot-config-\(UUID().uuidString)")
.appendingPathComponent("clawdbot.json") .appendingPathComponent("clawdbot.json")
.path .path
@@ -42,7 +42,7 @@ struct ClawdbotConfigFileTests {
@MainActor @MainActor
@Test @Test
func setRemoteGatewayUrlPreservesScheme() async { func setRemoteGatewayUrlPreservesScheme() async {
let override = FileManager.default.temporaryDirectory let override = FileManager().temporaryDirectory
.appendingPathComponent("clawdbot-config-\(UUID().uuidString)") .appendingPathComponent("clawdbot-config-\(UUID().uuidString)")
.appendingPathComponent("clawdbot.json") .appendingPathComponent("clawdbot.json")
.path .path
@@ -64,7 +64,7 @@ struct ClawdbotConfigFileTests {
@Test @Test
func stateDirOverrideSetsConfigPath() async { func stateDirOverrideSetsConfigPath() async {
let dir = FileManager.default.temporaryDirectory let dir = FileManager().temporaryDirectory
.appendingPathComponent("clawdbot-state-\(UUID().uuidString)", isDirectory: true) .appendingPathComponent("clawdbot-state-\(UUID().uuidString)", isDirectory: true)
.path .path

View File

@@ -6,7 +6,7 @@ import Testing
struct ClawdbotOAuthStoreTests { struct ClawdbotOAuthStoreTests {
@Test @Test
func returnsMissingWhenFileAbsent() { func returnsMissingWhenFileAbsent() {
let url = FileManager.default.temporaryDirectory let url = FileManager().temporaryDirectory
.appendingPathComponent("clawdbot-oauth-\(UUID().uuidString)") .appendingPathComponent("clawdbot-oauth-\(UUID().uuidString)")
.appendingPathComponent("oauth.json") .appendingPathComponent("oauth.json")
#expect(ClawdbotOAuthStore.anthropicOAuthStatus(at: url) == .missingFile) #expect(ClawdbotOAuthStore.anthropicOAuthStatus(at: url) == .missingFile)
@@ -24,7 +24,7 @@ struct ClawdbotOAuthStoreTests {
} }
} }
let dir = FileManager.default.temporaryDirectory let dir = FileManager().temporaryDirectory
.appendingPathComponent("clawdbot-oauth-\(UUID().uuidString)", isDirectory: true) .appendingPathComponent("clawdbot-oauth-\(UUID().uuidString)", isDirectory: true)
setenv(key, dir.path, 1) setenv(key, dir.path, 1)
@@ -85,9 +85,9 @@ struct ClawdbotOAuthStoreTests {
} }
private func writeOAuthFile(_ json: [String: Any]) throws -> URL { private func writeOAuthFile(_ json: [String: Any]) throws -> URL {
let dir = FileManager.default.temporaryDirectory let dir = FileManager().temporaryDirectory
.appendingPathComponent("clawdbot-oauth-\(UUID().uuidString)", isDirectory: true) .appendingPathComponent("clawdbot-oauth-\(UUID().uuidString)", isDirectory: true)
try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true) try FileManager().createDirectory(at: dir, withIntermediateDirectories: true)
let url = dir.appendingPathComponent("oauth.json") let url = dir.appendingPathComponent("oauth.json")
let data = try JSONSerialization.data(withJSONObject: json, options: [.prettyPrinted, .sortedKeys]) let data = try JSONSerialization.data(withJSONObject: json, options: [.prettyPrinted, .sortedKeys])

View File

@@ -12,16 +12,16 @@ import Testing
private func makeTempDir() throws -> URL { private func makeTempDir() throws -> URL {
let base = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) let base = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
let dir = base.appendingPathComponent(UUID().uuidString, isDirectory: true) let dir = base.appendingPathComponent(UUID().uuidString, isDirectory: true)
try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true) try FileManager().createDirectory(at: dir, withIntermediateDirectories: true)
return dir return dir
} }
private func makeExec(at path: URL) throws { private func makeExec(at path: URL) throws {
try FileManager.default.createDirectory( try FileManager().createDirectory(
at: path.deletingLastPathComponent(), at: path.deletingLastPathComponent(),
withIntermediateDirectories: true) withIntermediateDirectories: true)
FileManager.default.createFile(atPath: path.path, contents: Data("echo ok\n".utf8)) FileManager().createFile(atPath: path.path, contents: Data("echo ok\n".utf8))
try FileManager.default.setAttributes([.posixPermissions: 0o755], ofItemAtPath: path.path) try FileManager().setAttributes([.posixPermissions: 0o755], ofItemAtPath: path.path)
} }
@Test func prefersClawdbotBinary() async throws { @Test func prefersClawdbotBinary() async throws {
@@ -49,7 +49,7 @@ import Testing
let scriptPath = tmp.appendingPathComponent("bin/clawdbot.js") let scriptPath = tmp.appendingPathComponent("bin/clawdbot.js")
try self.makeExec(at: nodePath) try self.makeExec(at: nodePath)
try "#!/bin/sh\necho v22.0.0\n".write(to: nodePath, atomically: true, encoding: .utf8) try "#!/bin/sh\necho v22.0.0\n".write(to: nodePath, atomically: true, encoding: .utf8)
try FileManager.default.setAttributes([.posixPermissions: 0o755], ofItemAtPath: nodePath.path) try FileManager().setAttributes([.posixPermissions: 0o755], ofItemAtPath: nodePath.path)
try self.makeExec(at: scriptPath) try self.makeExec(at: scriptPath)
let cmd = CommandResolver.clawdbotCommand( let cmd = CommandResolver.clawdbotCommand(

View File

@@ -32,7 +32,7 @@ import Testing
} }
private static func swiftFiles(under root: URL) throws -> [URL] { private static func swiftFiles(under root: URL) throws -> [URL] {
let fm = FileManager.default let fm = FileManager()
guard let enumerator = fm.enumerator(at: root, includingPropertiesForKeys: [.isRegularFileKey]) else { guard let enumerator = fm.enumerator(at: root, includingPropertiesForKeys: [.isRegularFileKey]) else {
return [] return []
} }

View File

@@ -4,7 +4,7 @@ import Testing
@Suite struct GatewayLaunchAgentManagerTests { @Suite struct GatewayLaunchAgentManagerTests {
@Test func launchAgentPlistSnapshotParsesArgsAndEnv() throws { @Test func launchAgentPlistSnapshotParsesArgsAndEnv() throws {
let url = FileManager.default.temporaryDirectory let url = FileManager().temporaryDirectory
.appendingPathComponent("clawdbot-launchd-\(UUID().uuidString).plist") .appendingPathComponent("clawdbot-launchd-\(UUID().uuidString).plist")
let plist: [String: Any] = [ let plist: [String: Any] = [
"ProgramArguments": ["clawdbot", "gateway-daemon", "--port", "18789", "--bind", "loopback"], "ProgramArguments": ["clawdbot", "gateway-daemon", "--port", "18789", "--bind", "loopback"],
@@ -15,7 +15,7 @@ import Testing
] ]
let data = try PropertyListSerialization.data(fromPropertyList: plist, format: .xml, options: 0) let data = try PropertyListSerialization.data(fromPropertyList: plist, format: .xml, options: 0)
try data.write(to: url, options: [.atomic]) try data.write(to: url, options: [.atomic])
defer { try? FileManager.default.removeItem(at: url) } defer { try? FileManager().removeItem(at: url) }
let snapshot = try #require(LaunchAgentPlist.snapshot(url: url)) let snapshot = try #require(LaunchAgentPlist.snapshot(url: url))
#expect(snapshot.port == 18789) #expect(snapshot.port == 18789)
@@ -25,14 +25,14 @@ import Testing
} }
@Test func launchAgentPlistSnapshotAllowsMissingBind() throws { @Test func launchAgentPlistSnapshotAllowsMissingBind() throws {
let url = FileManager.default.temporaryDirectory let url = FileManager().temporaryDirectory
.appendingPathComponent("clawdbot-launchd-\(UUID().uuidString).plist") .appendingPathComponent("clawdbot-launchd-\(UUID().uuidString).plist")
let plist: [String: Any] = [ let plist: [String: Any] = [
"ProgramArguments": ["clawdbot", "gateway-daemon", "--port", "18789"], "ProgramArguments": ["clawdbot", "gateway-daemon", "--port", "18789"],
] ]
let data = try PropertyListSerialization.data(fromPropertyList: plist, format: .xml, options: 0) let data = try PropertyListSerialization.data(fromPropertyList: plist, format: .xml, options: 0)
try data.write(to: url, options: [.atomic]) try data.write(to: url, options: [.atomic])
defer { try? FileManager.default.removeItem(at: url) } defer { try? FileManager().removeItem(at: url) }
let snapshot = try #require(LaunchAgentPlist.snapshot(url: url)) let snapshot = try #require(LaunchAgentPlist.snapshot(url: url))
#expect(snapshot.port == 18789) #expect(snapshot.port == 18789)

View File

@@ -5,7 +5,7 @@ import Testing
@Suite struct LogLocatorTests { @Suite struct LogLocatorTests {
@Test func launchdGatewayLogPathEnsuresTmpDirExists() throws { @Test func launchdGatewayLogPathEnsuresTmpDirExists() throws {
let fm = FileManager.default let fm = FileManager()
let baseDir = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) let baseDir = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
let logDir = baseDir.appendingPathComponent("clawdbot-tests-\(UUID().uuidString)") let logDir = baseDir.appendingPathComponent("clawdbot-tests-\(UUID().uuidString)")

View File

@@ -77,9 +77,9 @@ struct LowCoverageHelperTests {
} }
@Test func pairedNodesStorePersists() async throws { @Test func pairedNodesStorePersists() async throws {
let dir = FileManager.default.temporaryDirectory let dir = FileManager().temporaryDirectory
.appendingPathComponent("paired-\(UUID().uuidString)", isDirectory: true) .appendingPathComponent("paired-\(UUID().uuidString)", isDirectory: true)
try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true) try FileManager().createDirectory(at: dir, withIntermediateDirectories: true)
let url = dir.appendingPathComponent("nodes.json") let url = dir.appendingPathComponent("nodes.json")
let store = PairedNodesStore(fileURL: url) let store = PairedNodesStore(fileURL: url)
await store.load() await store.load()
@@ -143,12 +143,12 @@ struct LowCoverageHelperTests {
} }
@Test @MainActor func canvasSchemeHandlerResolvesFilesAndErrors() throws { @Test @MainActor func canvasSchemeHandlerResolvesFilesAndErrors() throws {
let root = FileManager.default.temporaryDirectory let root = FileManager().temporaryDirectory
.appendingPathComponent("canvas-\(UUID().uuidString)", isDirectory: true) .appendingPathComponent("canvas-\(UUID().uuidString)", isDirectory: true)
defer { try? FileManager.default.removeItem(at: root) } defer { try? FileManager().removeItem(at: root) }
try FileManager.default.createDirectory(at: root, withIntermediateDirectories: true) try FileManager().createDirectory(at: root, withIntermediateDirectories: true)
let session = root.appendingPathComponent("main", isDirectory: true) let session = root.appendingPathComponent("main", isDirectory: true)
try FileManager.default.createDirectory(at: session, withIntermediateDirectories: true) try FileManager().createDirectory(at: session, withIntermediateDirectories: true)
let index = session.appendingPathComponent("index.html") let index = session.appendingPathComponent("index.html")
try "<h1>Hello</h1>".write(to: index, atomically: true, encoding: .utf8) try "<h1>Hello</h1>".write(to: index, atomically: true, encoding: .utf8)

View File

@@ -59,7 +59,7 @@ struct MacNodeRuntimeTests {
includeAudio: Bool?, includeAudio: Bool?,
outPath: String?) async throws -> (path: String, hasAudio: Bool) outPath: String?) async throws -> (path: String, hasAudio: Bool)
{ {
let url = FileManager.default.temporaryDirectory let url = FileManager().temporaryDirectory
.appendingPathComponent("clawdbot-test-screen-record-\(UUID().uuidString).mp4") .appendingPathComponent("clawdbot-test-screen-record-\(UUID().uuidString).mp4")
try Data("ok".utf8).write(to: url) try Data("ok".utf8).write(to: url)
return (path: url.path, hasAudio: false) return (path: url.path, hasAudio: false)

View File

@@ -19,9 +19,9 @@ struct ModelCatalogLoaderTests {
}; };
""" """
let tmp = FileManager.default.temporaryDirectory let tmp = FileManager().temporaryDirectory
.appendingPathComponent("models-\(UUID().uuidString).ts") .appendingPathComponent("models-\(UUID().uuidString).ts")
defer { try? FileManager.default.removeItem(at: tmp) } defer { try? FileManager().removeItem(at: tmp) }
try src.write(to: tmp, atomically: true, encoding: .utf8) try src.write(to: tmp, atomically: true, encoding: .utf8)
let choices = try await ModelCatalogLoader.load(from: tmp.path) let choices = try await ModelCatalogLoader.load(from: tmp.path)
@@ -42,9 +42,9 @@ struct ModelCatalogLoaderTests {
@Test @Test
func loadWithNoExportReturnsEmptyChoices() async throws { func loadWithNoExportReturnsEmptyChoices() async throws {
let src = "const NOPE = 1;" let src = "const NOPE = 1;"
let tmp = FileManager.default.temporaryDirectory let tmp = FileManager().temporaryDirectory
.appendingPathComponent("models-\(UUID().uuidString).ts") .appendingPathComponent("models-\(UUID().uuidString).ts")
defer { try? FileManager.default.removeItem(at: tmp) } defer { try? FileManager().removeItem(at: tmp) }
try src.write(to: tmp, atomically: true, encoding: .utf8) try src.write(to: tmp, atomically: true, encoding: .utf8)
let choices = try await ModelCatalogLoader.load(from: tmp.path) let choices = try await ModelCatalogLoader.load(from: tmp.path)

View File

@@ -6,16 +6,16 @@ import Testing
private func makeTempDir() throws -> URL { private func makeTempDir() throws -> URL {
let base = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) let base = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
let dir = base.appendingPathComponent(UUID().uuidString, isDirectory: true) let dir = base.appendingPathComponent(UUID().uuidString, isDirectory: true)
try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true) try FileManager().createDirectory(at: dir, withIntermediateDirectories: true)
return dir return dir
} }
private func makeExec(at path: URL) throws { private func makeExec(at path: URL) throws {
try FileManager.default.createDirectory( try FileManager().createDirectory(
at: path.deletingLastPathComponent(), at: path.deletingLastPathComponent(),
withIntermediateDirectories: true) withIntermediateDirectories: true)
FileManager.default.createFile(atPath: path.path, contents: Data("echo ok\n".utf8)) FileManager().createFile(atPath: path.path, contents: Data("echo ok\n".utf8))
try FileManager.default.setAttributes([.posixPermissions: 0o755], ofItemAtPath: path.path) try FileManager().setAttributes([.posixPermissions: 0o755], ofItemAtPath: path.path)
} }
@Test func fnmNodeBinsPreferNewestInstalledVersion() throws { @Test func fnmNodeBinsPreferNewestInstalledVersion() throws {
@@ -37,7 +37,7 @@ import Testing
let home = try self.makeTempDir() let home = try self.makeTempDir()
let missingNodeBin = home let missingNodeBin = home
.appendingPathComponent(".local/share/fnm/node-versions/v99.0.0/installation/bin") .appendingPathComponent(".local/share/fnm/node-versions/v99.0.0/installation/bin")
try FileManager.default.createDirectory(at: missingNodeBin, withIntermediateDirectories: true) try FileManager().createDirectory(at: missingNodeBin, withIntermediateDirectories: true)
let bins = CommandResolver._testNodeManagerBinPaths(home: home) let bins = CommandResolver._testNodeManagerBinPaths(home: home)
#expect(!bins.contains(missingNodeBin.path)) #expect(!bins.contains(missingNodeBin.path))

View File

@@ -6,10 +6,10 @@ import Testing
private func makeTempExecutable(contents: String) throws -> URL { private func makeTempExecutable(contents: String) throws -> URL {
let dir = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) let dir = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
.appendingPathComponent(UUID().uuidString, isDirectory: true) .appendingPathComponent(UUID().uuidString, isDirectory: true)
try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true) try FileManager().createDirectory(at: dir, withIntermediateDirectories: true)
let path = dir.appendingPathComponent("node") let path = dir.appendingPathComponent("node")
try contents.write(to: path, atomically: true, encoding: .utf8) try contents.write(to: path, atomically: true, encoding: .utf8)
try FileManager.default.setAttributes([.posixPermissions: 0o755], ofItemAtPath: path.path) try FileManager().setAttributes([.posixPermissions: 0o755], ofItemAtPath: path.path)
return path return path
} }

View File

@@ -109,7 +109,7 @@ enum TestIsolation {
} }
nonisolated static func tempConfigPath() -> String { nonisolated static func tempConfigPath() -> String {
FileManager.default.temporaryDirectory FileManager().temporaryDirectory
.appendingPathComponent("clawdbot-test-config-\(UUID().uuidString).json") .appendingPathComponent("clawdbot-test-config-\(UUID().uuidString).json")
.path .path
} }

View File

@@ -47,17 +47,17 @@ import Testing
.appendingPathComponent(UUID().uuidString, isDirectory: true) .appendingPathComponent(UUID().uuidString, isDirectory: true)
let dist = tmp.appendingPathComponent("dist/index.js") let dist = tmp.appendingPathComponent("dist/index.js")
let bin = tmp.appendingPathComponent("bin/clawdbot.js") let bin = tmp.appendingPathComponent("bin/clawdbot.js")
try FileManager.default.createDirectory(at: dist.deletingLastPathComponent(), withIntermediateDirectories: true) try FileManager().createDirectory(at: dist.deletingLastPathComponent(), withIntermediateDirectories: true)
try FileManager.default.createDirectory(at: bin.deletingLastPathComponent(), withIntermediateDirectories: true) try FileManager().createDirectory(at: bin.deletingLastPathComponent(), withIntermediateDirectories: true)
FileManager.default.createFile(atPath: dist.path, contents: Data()) FileManager().createFile(atPath: dist.path, contents: Data())
FileManager.default.createFile(atPath: bin.path, contents: Data()) FileManager().createFile(atPath: bin.path, contents: Data())
let entry = CommandResolver.gatewayEntrypoint(in: tmp) let entry = CommandResolver.gatewayEntrypoint(in: tmp)
#expect(entry == dist.path) #expect(entry == dist.path)
} }
@Test func logLocatorPicksNewestLogFile() throws { @Test func logLocatorPicksNewestLogFile() throws {
let fm = FileManager.default let fm = FileManager()
let dir = URL(fileURLWithPath: "/tmp/clawdbot", isDirectory: true) let dir = URL(fileURLWithPath: "/tmp/clawdbot", isDirectory: true)
try? fm.createDirectory(at: dir, withIntermediateDirectories: true) try? fm.createDirectory(at: dir, withIntermediateDirectories: true)

View File

@@ -1 +1 @@
c51e9080b032ccb0dd153452854f2ffdaca8e1db14d7b98ed56cca8f0f1a5257 cc37623c53c8c111322d9163926fa430b4f5c56c9d964001bd6d66ea42a2a8bc