manzanas

Kendalikan simulator iOS dari Claude Code, Cursor, atau Codex melalui label aksesibilitas, bukan dengan tangkapan layar dan koordinat ketukan, lengkap dengan sewa dan kumpulan hangat sehingga beberapa agen dapat berbagi satu Mac.

Dokumentasi

manzanas

Release CI License: Apache-2.0 AllMCPs Verified

A Mac daemon for multi-agent iOS simulator fleet orchestration: leases, actions, streaming, deterministic state, and an exportable run journal for AI agents (and humans) sharing simulators.

https://github.com/user-attachments/assets/deb577d0-1123-4a89-90d8-8d9c7684a3cf

The launch film — leases, actions, live streaming, warm pools, the broker, and the run journal in 52 seconds. Sound on.

manzanasd runs on each Mac host and owns everything stateful — the simulator registry, the lease table, the warm pool, action backends, streamers, golden images, and the run journal. Clients (manzanas CLI, MCP facade, SDKs) are thin and cross-platform, speaking a versioned JSON protocol over HTTP + WebSocket. manzanas-broker federates N daemons behind one endpoint — start with one Mac, and when you outgrow it the same clients lease across the whole fleet without changing a line.

https://github.com/user-attachments/assets/983b0548-df1a-41a3-812c-f0f39cfaa01c

1 orchestrator, 7 Codex agents, 7 simulators across 3 Macs — leases mean nobody trips over anybody. Real time, no edits.

Website: manzanas.dapsdev.dev (source in site/).

Install

brew tap baribarigood/tap https://github.com/BariBariGood/homebrew-tap
brew trust baribarigood/tap      # Homebrew >= 6 requires trusting third-party taps
brew install manzanasd            # daemon (+ pulls in the manzanas CLI)
brew services start manzanasd     # launchd service on port 7433

See docs/install.md for launchd tarball installs and alternatives, and docs/quickstart.md for a single-Mac walkthrough from install to first screenshot.

Why

