Skip to main content
kane-cli testrun run --remote sends a suite of _test.md files to HyperExecute instead of running it on your machine. The grid sets up the runtime, runs every test in the suite (each one is a member), and streams the job back to your terminal. You get one job, one exit code, one sealed evidence pack, and the same recordings and evidence as a run on your own machine. Remote runs cover every kind of test:
  • Web suites run in Chrome on a HyperExecute macOS runner. Your machine or CI runner needs no Chrome, the run is always headless, and --parallel N spreads the members across N grid runners.
  • Mobile suites (target: emulator or target: simulator) run on a virtual Android emulator or iOS simulator on a HyperExecute macOS host, so you can author and run mobile tests from any machine: Linux, Windows, Intel Macs, or a Mac without Xcode or Android Studio. The macOS Apple Silicon requirement applies only to emulator and simulator runs on your own machine.
  • Real-device suites (target: android or target: ios) run on real devices in the cloud, driven from a HyperExecute Linux runner. See Real-Device Suites on the Grid.
--remote is different from --ws-endpoint and --cdp-endpoint. Those flags connect a remote browser to a run that still executes on your machine (kane-cli run, kane-cli testmd run). --remote moves the whole suite to the grid. Kane CLI itself runs there, and your machine needs only Node and the plugin.

Prerequisites

The run uploads to the project and folder configured on your profile (kane-cli config project and kane-cli config folder). Kane CLI passes them to the login on the grid.

How a remote run works

  1. Preflight, then remote preflight. Kane CLI builds the normal testrun plan (one organisation, one project, see Preflight). It then checks that the selection fits in one grid job (see What one job can hold) and, for mobile, looks up the device in the grid catalog. If anything is wrong, the run stops here with exit 2 and nothing is dispatched.
  2. Payload. Kane CLI zips the current directory, respecting .gitignore, and uploads it with a generated job definition. Dispatching leaves .hyperexecute/, hyperexecute-cli.log, and .updatedhyperexecute.yaml in that directory. They are not inputs, so add them to .gitignore.
  3. On the grid (a macOS runner, or a Linux runner for real-device suites), a setup step installs Kane CLI and logs in with your credentials and project. For mobile, it also installs the device tooling and boots the device. Then each member runs as its own kane-cli testmd run, headless, exactly as it would on your machine. A member with recordings replays them. A member without recordings is authored on the grid.
  4. Back to you. The job streams its progress and the dashboard link to your terminal. When it ends, the members’ output-<stem>/ recordings and the sealed evidence pack are downloaded into your project, and the pack is published to Test Manager. The suite summary and exit code match a local testrun.
Allow some time beyond the tests themselves: about 15 seconds of setup for a web job, and a minute or more for a mobile job to boot the device and install the app. In CI, a wall-clock timeout of 10 minutes is a safe starting point.

Dispatching a run

--remote takes the normal testrun selection (paths, --match, --tags).
--dry-run runs both preflights and resolves any device without creating a job. It costs nothing, so use it before every new selection. A real run prints the job id and dashboard link, then follows the job until it completes:

Web suites on the grid

A web suite needs nothing beyond the prerequisites. The grid runner has Chrome, and Kane CLI finds it and runs each member headless. Use the grid when your runner cannot have Chrome, when you want the suite off your laptop, or when you need more parallel capacity than one machine offers.
You get a normal testrun summary, plus two extra lines for the dispatch and the job link. A member that is authored on the grid returns its output-<stem>/ recordings, so the next run, on the grid or on your machine, replays them. Commit those recordings as you would after a local run. Web tests and device tests cannot share a job (mobile_remote_mixed). Run them as two separate suites.

Choosing a grid device (emulator and simulator)

Remote devices come from the grid catalog, not from the AVDs or simulators on your machine. To see what the grid can provide:
Each row shows a device name and the OS versions available for it. Select a device with both: Kane CLI resolves each test’s device on your machine. It picks the device name and the OS version separately, each in this order:
  1. --device-name / --os-version
  2. The test file’s device_name: / os_version:
  3. Your saved device (kane-cli config set-device-name / set-os-version), when it is the same kind (emulator or simulator) and the grid has it. A saved device the grid does not have is skipped with a saved_device_unavailable note.
  4. The default: Pixel 8 Pro · Android 15 for emulators, iPhone 17 Pro Max · iOS 26.0 for simulators.
Before dispatch, Kane CLI prints where each test’s device came from:
The two platforms assign devices differently:
  • Emulator: the job uses one device for all its members, so they must all ask for the same Android version. If they differ, the preflight refuses with mobile_os_version_split. To force one version, pass --os-version. If a member names an AVD from your own machine, Kane CLI ignores it and shows a device_name_ignored note, because a remote job’s device name comes from the catalog.
  • Simulator: each task boots a simulator inside its own VM, so each member uses its own device_name: and os_version:, checked against the catalog. If you pass --device-name and --os-version, they apply to every member. Members can ask for different iOS versions as long as their major versions belong to the same HyperExecute pool. The catalog decides the pools: iOS 17 and 18 share one, and iOS 26 is in another. If members need different pools, the preflight refuses with mobile_pool_split.

