Files
bot/apps/macos/Sources/Bot/LaunchAgentManager.swift
zeekay 4bdc9ee3e4 chore: rebrand openclaw → hanzo-bot for bridge merge
Comprehensive rename of all openclaw references to hanzo-bot equivalents:
- Package: openclaw → @hanzo/bot
- Binary: openclaw → hanzo-bot
- Types: OpenClaw* → Bot*
- Env vars: OPENCLAW_* → BOT_*
- Android: ai.openclaw.android → ai.hanzo.bot.android
- iOS/macOS: ai.openclaw.* → ai.hanzo.*
- Swift modules: OpenClaw* → Bot*
- Plugins: openclaw.plugin.json → bot.plugin.json
- Paths: .openclaw/ → .hanzo/bot/
- URLs: github.com/openclaw → github.com/hanzoai
2026-02-26 22:36:01 -08:00

79 lines
2.9 KiB
Swift

import Foundation
enum LaunchAgentManager {
private static var plistURL: URL {
FileManager().homeDirectoryForCurrentUser
.appendingPathComponent("Library/LaunchAgents/ai.hanzo.bot.mac.plist")
}
static func status() async -> Bool {
guard FileManager().fileExists(atPath: self.plistURL.path) else { return false }
let result = await self.runLaunchctl(["print", "gui/\(getuid())/\(launchdLabel)"])
return result == 0
}
static func set(enabled: Bool, bundlePath: String) async {
if enabled {
self.writePlist(bundlePath: bundlePath)
_ = await self.runLaunchctl(["bootout", "gui/\(getuid())/\(launchdLabel)"])
_ = await self.runLaunchctl(["bootstrap", "gui/\(getuid())", self.plistURL.path])
_ = await self.runLaunchctl(["kickstart", "-k", "gui/\(getuid())/\(launchdLabel)"])
} else {
// 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).
try? FileManager().removeItem(at: self.plistURL)
}
}
private static func writePlist(bundlePath: String) {
let plist = """
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>ai.hanzo.bot.mac</string>
<key>ProgramArguments</key>
<array>
<string>\(bundlePath)/Contents/MacOS/HanzoBot</string>
</array>
<key>WorkingDirectory</key>
<string>\(FileManager().homeDirectoryForCurrentUser.path)</string>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>\(CommandResolver.preferredPaths().joined(separator: ":"))</string>
</dict>
<key>StandardOutPath</key>
<string>\(LogLocator.launchdLogPath)</string>
<key>StandardErrorPath</key>
<string>\(LogLocator.launchdLogPath)</string>
</dict>
</plist>
"""
try? plist.write(to: self.plistURL, atomically: true, encoding: .utf8)
}
@discardableResult
private static func runLaunchctl(_ args: [String]) async -> Int32 {
await Task.detached(priority: .utility) { () -> Int32 in
let process = Process()
process.launchPath = "/bin/launchctl"
process.arguments = args
let pipe = Pipe()
process.standardOutput = pipe
process.standardError = pipe
do {
_ = try process.runAndReadToEnd(from: pipe)
return process.terminationStatus
} catch {
return -1
}
}.value
}
}