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

# YAML 脚本运行器

:::info 兼容保留，建议迁移到 Midscene Test
“旧版 YAML”，特指由 `@midscene/cli` 提供的 YAML 自动化协议：工作流顶层使用 `tasks`，每个 task 使用 `flow`，平台和 Agent 等运行配置通常也写在 YAML 中，并通过 `midscene` 命令或包含 `files`、`setup`、`concurrent` 等字段的 YAML 批配置运行。

旧链路已进入维护状态：继续保证现有项目兼容，并处理必要的问题，但不再新增语法、能力、配置项或 CLI 参数。已有用例、批配置、目录结构和 `midscene` 命令可以保持不变；升级后会通过共享执行内核运行，并生成新的 Test 报告页面。新项目、新增用例和后续能力建设推荐直接使用 Midscene Test。

旧文件和 YAML 批配置使用 `midscene`，原生 `cases/steps` 文件和 TypeScript 项目配置使用 `midscene-test`。两种格式可以放在同一个仓库的不同目录中，但不能在同一个文件或同一次命令中混用。先阅读 [Midscene Test 概览](/zh/midscene-test/overview.md)，再按照[迁移到 Midscene Test](/zh/migrate-to-midscene-test.md)转换字段并切换命令。
:::

Midscene 定义了一种 YAML 格式的脚本，方便开发者快速编写自动化脚本，并提供了对应的命令行工具来快速执行这些脚本。

举例来说，你可以编写如下 YAML 格式脚本示例：

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

tasks:
  - name: 搜索天气
    flow:
      - ai: 搜索 "今日天气"
      - sleep: 3000
      - aiAssert: 结果显示天气信息
```

并通过一条命令来执行它：

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

命令行会输出执行进度，并在完成后生成可视化报告。整个运行过程大幅简化了开发者做环境配置的复杂度。

本文将介绍如何使用 Midscene 的命令行工具。关于更多 YAML 格式脚本的内容，可以参考 [使用 YAML 格式的自动化脚本](/zh/automate-with-scripts-in-yaml.md)。

## 使用 `.env` 配置环境变量

Midscene 命令行工具使用 [dotenv](https://www.npmjs.com/package/dotenv) 来加载 `.env` 文件。你可以在工具运行目录下创建一个 `.env` 文件，并添加以下配置：

```ini filename=.env
MIDSCENE_MODEL_BASE_URL="https://替换为你的模型服务地址/v1"
MIDSCENE_MODEL_API_KEY="替换为你的 API Key"
MIDSCENE_MODEL_NAME="替换为你的模型名称"
MIDSCENE_MODEL_FAMILY="替换为你的模型系列"
```

支持的模型和完整配置示例请参考[使用模型 API Key](/zh/model-api-setup.md)。

请注意：

- 这个文件不是必须的，你也可以通过全局环境变量的形式来配置
- 请注意这里没有 `export` 前缀，这是 dotenv 库的约定
- `.env` 文件必须放置在**工具运行目录**下，而与 YAML 文件所在的目录无关。
- 这些变量默认是**不覆盖**全局环境变量中已经的同名变量的，如需修改这个策略，请参考后文“--dotenv-override” 参数
- 如需调试此部分环境变量的逻辑，可使用 `--dotenv-debug` 参数

## 开始使用

### 安装命令行工具

安装 CLI 前，请确认运行 `midscene` 的终端使用 Node.js `20.19+`、`22.12+` 或 `24+`。CLI 的部分执行路径会使用 Rstest/Rspack 工具链，这些依赖会拒绝 `20.17.0` 这类较旧的 Node 20 patch 版本。如果看到 Rspack 抛出的 `Unsupported Node.js version` 提示，请升级 Node.js 后重新安装全局 CLI 或项目依赖。

全局安装 `@midscene/cli` （推荐新手使用）：

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

或在项目中按需安装

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

### 编写第一个脚本

编写一个名为 `bing-search.yaml` 的文件来驱动 Web 浏览器：

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

tasks:
  - name: 搜索天气
    flow:
      - ai: 搜索 "今日天气"
      - sleep: 3000
      - aiAssert: 结果显示天气信息
```

