npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@loadmill/droid-cua

v2.47.0

Published

AI-powered Android testing agent using OpenAI's computer-use model and ADB

Readme

droid-cua


AI-powered mobile testing desktop app for Android and iOS

Create, run, and manage mobile tests with natural language. The desktop app guides setup, connects to your target device or simulator, and turns AI exploration into reusable test scripts.

https://github.com/user-attachments/assets/b9e15a1d-8072-4a2f-a4c5-db180ae38620


droid-cua is a desktop app for AI-powered mobile testing.

It helps teams create, edit, and run tests for Android devices and emulators and iOS simulators on macOS using natural language instead of traditional test code.

Under the hood, droid-cua uses an AI agent to explore your app, execute actions, and save reusable test scripts that can also be run later in headless workflows.


1. Download the desktop app

2. Launch the app

Open the desktop app and choose the platform you want to test.

3. Choose your AI provider

In Settings, choose the CUA model you want to run. Loadmill Smart is the recommended option, Pulse is fast and efficient, and Beacon is experimental. The Loadmill models use your Loadmill sign-in and usage tracking. The OpenAI models use your own OpenAI API key.

4. Connect a target device

  • Android: connect a device or select an emulator
  • iOS: choose a simulator on macOS

No app of your own yet? The Apps page includes a built-in Demo Shop Android app. Install it on a connected Android device or emulator, or pick it for a cloud Android session, to try the agent right away.

5. Create or run a test

Open Your QA agent to describe a scenario, choose a project, and use the currently connected target. The device control links to Devices to connect or change targets. Local devices work directly with their installed apps. App and cloud configuration stay on the Apps and Devices pages. Suggestions fill an editable prompt; sending starts the existing exploration and test-generation flow. You can also edit an existing test or run a saved script with live execution logs.

Use the Back and Forward buttons next to the sidebar toggle to revisit pages and selections. Pending editor changes are saved before Back or Forward leaves the page. Automatic batch-test transitions do not add history entries. Report summaries remain visible while switching between saved runs. Navigation history lasts for the current app session; opening a new destination after going back clears forward history.

You can also keep project run history in a results folder and review past runs from desktop app reports. New design sessions are saved locally in Recent, including their messages, screenshots, and generated tests. Open a past session to review it or view its saved test; historical activity is read-only, while unsaved suggested tests can still be edited and saved without resuming the agent.


  • Android - Physical devices and emulators
  • iOS - Simulators on macOS, and iPhones connected to your Mac by USB

Desktop app support:

  • macOS: Android, iOS simulator, and iPhone (USB) workflows
  • Windows: Android workflows only

Requirements

All platforms:

  • Loadmill sign-in for the Loadmill models, or an OpenAI API key for the OpenAI models
  • Node.js 20 or newer for CLI use

Android:

  • Android Debug Bridge (ADB)
  • Android Emulator CLI for launchable emulators

iOS (macOS only):

  • Xcode with iOS Simulator
  • Appium
  • XCUITest driver

iPhone over USB (macOS only): see Testing on a real iPhone.


  • Desktop-first workflow - Create, run, and manage tests from one app
  • Setup guidance - Configure API access and platform prerequisites in the app
  • Device and simulator connection - Connect Android targets and iOS simulators
  • Network certificate preparation - Manage one active root CA and install it on a connected local simulator or supported emulator, or get manual setup steps for a physical iPhone or Android phone, from the desktop app's Settings → Network capture page
  • Natural-language test creation - Describe flows in plain English
  • Test management - Create, edit, save, and rerun reusable scripts, including tests organized in nested project folders
  • Live execution logs - Watch actions and progress as tests run
  • Reports and history - Review past runs from a project results folder inside the desktop app
  • JUnit XML output - Write standard test reports for CI systems and external tooling
  • Run video export - Generate and download a polished MP4 replay from a desktop run's screenshots and instruction timeline
  • Headless support - Reuse scripts in CLI and automation workflows

