Skip to content

Latest commit

 

History

224 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

DeviceDeck

Automate your iOS and Android app like a web app.

Your Playwright tests and AI agents drive it with the browser tools they already use. No Appium, no new tool.

A stock Playwright test on the left runs line by line — page.goto() to a Simulator on a Mac at 10.0.4.21, getByRole().fill() for username and password, click Sign In, expect the logged-in screen — while a browser tab on the right shows the iOS Simulator reacting live and the runner goes green.

Stock Playwright driving a real iOS Simulator from another machine — the same DOM an AI agent drives too.

Playwright Cypress Puppeteer Playwright MCP Claude Code Maestro flows maestro-runner

iOS Simulator Android Emulator macOS host Single binary

License: Apache 2.0 Go 1.26 by DeviceLab.dev

Get started · What it does · Using it · Docs · Known limits


Get started

Needs a Mac with Xcode (and the Android SDK for Android) — devicedeck doctor says what's missing.

# 1. Install, then start DeviceDeck with your app build (.app for a simulator, .apk for an emulator)
curl -fsSL https://open.devicelab.dev/install/devicedeck | bash
export PATH="$HOME/.devicedeck/bin:$PATH"   # or open a new terminal
devicedeck --app path/to/MyApp.app

# 2. Give Claude Code the browser tool and DeviceDeck's plugin
claude mcp add playwright npx @playwright/mcp@latest
claude plugin marketplace add devicelab-dev/DeviceDeck
claude plugin install devicedeck@devicedeck-marketplace
#    then start claude: /mcp should list both playwright and devicedeck

# 3. Ask Claude — it finds your app, boots a simulator, launches it, drives it, writes the test
#    "Write a Playwright test that logs in to my app"

# 4. Run it
npx playwright test

Need a simulator build of your app, or the step-by-step? See docs/getting-started.md. Using Gemini CLI, Codex, VS Code or Cursor? Only step 2 changes — see Other agents. Prefer to use devices by hand? Open the console at http://127.0.0.1:8787.

Or install with npm

In a Playwright project, add DeviceDeck as a dev dependency, so every machine and CI job runs the same version — no install script, nothing downloaded after npm install:

npm install --save-dev devicedeck
npx devicedeck --app path/to/MyApp.app

Or try it without installing: npx devicedeck --app path/to/MyApp.app. For the Claude plugin, which starts devicedeck mcp itself, install it globally so it is on your PATH: npm install -g devicedeck. Playwright can also start it for you, in playwright.config.ts:

webServer: { command: 'npx devicedeck --app path/to/MyApp.app', url: 'http://127.0.0.1:8787', reuseExistingServer: true },

What it does

Share your simulators. Run devicedeck on a Mac and everyone on your network can drive its simulators and emulators from their own browser — tap, type, and Inspect the native UI — with no Xcode, no Android Studio, and no device of their own.

Automate them with the tools you already use. The app's native UI is served as an ordinary web page, so Playwright, Cypress and Puppeteer tests drive it by selector, and your AI agent drives it through Playwright MCP — the same browser tool it uses for the web. Nothing mobile-specific to learn.

Record a flow. Use the app by hand in the console and DeviceDeck writes it down as a Maestro flow, with durable selectors, ready to review and replay — unchanged on real devices at devicelab.dev.

One binary behind all three — nothing to fork, no Xcode project to open.

How it works

The native UI tree is mirrored as real DOM — accessibility ids become data-testid, element types become ARIA roles — so stock selectors drive it, whether from an agent or a test:

await page.goto(`/device/${udid}?app=dev.devicelab.testhive`);
await page.getByRole('textbox', { name: 'Username' }).fill('devicelab');
await page.getByRole('button', { name: 'Sign In' }).click();
await expect(page.getByText('Hello, devicelab!')).toBeVisible();

A single Go binary streams the simulator or emulator to the browser and serves that DOM — the same page and selectors drive iOS and Android alike.

Using it

How the agent drives it

The agent drives the device the way it drives any web page: it snapshots the page, reasons over the tree, and acts by ref — no coordinates, no vision model. Here it picks the right Add among five identical ones:

An AI agent using Playwright MCP: browser_snapshot returns the product screen as an accessibility tree with five identical Add buttons, a reasoning step picks the one after Maestro (add-to-cart-2, ref e24), browser_click adds it, and opening the cart confirms Maestro was added — not Appium.

Other agents

In Get started, steps 1, 3 and 4 are the same; only step 2 changes.

Agent Step 2
Gemini CLI gemini extensions install https://github.com/devicelab-dev/DeviceDeck
Codex CLI codex mcp add playwright -- npx @playwright/mcp@latest
codex mcp add devicedeck -- devicedeck mcp
npx skills add devicelab-dev/DeviceDeck
VS Code / Copilot code --add-mcp '{"name":"playwright","command":"npx","args":["@playwright/mcp@latest"]}'
code --add-mcp '{"name":"devicedeck","command":"devicedeck","args":["mcp"]}'
npx skills add devicelab-dev/DeviceDeck
Cursor, and others add the same two MCP servers in the agent's settings, then npx skills add devicelab-dev/DeviceDeck — docs/agents.md

Each agent gets the same three pieces: Playwright MCP to drive the device page, DeviceDeck's device tools to boot devices and launch apps, and its skills for writing tests, recording flows and triaging failures.

Already have tests, or prefer to write them?

Point Playwright, Cypress or Puppeteer at http://127.0.0.1:8787/device/{udid}?app={bundleId}, run one worker per device, and select by data-testid (the app's accessibility id) or by role. To record a test instead, or start from a template, see docs/testing.md.

By hand — the console