驱动已连接 adb 的 Android 设备：

```yaml
android:
  deviceId: s4ey59 # device id 可以在 adb 命令行中通过 `adb devices` 命令获取

tasks:
  - name: 地图导航
    flow:
      - ai: 打开地图应用
      - ai: 在搜索栏输入 "杭州西湖"，然后点击搜索按钮
      - ai: 点击第一个搜索结果，进入详情页
      - ai: 点击 "路线" 按钮，进入路线规划页面
      - ai: 点击 "开始" 按钮开始导航
```

或者驱动配置好 WebDriverAgent 的 iOS 设备：

```yaml
ios:
  wdaPort: 8100

tasks:
  - name: 修改系统设置
    flow:
      - ai: 打开设置应用
      - ai: 点击 "显示与亮度"
      - ai: 开启 "深色模式"
      - aiAssert: 深色模式已开启
```

### 运行脚本

```bash
midscene ./bing-search.yaml
# 如果在项目中安装了 Midscene
npx midscene ./bing-search.yaml
```

命令行会输出执行进度，并在完成后生成可视化报告。

## 命令行工具的高级用法

### 在 `.yaml` 中使用环境变量来填入动态值

脚本中可以通过 `${variable-name}` 引用环境变量。环境变量会在 YAML 任务执行前完成替换，包括任务正文中的字符串。

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

```yaml
# ...
- ai: 在输入框中输入 ${topic}
# ...
```

### 运行多个脚本

`@midscene/cli` 支持使用通配符匹配多个脚本来批量执行脚本，这相当于 `--files` 参数的简写。

```bash
# 运行单个脚本
midscene ./bing-search.yaml

# 使用通配符模式运行所有匹配的脚本
midscene './scripts/**/*.yaml'
```

### 分析命令行运行结果

执行完成后，输出目录会包含：

- `--summary` 指定的 JSON 报告（默认 `index.json`），记录所有脚本的执行状态与统计数据。
- 每个 YAML 文件对应的独立执行结果（JSON 格式）。
- 每个脚本生成的可视化报告（HTML 格式）。

### 运行在可视化（Headed）模式

> 仅适用于 Web page 场景

Headed 模式会打开浏览器窗口。默认情况下脚本在无头模式运行。

```bash
# 运行在 headed 模式
midscene /path/to/yaml --headed

# 运行在 headed 模式并在结束后保留窗口
midscene /path/to/yaml --keep-window
```

### 使用 CDP 连接模式

> 仅适用于 `web` 场景

CDP 模式可以让 YAML 脚本通过 Chrome DevTools Protocol 连接到已有的浏览器实例，无需启动新浏览器。适用于需要复用已有浏览器会话、连接远程浏览器或云端浏览器服务的场景。

在 `page` 配置中设置 `cdpEndpoint`：

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

:::info

CDP 模式与桥接模式互斥，不可同时使用。CDP 模式下 Midscene 只会断开连接（disconnect），不会关闭浏览器。

:::

### 使用桥接模式

