mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-26 04:15:48 -06:00
218 lines
7.6 KiB
Swift
218 lines
7.6 KiB
Swift
import Darwin
|
|
import Foundation
|
|
|
|
enum ElevationExclusiveRename {
|
|
static let argument = "--elevation-rename-exclusive"
|
|
|
|
static func runIfRequested(arguments: [String] = CommandLine.arguments) -> Int32? {
|
|
guard arguments.dropFirst().first == self.argument else { return nil }
|
|
guard arguments.count == 4 else {
|
|
fputs("OpenClaw elevation rename requires absolute source and destination paths\n", stderr)
|
|
return 2
|
|
}
|
|
let source = arguments[2]
|
|
let destination = arguments[3]
|
|
guard source.hasPrefix("/"), destination.hasPrefix("/") else {
|
|
fputs("OpenClaw elevation rename paths must be absolute\n", stderr)
|
|
return 2
|
|
}
|
|
|
|
let result = source.withCString { sourcePath in
|
|
destination.withCString { destinationPath in
|
|
renamex_np(sourcePath, destinationPath, UInt32(RENAME_EXCL))
|
|
}
|
|
}
|
|
guard result == 0 else {
|
|
let code = errno
|
|
fputs("OpenClaw elevation rename failed: \(String(cString: strerror(code)))\n", stderr)
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
}
|
|
|
|
enum ElevationFilesystemSync {
|
|
static let fileArgument = "--elevation-sync-file"
|
|
static let directoryArgument = "--elevation-sync-directory"
|
|
static let treeArgument = "--elevation-sync-tree"
|
|
|
|
static func runIfRequested(arguments: [String] = CommandLine.arguments) -> Int32? {
|
|
guard let argument = arguments.dropFirst().first,
|
|
argument == self.fileArgument || argument == self.directoryArgument || argument == self.treeArgument
|
|
else { return nil }
|
|
guard arguments.count == 3 else {
|
|
fputs("OpenClaw elevation sync requires one absolute path\n", stderr)
|
|
return 2
|
|
}
|
|
let path = arguments[2]
|
|
guard path.hasPrefix("/") else {
|
|
fputs("OpenClaw elevation sync path must be absolute\n", stderr)
|
|
return 2
|
|
}
|
|
if argument == self.treeArgument {
|
|
return self.syncTree(path) ? 0 : 1
|
|
}
|
|
let expectsDirectory = argument == self.directoryArgument
|
|
return self.syncPath(path, expectsDirectory: expectsDirectory) ? 0 : 1
|
|
}
|
|
|
|
private static func syncPath(_ path: String, expectsDirectory: Bool) -> Bool {
|
|
let descriptor = open(path, O_RDONLY | O_NOFOLLOW | (expectsDirectory ? O_DIRECTORY : 0))
|
|
guard descriptor >= 0 else {
|
|
fputs("OpenClaw elevation sync could not open path\n", stderr)
|
|
return false
|
|
}
|
|
defer { close(descriptor) }
|
|
var metadata = stat()
|
|
guard fstat(descriptor, &metadata) == 0,
|
|
expectsDirectory ? (metadata.st_mode & S_IFMT) == S_IFDIR : (metadata.st_mode & S_IFMT) == S_IFREG
|
|
else {
|
|
fputs("OpenClaw elevation sync path has an unsupported type\n", stderr)
|
|
return false
|
|
}
|
|
if expectsDirectory {
|
|
guard fsync(descriptor) == 0 else {
|
|
fputs("OpenClaw elevation directory sync failed\n", stderr)
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
guard fcntl(descriptor, F_FULLFSYNC) == 0 || fsync(descriptor) == 0 else {
|
|
fputs("OpenClaw elevation file sync failed\n", stderr)
|
|
return false
|
|
}
|
|
let parent = URL(fileURLWithPath: path).deletingLastPathComponent().path
|
|
let parentDescriptor = open(parent, O_RDONLY | O_NOFOLLOW | O_DIRECTORY)
|
|
guard parentDescriptor >= 0 else {
|
|
fputs("OpenClaw elevation sync could not open parent directory\n", stderr)
|
|
return false
|
|
}
|
|
defer { close(parentDescriptor) }
|
|
guard fsync(parentDescriptor) == 0 else {
|
|
fputs("OpenClaw elevation parent directory sync failed\n", stderr)
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
private static func syncTree(_ path: String) -> Bool {
|
|
let root = URL(fileURLWithPath: path, isDirectory: true)
|
|
let keys: [URLResourceKey] = [.isDirectoryKey, .isRegularFileKey, .isSymbolicLinkKey]
|
|
var enumerationFailed = false
|
|
guard let enumerator = FileManager.default.enumerator(
|
|
at: root,
|
|
includingPropertiesForKeys: keys,
|
|
options: [],
|
|
errorHandler: { _, _ in
|
|
enumerationFailed = true
|
|
return false
|
|
})
|
|
else {
|
|
fputs("OpenClaw elevation sync could not enumerate tree\n", stderr)
|
|
return false
|
|
}
|
|
var directories = [root]
|
|
for case let url as URL in enumerator {
|
|
guard let values = try? url.resourceValues(forKeys: Set(keys)) else {
|
|
fputs("OpenClaw elevation sync could not inspect tree entry\n", stderr)
|
|
return false
|
|
}
|
|
if values.isSymbolicLink == true {
|
|
continue
|
|
}
|
|
if values.isDirectory == true {
|
|
directories.append(url)
|
|
} else if values.isRegularFile == true {
|
|
guard self.syncPath(url.path, expectsDirectory: false) else { return false }
|
|
} else {
|
|
fputs("OpenClaw elevation sync tree contains an unsupported entry\n", stderr)
|
|
return false
|
|
}
|
|
}
|
|
guard !enumerationFailed else {
|
|
fputs("OpenClaw elevation sync could not enumerate complete tree\n", stderr)
|
|
return false
|
|
}
|
|
for directory in directories.reversed() {
|
|
guard self.syncPath(directory.path, expectsDirectory: true) else { return false }
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
struct AppLaunchRuntimePlan: Equatable {
|
|
enum Mode: Equatable {
|
|
case interactive
|
|
case background
|
|
case elevationHost
|
|
}
|
|
|
|
let mode: Mode
|
|
let attachOnly: Bool
|
|
|
|
init(arguments: [String]) {
|
|
if arguments.contains("--elevation-host") {
|
|
self.mode = .elevationHost
|
|
self.attachOnly = true
|
|
} else {
|
|
self.mode = arguments.contains("--background-only") ? .background : .interactive
|
|
self.attachOnly = arguments.contains("--attach-only") || arguments.contains("--no-launchd")
|
|
}
|
|
}
|
|
|
|
static var current: Self {
|
|
Self(arguments: CommandLine.arguments)
|
|
}
|
|
|
|
var isElevationHost: Bool {
|
|
self.mode == .elevationHost
|
|
}
|
|
|
|
func resolvePaused(_ storedValue: Bool) -> Bool {
|
|
self.isElevationHost ? false : storedValue
|
|
}
|
|
|
|
func resolveComputerControlEnabled(_ storedValue: Bool) -> Bool {
|
|
self.isElevationHost || storedValue
|
|
}
|
|
|
|
func resolvePeekabooBridgeEnabled(_ storedValue: Bool) -> Bool {
|
|
self.isElevationHost || storedValue
|
|
}
|
|
|
|
var allowsAutomaticPresentation: Bool {
|
|
self.mode == .interactive
|
|
}
|
|
|
|
/// GUI-owned Keychain items may present SecurityAgent when a newly signed build is not in an item's ACL.
|
|
/// Background hosts keep that state cold; config and environment still own their primary Gateway route.
|
|
var allowsGatewayUIKeychainAccess: Bool {
|
|
self.mode == .interactive
|
|
}
|
|
|
|
var allowsUpdater: Bool {
|
|
!self.isElevationHost
|
|
}
|
|
|
|
var allowsDockIcon: Bool {
|
|
!self.isElevationHost
|
|
}
|
|
|
|
var allowsInteractiveServices: Bool {
|
|
!self.isElevationHost
|
|
}
|
|
|
|
var allowsCuaComputerControl: Bool {
|
|
!self.isElevationHost
|
|
}
|
|
|
|
func shouldAutoOpenChat(arguments: [String]) -> Bool {
|
|
self.allowsAutomaticPresentation &&
|
|
(arguments.contains("--chat") || arguments.contains("--webchat"))
|
|
}
|
|
|
|
func shouldAutoOpenDashboard(arguments: [String]) -> Bool {
|
|
self.allowsAutomaticPresentation && arguments.contains("--dashboard")
|
|
}
|
|
}
|