diff --git a/apps/macos/Sources/OpenClaw/AppState.swift b/apps/macos/Sources/OpenClaw/AppState.swift index 935b104456ce..8c84a7709b4a 100644 --- a/apps/macos/Sources/OpenClaw/AppState.swift +++ b/apps/macos/Sources/OpenClaw/AppState.swift @@ -264,8 +264,20 @@ final class AppState { didSet { self.ifNotPreview { UserDefaults.standard.set(self.peekabooBridgeEnabled, forKey: peekabooBridgeEnabledKey) - Task { await PeekabooBridgeHostCoordinator.shared.setEnabled(self.peekabooBridgeEnabled) } } + self.applyPeekabooBridgeHostState() + } + } + + /// PeekabooBridge shares Computer Control's local UI-automation surface, so the host only + /// runs while Computer Control is enabled. With Computer Control off, users drive Peekaboo + /// via its own Mac app instead of a second, separately toggled bridge here. + func applyPeekabooBridgeHostState() { + self.ifNotPreview { + let computerControlEnabled = UserDefaults.standard + .object(forKey: computerControlEnabledKey) as? Bool ?? false + let shouldRun = self.peekabooBridgeEnabled && computerControlEnabled + Task { await PeekabooBridgeHostCoordinator.shared.setEnabled(shouldRun) } } } diff --git a/apps/macos/Sources/OpenClaw/GeneralSettings.swift b/apps/macos/Sources/OpenClaw/GeneralSettings.swift index d27d2266da2d..f4403bd2f0c8 100644 --- a/apps/macos/Sources/OpenClaw/GeneralSettings.swift +++ b/apps/macos/Sources/OpenClaw/GeneralSettings.swift @@ -59,6 +59,10 @@ struct GeneralSettings: View { CanvasManager.shared.hideAll() } } + .onChange(of: self.computerControlEnabled) { _, _ in + // Turning Computer Control on/off must start or stop the gated PeekabooBridge host. + self.state.applyPeekabooBridgeHostState() + } .onDisappear { self.gatewayDiscovery.stop() } } @@ -114,9 +118,13 @@ struct GeneralSettings: View { SettingsCardToggleRow( title: "Enable Peekaboo Bridge", - subtitle: "Allow signed tools (e.g. `peekaboo`) to drive UI automation via PeekabooBridge.", - binding: self.$state.peekabooBridgeEnabled, + subtitle: """ + Allow signed tools (e.g. `peekaboo`) to drive UI automation via PeekabooBridge. \ + Requires Computer Control; otherwise run Peekaboo's own Mac app. + """, + binding: self.peekabooBridgeBinding, showsDivider: false) + .disabled(!self.computerControlEnabled) } SettingsCardGroup("Browser") { @@ -247,6 +255,14 @@ struct GeneralSettings: View { set: { self.state.isPaused = !$0 }) } + // Reflect the effective bridge state: off (and disabled) whenever Computer Control is off, + // so the row matches the host that actually runs instead of a standalone toggle. + private var peekabooBridgeBinding: Binding { + Binding( + get: { self.computerControlEnabled && self.state.peekabooBridgeEnabled }, + set: { self.state.peekabooBridgeEnabled = $0 }) + } + private func updateActiveWork(active: Bool) { guard !self.isPreview else { return } if active { diff --git a/apps/macos/Sources/OpenClaw/MenuBar.swift b/apps/macos/Sources/OpenClaw/MenuBar.swift index dbc63f3c4418..49800952f6d6 100644 --- a/apps/macos/Sources/OpenClaw/MenuBar.swift +++ b/apps/macos/Sources/OpenClaw/MenuBar.swift @@ -450,7 +450,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate { Task { PresenceReporter.shared.start() } Task { await HealthStore.shared.refresh(onDemand: true) } Task { await PortGuardian.shared.sweep(mode: AppStateStore.shared.connectionMode) } - Task { await PeekabooBridgeHostCoordinator.shared.setEnabled(AppStateStore.shared.peekabooBridgeEnabled) } + AppStateStore.shared.applyPeekabooBridgeHostState() DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { CLIInstallPrompter.shared.checkAndPromptIfNeeded(reason: "launch") } diff --git a/docs/platforms/mac/peekaboo.md b/docs/platforms/mac/peekaboo.md index f21b4018f7cb..de97c5b9f3ce 100644 --- a/docs/platforms/mac/peekaboo.md +++ b/docs/platforms/mac/peekaboo.md @@ -29,9 +29,9 @@ Use Peekaboo for the broad macOS automation surface via OpenClaw.app's permissio ## Enable the bridge -In the macOS app: **Settings -> Enable Peekaboo Bridge**. +In the macOS app: **Settings -> Enable Peekaboo Bridge**. The toggle requires **Allow Computer Control** to be on, since both grant local UI automation; with Computer Control off the toggle is disabled and the host does not run. To drive Peekaboo without Computer Control, run Peekaboo's own Mac app as the host instead. -When enabled, OpenClaw starts a local UNIX socket server at `~/Library/Application Support/OpenClaw/`. If disabled, the host stops and `peekaboo` falls back to other available hosts. The coordinator also maintains legacy socket symlinks (`clawdbot`, `clawdis`, `moltbot` under Application Support) pointing at the current socket for older `peekaboo` installs. +When enabled (and Computer Control is on), OpenClaw starts a local UNIX socket server at `~/Library/Application Support/OpenClaw/`. If disabled, the host stops and `peekaboo` falls back to other available hosts. The coordinator also maintains legacy socket symlinks (`clawdbot`, `clawdis`, `moltbot` under Application Support) pointing at the current socket for older `peekaboo` installs. ## Client discovery order