diff --git a/docs/clean-install-vm.md b/docs/clean-install-vm.md index 55b322c..1151601 100644 --- a/docs/clean-install-vm.md +++ b/docs/clean-install-vm.md @@ -24,6 +24,8 @@ To install a build, copy `~/byre-deploy/byre.dmg` (or a `mac/bundle.sh` build) i The app's Tailscale sign-in opens Safari in the VM. To have the human approve it from their own Mac, read the address over SSH (`sudo launchctl asuser 501 sudo -u admin osascript -e 'tell application "Safari" to get URL of front document'`, which asks once in the VM for Automation of Safari) and open the `https://login.tailscale.com/a/` it names with `show_page` on the human's Mac. +`claude auth login` (run in a Terminal window in the VM) opens claude.ai's login in the VM's Safari. It has to finish there, since the CLI waits for the browser's callback in the VM. Choose email: claude.ai emails the human a code, which goes into the page's "Enter verification code" box. `claude auth status` run over SSH reports `loggedIn: false` even afterwards, because the credentials sit in the GUI session's keychain; the lead running is the real check. + ## Driving the VM's screen - Start each `act` with a click on an empty part of the VM's screen, not its title bar: the VM takes clicks and keys only after a click inside it. diff --git a/hands/Sources/byre-hands/Check.swift b/hands/Sources/byre-hands/Check.swift index 638a194..12f679c 100644 --- a/hands/Sources/byre-hands/Check.swift +++ b/hands/Sources/byre-hands/Check.swift @@ -5,7 +5,9 @@ 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. +// The server and the app each run byre-hands. Device Control and Automation list the server's helper by its file name, but Screen Recording lists it under the app it sits in. +let screenGrantee = "Byre (with byre's app icon)" + let grantee: String = { var pid = getppid() while pid > 1 { @@ -32,7 +34,7 @@ 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 \(grantee) on in Screen Recording.", "ScreenCapture") + return permission("Screen Recording", "missing", "Listing the screen failed (\(error.localizedDescription)). Switch \(screenGrantee) on in Screen Recording.", "ScreenCapture") } } diff --git a/internal/agents/antigravity.go b/internal/agents/antigravity.go index b0a70af..f7dcce6 100644 --- a/internal/agents/antigravity.go +++ b/internal/agents/antigravity.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "log/slog" + "os/exec" "strings" "time" @@ -173,6 +174,10 @@ func (m *Manager) agyTurnEnded(ctx context.Context, l *live, p antigravityProces var agyWindowMins = map[string]int{"5h": 300, "weekly": 7 * 24 * 60} func (m *Manager) readAgyRateLimit() { + _, err := exec.LookPath("agy") + if err != nil { + return + } ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() groups, err := antigravity.ReadUsage(ctx) diff --git a/internal/agents/codex.go b/internal/agents/codex.go index 88f923a..e00c16f 100644 --- a/internal/agents/codex.go +++ b/internal/agents/codex.go @@ -6,6 +6,7 @@ import ( "fmt" "log/slog" "os" + "os/exec" "strings" "time" @@ -275,7 +276,11 @@ func imageBlock(mime, data string) map[string]any { } func (m *Manager) readCodexRateLimit() { - err := os.MkdirAll(m.codexHome, 0o700) + _, err := exec.LookPath("codex") + if err != nil { + return + } + err = os.MkdirAll(m.codexHome, 0o700) if err != nil { slog.Error("read codex rate limits", "err", err) return diff --git a/internal/agents/kb/setup.md b/internal/agents/kb/setup.md index 76e1945..a71ab73 100644 --- a/internal/agents/kb/setup.md +++ b/internal/agents/kb/setup.md @@ -37,7 +37,7 @@ d=$(mktemp -d) && cp /Applications/Byre.app/Contents/Helpers/byre-hands "$d/" && It prints a JSON array, one entry per grant: `name`, `status` (`granted`, `missing`, or `unchecked` when the test couldn't run, such as Automation of Orion while Orion isn't running), `detail` (what to switch on) and `settings`, the `x-apple.systempreferences` URL of its System Settings pane. - A missing folder grant makes macOS show a prompt on that Mac's screen during the check, and the check waits until it is answered, so tell the human to be at that Mac before you run it, and give the call a timeout of a few minutes. -- For Screen Recording, Device Control and Data Access (what macOS 27 calls Accessibility) and Automation, open the pane for the human with `open ''` and tell them which switch to turn on. On a Mac that runs a server, the panes list two entries: `byre` with a black terminal icon is the server, whose grants agents on that Mac use, and `Byre` with byre's app icon is the app, whose grants client tools on that Mac use. The check's `detail` names the one it checked. Device Control and Data Access can list byre switched off; only the switch being on counts. +- For Screen Recording, Device Control and Data Access (what macOS 27 calls Accessibility) and Automation, open the pane for the human with `open ''` and tell them which switch to turn on. On a Mac that runs a server, Device Control and Data Access and Automation list two entries: `byre` with a black terminal icon is the server, whose grants agents on that Mac use, and `Byre` with byre's app icon is the app, whose grants client tools on that Mac use. Screen Recording lists only `Byre` with byre's app icon, and switching it on grants both. The check's `detail` names the switch to turn on. Device Control and Data Access can list byre switched off; only the switch being on counts. - Check again after the human says they've done it. ## Client Macs