Network certificate preparation: Use Settings → Network capture → Manage certificates to see the active CA, upload a single .pem containing a CA certificate and its matching unencrypted private key, generate a new Droid CA, or remove the active CA. Droid stores one active pair in ~/.droid-cua/network/; upload copies the PEM material without changing the source file, and removal does not change device trust. Connect a local iOS Simulator or a rootable Android Emulator, then choose Settings → Network capture → Install on connected device. This installs the active CA, generating a Droid CA if none exists. Devices prepared with a previous CA may need installation again. Droid may reapply Android system trust when a prepared emulator reconnects after a cold boot. Restart an already running Android app after installation so it loads the updated trust store. With a local iPhone connected by USB, Settings → Network capture → Install on connected device does not change the phone; it shows the manual steps instead. Send to iPhone opens the macOS share menu with a DER .cer copy of the active CA named after its common name (for example Droid CUA Root fbd877ab.cer, written to ~/.droid-cua/network/iphone/); choose AirDrop and your iPhone, install it from Settings → Profile Downloaded, then turn on full trust for that common name under Settings → General → About → Certificate Trust Settings. Android Emulator certificate setup supports capable rootable images through Android 16; Google Play images without root access and Android 17 are not supported. With a physical Android phone connected over ADB, Settings → Network capture → Install on connected device writes a PEM .crt copy of the active CA named after its common name (for example Droid CUA Root fbd877ab.crt, in ~/.droid-cua/network/android/), copies it to the phone's /sdcard/Download/ folder with adb push, and shows the manual steps; it changes no phone settings. Install it from Settings → Security → Encryption & credentials → Install a certificate → CA certificate (names vary by vendor), then restart the tested app. If the copy fails, Droid shows the local file path so you can copy it to the phone yourself.

Network capture: Enable Network Capture in desktop Settings or pass --capture-network for CLI tests. Local Chrome and Edge capture through Playwright with no certificate or proxy setup, using the same network queries and report cards as mobile. Capture covers the test browser context, including frames and popups, from run start. Previously loaded traffic is not recovered and existing pages are not automatically reloaded. CLI --url navigation happens after capture starts. Cloud browser capture, WebSocket messages and HAR export are not supported.

Mobile setup: Manually route a prepared local simulator or emulator to 127.0.0.1:8765 (10.0.2.2:8765 from Android Emulator). Enable Network Capture in desktop Settings to keep the proxy forwarding while Droid is open and automatically capture every local mobile test, including each project or scenario case. Existing Network Proxy settings do not turn capture on; opt in to the new setting. For CLI tests, pass --capture-network; this starts the proxy and writes one JSONL log per test. --network-proxy remains a forwarding-only CLI flag. Droid prints or logs each capture path and a best-effort health summary. Desktop artifacts are in the project's results folder; CLI artifacts are beside --report output when enabled, or under logs/. A missing CA stops mobile capture before test actions. No observed traffic or failed HTTPS decryption is reported separately from test assertions, but capture health does not prove that all traffic was observed. WebSocket messages are unsupported. Manual proxy setup is the V1 workflow. Droid does not change device or Mac proxy settings; turn off manually configured routing when finished. If Droid exits while routing remains enabled, restart Droid or clear the proxy to restore connectivity.

Physical iPhone capture: A USB-connected iPhone reaches the proxy over Wi-Fi, so Droid listens on all Mac network interfaces (0.0.0.0:8765) instead of 127.0.0.1 while an iPhone is the target: in the CLI when --avd ios-device:<udid|auto> is used with --capture-network or --network-proxy, and in the desktop app while Network Capture is on and a local iPhone is connected (the proxy switches back to 127.0.0.1 on disconnect, but not during a run). Droid prints the setting to use, for example On the iPhone, set Wi-Fi → Configure Proxy → Manual: server 192.168.1.23, port 8765. Put the Mac and iPhone on the same Wi-Fi network with device-to-device traffic allowed, install and fully trust the Droid CA manually as described above, and set the iPhone proxy by hand; Droid never changes iPhone settings. Turn the iPhone proxy off after testing. Anyone on the same network can use the proxy while it listens on 0.0.0.0, so use a trusted network. Apps that pin certificates still connect through the proxy, but their HTTPS is not decrypted.

Physical Android phone capture: A phone connected over ADB (any serial other than emulator-<port>) is handled the same way as a physical iPhone: Droid listens on 0.0.0.0:8765, in the CLI once the connected device turns out to be a phone (with --capture-network or --network-proxy) and in the desktop app while Network Capture is on and the phone is connected. Droid prints the setting to use, for example On the Android phone, set Wi-Fi → Edit network → Proxy → Manual: hostname 192.168.1.23, port 8765. Install the CA as a user certificate as described above and set the phone's Wi-Fi proxy by hand; Droid does not root the phone or change its settings, and turn the proxy off after testing. Since Android 7, apps trust user-installed CAs only when their network security config allows it, for example <certificates src="user"/> in network_security_config.xml or a <debug-overrides> block in a debuggable build. Debug and QA builds usually opt in; Play Store builds usually do not, so their HTTPS is not decrypted. Chrome trusts user CAs. Rooted-device system trust is not supported.

Captures stop recording at 100 MiB per run, retaining a partial JSONL with a CAPTURE_LIMIT error. Traffic continues forwarding; live network queries report incomplete evidence. Captured bodies are limited to 1 MiB, both on the wire and after decompression. Compressed bodies that expand beyond that limit are omitted with decoded-body-too-large; this is incomplete evidence, not a missing response. The separate query evidence limit is 1,000,000 serialized characters. A capture limit or missing traffic is not proof that an application request never occurred.

