> For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt.

# YAML script runner

:::info Compatibility remains; migrate to Midscene Test for new work
“Legacy YAML” specifically means the YAML automation protocol provided by `@midscene/cli`: a workflow uses top-level `tasks`, each task contains `flow`, platform and Agent runtime configuration commonly lives in YAML, and execution uses the `midscene` command or YAML batch configuration with fields such as `files`, `setup`, and `concurrent`.

The legacy path is now in maintenance mode. Existing projects remain compatible and necessary issues will be fixed, but it will not receive new syntax, capabilities, configuration fields, or CLI options. Existing cases, batch configuration, directory layout, and the `midscene` command can remain unchanged. Upgraded versions execute them through the shared kernel and generate the new Test report page. Use Midscene Test for new projects, new cases, and future capability development.

Run legacy files and YAML batch configuration with `midscene`; run native `cases/steps` files and TypeScript project configuration with `midscene-test`. The two formats may live in separate directories in one repository, but a file or command invocation must not mix them. Start with [Midscene Test overview](/midscene-test/overview.md), then follow [Migrate to Midscene Test](/migrate-to-midscene-test.md) to convert fields and switch commands.
:::

Midscene defines a YAML-based scripting format so you can quickly author automation scripts, then run them from the command line without extra setup. For more details on YAML scripts, see [Automate with scripts in YAML](/automate-with-scripts-in-yaml.md).

For example, you can write a YAML script like this:

```yaml
page:
  url: https://www.bing.com

tasks:
  - name: Search for weather
    flow:
      - ai: Search for "today's weather"
      - sleep: 3000
      - aiAssert: The results show weather information
```

Run it with one command:

```bash
midscene ./bing-search.yaml
```

The CLI prints execution progress and generates a visual report when it finishes, while keeping setup simple.

## Configure environment variables with `.env`