Agents driving simulators over raw SSH + CLI tools trip over each other and pay huge fixed costs. manzanasd removes both, with measured numbers (M3 Pro, macOS 26.5, Xcode 26.5; reproduce with make bench):

  • Leases, not locks: TTL-bounded exclusive claims with FIFO queues — no two agents ever drive the same sim.
  • Park/thaw warm pool: idle sims are SIGSTOPped (a parked tree is unschedulable — ~0 idle host CPU no matter what the sim's daemons are doing) and thawed on lease grant: ~0.28 s lease-to-live vs ~7 s for a cold boot (~29 s first boot). The thaw itself is a cached-PID SIGCONT and takes under a millisecond.
  • Warm actions: a resident per-sim helper makes an end-to-end tap ~36 ms vs ~950 ms cold (per-action AXe spawn) — ~3 s cold on Intel.
  • Deterministic state: snapshots, fixtures, per-lease auto-reset, and golden images that stamp out slimmed sims in seconds (~0.75 GB vs ~5 GB stock, via simslim) — how one Mac runs a dozen sims.
  • Evidence: every mutating op under a lease is journaled, with content-addressed artifacts and a PR-ready markdown export.
LeasesTTL-bounded exclusive claims, labels, FIFO queues, auto-reset
Warm poolpark/thaw (SIGSTOP) pool sims: ~0.28 s lease-to-live, ~0 idle CPU
Actionscold (AXe) + warm (resident helper) taps/swipes/typing, composite tap_element with a structured predicate DSL, batches
Auditdeterministic UI checks (touch targets, clipping, alignment, spacing, safe area, missing labels) → findings + annotated screenshot in the journal
StreamingMJPEG fan-out, browser /view page, WS frames
Videoper-lease simctl recordings that land in the journal
Statesnapshots, fixtures, per-lease auto-reset, golden images
Journalappend-only evidence per run, artifacts, export.md
Dashboardread-only web dashboard at /dash: fleet, leases, multiview, journal browser
Devicesphysical iPhones via devicectl + WebDriverAgent (--devices)
Fleetmanzanas-broker federates N Macs behind one endpoint
Clientsmanzanas CLI, MCP tools over stdio, npm wrapper, GitHub Action
Live MJPEG view of a leased simulator at /viewBuilt-in fleet dashboard at /dash
the browser /view MJPEG live pagethe built-in /dash fleet dashboard

Quickstart

New here? docs/quickstart.md walks through a full single-Mac session (install → lease → tap → screenshot). The short version:

make build            # builds bin/manzanasd, bin/manzanas, bin/manzanas-broker

# on a Mac with Xcode:
./bin/manzanasd --addr :7433

# anywhere (Linux/dev/CI), with a mock fleet and a full mock action
# backend — the whole lease→observe→tap→audit loop, no Mac (docs/mock.md):
./bin/manzanasd --addr :7433 --mock

# list simulators
curl -s localhost:7433/v0/targets | jq

# lease one by label, boot it, release it
curl -s -X POST localhost:7433/v0/leases \
  -d '{"labels":["ios26"],"agent_id":"me","ttl_seconds":300}' | jq
curl -s -X POST localhost:7433/v0/targets/<udid>/boot -d '{"lease_id":"<id>"}' | jq
curl -s -X DELETE localhost:7433/v0/leases/<id> | jq

Or the thin client (--json for machine output; see clients/):

manzanas targets
manzanas lease acquire --labels ios26 --agent me --wait
manzanas tap 200 400 --lease lse_...
manzanas mcp        # lease-scoped MCP tools over stdio for agents

Production installs (launchd, releases): docs/install.md.

MCP (Claude Code, Cursor, Codex)

manzanas mcp serves the whole toolset over the Model Context Protocol (stdio), so any MCP-capable agent can lease and drive simulators — with per-session auto-release of leases and self-describing tool errors. Ready-to-paste client configs and troubleshooting: docs/mcp.md.

claude mcp add manzanas -e MANZANASD_ADDR=mac-host:7433 -- /path/to/manzanas mcp

Architecture

┌ Linux / CI / anywhere ────────────┐        ┌ each Mac host ─────────────────────────────┐
│ manzanas (thin client, Go)         │   WS   │ manzanasd (Go daemon)  :7433                │
│  - CLI: lease/tap/observe/...     │◄──────►│  registry ── warm pool (park/thaw, gates)  │
│  - MCP facade (stdio)             │  HTTP  │  leases (TTL, labels, FIFO, auto-reset)    │
│  - eval harness (manzanas-eval)    │        │  actions ── cold AXe / warm simbridge      │
├───────────────────────────────────┤        │  streams (MJPEG fan-out, browser view)     │
│ manzanas-broker  :7440             │        │  state (snapshots, fixtures, golden images)│
│  fleet-wide placement: leases are │───────►│  journal (evidence, artifacts, export.md)  │
│  scheduled across N daemons, then │ probe/ └────────────────────────────────────────────┘
│  clients talk to the owning       │ lease         × one daemon per Mac in the fleet
│  daemon directly (host_addr)      │
└───────────────────────────────────┘

Documentation

Protocol:

  • proto/PROTOCOL.md — the v0 wire protocol: targets, leases, actions (incl. composite tap_element/type_into_element and actions:batch), streams, state, images, journal, WS surface.

Subsystems:

Operations:

The physical fleet this runs on (machines, locks, build caching) is site-specific; docs/fleet.md covers the daemon's part.

Status

Everything above is implemented and running on the fleet — leases + queues, warm pool with safety gates, cold (AXe) + warm (simbridge) action backends, composite/batch actions, MJPEG streaming, video capture, snapshots/fixtures/auto-reset, golden images, journal, dashboard, physical-device support, broker, eval harness, CLI + MCP. Verify with go build ./... && go vet ./... && go test ./... (Linux-safe; simctl paths are mocked).

License

Apache-2.0 — see LICENSE. Releases up to and including v0.6.0 (and their existing tags) were published under MIT and remain MIT; later releases are Apache-2.0.