完成 [Chrome Bridge 环境配置](/zh/bridge-mode.md)后，在 YAML 中选择当前标签页或新建标签页。配置字段、示例和不支持的选项见 [YAML 中的 Chrome Bridge](/zh/automate-with-scripts-in-yaml.md#chrome-bridge)。

### 使用 JavaScript 运行 YAML 脚本

调用 Agent 的 [`runYaml`](/zh/reference.md#runyaml) 方法同样可以在 JavaScript 中执行 YAML，注意该方法只会运行脚本中的 `tasks` 部分。

## 命令行参数

命令行工具提供了多项参数，用于控制脚本的执行行为：

- `--files <file1> <file2> ...`：指定脚本文件列表。默认按顺序执行（`--concurrent` 为 `1`），可通过 `--concurrent` 设置并发数量。支持 [glob](https://www.npmjs.com/package/glob) 通配符语法；当 glob 或目录匹配到多个文件时，匹配结果会按文件路径的字典序排序后加入执行列表。
- `--setup <file>`：在主 `--files` 之前执行的前置脚本，适用于所有受支持的 target。如果所有 setup attempt 都失败，整个批次会中止，主脚本会被标记为未执行。Puppeteer Web setup 必须配合 `--share-browser-context` 使用；此时每次 retry 都从全新的 BrowserContext 和 Page 开始，成功 attempt 的上下文会共享给主脚本。默认情况下，每个 YAML 使用独立 Page；串行批次可以加上 `--reuse-page`，让主脚本继续使用 setup 的 Page。桥接模式和非 Web target 必须省略这些共享选项；它们在 retry 时会创建新的 player 和 Agent，但不会重置底层浏览器、设备、桌面或外部 Interface 的状态。
- `--concurrent <number>`：设置并发执行的数量，默认 `1`。
- `--continue-on-error`：启用后，即使某个脚本失败也会继续执行后续脚本。默认关闭。
- `--retry <number>`：失败脚本的额外尝试次数。只有失败的脚本会被重试，可缓解网络波动或大模型输出不稳定导致的偶发失败。默认 `0`。Puppeteer Web setup retry 使用全新的 BrowserContext 和 Page，主脚本 retry 则保留成功的 setup 上下文。其他 target 每次 retry 会创建新的 player 和 Agent，但不会重置底层运行环境。
- `--share-browser-context`：在多个 Puppeteer Web 脚本之间共享同一个 BrowserContext（Cookies、同源 `localStorage` 等），同时每个 YAML 使用独立 Page。即使 `--concurrent 1`，`sessionStorage`、DOM、URL、`window.name` 等页面级状态也不会共享。同一批次中的 setup 和主脚本必须全部使用 Puppeteer Web target；不支持桥接模式和非 Web target。由于 Browser 只会创建或连接一次，浏览器级选项（`cdpEndpoint`、`chromeArgs`、`acceptInsecureCerts` 和 `downloadPath`）必须放在批次配置的全局 Web target 中，不能放在单个 setup 或主脚本中。默认关闭。
- `--reuse-page`：让多个 Puppeteer Web 脚本复用同一个 Page 对象，可保留 `sessionStorage` 和 `window.name` 等页面级状态。必须同时设置 `--share-browser-context` 和 `--concurrent 1`。默认关闭。
- `--summary <filename>`：指定生成的 JSON 总结报告路径。
- `--headed`：在带界面的浏览器中运行脚本，而非默认的无头模式。
- `--keep-window`：脚本执行完成后保持浏览器窗口，会自动开启 `--headed` 模式。
- `--config <filename>`：指定配置文件，文件中的参数会作为命令行参数的默认值。
- `--web.userAgent <ua>`：设置浏览器 UA，覆盖所有脚本中的 `web.userAgent`。
- `--web.viewportWidth <width>`：设置浏览器视口宽度，覆盖所有脚本中的 `web.viewportWidth`。
- `--web.viewportHeight <height>`：设置浏览器视口高度，覆盖所有脚本中的 `web.viewportHeight`。
- `--android.deviceId <device-id>`：设置安卓设备 ID，覆盖所有脚本中的 `android.deviceId`。
- `--harmony.deviceId <device-id>`：设置 HarmonyOS 设备 ID，覆盖所有脚本中的 `harmony.deviceId`。
- `--ios.wdaPort <port>`：设置 WebDriverAgent 端口，覆盖所有脚本中的 `ios.wdaPort`。
- `--ios.wdaHost <host>`：设置 WebDriverAgent 主机地址，覆盖所有脚本中的 `ios.wdaHost`。
- `--computer.displayId <display-id>`：设置显示器 ID，覆盖所有脚本中的 `computer.displayId`。
- `--dotenv-debug`：开启 dotenv 的调试日志，默认关闭。
- `--dotenv-override`：允许 dotenv 覆盖同名的全局环境变量，默认关闭。

示例：

使用 `--files` 指定执行顺序：

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

以 4 个并发执行多个互不依赖的搜索脚本，并在出错时继续运行：

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

### 通过文件编写命令行参数

可以把参数写到 YAML 配置文件中，并通过 `--config` 引用。命令行传入的参数优先级高于配置文件。

配置文件还可以包含一个全局 target 部分：`page`、`browser`、`web`、已弃用的 `target`、`android`、`ios`、`harmony`、`computer` 或 `interface`。其中的字段会深度合并到每个脚本对应的 target 部分，并覆盖脚本中的同名字段。完整优先级为：命令行 target 参数 > 配置文件中的 target 部分 > 单个脚本中的 target 部分；未被覆盖的字段会保留。合并后的每个脚本仍必须且只能解析出一种 target 类型。

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

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

例如，下面的配置会为所有匹配的脚本统一指定 HarmonyOS 设备，同时保留单个脚本中未被覆盖的其他 HarmonyOS 设置：

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

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

运行方式：

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

当脚本必须严格按照 `files` 列表的顺序执行时，请设置 `concurrent: 1`（默认值）。
当该值大于 `1` 时，执行顺序不确定；脚本不得依赖其他脚本的启动或完成顺序。

#### 在并行脚本之前执行前置任务

当多个互不依赖的 Puppeteer Web 脚本都依赖同一个前置条件（例如登录）时，可以把前置任务写在 `setup` 下。前置脚本会在主 `files` 之前执行；成功后，主脚本再按配置的并发数运行。设置 `shareBrowserContext: true` 后，成功 setup attempt 的浏览器上下文（包括 Cookies 和同源 `localStorage`）会被主脚本复用。每个脚本使用独立 Page，因此 setup Page 的 `sessionStorage`、DOM、URL、`window.name` 和其他页面级状态不会复制到主 Page。同一共享批次中的所有脚本都必须使用 Puppeteer Web target，不支持桥接模式。

共享 Browser 只会根据批次配置创建或连接一次。请在批次配置的全局 Web target 中设置 `cdpEndpoint`、`chromeArgs`、`acceptInsecureCerts` 和 `downloadPath`。如果在单个 setup 或主脚本中定义这些浏览器级选项，CLI 会直接报错，因为它们无法应用到已经创建的共享 Browser。

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

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

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

`retry` 表示额外尝试次数，因此 `retry: 2` 最多会执行三次。每次 setup retry 都会创建新的 BrowserContext 和 Page，避免失败 attempt 留下的 Cookies、`localStorage`、页面导航状态及其他浏览器副作用污染下一次尝试。setup 成功后，其上下文会与主脚本共享；主脚本失败时会在同一上下文中重试，从而保留成功 setup 建立的状态。如果所有 setup attempt 都失败，整个批次会被中止，主脚本将被标记为未执行。

:::warning 页面级状态不会共享
`shareBrowserContext` 遵循浏览器原生的存储边界，不会在 Page 之间复制或同步 `sessionStorage`。如果 setup 只把登录态写入 `sessionStorage`，主脚本将无法继承该登录态。需要让多个脚本看到前置状态时，请优先使用 Cookies、同源 `localStorage` 或服务端状态。脚本并发运行时，共享状态的写入可能发生竞争，需要显式协调。
:::

如果串行流程必须接着使用 setup 创建的 Page，可以显式开启 Page 复用：

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

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

主脚本仍会应用自己的 Web target 配置，并打开其中设置的 `url`，但导航会发生在 setup 创建的同一个 Page 对象中。在 `browser` 模式下，弹窗或新标签页操作后来打开的 Page 不会替换这个被复用的 Page。

`setup` 也支持桥接模式、Android、iOS、HarmonyOS、Computer 和自定义 Interface target。这些 target 应省略 `shareBrowserContext`。retry 会创建新的脚本 player 和 Agent，但不会自动重置底层浏览器配置、设备、桌面会话或自定义 Interface；如果 retry 必须从干净环境开始，需要由 setup 脚本显式恢复环境。

## 常见问题

**如何导出 JSON 格式的 Cookies？**

可以借助 [Chrome 扩展](https://chromewebstore.google.com/detail/get-cookiestxt-locally/cclelndahbckbenkjhflpdbgdldlbecc) 导出 Cookies。

**如何查看 dotenv 的调试日志？**

使用 `--dotenv-debug` 参数即可：

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