Component: agents/macos/ — macOS in-VM agent
Swift package that runs inside a macOS VM. Exposes the TestAnyware
agent HTTP surface on port 8648 using Apple's native accessibility APIs
(ApplicationServices / AXUIElement). Transport is
Hummingbird.
Layout
agents/macos/
├── Package.swift
├── Sources/
│ ├── testanyware-agent/ # Entry point (executable)
│ │ └── AgentServer.swift # Router + endpoint registration
│ ├── TestAnywareAgent/ # Library (endpoint handlers, AX walkers)
│ └── TestAnywareAgentProtocol/ # Self-contained copy of the wire types
└── Tests/
Key design notes
- Self-contained — deliberately does not path-depend on the host
CLI's copy of
TestAnywareAgentProtocol. Vendors its own copy of the Swift sources so the agent builds in any working copy without requiring thecli/package. - Single-process — one
AsyncHTTPServerbinds0.0.0.0:8648and dispatches to handler closures inAgentServer.swift. - No state between requests — each handler walks the AX tree fresh. Clients responsible for retries when AX isn't ready.
Endpoint wiring
All routes live in
agents/macos/Sources/testanyware-agent/AgentServer.swift:
GET /health
POST /windows, /snapshot, /inspect
POST /press, /set-value, /focus, /show-menu
POST /window-focus, /window-resize, /window-move, /window-close, /window-minimize
POST /wait
POST /exec, /upload, /download, /shutdown
POST /debug/ax (macOS-only diagnostic)
Wire shapes are documented in docs/architecture/agent-protocol.md.
Build / test
Inside the VM (or cross-built on the host for installation into the golden image):
cd agents/macos
swift build -c release
# Binary ends up at .build/release/testanyware-agent
swift test
The binary is installed at /usr/local/bin/testanyware-agent inside
the macOS golden and launched by a LaunchAgent with label
com.linkuistics.testanyware-agent.
Common pitfalls
- TCC grant requires SIP cycle. The macOS golden-image creation script boots into Recovery, disables SIP, writes a direct TCC database row granting AX to the binary with its code-signing requirement, then re-enables SIP. Modifying the binary after installation invalidates the grant — the installed path and signature are load-bearing.
- AX tree not ready at login. Immediately after desktop login,
AX calls can return empty trees for up to several seconds. Call
POST /waitfirst in scripts that run right after boot. - Tahoe drop-shadow inset. AX-reported window origin includes the
drop-shadow inset (~40 px). See
docs/user/troubleshooting.md. NSStackViewelement resolution. Text fields hosted insideNSStackViewcontainers on Tahoe may not appear viakAXChildrenAttribute. Workaround in troubleshooting.md.- Keep the protocol copy in sync. If you change wire shapes, apply
the same change in
cli/Sources/TestAnywareAgentProtocol/. The round-trip test incli/Tests/TestAnywareAgentProtocolTests/is the safety net.