Real-device suites on the grid

target: android and target: ios tests run on real devices in the cloud. The job runs on a HyperExecute Linux runner, and each member opens its own device session, so:
  • --parallel N runs N members at once, up to the real devices your plan allows. A session beyond that queues for up to 15 minutes (see Plan Requirements).
  • Android and iOS real-device members can share one job.
  • A job cannot mix real-device members with web, emulator, or simulator members (mobile_remote_mixed).
Kane CLI resolves each member’s device on your machine. It picks the device name and the OS version separately, each in this order:
  1. --device-name / --os-version
  2. The test file’s device_name: / os_version:
  3. The hub picks any free device. The hub is the service that assigns real devices to sessions.
You can set the name alone or the version alone, and a name can be a pattern. To list the real devices the cloud has:
Kane CLI checks each name and version against that list before dispatch and prints the Devices: block:
If Kane CLI cannot read the device list, it dispatches the job anyway with a real_device_list_unavailable note, and the hub decides which device each member gets. Before you dispatch a real-device suite, check these rules:
  • Every member needs an app: in its file: .apk or .aab for Android, .ipa for iOS, or an APP… id. Without one, the run stops with mobile_app_required. testrun has no --app flag.
  • --device-slug is refused for real devices.
  • Adaptive heal is off on real devices, so a failed replay step fails the test.
For the TUI, live view, settings, and limits on real devices, see Real Devices.

The app under test on the grid

The grid machine has to get the app from somewhere. A build is never part of the payload. It reaches the grid by id: Each member gets its own id, so members in one run can use different builds. An .ipa is refused for emulator and simulator tests, because it is a device build. Use it with target: ios. Every upload is reported, as app: <file> → APP… (uploaded) in the terminal or as the remote_app event for agents. kane-cli apps list --target emulator|simulator|android|ios shows the uploaded builds your account can use. Put a value from the APP ID column in app:. There is no separate upload command: any run with a local build, on the grid or on your machine, uploads it and prints the APP… id. Uploads belong to an organisation, so apps list for your current profile is the definitive list of ids a run can use.

What one job can hold

Each remote run is one HyperExecute job, and a job provides one kind of runtime. The remote preflight refuses a selection that needs more than one, and tells you how to split it with --match or --tags: Each reason comes with the paths that caused it, both in the terminal and in a remote_error event for agents.

What comes back

  • Recordings: the output-<stem>/ directories of authored members land in your project exactly as a run on your machine would leave them, so the next run replays from cache.
  • Evidence: one sealed pack for the suite in .testmuai/evidence/, published to your project’s execution history in Test Manager.
  • Job logs: the per-member session logs under ~/.testmuai/kaneai/sessions/remote/<job-id>/, and the full stage logs on the HyperExecute dashboard at the printed job link.
  • Exit code: the same as a local testrun. 0 all passed, 1 a member failed or broke, 2 preflight, auth or usage (nothing dispatched), 3 cancelled.

When a remote run fails

  • A member failed or broke (exit 1): read it like any other failure. output-<stem>/Result.md names the failing step and reason, and the evidence pack has the screenshots and logs. See Debugging from a Pack.
  • A member is broken with no steps and nothing was published: Kane CLI on the grid refused to start. Open the job link and read the scenario stage log. For mobile, the usual cause is an APP… id that belongs to a different organisation than the account running the job.
  • Nothing was dispatched (exit 2): the printed reason is one of the preflight codes above. Run kane-cli plugin doctor remote-execution to see whether the plugin, binary, or login is missing.

In CI

A remote run needs no Chrome, Xcode, or Android Studio on the CI runner, only Node and the plugin:
Archive .testmuai/evidence/*.evidence as the build artifact. More pipeline shapes are in CI/CD.

For agents: NDJSON events

In agent or non-TTY mode, a remote run wraps the normal testrun_* stream in additional typed events (see For agents): testrun_summary also carries a remote object (backend, jobId, jobUrl, sessionsPath). When you parse the stream of a dispatched run, keep reading after testrun_done until remote_done and the process exit, and keep the remote status and the session-log path. A preflight refusal or a dry run can end without remote_done.

Known limitations

  • --bug-detection is not forwarded. Remote dispatch does not pass --bug-detection to the member commands, so do not rely on it for bug detection in a remote run.
  • --on-failure is not applied. Remote dispatch does not map --on-failure into the job template, so do not rely on it for fail-fast behavior in a remote run.
  • --name names the suite only. It is suite metadata and is not passed to member commands.

Next Steps