The console lists every simulator, emulator and --app build on the Mac. Pick a device to boot and stream it, Launch an app onto it, and drive it with your mouse and keyboard; Inspect shows each element's id, role and text — the selectors your tests use. Teammates open the network address devicedeck prints. Guide: docs/console.md.

Record a flow

Press Record, use the app, press Stop: DeviceDeck writes it as a Maestro flow with graded, durable selectors — replayable with maestro-runner and unchanged on real devices at devicelab.dev. Guide: docs/flows.md.

Install options

With npm: npm install --save-dev devicedeck in a project, or npm install -g devicedeck — see Or install with npm. The host is a Mac; installing it on Linux (a CI job that drives a remote Mac, say) is harmless.

The install script puts DeviceDeck in ~/.devicedeck and adds its bin folder to your PATH — no sudo, and nothing else to install: the Android driver ships inside the binary. Pin a version with curl -fsSL https://open.devicelab.dev/install/devicedeck | bash -s -- --version 0.1.0.

Or download an archive from Releases and run it in place (the two sidecars sit beside the binary in bin/):

tar xzf devicedeck-<version>-darwin-arm64.tar.gz
./devicedeck-<version>-darwin-arm64/bin/devicedeck

Or build from source (needs the Xcode toolchain for the Swift sidecars): make sidecar && make build.

devicedeck on its own starts the console at http://127.0.0.1:8787. --app takes .app simulator builds, .apk files, or a folder of them, and installs each the first time it is launched on a device. On start it prints the console link (and the network address teammates use), your apps, how to connect Claude Code, and any missing tools with how to fix them.

Everything DeviceDeck writes lives in ~/.devicedeck (set DEVICEDECK_HOME to move it). To uninstall, delete that folder and the # DeviceDeck line from your shell profile.

Requirements

  • A Mac to host. iOS Simulators, simctl/CoreSimulator and the Swift sidecars are macOS-only, so the machine that runs the devices is a Mac. Clients can be any OS: the surface is a web page, so people, tests and agents drive it from Linux, Windows or another Mac.
  • Xcode with at least one iOS Simulator runtime — iOS 26.2 or newer is strongly recommended; on 18.6 the simulator's render server crashes under repeated capture.
  • For Android: the Android SDK, with adb and emulator on PATH, and at least one virtual device.

devicedeck doctor checks all of this and says how to fix anything missing.

Status

Early release. Both platforms drive end to end: Playwright logs into and checks out of TestHive on iOS and Android, an agent writes those tests itself through Playwright MCP, and the console drives any device by hand. click() and fill() return only once the device has acted, and a filled value is read back from the device — on iOS and on Android's masked password fields. It is an ordinary web page, so other browser drivers (Cypress, Puppeteer) see the same DOM, but this release is tested with Playwright.

It has run on a handful of Macs so far, so expect some first-contact problems — please open an issue with the log folder it prints. See Known limits.

Scope

Simulators and emulators only — no real hardware, no camera, biometrics or carrier. Within that, the ceiling is physics, not an artificial limit.

Known limits

  • The server is unauthenticated. It listens on all interfaces (0.0.0.0:8787) by default, so anyone on your network can view and drive your devices. That suits a trusted office or home network; on shared Wi-Fi run devicedeck --addr 127.0.0.1:8787 to keep it to this Mac. There is no access control yet — do not put it on the open internet as-is.
  • A driver that dies without closing its connection holds its device for up to half a minute, until a missed ping releases it — see docs/testing.md.
  • Android video is ~18 fps and heavier than iOS. The emulator's gRPC screenshot stream offers no video codec, so every frame is a full PNG rather than an H.264 delta. Emulators DeviceDeck boots run headless, because macOS throttles an occluded window — and the emulator's window is occluded exactly when you are watching the browser.
  • A freshly launched app swallows touches for about a second after its screen is already in the accessibility tree. POST /app/launch waits this window out, so a test that launches through it can act at once; a raw terminate-and-launch outside it cannot.
  • A raw relaunch does not reset app state — a native app stays logged in across terminate-and-launch. POST /app/launch wipes the app's data first by default, starting at a first-run screen; pass ?reset=no to resume where it was left.
  • Two-finger gestures are dropped on Android. They work on iOS; the Android driver has no mapping for them, so they are discarded rather than guessed at.

Troubleshooting

Every command, flag, environment variable and HTTP endpoint: docs/cli-reference.md.

devicedeck doctor checks the tools DeviceDeck needs: Xcode, the iOS runtime, adb, an Android emulator, Node.js, Claude Code and maestro-runner.

Every run writes a folder under ~/.devicedeck/logs (the path is printed at startup): devicedeck.log with every request, device event and tool call, runner.log from the device driver, one log per sidecar and device, and crash.log if the process panics. The terminal shows only what needs attention; DEVICEDECK_LOG=info or debug shows more there too. The last 20 runs are kept — attach the folder to an issue.

Claude wrote a web test, or says the folder is empty. It has no DeviceDeck tools: run /mcp in Claude. If devicedeck is missing, the plugin is not installed — run all three commands of step 2 (marketplace add alone is not enough), then start Claude again. If it is listed but failing, the devicedeck binary is not on the PATH Claude was started from. Also check that devicedeck is running with your --app build in another terminal.

Licence

Apache License 2.0 — see LICENSE.

Built on

maestro-runner for the device drivers, Apache-2.0. The Swift sidecars derive from baguette (Apache-2.0) and tapflow (MIT); ATTRIBUTION.md records what was reused and where it lives.

DeviceLab
Built by devicelab.dev

About

Your simulators and emulators in a browser — and what you do by hand becomes a test your existing framework can run.

Resources

Stars

7 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages