refactor(macos): replace FileManager.default for Swift 6
This commit is contained in:
committed by
Peter Steinberger
parent
44d55667de
commit
87d995bcde
@@ -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))
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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?
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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])
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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? {
|
||||||
|
|||||||
@@ -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 }
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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],
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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 }
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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])
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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() }
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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": [
|
||||||
|
|||||||
@@ -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) }
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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])
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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 []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)")
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
c51e9080b032ccb0dd153452854f2ffdaca8e1db14d7b98ed56cca8f0f1a5257
|
cc37623c53c8c111322d9163926fa430b4f5c56c9d964001bd6d66ea42a2a8bc
|
||||||
|
|||||||
Reference in New Issue
Block a user