For troubleshooting, “No traffic observed” points to routing or an app that did not send traffic; it does not prove an expected request was absent. HTTPS connections with no decrypted requests point to certificate trust or interception compatibility. A port-in-use error means another Droid instance or proxy already owns the listener. Stop that listener before starting the CLI. A capture-size or write failure retains only partial evidence. Ordinary iOS runs without capture do not require proxy or certificate preparation.

Network questions and assertions: With capture enabled, tests can ask Droid to inspect HTTP traffic and compare values across requests in natural language. For example: “Assert that the POST to /pageview after opening Home contains the item_id returned by /home.” The query_network tool searches by URL substring, method, status, flow IDs, request timestamps, or a starting step. It considers all matching exchanges in time order and sends only selected headers/bodies to one side call using the selected run model. Large results require a narrower search; they are not silently truncated. Optional observation windows wait the full duration, up to 30 seconds per query. Absence means only “no matching traffic observed during this window”; unavailable capture or insufficient evidence cannot establish absence. Desktop and HTML reports show one collapsed Network query card per call; expand it to see the question and answer. The agent receives structured flow references and capture diagnostics, while model details stay out of the timeline.

To inspect a saved capture without a model, run node scripts/query-network.mjs path/to/network.jsonl '{"url_contains":"/pageview","method":"POST","sections":["request_body"]}' from the repository. Select request_headers, response_headers, request_body, or response_body; use sections: [] for metadata. Saved-log queries accept after and before timestamps, but not live step IDs or observation waits.

Developer network capture: node scripts/network-capture-smoke.mjs still offers a standalone proxy checkpoint. Leave desktop Network Capture off and omit CLI network flags while it owns 127.0.0.1:8765. Press Ctrl+C when finished; use --output path.jsonl to choose the JSONL path. Request and response headers are written immediately, body records when streams finish or abort, binary bodies are omitted, and captured bodies are limited to 1 MiB on the wire and after decompression. Filtering is partial: JSONL captures may contain credentials, tokens, cookies, or other sensitive data. Treat them as private run artifacts.


  1. The desktop app connects to an Android device or emulator through ADB, or to an iOS simulator through Appium + XCUITest
  2. Captures full-screen device screenshots
  3. Scales down the screenshots for OpenAI model compatibility
  4. Sends screenshots and user instructions to OpenAI's computer-use model
  5. Receives structured actions such as click, scroll, type, keypress, wait, and drag
  6. Rescales model outputs back to real device coordinates
  7. Executes the actions on the device or simulator
  8. Validates assertions and handles failures
  9. Repeats until task completion

The desktop app is the primary way to use droid-cua.

For CI, scripting, or advanced workflows, droid-cua also includes a CLI for running saved instructions headlessly.

Desktop projects can also keep run reports in a results folder, including JUnit XML output that the app can read back as project history.

When a desktop project contains nested folders, the app discovers .dcua files recursively and displays their directory structure in the project tree. You can create tests in nested folders, open those folders in Finder, drag tests between project roots and nested folders, run an individual test, run a nested folder and all its descendants, or run the entire project. Nested tests use the configured project folder's context.md, test-data.md, .secrets, results folder, and desktop execution settings.

The recommended workflow is:

  • design and debug tests in the desktop app,
  • commit the .dcua file plus a headless CLI config file,
  • run the same test headlessly in CI with --config for prompt parity.

Install:

npm install -g @loadmill/droid-cua

Install the bundled Agent Skill for your coding agent:

In the desktop app, choose Help → Install Droid CUA Skill, then select your coding agent from the submenu. The app installs the skill globally for your user account.

Alternatively, install it from the CLI:

# Global installation (default)
droid-cua skill install cursor
droid-cua skill install claude-code
droid-cua skill install codex

# Or install in the current project
droid-cua skill install codex --local

Use droid-cua skill status <agent> to see the installed and bundled skill versions and installation path; add --local to inspect a project installation. The skill version is stored in SKILL.md under metadata.version, independently of the CLI package version. Older copies without version metadata show unknown. The modified status means the files differ from the bundled copy, which can indicate an older release or local edits even when version labels match. Use droid-cua skill uninstall <agent> to remove an installation. Installed skill files are never overwritten or removed after local modification unless --force is supplied. After upgrading the npm package, update an installed skill explicitly:

droid-cua skill install <agent> --force

Examples:

# Interactive CLI
droid-cua

# Headless Android run
droid-cua run tests/login.dcua --avd adb:emulator-5554

# Headless Android folder run
droid-cua run tests --avd adb:emulator-5554

# Select tests from different folders
droid-cua run tests/auth/login.dcua tests/checkout/purchase.dcua admin/refund.dcua \
  --avd adb:emulator-5554

