Files
nucleic/guest/NucleicVMAgent

NucleicVMAgent

The native in-guest automation agent for Nucleic's macOS VMs — design and rationale in docs/MACOS_VM_NATIVE_AGENT.md. It runs inside the guest as a LaunchAgent in the auto-login Aqua session, listens on vsock port 2035, and executes NDJSON ops with Apple's frameworks directly:

  • AXUIElement — semantic observe + control (ax_dump, ax_action, ax_set_value, ax_focus, ax_element_at). Framebuffer-independent by-identity control: drive the UI without pixels, and keep working even when screenshots are blank or unavailable.
  • ScreenCaptureKit — in-process screenshot.
  • CGEvent — raw click/move/drag/type/key/scroll (a real wheel event).

A separate SwiftPM package from the main Nucleic one: the host app's floor is macOS 26, but this binary runs INSIDE the guest (supported guest is macOS 27). The package keeps a low platform floor so the same binary builds against older SDKs; SwiftPM has no per-target platform floors.

Build & install

# On the host (needs Swift; sign stably or TCC grants break on rebuild):
NUCLEIC_VMAGENT_SIGN_IDENTITY="Developer ID Application: …" ../../scripts/build-vm-agent.sh
# → dist/NucleicVMAgent.app + dist/xyz.blakeslee.nucleic.vmagent.plist

# Stage dist/* next to scripts/provision-macos-guest.sh inside the guest and run the provisioner —
# Phase 7d installs the app, the LaunchAgent, and the three TCC grants (Accessibility, PostEvent,
# ScreenCapture). On the supported macOS 27 build these grants are written automatically.

The host side (MacVMAgentClient in Sources/NucleicCore/MacVM/) probes the agent with a vsock ping per VM boot and transparently falls back to the SSH + cliclick path when it's absent — the agent is strictly additive. Wire constants are mirrored between VMAgentCore.AgentWire (here) and MacVMAgentWire (host); keep them in lockstep.

Agent log inside the guest: /tmp/nucleic-vmagent.log.