The Midscene CLI uses [dotenv](https://www.npmjs.com/package/dotenv) to load a `.env` file from the directory where you run the tool. Create a `.env` file and add:

```ini filename=.env
MIDSCENE_MODEL_BASE_URL="replace with your model service URL/v1"
MIDSCENE_MODEL_API_KEY="replace with your API Key"
MIDSCENE_MODEL_NAME="replace with your model name"
MIDSCENE_MODEL_FAMILY="replace with your model family"
```

For supported models and complete setup examples, see [Use a model API key](/model-api-setup.md).

Notes:

- The file is optional; you can also set global environment variables instead.
- Do not add an `export` prefix—this is how dotenv expects values.
- Place `.env` in the directory where you run the tool, not necessarily next to the YAML file.
- These values do **not** override existing global environment variables unless you enable `--dotenv-override` (see below).
- Use `--dotenv-debug` if you need to debug how environment variables load.

## Get started

### Install the CLI

Before installing the CLI, make sure the terminal that runs `midscene` uses Node.js `20.19+`, `22.12+`, or `24+`. Some CLI execution paths use the Rstest/Rspack toolchain, which rejects older Node 20 patch versions such as `20.17.0`. If you see an `Unsupported Node.js version` message from Rspack, upgrade Node.js and reinstall the global CLI or project dependencies.

Install `@midscene/cli` globally (recommended for first-time users):

```bash
npm i -g @midscene/cli
```

Or install it per project:

```bash
npm i @midscene/cli --save-dev
```

### Write your first script

Create `bing-search.yaml` to drive a web browser:

```yaml
page:
  url: https://www.bing.com

tasks:
  - name: Search for weather
    flow:
      - ai: Search for "today's weather"
      - sleep: 3000
      - aiAssert: The results show weather information
```

Drive an Android device connected over adb:

```yaml
android:
  deviceId: s4ey59 # find the device id with `adb devices`

tasks:
  - name: Maps Navigation
    flow:
      - ai: Open the Maps app
      - ai: Input 'West Lake, Hangzhou' in the search bar, and click the search button
      - ai: Click the first search result, enter the details page
      - ai: Click "Directions" button, enter the route planning page
      - ai: Click "Start" button to start navigation
```

Or drive an iOS device with WebDriverAgent configured:

```yaml
ios:
  wdaPort: 8100

tasks:
  - name: Change System Settings
    flow:
      - ai: Open the Settings app
      - ai: Tap "Display & Brightness"
      - ai: Turn on "Dark Mode"
      - aiAssert: Dark Mode is enabled
```

### Run the script

```bash
midscene ./bing-search.yaml
# If Midscene is installed in your project
npx midscene ./bing-search.yaml
```

The CLI prints execution progress and generates a visual report when it finishes.

## Advanced usage of the command-line tool

### Use environment variables in `.yaml`

Reference environment variables in your scripts with `${variable-name}`. Environment-variable interpolation is applied before YAML task execution, including task strings.

```ini filename=.env
topic=weather today
```

```yaml
# ...
- ai: type ${topic} in input box
# ...
```

### Run multiple scripts

`@midscene/cli` supports glob patterns to batch-execute scripts, which is a shorthand for the `--files` argument.

```bash
# Run a single script
midscene ./bing-search.yaml

# Use a glob pattern to run all matching scripts
midscene './scripts/**/*.yaml'
```

### Analyze command-line output

After execution, the output directory contains:

- A JSON summary specified by `--summary` (defaults to `index.json`) with execution status and statistics for all scripts.
- Individual execution results for each YAML file (JSON).
- Visual reports for each script (HTML).

### Run in headed mode

> Web page scenarios only

Headed mode opens the browser window. By default, scripts run headless.

```bash
# Run in headed mode
midscene /path/to/yaml --headed

# Run in headed mode and keep the window after finishing
midscene /path/to/yaml --keep-window
```

### Use CDP connection mode

> `web` scenarios only

CDP mode lets YAML scripts connect to an existing browser instance via Chrome DevTools Protocol, without launching a new browser. This is useful for reusing an existing browser session, connecting to remote browsers, or cloud browser services.

Set `cdpEndpoint` in the `page` section:

```diff
page:
  url: https://www.bing.com
+ cdpEndpoint: ws://localhost:9222/devtools/browser
```

:::info

CDP mode and bridge mode are mutually exclusive. In CDP mode, Midscene will only disconnect from the browser, not close it.

:::

### Use bridge mode

After [Chrome Bridge environment setup](/bridge-mode.md), select the current tab or a new tab in YAML. See [Chrome Bridge in YAML](/automate-with-scripts-in-yaml.md#chrome-bridge) for fields, examples, and unsupported options.

### Run YAML scripts with JavaScript

Call the Agent's [`runYaml`](/reference.md#runyaml) method to execute YAML from JavaScript. This runs only the `tasks` section of the script.

## Command-line options

The CLI provides parameters to control how scripts run:

- `--files <file1> <file2> ...`: List of script files. Executes in order, sequentially by default (`--concurrent` is `1`), or concurrently when `--concurrent` is set. Supports [glob](https://www.npmjs.com/package/glob) patterns; when a glob pattern or directory matches multiple files, matched files are added to the execution list in lexicographic path order.
- `--setup <file>`: A setup script that runs before the main `--files` for any supported target. If all setup attempts fail, the batch is aborted and the main scripts are reported as not executed. Puppeteer Web setup requires `--share-browser-context`; each retry then starts with a clean BrowserContext and Page, and the successful context is shared with the main scripts. Every YAML script runs in its own Page by default. Add `--reuse-page` to continue on the setup Page in a sequential batch. Bridge mode and non-Web targets must omit these sharing options; their retries create a new player and Agent but do not reset the underlying browser, device, desktop, or external interface state.
- `--concurrent <number>`: Number of concurrent executions. Default `1`.
- `--continue-on-error`: Continue running remaining scripts even if one fails. Default off.
- `--retry <number>`: Number of extra attempts for a failed script. Only failed scripts are retried, which helps with unstable networks or unstable model output. Default `0`. Puppeteer Web setup retries use a clean BrowserContext and Page, while main-script retries preserve the successful setup context. Other targets create a new player and Agent for each retry without resetting their underlying environment.
- `--share-browser-context`: Share one Puppeteer BrowserContext (cookies, same-origin `localStorage`, etc.) across scripts while giving every YAML script an independent Page. Page-scoped state such as `sessionStorage`, the DOM, URL, and `window.name` is not shared, even with `--concurrent 1`. Every setup and main script in the batch must use a Puppeteer Web target. Bridge mode and non-Web targets are not supported. Because the browser is created or connected only once, put browser-level options (`cdpEndpoint`, `chromeArgs`, `acceptInsecureCerts`, and `downloadPath`) in the batch config's global Web target, not in an individual setup or main script. Default off.
- `--reuse-page`: Reuse the same Puppeteer Page object across scripts. This preserves page-scoped state such as `sessionStorage` and `window.name`. It requires `--share-browser-context` and `--concurrent 1`. Default off.
- `--summary <filename>`: Path for the JSON summary report.
- `--headed`: Run in a headed browser instead of headless.
- `--keep-window`: Keep the browser window after execution; enables `--headed` automatically.
- `--config <filename>`: Config file whose values become defaults for CLI arguments.
- `--web.userAgent <ua>`: Override `web.userAgent` for all scripts.
- `--web.viewportWidth <width>`: Override `web.viewportWidth` for all scripts.
- `--web.viewportHeight <height>`: Override `web.viewportHeight` for all scripts.
- `--android.deviceId <device-id>`: Override `android.deviceId` for all scripts.
- `--harmony.deviceId <device-id>`: Override `harmony.deviceId` for all scripts.
- `--ios.wdaPort <port>`: Override `ios.wdaPort` for all scripts.
- `--ios.wdaHost <host>`: Override `ios.wdaHost` for all scripts.
- `--computer.displayId <display-id>`: Override `computer.displayId` for all scripts.
- `--dotenv-debug`: Enable dotenv debug logs. Default off.
- `--dotenv-override`: Allow dotenv to override global environment variables. Default off.

Examples:

Use `--files` to specify execution order:

```bash
midscene --files ./login.yaml ./buy/*.yaml ./checkout.yaml
```

Run multiple independent search scripts with a concurrency of 4 and continue when errors occur:

```bash
midscene --files './scripts/search-*.yaml' --concurrent 4 --continue-on-error
```

### Write command-line arguments in a file

You can place arguments in a YAML config file and reference it with `--config`. Command-line arguments take priority over the config file.

A config file can also contain one global target section: `page`, `browser`, `web`, the deprecated `target`, `android`, `ios`, `harmony`, `computer`, or `interface`. Its fields are deep-merged over the corresponding target section in every script. The complete priority order is command-line target options, then the config file's target section, then each script's target section. Fields that are not overridden are preserved. Each resulting script must still resolve to exactly one target type.

```yaml
files:
  - './scripts/search-iphone.yaml'
  - './scripts/search-laptop.yaml'
  - './scripts/search-headphones.yaml'
  - './scripts/search-camera.yaml'

concurrent: 4
continueOnError: true
retry: 2
```

For example, this config selects one HarmonyOS device for every matched script while allowing individual scripts to keep other HarmonyOS settings that are not overridden:

```yaml
files:
  - './scripts/harmony/*.yaml'

harmony:
  deviceId: '127.0.0.1:5555'
  autoDismissKeyboard: true
```

Run with:

```bash
midscene --config ./config.yaml
```

Set `concurrent: 1` (the default) when scripts must run in the exact order of
the `files` list. With a value greater than `1`, execution order is unspecified;
scripts must not depend on another script's start or completion order.

#### Run a setup before parallel scripts

When several independent Puppeteer Web scripts all depend on the same prerequisite (for example a login), put the prerequisite under `setup`. The setup script runs before the main `files`; once it succeeds, the main scripts run with the configured concurrency. Set `shareBrowserContext: true` so the successful setup attempt's browser context, including cookies and same-origin `localStorage`, is carried over. Every script receives an independent Page, so the setup Page's `sessionStorage`, DOM, URL, `window.name`, and other page-scoped state are not copied into main Pages. Every script in this shared batch must use a Puppeteer Web target; bridge mode is not supported.

The shared Browser is launched or connected from the batch config once. Configure `cdpEndpoint`, `chromeArgs`, `acceptInsecureCerts`, and `downloadPath` in the batch config's global Web target. Defining any of these browser-level options in an individual setup or main script is rejected because it cannot be applied to the already-created shared Browser.

```yaml
setup: ./scripts/login.yaml

files:
  - ./scripts/search.yaml
  - ./scripts/report.yaml
  - ./scripts/settings.yaml

shareBrowserContext: true
concurrent: 3
retry: 2
```

`retry` is the number of extra attempts, so `retry: 2` allows up to three attempts. Every setup retry gets a new BrowserContext and Page, preventing cookies, local storage, navigation state, and other browser-side effects from a failed attempt from leaking into the next one. After setup succeeds, its context is shared with the main scripts. A failed main script is retried in that same context so the successful setup state is preserved. If all setup attempts fail, the batch is aborted and the main scripts are reported as not executed.

:::warning Page-scoped state is not shared
`shareBrowserContext` follows browser-native storage boundaries: it does not copy or synchronize `sessionStorage` between Pages. If setup establishes authentication only in `sessionStorage`, the main scripts will not inherit that login. Prefer cookies, same-origin `localStorage`, or backend state for prerequisites that must be visible to multiple scripts. When scripts run concurrently, shared-state writes can race, so coordinate them explicitly.
:::

For a sequential workflow that must keep using the Page created for setup, enable page reuse explicitly:

```yaml
setup: ./scripts/login.yaml
files:
  - ./scripts/web_format.yaml

shareBrowserContext: true
reusePage: true
concurrent: 1
```

The main script still applies its own Web target configuration and navigates to its configured `url`, but it does so in the same Page object created for setup. In `browser` mode, pages opened later by popups or new-tab actions do not replace this reusable Page.

`setup` also works with bridge mode, Android, iOS, HarmonyOS, Computer, and custom Interface targets. Omit `shareBrowserContext` for those targets. A retry creates a new script player and Agent, but the underlying browser profile, device, desktop session, or custom interface is not automatically reset. If a clean retry environment is required, make the setup script restore that environment explicitly.

## FAQ

**How can I export cookies from Chrome as JSON?**

Use this [Chrome extension](https://chromewebstore.google.com/detail/get-cookiestxt-locally/cclelndahbckbenkjhflpdbgdldlbecc) to export cookies.

**How can I view dotenv debug logs?**

Use the `--dotenv-debug` flag:

```bash
midscene /path/to/yaml --dotenv-debug=true
```