# Headless iOS simulator run
droid-cua run tests/login.dcua --platform ios --avd "iPhone 16"

# Check an iPhone connected by USB, then run on it
droid-cua ios-device check --fix
droid-cua run tests/login.dcua --avd ios-device:auto

# List running desktop applications and their window IDs (macOS or Windows)
droid-cua --target desktop --list-desktop-apps

# Control the full primary display across applications
droid-cua --target desktop --instructions tests/launcher.dcua

# Control one already-running desktop application window
droid-cua --target desktop --desktop-app com.apple.TextEdit --desktop-window 123 --instructions tests/editor.dcua

# Explicitly use cursor-free accessibility input instead of the default physical input
droid-cua --target desktop --desktop-app com.apple.TextEdit --desktop-input accessibility --instructions tests/editor.dcua

# Headless run with prompt-parity config
droid-cua --avd adb:emulator-5554 --instructions tests/login.dcua --config ci/droid-cua.json

# Headless LambdaTest real-device run
LAMBDATEST_USERNAME=... LAMBDATEST_ACCESS_KEY=... droid-cua \
  --device-source lambdatest \
  --platform android \
  --device-name "Galaxy S24" \
  --os-version "14" \
  --app ./app/build/outputs/apk/debug/app-debug.apk \
  --instructions tests/login.dcua \
  --config ci/droid-cua.json

# List AWS Device Farm remote-access devices
AWS_DEVICE_FARM_ACCESS_KEY_ID=... \
AWS_DEVICE_FARM_SECRET_ACCESS_KEY=... \
AWS_DEVICE_FARM_PROJECT_ARN=... \
droid-cua --device-source aws-device-farm --list-devices

# Headless AWS Device Farm real-device run
AWS_DEVICE_FARM_ACCESS_KEY_ID=... \
AWS_DEVICE_FARM_SECRET_ACCESS_KEY=... \
AWS_DEVICE_FARM_PROJECT_ARN=... \
droid-cua \
  --device-source aws-device-farm \
  --platform android \
  --device-name "Google Pixel 10 Pro" \
  --os-version "16" \
  --app ./app/build/outputs/apk/debug/app-debug.apk \
  --instructions tests/login.dcua

# Headless Loadmill Cloud iOS run in the French (France) locale
LOADMILL_API_TOKEN=... droid-cua run tests/login.dcua \
  --device-source loadmill-cloud \
  --platform ios \
  --device-name "iPhone 16" \
  --os-version "18" \
  --app ./build/MyApp.ipa \
  --locale fr_FR

droid-cua run accepts one or more .dcua files or directories. Inputs are expanded in the order provided, files inside each directory retain their natural name ordering, and overlapping paths are run only once. A single config, target, context, test-data file, and secrets source applies to the entire invocation. The run command must be the first argument. Keep all selected paths contiguous and place options after the complete path list. Arbitrary option/path interleaving is not supported. The existing --instructions <file-or-directory> form remains supported for backward compatibility.

Example headless config:

{
  "cuaModel": "gpt-6-sol",
  "promptCustomizations": {
    "basePromptInstructions": "",
    "designModeInstructions": "",
    "executionModeInstructions": ""
  },
  "appContextEnabled": true,
  "appContextPath": "../tests/context.md",
  "contextOptimizationEnabled": true,
  "contextOptimizationThreshold": 60000
}

Typical CI-style usage:

droid-cua \
  --avd adb:emulator-5554 \
  --instructions tests/login.dcua \
  --config ci/droid-cua.json \
  --debug

Supported CLI options include:

  • --avd (including ios-device:<udid> or ios-device:auto for an iPhone connected by USB)
  • --ios-team
  • --platform
  • --device-source
  • --device-name
  • --os-version
  • --app
  • --list-devices
  • --browser
  • --target (mobile, web, or desktop)
  • --desktop-app (limit desktop control to an application ID or exact name; omit for full desktop)
  • --desktop-window (select a window within --desktop-app)
  • --desktop-input (physical by default; use accessibility for cursor-free input)
  • --list-desktop-apps
  • --instructions
  • --config
  • --cua-model
  • --context
  • --no-context
  • --context-optimization-threshold
  • --no-context-optimization
  • --explain
  • --step-watchdog <off|on>
  • --base-prompt
  • --execution-prompt
  • --base-prompt-file
  • --execution-prompt-file
  • --secrets
  • --record
  • --network-proxy (local mobile sessions only; starts idle forwarding at 127.0.0.1:8765, or 0.0.0.0:8765 for --avd ios-device:<udid|auto> and physical Android phones)
  • --capture-network (local mobile and Chrome/Edge test runs; writes a JSONL log for each test, using a proxy only for mobile)
  • --report
  • --report-metadata <path>
  • --no-cloud-report
  • --debug

