Agent-readable docs index: /llms.txt. Full docs in one file: /llms-full.txt. Download /docs.zip to grep all markdown files locally.

Read whether input goes over Bluetooth or XCTest

/v1/leases/{lease}/controlGET
How the daemon sends input to the iPhone (Bluetooth or XCTest), and the XCTest runner state.
Authorizationoptional
bearerAuth?Bearer <token>
Required on the tunnel. An API token of the account, from ghosthand tunnel url --new-token. The unix socket needs no token
Response
200 ยท Control mode
control *"bluetooth" | "xctest"
How input reaches the iPhone. bluetooth (default): the Mac is a Bluetooth mouse and keyboard; screenshots come from USB screen capture. xctest: WebDriverAgent, an XCUITest runner, taps, types and takes screenshots (they show dialogs that USB capture hides). Needs Xcode, Developer Mode and UI Automation. No passcode unlock, mute or keyboard shortcuts.
xctest?object
WebDriverAgent on this iPhone. Present only in XCTest mode.
Show properties
team *string
Apple team id that signs the runner, e.g. 92MF9DG2LR
state *"stopped" | "building" | "starting" | "running" | "failed"
stopped: not started since the daemon started; the next command starts it (about 15 s). building: Xcode builds it (1-3 min the first time). starting: launched, waiting for its HTTP server. running: input works. failed: see error.
error?string
Why the last start failed and what to do
Default ยท Error (RFC 9457 problem details). `code` is stable; switch on it, not on `title` or `detail`.
code *"invalid-request" | "unauthorized" | "not-found" | "device-not-found" | "no-device" | "multiple-devices" | "permission-denied" | "bluetooth-unavailable" | "bluetooth-disconnected" | "no-frame" | "device-locked" | "lock-failed" | "pairing-timeout" | "pointer-not-found" | "lock-state-unavailable" | "lockdown-unavailable" | "unlock-failed" | "mute-unconfirmed" | "text-not-found" | "wait-text-timeout" | "cancelled" | "lease-invalid" | "device-leased" | "recording-active" | "no-recording" | "recording-failed" | "daemon-busy" | "tunnel-offline" | "login-required" | "subscription-required" | "unsupported-in-mode" | "xctest-unavailable" | "task-failed" | "internal-error"
Stable error code. Switch on it; title and detail are for people.
title *string
Short summary of the code, the same for every error with this code
status *integer
HTTP status of the error. In stream mode the response status stays 200.
detail *string
What happened and what to do next. Agents should read it.
Request example
curl -X GET "https://ghosthand.dev/macs/studio/v1/leases/42/control" \ -H "Authorization: Bearer <token>"
Response example
{ "code": "device-leased", "title": "iPhone leased by another client", "status": 409, "detail": "Tommy's iPhone is in use by another lease:\n ses_456 (book a table on OpenTable), acquired 2 min ago, last used 10 s ago (tap)\nDecide first: if that task is still active, give up or tell the user the iPhone is busy." }