mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 17:17:38 +00:00
* fix(computer): fail set-value when its read-back shows a different value A value_mismatch came back as ok with exit 0, so callers that check only the exit status took an ignored or misplaced write as success. The runtime now turns it into a verification_failed error at the sidecar boundary, which covers every provider and every caller of the RPC. Part of #33511 * fix(computer): pair macOS AX windows with their CGWindowID AppKit and WebKit windows don't answer AXWindowNumber, so matching a capture to its accessibility window fell back to frames. Two windows with the same frame then paired the screenshot of one with the tree and title of whichever AX listed first, under the first one's id. Read the id with _AXUIElementGetWindow, as yabai and Hammerspoon do, and keep AXWindowNumber as the fallback. Fixes #27252 * fix(computer): fail fast when a macOS app stops answering accessibility Every request runs behind one helper lock, and a hung app cost a timeout per AX read, so one get-app-state on it held every agent for up to the 60 s RPC timeout. Pin the AX messaging timeout at 1.5 s, refuse an app that doesn't answer a root probe, and stop a tree walk at the first read that waits out the timeout, each with an action_timeout error. A press or write that the app received but didn't answer now fails the same way, saying it may have taken effect. Before, the click fell through to AXConfirm and then a synthetic click, so a button whose handler blocked ran twice, and type-text typed the text again as keys. * fix(computer): report macOS accessibility actions that changed nothing AXPress and other accessibility actions came back as plain unasserted success even when the app ignored them, as Chromium does for a role=button whose handler sits on a child. After such an action the helper now compares the target's tree, frame and on-screen windows with the snapshot it took just before. If they still match after re-reading for about a second, the result says unverified/suspected_noop; if they change late, the result carries the settled state instead. The new reason is additive; older CLIs print it like any other reason. Refs #31758 * fix(computer): propagate late macOS observation failures * fix(computer): select existing text before replacement * fix(computer): share native macOS window identity
46 lines
1.8 KiB
Swift
46 lines
1.8 KiB
Swift
import ApplicationServices
|
|
import CoreGraphics
|
|
import Darwin
|
|
import Foundation
|
|
|
|
public enum AccessibilityWindowIdentity {
|
|
private enum Failure: Error {
|
|
case unreadable
|
|
}
|
|
|
|
/// Nil permits geometry fallback; unreadable identity must refuse input.
|
|
public static func windowID(of element: AXUIElement) throws -> CGWindowID? {
|
|
var nativeAbsent = true
|
|
if let getWindow {
|
|
var windowID: CGWindowID = 0
|
|
let status = getWindow(element, &windowID)
|
|
if status == .success, windowID != 0 { return windowID }
|
|
nativeAbsent = status == .attributeUnsupported || status == .noValue
|
|
}
|
|
|
|
var value: CFTypeRef?
|
|
switch AXUIElementCopyAttributeValue(element, "AXWindowNumber" as CFString, &value) {
|
|
case .success:
|
|
guard let number = value as? NSNumber,
|
|
CFGetTypeID(number) != CFBooleanGetTypeID(),
|
|
let windowID = CGWindowID(exactly: number.doubleValue), windowID != 0
|
|
else { throw Failure.unreadable }
|
|
return windowID
|
|
case .attributeUnsupported, .noValue:
|
|
guard nativeAbsent else { throw Failure.unreadable }
|
|
return nil
|
|
default:
|
|
throw Failure.unreadable
|
|
}
|
|
}
|
|
|
|
private typealias GetWindow = @convention(c) (AXUIElement, UnsafeMutablePointer<CGWindowID>) -> AXError
|
|
|
|
// AppKit and WebKit windows often omit AXWindowNumber.
|
|
private static let getWindow: GetWindow? = {
|
|
guard let symbol = dlsym(UnsafeMutableRawPointer(bitPattern: -2), "_AXUIElementGetWindow") else { return nil }
|
|
// SAFETY: HIServices exports AXError _AXUIElementGetWindow(AXUIElementRef, CGWindowID *).
|
|
return unsafeBitCast(symbol, to: GetWindow.self)
|
|
}()
|
|
}
|