--cua-model accepts loadmill-smart, loadmill-pulse, loadmill-beacon, gpt-6-sol, and gpt-6-luna. OpenAI models use direct OpenAI mode and require OPENAI_API_KEY. Saved direct OpenAI selections of GPT-5.6 Terra, GPT-5.6 Luna, or GPT-5.4 move to GPT-6 Sol, GPT-6 Luna, or GPT-6 Sol, respectively.

Desktop control supports macOS and Windows. In the desktop app, enable Experimental desktop control at the bottom of Settings to show Desktop in the platform picker. Desktop runs use physical pointer and keyboard input by default. Droid shows a system notice for three seconds before taking control and a completion notice when the run finishes, fails, or is stopped. The delay is fixed; use Stop in Droid once the run starts. Avoid using the mouse or keyboard during the run. macOS requires Screen & System Audio Recording and Accessibility permissions for Droid or the terminal running the CLI. On Windows, Droid must run at the same integrity level as the target application. System notices and automatic minimization apply only to runs started in the desktop app.

CLI runs with --target desktop control the full primary display by default, with no application flag needed. In the desktop app, choose Full desktop, the first option in the Application dropdown. This mode captures and controls the primary display across applications, including apps launched during the test; no target application needs to be running when connecting. Keep all test windows on the primary display. Full desktop requires physical input. Droid minimizes itself after the three-second notice and remains minimized when the run ends; open Droid to review the result or use Stop. Existing saved application selections are preserved.

Use --desktop-app <id-or-exact-name> or select an application in Droid to limit control to that application's windows. Application mode brings the selected window forward and leaves it there when the run ends. Add --desktop-window <id> to select a particular window, or --desktop-input accessibility for cursor-free CLI input; both options require --desktop-app. Before each run and screenshot, Droid refreshes window selection within the original application so it can follow a recreated login window. If replacement is ambiguous, select a window again. If a captured window disappears or moves before input, Droid requests a fresh screenshot instead of transferring its coordinates to a replacement. Focus errors report the activation reason; they do not establish an elevation problem.

If Windows returns an all-black window capture, Droid brings the target forward and crops the visible desktop to the window's visible frame, excluding its invisible resize border. The fallback logs a warning, crops before resizing to preserve detail, and requires the target to remain unobstructed on the primary display; off-screen portions appear black. With accessibility input, bring the target forward yourself when requested. Full desktop reports capture failures directly, without this window capture fallback.

Desktop Test design uses the same notices once you submit your instructions and exploration starts. Full desktop minimizes Droid at that point. Droid sends the completion notice when the generated test is ready, exploration is stopped, or recovery ends in an error. It does not automatically bring itself forward; open Droid whenever you need to give guidance, use Stop, or review the generated test. Opening Test design alone does not take desktop control. When a controller rejects an action, Droid skips the remaining actions in that batch and asks the model to continue from a fresh screenshot with the failure reason. Rejected actions are not counted as completed.

The system notices are shown only for desktop control runs started in the desktop app. On macOS, they may not appear in the unsigned Electron dev app; verify them in a signed packaged build.

Config and precedence rules:

  • Use --config <file> to supply prompt-affecting settings for headless runs.
  • CLI flags override config file values.
  • --context overrides the config app-context path.
  • --no-context disables app context entirely.
  • Context Optimization is enabled by default with a 60000 input-token threshold.
  • --context-optimization-threshold <tokens> sets the threshold and enables Context Optimization.
  • --no-context-optimization disables Context Optimization.
  • Explain Mode is enabled by default and asks the agent for concise explanations of non-obvious decisions, skipped actions, and completion evidence; explanations appear in terminal output and reports.
  • Set "explainMode": false in the JSON config to disable it. The --explain flag remains available to explicitly enable it for a run.
  • Step Watchdog is experimental and defaults to off. Use --step-watchdog on to check for failed operations or blocked execution, or --step-watchdog off to disable checks. The JSON field is "stepWatchdogMode": "on"; the CLI flag overrides it. Desktop users can toggle Settings → Agent → Verify Step Results on or off. Successful checks run silently. Failures use an assertion-style Step Failed card with the expected instruction, actor explanation, and screenshot. Interactive runs offer Skip to continue or Stop to fail; headless runs fail automatically. Evaluator errors end execution. Enable --debug or desktop Debug Mode to inspect step_watchdog.result entries; probabilities and evaluator diagnostics remain out of normal output and reports. See Step Watchdog for provider requirements, logging, and calibration.
  • --base-prompt and --execution-prompt let you pass prompt customizations inline on the command line.
  • --base-prompt-file and --execution-prompt-file override the corresponding prompt customizations from config.
  • If both inline and file-based prompt overrides are provided, the inline prompt flags win.

