Fix setup: shell PATH, restart race, Tailscale wait, macOS 27 grants

This commit is contained in:
Ian Gulliver
2026-10-22 20:20:41 -07:00
parent 5ed184e975
commit eed9b40533
10 changed files with 51 additions and 23 deletions
+1 -1
View File
@@ -80,7 +80,7 @@ func appElement(_ pid: pid_t) -> AXUIElement {
func requireTrust() throws {
if !AXIsProcessTrusted() {
throw ToolError("byre lacks Accessibility, so no app's tree can be read or acted on; the human must switch byre on in System Settings → Privacy & Security → Accessibility")
throw ToolError("byre lacks \(deviceControl), so no app's tree can be read or acted on; the human must switch \(grantee) on in System Settings → Privacy & Security → \(deviceControl)")
}
}
+27 -9
View File
@@ -3,6 +3,24 @@ import ApplicationServices
import ScreenCaptureKit
let orionBundle = "com.kagi.kagimacOS"
let deviceControl = "Device Control and Data Access"
// The server and the app each run byre-hands, and System Settings lists them as two entries: the server's helper by its file name, the app by its own.
let grantee: String = {
var pid = getppid()
while pid > 1 {
var buf = [CChar](repeating: 0, count: Int(MAXPATHLEN))
if proc_pidpath(pid, &buf, UInt32(buf.count)) > 0, String(cString: buf).hasSuffix(".app/Contents/MacOS/Byre") {
return "Byre (with byre's app icon)"
}
var info = proc_bsdinfo()
if proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, &info, Int32(MemoryLayout<proc_bsdinfo>.size)) <= 0 {
break
}
pid = pid_t(info.pbi_ppid)
}
return "byre (with the black terminal icon)"
}()
func permission(_ name: String, _ status: String, _ detail: String, _ pane: String) -> [String: String] {
return ["name": name, "status": status, "detail": detail, "settings": "x-apple.systempreferences:com.apple.preference.security?Privacy_\(pane)"]
@@ -14,26 +32,26 @@ func checkScreenRecording() async -> [String: String] {
return permission("Screen Recording", "granted", "Listed the screen (displays: \(content.displays.count), windows: \(content.windows.count)).", "ScreenCapture")
} catch {
CGRequestScreenCaptureAccess()
return permission("Screen Recording", "missing", "Listing the screen failed (\(error.localizedDescription)). Switch byre on in Screen Recording.", "ScreenCapture")
return permission("Screen Recording", "missing", "Listing the screen failed (\(error.localizedDescription)). Switch \(grantee) on in Screen Recording.", "ScreenCapture")
}
}
func checkAccessibility() -> [String: String] {
if !AXIsProcessTrustedWithOptions([kAXTrustedCheckOptionPrompt.takeUnretainedValue(): true] as CFDictionary) {
return permission("Accessibility", "missing", "Switch byre on in Accessibility.", "Accessibility")
return permission(deviceControl, "missing", "Switch \(grantee) on in \(deviceControl).", "Accessibility")
}
guard let finder = NSRunningApplication.runningApplications(withBundleIdentifier: "com.apple.finder").first else {
return permission("Accessibility", "unchecked", "Granted, but Finder isn't running to read its menu bar.", "Accessibility")
return permission(deviceControl, "unchecked", "Granted, but Finder isn't running to read its menu bar.", "Accessibility")
}
do {
guard let bar = try attr(appElement(finder.processIdentifier), kAXMenuBarAttribute) else {
return permission("Accessibility", "missing", "Granted, but reading Finder's menu bar returned nothing. Switch byre off and on again in Accessibility.", "Accessibility")
return permission(deviceControl, "missing", "Granted, but reading Finder's menu bar returned nothing. Switch \(grantee) off and on again in \(deviceControl).", "Accessibility")
}
return permission("Accessibility", "granted", "Read Finder's menu bar (menus: \(try children(bar as! AXUIElement).count)).", "Accessibility")
return permission(deviceControl, "granted", "Read Finder's menu bar (menus: \(try children(bar as! AXUIElement).count)).", "Accessibility")
} catch let e as ToolError {
return permission("Accessibility", "missing", e.message, "Accessibility")
return permission(deviceControl, "missing", e.message, "Accessibility")
} catch {
return permission("Accessibility", "missing", "\(error)", "Accessibility")
return permission(deviceControl, "missing", "\(error)", "Accessibility")
}
}
@@ -44,7 +62,7 @@ func checkOrion() -> [String: String] {
let target = NSAppleEventDescriptor(bundleIdentifier: orionBundle)
let status = AEDeterminePermissionToAutomateTarget(target.aeDesc, typeWildCard, typeWildCard, true)
if status != noErr {
return permission("Automation of Orion", "missing", "macOS refused Apple events to Orion (\(status)). Switch Orion on under byre in Automation.", "Automation")
return permission("Automation of Orion", "missing", "macOS refused Apple events to Orion (\(status)). Switch Orion on under \(grantee) in Automation.", "Automation")
}
do {
let windows = try osa("tell application \"Orion\" to count windows").int32Value
@@ -65,7 +83,7 @@ func checkFolder(_ name: String, _ path: String) -> [String: String] {
let entries = try FileManager.default.contentsOfDirectory(atPath: dir)
return permission(name, "granted", "Listed \(dir) (entries: \(entries.count)).", "FilesAndFolders")
} catch {
return permission(name, "missing", "Listing \(dir) failed (\(error.localizedDescription)). Switch \(name) on under byre in Files & Folders.", "FilesAndFolders")
return permission(name, "missing", "Listing \(dir) failed (\(error.localizedDescription)). Switch \(name) on under \(grantee) in Files & Folders.", "FilesAndFolders")
}
}