fix(acr-wedge): use AppleScript on macOS for keystroke injection

pyautogui's Quartz-based keystroke path often fails on newer macOS
because the Python CLI binary doesn't auto-surface in System Settings
> Accessibility. User reported the daemon detected taps fine but
keystrokes never landed in any window.

Switch to AppleScript / System Events on macOS. Permission attaches
to whatever terminal/app launched the Python process (Terminal.app,
iTerm, etc.) — a familiar named app the user can grant Accessibility
to in one click. Combined keystroke + Return in a single osascript
call to keep latency ~100ms per tap.

Fall back to pyautogui if osascript fails (handles edge cases) and
on non-macOS platforms.
This commit is contained in:
gsinghpal
2026-05-15 19:56:49 -04:00
parent d009a1ef50
commit d75198be9f

View File

@@ -112,23 +112,62 @@ def read_uid(connection):
return ":".join(f"{b:02X}" for b in data)
def _emit_macos(uid):
"""Type via AppleScript / System Events.
Permission attaches to the terminal/app that launched this Python
process — typically Terminal.app, iTerm, or another known GUI app
that the user can easily find in Accessibility settings. More
reliable than pyautogui's Quartz path which often fails to surface
Python in the Accessibility list on newer macOS.
"""
import subprocess
script = (
'tell application "System Events"\n'
f' keystroke "{uid}"\n'
' key code 36\n' # Return
'end tell'
)
result = subprocess.run(
["osascript", "-e", script],
capture_output=True, text=True, check=False, timeout=3,
)
if result.returncode != 0:
raise RuntimeError(f"osascript failed: {result.stderr.strip()}")
def _emit_other(uid):
"""Type via pyautogui (works on Linux, Windows, and as macOS fallback)."""
import pyautogui
pyautogui.typewrite(uid, interval=0.005)
pyautogui.press("enter")
def emit_uid(uid, type_keys=True, verbose=False):
"""Type UID + Enter into the focused window via pyautogui."""
"""Type UID + Enter into the focused window."""
if verbose:
print(f"[wedge] TAP {uid}")
if not type_keys:
return
if sys.platform == "darwin":
try:
import pyautogui
_emit_macos(uid)
return
except Exception as e:
if verbose:
print(f"[wedge] osascript path failed ({e}); falling back to pyautogui")
try:
_emit_other(uid)
except ImportError:
print(
"[wedge] pyautogui is not installed; cannot type. "
"Run: pip3 install pyautogui (or use --no-type for detect-only)",
file=sys.stderr,
)
return
pyautogui.typewrite(uid, interval=0.005)
pyautogui.press("enter")
except Exception as e:
print(f"[wedge] type error: {e}", file=sys.stderr)
def run_loop(args):