Secret typing:

  • Put runtime typing secrets in a project .secrets file using dotenv-style KEY=value lines.
  • Desktop execution and design mode load .secrets from the selected project folder. Headless CLI loads .secrets from the current working directory.
  • Headless CLI also accepts --secrets KEY=value,OTHER=value; these values override .secrets for that run.
  • The agent sees only secret keys and may call type_sensitive_text({ key }). Logs and reports may include keys, but not resolved values.
  • Screenshot masking is out of scope. Secret values also necessarily pass through ADB, Appium, or browser automation while being typed.

HTML report (--report):

  • Desktop, CLI, and Loadmill-downloaded HTML reports include a hidden pre#agent-report containing HTML-escaped JSON (schemaVersion: 1). Parse its textContent for the recorded report timeline and metadata; each event’s optional screenshotRef identifies an existing lightbox whose img contains the screenshot. No debug mode is required, and images are not duplicated in this section. Reports generated by older Droid versions do not contain it.
  • Writes a self-contained HTML report of the run — the same Run Details view the desktop app shows, with screenshots inlined — that you can share and open in any browser.
  • With no value it saves <test>--<date>--<time>.html (e.g. login--2026-06-03--16-09-20.html) in the current directory; pass --report <file> to pick a filename or --report <dir> to pick a folder.
  • A multi-path selection writes the individual test reports plus one aggregate Selected tests report with a child result and timeline for every selected test. When --report <file.html> is used, that exact path is used for the aggregate report.
  • Aggregate HTML reports show each test as an expandable row with its duration and status. Passed tests start collapsed; failed, errored, and stopped tests start expanded. Instruction timelines use the same rendering as single-test reports.
  • In the desktop app, click an instruction title or its duration to expand its recorded actions.
  • In the desktop app the same report is available from a run's Share → Download HTML report button.
  • Generated HTML reports opt out of Cloudflare email obfuscation so ordinary email values remain visible in hosted reports. Secret-typing values are omitted from typing logs, but may still appear in screenshots.
  • Loadmill flow entries link Test started to the flow's test-suite run results, including in reports generated by CI.
  • Runs with captured screenshots also expose Share → Generate video. The desktop app renders portrait mobile runs as square replays and landscape mobile, web, and desktop runs as 16:9 replays. Landscape Android and iOS screenshots keep a device frame sized to the captured screen instead of browser chrome. Rendering happens locally, then the app prompts for a save location using <test>--<date>--<time>--test-replay.mp4 as the suggested filename. While rendering, a compact Background tasks card shows progress for Generating test replay; it can be minimized to a task-count card or canceled without opening the save dialog.
  • Authenticated headless runs upload their HTML report and screenshots to Loadmill by default. The Loadmill run record also includes the resolved Droid version, model, provider, Agent Pace, Aim Assist, Context Optimization, and target environment. Use --no-cloud-report to disable artifact upload; the Droid run record is still reported.
  • Use --report-metadata <path> when automation needs the exact published Loadmill run ID and last uploaded screenshot object name. Droid clears that destination when the invocation starts and writes it only after report publication succeeds, including for published failure reports.
droid-cua run tests/login.dcua --avd adb:emulator-5554 --report ./reports

