Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Apps/CLI/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added
- Add `app list --include-installed` and MCP `includeInstalled` as native PID-free installed-application sidecars with declared UI/background classification.
- Add durable Bridge 1.38 browser namespaces for opaque page capabilities and exact native PID-generation/window binding across CLI invocations.
- Report per-window `combined_eligible`, `pixels_only`, or `unknown` observation eligibility, including screenshot-only recovery.
- Add `type --at` for atomic exact-window background focus-only Accessibility input plus typing from one fresh screenshot snapshot.
- Add `click --modifiers ... --foreground` with exact snapshot preflight and truthful cursor/focus restoration reporting.

### Changed
- Require the exact issuing `--bridge-socket` alongside the owner-private namespace file on every durable browser namespace invocation.
- Read `config credential set` secrets from no-echo prompts, stdin, or owner-only files; let `config provider add` also accept non-secret references; retain deprecated argv compatibility.
- Skip provider discovery and Agent construction for caller-local commands that cannot invoke the Agent.
- Avoid reopening and hashing Bridge screenshot artifacts twice before CLI or MCP consumption while retaining signed client verification and use-time publication checks.
Expand All @@ -34,7 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Report background text, editable special keys, and clears with their actual AXValue, event, or composite delivery; count only real key events as key presses; preserve the planned receiver literal after escape processing; and require protocol 1.36 before AX-capable remote type requests.
- Revalidate exact-window focused elements and the application's internal key window before typing, reject parent targets with attached sheets while preserving independently identified exact sheet targets, confirm clear-plus-literal text only from a generation-bound value change after bounded event settlement, keep pixel-focus setup confirmation separate from its typing leaf, and stop reporting no-change, missing, or dispatched-but-unverified outcomes as typed characters.
- Require explicit standalone CLI foreground consent for application focus/switch and Dock visibility changes, and reject contradictory app-switch selectors before runtime discovery.
- Scope process-local persistent MCP and Agent browser refs to one caller, provider child epoch, page, snapshot, and document generation; require the pinned provider's structured capability data, reserve targets before provider setup, preserve post-dispatch failure evidence while withholding invalid refs, and let different background session lanes overlap under origin-recoverable durable cross-process invalidation while same-target access and Bridge-backed opaque refs remain fail closed.
- Scope browser refs to one caller, provider child epoch, page, snapshot, and document generation; require the pinned provider's structured capability data, reserve targets before provider setup, preserve post-dispatch failure evidence while withholding invalid refs, and let different background session lanes overlap under origin-recoverable durable invalidation while legacy Bridge calls remain fail closed and explicit Bridge 1.38 namespaces carry authenticated durable authority.
- Bind Bridge 1.34 Chrome channel connections to an exact live Chrome bundle, native process-owned DevTools listener, and approval-gated WebSocket under one 90-second deadline, verifying `Browser.getVersion` once without legacy HTTP discovery or repeated permission probes and failing closed on helper-service names, file, socket, generation, or endpoint drift.
- Authenticate native Chrome channels against Google Team ID `EQHXZ8M8AV`, pin the exact signed identifier and CDHash for the process generation, and enumerate the target process's complete listener inventory independently of Peekaboo's file-descriptor limit.
- Honor the configured default save directory for pathless pixel-only `see` captures and add collision-resistant generated filenames for concurrent callers, while preserving explicit paths and stdout streaming. Thanks @PollyBot13 for #607.
Expand Down
12 changes: 8 additions & 4 deletions Apps/CLI/Sources/PeekabooCLI/CLI/CommanderRuntimeExecutor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,14 @@ enum CommanderRuntimeExecutor {
runtimeFactory: RuntimeFactory
) async throws {
if var runtimeCommand = command as? any AsyncRuntimeCommand {
let runtimeOptions = try CommanderCLIBinder.makeRuntimeOptions(
from: resolved.parsedValues,
commandType: resolved.type
)
let runtimeOptions = if let configurable = runtimeCommand as? any RuntimeOptionsConfigurable {
configurable.runtimeOptions
} else {
try CommanderCLIBinder.makeRuntimeOptions(
from: resolved.parsedValues,
commandType: resolved.type
)
}
if self.shouldExportCaptureEnginePreference(runtimeOptions),
let capturePreference = runtimeOptions.captureEnginePreference {
// Respect explicit engine choice; also allow disabling CG globally.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ struct CommandRuntimeOptions {
var requiredElementActionOperations: Set<PeekabooBridgeOperation> = []
var requiresInspectAccessibilityTree = false
var requiresBrowserMCP = false
/// Protocol 1.38 durable browser capability namespaces require a negotiated remote Bridge owner.
/// They must never fall back to the caller-local or legacy raw browser provider.
var requiresBrowserCapabilityNamespace = false
var requiresApplicationLaunchOptions = false
var requiresSafeBackgroundApplicationLaunchNoOp = false
var requiresNewApplicationInstanceLaunch = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ enum BridgeCapabilityPolicy {
return false
}

if options.requiresBrowserMCP, !self.supportsBrowserMCP(for: handshake) {
if !self.supportsBrowserRequirements(for: handshake, options: options) {
return false
}

Expand Down Expand Up @@ -129,6 +129,20 @@ enum BridgeCapabilityPolicy {
return true
}

private static func supportsBrowserRequirements(
for handshake: PeekabooBridgeHandshakeResponse,
options: CommandRuntimeOptions
) -> Bool {
if options.requiresBrowserMCP, !self.supportsBrowserMCP(for: handshake) {
return false
}
if options.requiresBrowserCapabilityNamespace,
!PeekabooBridgeClient.supportsBrowserCapabilityNamespaces(handshake) {
return false
}
return true
}

private static func supportsObservationRequirements(
for handshake: PeekabooBridgeHandshakeResponse,
options: CommandRuntimeOptions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,10 @@ enum RuntimeHostResolver {
}

static func requiredHostFailure(explicitSocket: String?, options: CommandRuntimeOptions) -> String? {
if options.requiresBrowserCapabilityNamespace {
return "No authenticated on-demand Bridge host negotiated protocol 1.38 browser capability " +
"namespaces and exact native-window binding. Update and relaunch Peekaboo before retrying."
}
if options.requiresExactWindowPixelFocusTyping {
return "No compatible Bridge host advertises atomic exact-window pixel-focus typing. " +
"Update and relaunch Peekaboo, then observe the exact target again before retrying."
Expand Down Expand Up @@ -533,6 +537,10 @@ enum RuntimeHostResolver {
return .local(snapshotInvalidationRemoteSocketPaths: [])
}

if options.requiresBrowserCapabilityNamespace {
return .remote
}

if self.inputPolicyRequiresLocal(
options: options,
environment: environment,
Expand Down Expand Up @@ -629,6 +637,7 @@ enum RuntimeHostResolver {
return options.requiresScreenCapturePermission ||
options.requiresInspectAccessibilityTree ||
options.requiresBrowserMCP ||
options.requiresBrowserCapabilityNamespace ||
options.requiresImplicitSnapshotInvalidation ||
options.usesPerToolSnapshotInvalidation ||
options.requiresForegroundModifierClickSnapshotLease ||
Expand Down Expand Up @@ -667,7 +676,7 @@ enum RuntimeHostResolver {
daemons.append(ImplicitRemoteCandidate(
socketPath: socketPath,
requireReusableDaemon: true,
requiredHostKind: nil,
requiredHostKind: options.requiresBrowserCapabilityNamespace ? .onDemand : nil,
requiresValidatedHistoricalDaemon: false
))
}
Expand All @@ -687,6 +696,10 @@ enum RuntimeHostResolver {
requiresValidatedHistoricalDaemon: false
)

if options.requiresBrowserCapabilityNamespace {
return daemons
}

if options.requiresApplicationRelaunch || options.requiresSurvivingApplicationHost {
return daemons
}
Expand Down Expand Up @@ -823,6 +836,8 @@ enum RuntimeHostResolver {
)
return RemotePeekabooServices(
client: client,
supportsBrowserCapabilityNamespaces:
PeekabooBridgeClient.supportsBrowserCapabilityNamespaces(handshake),
supportsTargetedHotkeys: targetedHotkey.isEnabled,
supportsProcessGenerationPinnedHotkeys:
BridgeCapabilityPolicy.supportsProcessGenerationPinnedHotkeys(for: handshake),
Expand Down
Loading