The desktop app and the CLI can run tests on your own iPhone connected to your Mac by USB, on the apps already installed on it (TestFlight, App Store, or your team's builds). droid-cua installs a small helper app (WebDriverAgent) on the iPhone, signed with your company's Apple team; nothing is built from source at run time.

Requirements:

  • macOS with an Xcode that supports the iPhone's iOS version, opened once. Its first launch installs the device components and asks for a Mac admin password.
  • The iOS platform installed in that Xcode (Xcode > Settings > Components, or xcodebuild -downloadPlatform iOS; about 10 GB). Xcode 26 doesn't include it.
  • Your Apple ID signed into Xcode (Xcode > Settings > Apple Accounts), as a member of your company's paid Apple Developer team, with at least the Developer role and Access to Certificates, Identifiers & Profiles. Free Apple IDs are not supported.
  • The Appium XCUITest driver, as for Simulators.
  • On the iPhone, once: connect it by USB, unlock it and tap Trust; turn on Developer Mode (Settings > Privacy & Security > Developer Mode, then let it restart and confirm); set Auto-Lock to Never (Settings > Display & Brightness).
  • A test-only iPhone is recommended: droid-cua tests the apps and data already on the phone and never resets it.

Check the iPhone and set it up:

droid-cua ios-device check          # report each check and how to fix it
droid-cua ios-device check --fix    # also set up signing and install the helper app

ios-device check prints every check as passed (✔), failed (✖), or a reminder (!), with the exact fix for each failure, and exits 0 only when the iPhone is ready. Use --udid <udid> to choose an iPhone (the first one connected by USB is used otherwise). The first --fix on a Mac runs a one-time Xcode signing build (about a minute); installing the helper app takes a few seconds. A test run performs the same checks and fixes automatically before it starts.

Xcode installed names what's wrong: Xcode isn't in /Applications, the Command Line Tools are selected instead of Xcode (fix: sudo xcode-select -s /Applications/Xcode.app), or the Xcode license isn't accepted (fix: sudo xcodebuild -license accept). iOS platform installed checks that Xcode has the iOS platform for its iOS SDK, without which Xcode can't build for an iPhone (fix: install it from Xcode > Settings > Components, or run xcodebuild -downloadPlatform iOS, about 10 GB). Apple team access reads your teams from Xcode; on a fresh Mac, where Xcode hasn't saved them yet, droid-cua fetches them from Apple with a short Xcode build (a few seconds, at most once a minute). It then says whether no Apple account is signed into Xcode, or you're signed in but none of your accounts is in a paid Apple Developer team.

Run tests on the iPhone:

droid-cua run tests/login.dcua --avd ios-device:auto
droid-cua run tests/login.dcua --avd ios-device:00008120-001C18501A81A01E --ios-team ABCDE12345
  • --avd ios-device:<udid> selects an iPhone; ios-device:auto picks the first iPhone connected by USB. The platform is detected as iOS from the token. Running droid-cua without --avd lists connected iPhones next to Simulators.
  • --ios-team <TEAMID> chooses the Apple team that signs the helper app when you belong to several paid teams. DROID_CUA_IOS_TEAM_ID works too; the flag wins.
  • --app is not used for a local iPhone: tests run against apps already installed on it, so open the app in your test instructions (for example, Open the Settings app).
  • After the iPhone restarts, unlock it. On the first run after a restart or a new helper install, the iPhone may ask for your passcode to allow automation; enter it and droid-cua continues. If the iPhone locks during a run, droid-cua pauses until you unlock it.
  • After a major iOS update, droid-cua tells you which Xcode to install and open once.

In the desktop app:

  • Run the Setup Wizard (Help > Run Setup Wizard) and choose iPhone (USB). The Connect iPhone checklist runs the same checks as ios-device check. Steps you do on the iPhone or in Xcode (plug in, Trust, Developer Mode, unlock, device components) re-check every few seconds and turn green on their own. Set up signing and Install helper app run the fix and stream its progress. When the Command Line Tools are selected or the Xcode license isn't accepted, Xcode installed offers Set Xcode Developer Path or Accept Xcode License (both ask for a Mac admin password). When the iOS platform is missing, iOS platform installed offers Download iOS Platform, which runs xcodebuild -downloadPlatform iOS (about 10 GB). Auto-Lock and the dedicated test iPhone are shown as reminders and don't block.
  • If you belong to several paid Apple teams, choose the team in the wizard. The desktop app remembers it and uses it for every iPhone run.
  • Connected iPhones appear in the iOS device picker next to Simulators, with a hint when the iPhone isn't trusted yet or Developer Mode is off. droid tests the apps already installed on the iPhone and never installs any.
  • The connection log and the run view highlight what needs you on the iPhone: Enter your passcode on the iPhone to allow automation, iPhone locked — unlock to continue, and Your iPhone is on iOS N. Install Xcode N and open it once.

Known limits: USB only (no Wi-Fi), one iPhone at a time, attended runs (someone needs to unlock the phone and approve prompts), macOS only, and no .ipa installation. Verified on iOS 26; iOS 17 to 26 are expected to work. iOS betas and iOS 27 are not supported yet.

LambdaTest CLI runs:

  • Use --device-source lambdatest with --platform, --device-name, --os-version, --app, and --instructions.
  • Set LAMBDATEST_USERNAME and LAMBDATEST_ACCESS_KEY in the environment.
  • Android LambdaTest runs require an .apk; iOS LambdaTest runs require an .ipa.
  • The CLI uploads the app to LambdaTest for each run and closes the cloud session when the run finishes or fails.
  • Each CLI LambdaTest run gets a unique LambdaTest build name so repeated local runs are easy to distinguish in the LambdaTest UI.

AWS Device Farm CLI runs:

  • Use --device-source aws-device-farm with --platform, --device-name, --os-version, --app, and --instructions.
  • Set AWS_DEVICE_FARM_ACCESS_KEY_ID, AWS_DEVICE_FARM_SECRET_ACCESS_KEY, and AWS_DEVICE_FARM_PROJECT_ARN in the environment (an IAM user with Device Farm access and the ARN of a Device Farm project in us-west-2).
  • --device-source aws-device-farm --list-devices prints the remote-access device catalog (name, platform, OS) so you can pick valid --device-name/--os-version values, then exits.
  • Android runs require an .apk; iOS runs require an .ipa. .app bundles are not supported.
  • Re-running with the same app binary skips the upload: uploads are cached by checksum and reused for up to 30 days (Device Farm's upload retention).
  • Device provisioning takes ~1 minute; the CLI shows live progress while it waits.
  • The session is stopped when the run finishes, fails, or is interrupted with Ctrl-C — billing stops as soon as the stop is requested, so the CLI does not wait for the session to fully wind down.

AWS Device Farm in the desktop app:

  • Open Integrations → AWS Device Farm and enter an AWS Access Key ID + Secret Access Key. The keys are validated on connect, a droid-cua Device Farm project is created automatically (the IAM user needs the minimal Device Farm policy, including ListProjects/CreateProject), and the remote-access device catalog loads. Credentials are stored in plaintext desktop-config.json, the same as BrowserStack/LambdaTest.
  • On the Devices page, pick AWS Device Farm as the source, choose a device + an uploaded .apk/.ipa, and start a Design/Run session. Provisioning takes ~1 minute and live progress flows into the device log.
  • Pause caveat: a Device Farm session paused for more than 5 minutes may be ended by AWS and need reconnecting.

Loadmill Cloud locale:

  • Pass --locale <lang_REGION> (for example fr_FR, en_NL, hi_IN) with --device-source loadmill-cloud to start a mobile run in that locale. In the desktop app, use the gear next to the connected Loadmill Cloud device in the test header to pick a locale; it applies to Run and Design sessions started afterwards.
  • Android changes the device language and region and relaunches the app. iOS relaunches the app with -AppleLanguages and -AppleLocale, so only the app under test sees the locale.
  • The locale is applied once, when the session starts. --locale cannot be combined with --cloud-session-id.

Minimal GitHub Actions shape:

jobs:
  droid-cua:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm install -g @loadmill/droid-cua
      - run: |
          droid-cua \
            --device-source lambdatest \
            --platform android \
            --device-name "Galaxy S24" \
            --os-version "14" \
            --app ./app/build/outputs/apk/debug/app-debug.apk \
            --instructions tests/login.dcua \
            --config ci/droid-cua.json \
            --debug
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
          LAMBDATEST_USERNAME: ${{ secrets.LAMBDATEST_USERNAME }}
          LAMBDATEST_ACCESS_KEY: ${{ secrets.LAMBDATEST_ACCESS_KEY }}

The same shape works for AWS Device Farm — swap the run step and env:

      - run: |
          droid-cua \
            --device-source aws-device-farm \
            --platform android \
            --device-name "Google Pixel 10 Pro" \
            --os-version "16" \
            --app ./app/build/outputs/apk/debug/app-debug.apk \
            --instructions tests/login.dcua \
            --config ci/droid-cua.json \
            --debug
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
          AWS_DEVICE_FARM_ACCESS_KEY_ID: ${{ secrets.AWS_DEVICE_FARM_ACCESS_KEY_ID }}
          AWS_DEVICE_FARM_SECRET_ACCESS_KEY: ${{ secrets.AWS_DEVICE_FARM_SECRET_ACCESS_KEY }}
          AWS_DEVICE_FARM_PROJECT_ARN: ${{ secrets.AWS_DEVICE_FARM_PROJECT_ARN }}

Example without a config file:

droid-cua \
  --avd adb:emulator-5554 \
  --instructions tests/login.dcua \
  --context tests/context.md \
  --base-prompt "stop and look at the screen after every action you take."

Headless debug artifacts:

  • --debug writes desktop-style structured JSONL artifacts under logs/.
  • Each run creates logs/execution-<runId>-<timestamp>.jsonl. New run IDs are UUIDs; a batch shares one ID across its tests and aggregate report, and a rerun gets a new ID.
  • Each run also creates a sibling screenshot folder next to that JSONL file.
  • Shared device events are written to logs/device-events.jsonl.
  • --debug no longer creates the legacy logs/debug-*.log file for headless runs.
  • If --debug and --record are both used, screenshots are written to both the debug artifacts folder and the legacy droid-cua-recording-<timestamp> folder.

Current headless behavior is documented in docs/headless-cli-spec.md.


© 2026 Loadmill. All rights reserved.

Desktop browser compatibility

Droid disables password-saving prompts and password-breach warnings in every local Chrome/Edge automation profile before launch, for both fresh and persistent sessions in the CLI and desktop app. Other profile preferences are preserved, and personal browser profiles are unaffected. Reconnect an already-open session to apply these defaults. Cloud and externally attached WebDriver browsers must be configured by their provisioner.

For sites that block automated browsers, enable Settings > General > Capabilities > Reduce browser automation detection, then reconnect. This desktop preference defaults off and applies across projects to local Chrome/Edge connections. It suppresses Chromium's automation signal but does not guarantee compatibility. CLI and Cloud sessions are unaffected.