# 3.3 Project Interface V2 协议

> 注意: 本文档是关于 `ProjectInterface` 的编写和使用
>
> 文中将使用 PI 代指 `ProjectInterface`，Client 代指可以处理 PI 的工具

## 简介

所谓 `ProjectInterface`，即 MaaFramework 的一个标准化的项目结构声明，该声明目前包含 `interface.json` 一个文件。通过定义 PI，你可以使用 MaaFramework 的各种衍生工具。因此，即使你打算纯粹依靠通用编程语言集成，也建议定义包含基础信息的 PI。

> [!TIP]
>
> 你可以前往 [社区项目](https://github.com/MaaXYZ/MaaFramework#%E7%A4%BE%E5%8C%BA%E9%A1%B9%E7%9B%AE) 中查找使用 PI 的通用 UI 及其他工具

### 版本说明

由于业务进度高速迭代，MaaFramework 的发版节奏与 PI 的迭代节奏无法很好对齐。因此，PI 将进行单独的版本控制，PI 的版本号与 MaaFramework release 版本号并不完全同步。

我们将独立版本时（2026-1-30）的版本号定为 `v2.1.0`，后续新增与修复将会在文档中标注版本号。

| 日期 | 版本 | 变更内容 |
| ---- | ---- | -------- |
| 2026-1-30 | v2.1.0 | 独立版本发布 |
| 2026-1-30 | v2.2.0 | 新增 `attach_resource_path` 和 `import` 字段 |
| 2026-2-23 | v2.3.0 | 新增 `checkbox` 多选类型、`option.controller`、`option.resource`、全局/resource/controller 级 option、`focus.display` 展示渠道标签、`preset` 预设配置字段、`import` 支持导入 `preset` |
| 2026-3-5 | v2.3.1 | 明确 option 适用性过滤规则 |
| 2026-3-9 | v2.4.0 | 新增顶层 `group` 分组声明与 `task.group` 任务分组字段 |
| 2026-3-23 | v2.5.0 | 约定 Client 启动 Agent 子进程时注入 `PI_*` 环境变量，传递 UI / Client 属性及当前选中的控制器与资源（i18n 已解析） |
| 2026-4-18 | v2.6.0 | 新增 `resource.hash` 资源完整性校验字段 |

## `interface.json`

> [!TIP]
>
> 该文件可以通过 [schema](https://github.com/MaaXYZ/MaaFramework/blob/main/tools/interface.schema.json) 文件获得提示和校验功能
>
> 使用 VSCode 打开 项目模板 文件夹，可自动关联 schema 和文件

### 整体结构

- interface_version `number`  
  接口版本号，当前为 2，固定且必须设置。用于标识 `interface.json` 的 **JSON 结构主版本**。PI 文档另有独立的语义化版本（如 v2.5.0 的环境变量约定），二者不必一一对应。

- languages `object`  
  多语言支持配置，键为语言代码，值为对应的翻译文件路径。若不指定，则默认仅支持中文。  
  文件相对路径为 interface.json 同目录下的相对路径。
  
  ```jsonc
  "languages": {
      "zh_cn": "interface_zh.json",
      "en_us": "interface_en.json"
  }
  ```

- name `string`  
  项目唯一标识符，用作项目ID。

- label `string`  
  项目显示名称，用于在用户界面中展示。支持国际化字符串（以`$`开头）。如果未设置，则显示 `name` 字段的值。可选。

  ```jsonc
  {
    "name": "MyProject", 
    "label": "$project_name"  // 国际化项目名称
  }
  ```

- title `string`  
  窗口标题，Client 会直接显示该内容，不添加其他修饰。可选，默认使用 `name` 和 `version` 拼接生成。支持国际化（以`$`开头）。

- icon `string`  
  应用图标文件路径，相对于项目根目录。若不指定，则使用默认图标。支持国际化（以`$`开头）。

- mirrorchyan_rid `string`  
  MirrorChyan 资源包标识符，用于资源管理和分发。

- mirrorchyan_multiplatform `boolean`  
  是否支持多平台，影响资源包的打包和分发策略。

- github `string`  
  项目GitHub仓库地址，用于版本更新检查和问题反馈。  
  **软件更新约定：**  
  ~~经过版本历史的惨痛教训，~~ 我们期望通用 UI 仅提供对该 github release 的更新功能，不要提供单独更新 UI 本体 / MaaFW 的功能。  
  资源作者发版时自行打包其指定的 UI / MaaFW，以实现用户侧的资源版本对应唯一的 UI 和 MaaFW 版本，规避版本混搭带来的各种问题。

- version `string`  
  项目版本号，Client 可以展示给用户，同时用于版本更新检查。

- contact `string`  
  联系方式信息，显示在"关于"页面。支持文件路径、URL或直接文本，内容支持Markdown格式。支持国际化（以`$`开头）。

- license `string`  
  项目许可证信息，显示在"关于"页面。支持文件路径、URL或直接文本，内容支持Markdown格式。支持国际化（以`$`开头）。

- welcome `string`  
  欢迎消息，在用户首次使用时弹窗显示，亦可作为公告使用。支持文件路径、URL或直接文本，内容支持Markdown格式。系统会记录显示内容，当内容更新时会再次弹窗显示。支持国际化（以`$`开头）。

- description `string`  
  项目描述信息，显示在"关于"页面。支持文件路径、URL或直接文本，内容支持Markdown格式。支持国际化（以`$`开头）。

- controller `object[]`  
  控制器配置，为一个对象数组，含有预设的控制器信息。

  - name `string`  
    唯一名称标识符，用作控制器ID。

  - label `string`  
    显示名称，用于在用户界面中展示。支持国际化字符串（以`$`开头）。如果未设置，则显示 `name` 字段的值。

  - description `string`  
    控制器详细描述信息。支持文件路径、URL或直接文本，内容支持Markdown格式。可选。支持国际化（以`$`开头）。

  - icon `string`  
    控制器图标文件路径，相对于项目根目录。可选。支持国际化（以`$`开头）。

  - type `'Adb' | 'Win32' | 'MacOS' | 'PlayCover' | 'Gamepad' | 'WlRoots'`  
    控制器类型，取值为 `Adb`、`Win32`（仅 Windows）、`MacOS`（仅 macOS）、`PlayCover`（仅 macOS）、`Gamepad`（仅 Windows）和 `WlRoots`（仅 Linux）。

  - display_short_side `number`  
    默认缩放分辨率的短边长度，用于屏幕适配。可选，默认720。与`display_long_side`和`display_raw`互斥。

  - display_long_side `number`  
    默认缩放分辨率的长边长度，用于屏幕适配。可选。与`display_short_side`和`display_raw`互斥。

  - display_raw `boolean`  
    是否使用原始分辨率进行截图，不进行缩放。可选，默认false。与缩放分辨率设置互斥。

  - permission_required `boolean`  
    是否需要管理员权限运行该控制器。可选，默认 false。  
    在运行任务前，若当前进程不是管理员，会提示并尝试以管理员身份重新启动。

  - attach_resource_path `string[]` **💡 v2.2.0**  
    可选。附加资源路径数组。将会在 `resource.path` 加载完成后，额外加载这些路径下的资源。

  - option `string[]` **💡 v2.3.0**  
    可选。控制器级的选项配置，为一个字符串数组，数组元素应与外层 option 配置中的键名对应。  
    该选项生成的参数会参与到所有使用该控制器的任务的 pipeline override 中，起到控制器级参数传递的作用。  
    若被引用的 option 不支持当前 controller/resource 条件，则该 option 的 `pipeline_override`（含其嵌套 `option.option`）不参与合并。 **💡 v2.3.1**

  - adb `object`  
    `Adb` 控制器的具体配置。  

    > **注意：** V2 协议中。Adb 控制器的 input/screencap 由 MaaFramework 自动检测和选择最优方式，无需手动配置。

  - win32 `object`  
    `Win32` 控制器的具体配置。

    - class_regex `string`  
      可选。`Win32` 控制器搜索窗口类名使用的正则表达式。

    - window_regex `string`  
      可选。`Win32` 控制器搜索窗口标题使用的正则表达式。

    - mouse `string`  
      可选。`Win32` 控制器的鼠标控制方式，不提供则使用默认。详见 [控制方式说明](2.4-控制方式说明.md#win32-input)。

    - keyboard `string`  
      可选。`Win32` 控制器的键盘控制方式，不提供则使用默认。详见 [控制方式说明](2.4-控制方式说明.md#win32-input)。

    - screencap `string`  
      可选。`Win32` 控制器的截图方式，不提供则使用默认。详见 [控制方式说明](2.4-控制方式说明.md#win32-screencap)。

  - macos `object`  
    `MacOS` 控制器的具体配置。

    - title_regex `string`  
      可选。`MacOS` 控制器搜索窗口标题使用的正则表达式。

    - screencap `string`  
      可选。`MacOS` 控制器的截图方式，不提供则使用默认。详见 [控制方式说明](2.4-控制方式说明.md#macos-screencap)。

    - input `string`  
      可选。`MacOS` 控制器的输入方式，不提供则使用默认。详见 [控制方式说明](2.4-控制方式说明.md#macos-input)。

  - playcover `object`  
    `PlayCover` 控制器的具体配置（仅 macOS）。用于控制通过 PlayCover 运行的 iOS 应用。详见 [控制方式说明](2.4-控制方式说明.md#playcover-macos)。

    - uuid `string`  
      可选。目标应用的 Bundle Identifier，不提供则使用默认 `maa.playcover`，仅作为控制器标识符，与被操控应用无关。

  - gamepad `object`  
    `Gamepad` 控制器的具体配置（仅 Windows）。用于创建虚拟游戏手柄进行游戏控制。需要安装 [ViGEm Bus Driver](https://github.com/ViGEm/ViGEmBus)。详见 [控制方式说明](2.4-控制方式说明.md#gamepad-windows)。

    - class_regex `string`  
      可选。`Win32` 控制器搜索窗口类名使用的正则表达式。

    - window_regex `string`  
      可选。`Win32` 控制器搜索窗口标题使用的正则表达式。

    - gamepad_type `string`  
      可选。虚拟手柄类型，取值为 `Xbox360`、`DualShock4`（或 `DS4`）。不提供则默认使用 `Xbox360`。

    - screencap `string`  
      可选。截图方式，不提供则使用默认。仅当配置了窗口正则时有效。详见 [控制方式说明](2.4-控制方式说明.md#win32-screencap)。

  - wlroots `object`  
    `wlroots` 控制器的具体配置（仅 Linux）。用于控制在 wlroots
    合成器中运行的应用程序。详见 [控制方式说明](2.4-控制方式说明.md#wlroots-linux)。

    - use_win32_vk_code `bool`
      可选。为 `true` 时按键被视为 Win32 Virtual-Key 键码，内部转换为 Linux evdev 码；为 `false` 时按原始 evdev 码处理。默认 `false`。

- resource `object[]`  
  资源配置，为一个对象数组，含有资源加载的信息。

  - name `string`  
    唯一名称标识符，用作资源包ID。

  - label `string`  
    显示名称，用于在用户界面中展示。支持国际化字符串（以`$`开头）。如果未设置，则显示 `name` 字段的值。可选。

  - description `string`  
    资源详细描述信息。支持文件路径、URL或直接文本，内容支持Markdown格式。可选。

  - icon `string`  
    资源图标文件路径，相对于项目根目录。可选。支持国际化（以`$`开头）。

  - path `string[]`  
    加载的路径数组。如果提供多个路径，会依次加载，后加载的资源会[覆盖](#资源覆盖)前加载的资源。  
    文件相对路径为 interface.json 同目录下的相对路径。  
    注意: 资源不仅仅是 `pipeline`，也包含 `image` 和 `model`，因此不要直接指定 `pipeline` 目录。

  - controller `string[]`  
    可选。指定该资源包支持的控制器类型列表。数组元素应与 `controller` 配置中的 `name` 字段对应。若不指定，则表示支持所有控制器类型。  
    当用户选择了某个控制器时，只有支持该控制器的资源包才会显示在用户界面中供选择。这允许为不同控制器类型提供专门优化的资源包。

  - option `string[]`  
    可选。资源包级的选项配置，为一个字符串数组，数组元素应与外层 option 配置中的键名对应。  
    该选项生成的参数会参与到所有任务的 pipeline override 中，起到资源包级参数传递的作用。  
    若被引用的 option 不支持当前 controller/resource 条件，则该 option 的 `pipeline_override`（含其嵌套 `option.option`）不参与合并。 **💡 v2.3.1**

  - hash `string` **💡 v2.6.0**  
    可选。资源完整性校验值。该值应为仅加载 `path` 后通过 `MaaResourceGetHash` 获取的 hash 字符串。  
    校验时机为加载完 `path` 后、加载 `controller.attach_resource_path` 之前。  
    若实际 hash 与该值不匹配，应向用户发出警告（例如建议重新下载资源包），但不应阻止继续使用。  
    调试版本（如预发布版本）可跳过此校验。若未设置此字段，无需校验。
  
    ```jsonc
    "resource": [
        {
            "name": "Android专用资源",
            "label": "$Android专用资源",
            "controller": ["Android"],
            "path": ["resource_android"]
        },
        {
            "name": "通用资源",
            "label": "$通用资源",
            "path": ["resource"],
            "hash": "1a2b3c4d"
        }
    ]
    ```

- agent `object | object[]`  
  代理配置，可以是单个对象或对象数组，含有子进程（AgentServer）的信息。支持同时配置多个 Agent 以实现更复杂的自动化场景。  
  自 **v2.5.0** 起，Client 在启动该子进程时还应按约定注入以 `PI_` 为前缀的环境变量，详见下文 [Agent 子进程环境变量](#agent-subprocess-env-v25) 小节。

  - child_exec `string`  
    子进程路径，为系统路径中可执行文件。如在环境变量（系统变量、用户变量）中存在 Python 路径，可直接写 `"python"`。  
    CWD 为 interface.json 所在目录。

  - child_args `string[]`  
    可选。子进程参数数组。

  - identifier `string`  
    可选。连接标识符，被用来创建一个通信套接字。填写则会被使用，否则自动创建。

  **单个 Agent 示例：**

  ```jsonc
  "agent": {
      "child_exec": "python",
      "child_args": [
          "./agent/main.py",              // 注意 cwd 为 interface.json 所在目录
          "--test-mode"                   // 固定字符串，原样传递
      ]
  }
  ```

  **多个 Agent 示例：**

  ```jsonc
  "agent": [
      {
          "child_exec": "python",
          "child_args": ["./agent/recognition.py"]
      },
      {
          "child_exec": "python",
          "child_args": ["./agent/action.py"]
      }
  ]
  ```

<a id="agent-subprocess-env-v25"></a>

### Agent 子进程环境变量 **💡 v2.5.0**

Agent 与 MaaFramework 主进程分离运行时，子进程无法自动获知通用 UI 的名称与版本、界面语言、用户当前选中的控制器与资源包等信息；这些内容也不在 MaaFW C API 的常规回调中暴露。为便于自定义识别/动作等逻辑读取 **Client 侧上下文** 与 **当前 PI 选择在运行时的快照**，约定由 **Client** 在启动 `agent` 子进程时，向进程环境注入下列变量（名称全大写，值为字符串）。

| 变量名 | 说明 |
| ------ | ---- |
| `PI_INTERFACE_VERSION` | Client 在「PI 扩展能力」侧实现的协议版本，**语义化版本**字符串（如 `v2.5.0`）。子进程可据此判断是否依赖某批变量或行为；**不要**与 `interface.json` 中的数字字段 `interface_version`（当前固定为 `2`）混为一谈。 |
| `PI_CLIENT_NAME` | 通用 UI / Client 名称标识，如 `MFAA`、`MXU`、`VSCODE`、`MPE`、`MaaDebugger` 等。 |
| `PI_CLIENT_VERSION` | Client 自身版本号（语义化版本字符串，由 Client 定义）。 |
| `PI_CLIENT_LANGUAGE` | 当前 UI 语言代码，如 `zh_cn`、`en_us`（与 `languages` 键或 Client 约定一致即可）。 |
| `PI_CLIENT_MAAFW_VERSION` | 该 Client 集成/打包的 MaaFramework 库版本（语义化版本字符串）。 |
| `PI_VERSION` | 与 `interface.json` 顶层 `version` 字段一致，表示**资源项目**版本号。 |
| `PI_CONTROLLER` | **单行 JSON 字符串**：当前用户选中的那条 `controller` 数组元素对应的完整对象。所有支持 i18n 的字段（如 `label`、`description` 等）应已替换为**展示用最终文本**，不再保留以 `$` 开头的翻译键引用。 |
| `PI_RESOURCE` | **单行 JSON 字符串**：当前用户选中的那条 `resource` 数组元素对应的完整对象，同样完成 i18n 解析，格式为紧凑 JSON（无换行），便于通过环境变量传递。 |

**约定说明：**

- 若某项信息当前不可用，Client 可不设置对应变量或置空；子进程应做容错，勿假定全部存在。
- `PI_CONTROLLER` / `PI_RESOURCE` 仅描述**当前选择**；若运行期切换了控制器或资源包，Client 应在重启子进程或约定的时机更新环境（具体是否重启由 Client 实现决定，本协议不强制）。
- 变量值需符合操作系统对环境变量长度与字符集的限制；JSON 宜采用紧凑序列化（minify）。

**示例（值已转义示意，实际为单行）：**

```text
PI_INTERFACE_VERSION=v2.5.0
PI_CLIENT_NAME=MFAA
PI_CLIENT_VERSION=v1.0.0
PI_CLIENT_LANGUAGE=zh_cn
PI_CLIENT_MAAFW_VERSION=v5.9.0
PI_VERSION=v2.2.0
PI_CONTROLLER={"name":"Win32-Window","label":"Win32-默认","description":"默认控制器","type":"Win32","win32":{"class_regex":"UnityWndClass","window_regex":"Endfield","screencap":"Background","mouse":"SendMessageWithCursorPos","keyboard":"PostMessage"},"permission_required":true}
PI_RESOURCE={"name":"官服","label":"官服","path":["./resource"]}
```

- group `object[]` **💡 v2.4.0**  
  可选。任务分组声明，为一个对象数组，用于声明任务分组及其展示属性。

  - name `string`  
    分组唯一标识符，用作分组 ID。任务通过 `task.group` 引用该名称。

  - label `string`  
    分组显示名称，用于在用户界面中展示。支持国际化字符串（以`$`开头）。如果未设置，则显示 `name` 字段的值。可选。

  - description `string`  
    分组详细描述信息，帮助用户理解该分组的用途。支持文件路径、URL或直接文本，内容支持 Markdown 格式。可选。

  - icon `string`  
    分组图标文件路径，相对于项目根目录。用于在用户界面中显示。可选。支持国际化（以`$`开头）。

  - default_expand `boolean`  
    可选，默认 `true`。该分组在 Client 中是否默认展开。

  ```jsonc
  "group": [
      {
          "name": "daily",
          "label": "$日常任务",
          "description": "$日常任务说明",
          "icon": "groups/daily.png",
          "default_expand": true
      },
      {
          "name": "battle",
          "label": "$战斗任务"
      }
  ]
  ```

- task `object[]`  
  任务配置，为一个对象数组，含有可执行任务的信息。

  - name `string`  
    任务唯一标识符，用作任务ID。

  - label `string`  
    任务显示名称，用于在用户界面中展示。支持国际化字符串（以`$`开头）。如果未设置，则显示 `name` 字段的值。可选。

  - entry `string`  
    任务入口，为 `pipeline` 中 `Task` 的名称。

  - default_check `boolean`  
    是否默认选中该任务。可选，默认false。Client 在初始化时会根据该值决定是否默认勾选该任务。

  - description `string`  
    任务详细描述信息，帮助用户理解任务功能。支持文件路径、URL或直接文本，内容支持 Markdown 格式。可选。

  - icon `string`  
    任务图标文件路径，相对于项目根目录。用于在用户界面中显示。可选。支持国际化（以`$`开头）。

  - group `string[]` **💡 v2.4.0**  
    可选。指定该任务所属的分组列表。数组元素应与顶层 `group` 配置中的 `name` 字段对应。若不指定，则表示该任务不属于任何显式分组。

    同一个任务可以同时属于多个分组。Client 应将这些值仅视为分组归属关系；任务身份、勾选状态、预设引用以及配置项取值仍然以 `task.name` 作为唯一键。

    ```jsonc
    "task": [
        {
            "name": "领取奖励",
            "label": "$领取奖励",
            "entry": "CollectReward",
            "group": ["daily"]
        },
        {
            "name": "刷图",
            "label": "$刷图",
            "entry": "FarmStage",
            "group": ["daily", "battle"]
        }
    ]
    ```

  - resource `string[]`  
    可选。指定该任务支持的资源包列表。数组元素应与 `resource` 配置中的 `name` 字段对应。若不指定，则表示该任务在所有资源包中都可用。  
    当用户选择了某个资源包时，Client 可将不支持该资源包的任务隐藏，或以不可用（灰色/禁用）状态展示以提示用户。这允许为不同资源包提供专门的任务配置，比如活动任务只在特定资源包中可用。

    ```jsonc
    "task": [
        {
            "name": "活动任务",
            "label": "$活动任务",
            "entry": "ActivityTask",
            "resource": ["Official"],
            "description": "仅在官服资源包中可用的活动任务"
        },
        {
            "name": "通用任务",
            "label": "$通用任务",
            "entry": "CommonTask"
        }
    ]
    ```

  - controller `string[]`  
    可选。指定该任务支持的控制器类型列表。数组元素应与 `controller` 配置中的 `name` 字段对应。若不指定，则表示该任务在所有控制器类型中都可用。  
    当用户选择了某个控制器时，Client 可将不支持该控制器的任务隐藏，或以不可用（灰色/禁用）状态展示以提示用户。这允许为不同控制器类型提供专门的任务配置，比如某些任务只适用于 Adb 控制器，某些任务只适用于 Win32 控制器。

    ```jsonc
    "task": [
        {
            "name": "安卓专属任务",
            "label": "$安卓专属任务",
            "entry": "AndroidOnlyTask",
            "controller": ["Android"],
            "description": "仅在安卓控制器中可用的任务"
        },
        {
            "name": "PC专属任务",
            "label": "$PC专属任务",
            "entry": "Win32OnlyTask",
            "controller": ["Win32Emulator"],
            "description": "仅在Win32控制器中可用的任务"
        },
        {
            "name": "通用任务",
            "label": "$通用任务",
            "entry": "CommonTask"
        }
    ]
    ```

  - pipeline_override `pipeline`  
    可选。任务参数，执行任务时会[覆盖](#资源覆盖)已加载的资源。该项结构与 `pipeline` 中的 `json` 文件完全一致，需要包含 任务名 部分，例如:

    ```jsonc
    "pipeline_override": {
        "Quit": {
            "enabled": true
        }
    }
    ```

  - option `string[]`  
    可选。任务配置项，为一个数组，含有若干后续 `option` 对象中的键的值，Client 会根据要求用户进行选择。  
    Client 可以使用 `option` 中的顺序来展示配置项。

- option `record<string, object>`  
  配置项定义，为一个对象映射，含有配置项的信息。

  - _key_  
    唯一名称标识符，任务会使用该名称进行引用。

  - type `string`  
    配置项类型。可选，默认 `"select"`。可选值：
    - `"select"`: 下拉选项框，用户从预定义的选项中选择一个
    - `"checkbox"`: 多选框，用户从预定义的选项中选择多个 **💡 v2.3.0**
    - `"input"`: 用户输入框，允许用户手动输入内容
    - `"switch"`: 选择框，Yes or No

  - controller `string[]` **💡 v2.3.0**  
    可选。指定该配置项适用的控制器类型列表。数组元素应与 `controller` 配置中的 `name` 字段对应。若不指定，则表示该配置项在所有控制器类型中都可用。  
    当用户选择了某个控制器时，Client 可将不适用于该控制器的配置项隐藏，或以不可用（灰色/禁用）状态展示以提示用户。  
    当当前控制器不在该列表中时，该配置项视为未激活：其自身及其子配置项（`option.option`）产生的所有 `pipeline_override` 均不参与合并。 **💡 v2.3.1**

  - resource `string[]` **💡 v2.3.0**  
    可选。指定该配置项适用的资源包列表。数组元素应与 `resource` 配置中的 `name` 字段对应。若不指定，则表示该配置项在所有资源包中都可用。  
    当用户选择了某个资源包时，Client 可将不适用于该资源包的配置项隐藏，或以不可用（灰色/禁用）状态展示以提示用户。  
    当当前资源包不在该列表中时，该配置项视为未激活：其自身及其子配置项（`option.option`）产生的所有 `pipeline_override` 均不参与合并。 **💡 v2.3.1**

  - label `string`  
    配置项显示标签，用于在用户界面中展示。支持国际化字符串（以`$`开头）。可选。

  - description `string`  
    配置项详细描述信息，帮助用户理解配置项的作用。支持文件路径、URL或直接文本，内容支持Markdown格式。可选。支持国际化（以`$`开头）。

  - icon `string`  
    配置项图标文件路径，相对于项目根目录。可选。支持国际化（以`$`开头）。

  - cases `object[]`  
    仅在 `type` 为 `"select"`/`"checkbox"`/`"switch"` 时使用。可选项，为一个对象数组，含有各个可选项的信息。

    **注意：** 当 `type` 为 `"checkbox"` 时，用户可以同时选中多个 case，所有被选中的 case 的 `pipeline_override` 会按照 `cases` 数组中的定义顺序依次合并生效，与用户勾选的先后顺序无关。

    **注意：** 当 `type` 为 `"switch"` 时，仅支持两个 cases，且需遵循以下规则：
    - 如果 `case.name` 为 `"Yes"`、`"yes"`、`"Y"` 或 `"y"` 之一，该 case 会被识别为 **Yes 选项**
    - 如果 `case.name` 为 `"No"`、`"no"`、`"N"` 或 `"n"` 之一，该 case 会被识别为 **No 选项**
    - Client 会根据 case 的 name 来匹配用户的 Y/N 输入，其他输入会被要求重新输入

    建议使用 `"Yes"` 和 `"No"` 作为两个 case 的 name，以保证跨 Client 的一致性。

    Client 可以使用 `cases` 中的顺序来展示可选项。

    - name `string`  
      选项唯一标识符，用作选项ID。

    - label `string`  
      选项显示名称，用于在用户界面中展示。支持国际化字符串（以`$`开头）。如果未设置，则显示 `name` 字段的值。可选。

    - description `string`  
      选项详细描述信息。支持文件路径、URL或直接文本，内容支持Markdown格式。可选。支持国际化（以`$`开头）。

    - icon `string`  
      选项图标文件路径，相对于项目根目录。可选。支持国际化（以`$`开头）。

    - option `string[]`  
      子配置项列表。可选。只有当用户选中当前选项时，才会显示这些子配置项。这些子配置项同样放在外层的 `option` 中定义，支持无限嵌套。

    - pipeline_override `pipeline`  
      同 `task` 中的 `pipeline_override`，在选项激活时生效。

  - inputs `object[]`  
    仅在 `type` 为 `"input"` 时使用。输入配置，为一个对象数组，定义用户可输入的字段。

    - name `string`  
      输入字段唯一标识符，用作输入字段ID。

    - label `string`  
      输入字段显示名称，用于在用户界面中展示。支持国际化字符串（以`$`开头）。如果未设置，则显示 `name` 字段的值。可选。

    - description `string`  
      输入字段详细描述信息，帮助用户理解输入要求。支持文件路径、URL或直接文本，内容支持Markdown格式。可选。支持国际化（以`$`开头）。

    - default `string`  
      输入字段的默认值。可选。

    - pipeline_type `string`  
      输入字段在 pipeline_override 中的数据类型。可选值：`"string"`, `"int"`, `"bool"`。当使用 pipeline_override 中的变量替换时，会根据该类型进行类型转换。

    - verify `string`  
      正则表达式，用于校验用户输入是否合法。可选。

    - pattern_msg `string`  
      正则校验用户输入错误时，显示的信息。可选。支持国际化（以`$`开头）。

  - pipeline_override `pipeline`  
    当配置项为 `"input"` 类型时使用，作为用户输入内容的替换模板。支持在字符串中使用 `{名称}` 格式引用输入字段的值。可选。

  - default_case `string` | `string[]`  
    默认选项名称。可选。
    - 当 `type` 为 `"select"` / `"switch"` 时，填写单个字符串，Client 使用该值作为选项的初始选中值。
    - 当 `type` 为 `"checkbox"` 时，填写字符串数组，Client 使用该值作为多选框的初始选中值。 **💡 v2.3.0**

- global_option `string[]` **💡 v2.3.0**  
  可选。全局选项配置，为一个字符串数组，数组元素应与 `option` 配置中的键名对应。  
  该选项生成的参数会参与到所有任务的 pipeline override 中，无论用户选择了什么资源包或控制器。  
  与 `resource.option` 和 `controller.option` 不同，全局选项不依赖于任何资源包或控制器的选择。  
  但 `global_option` 引用的 option 仍需满足该 option 自身的 `resource` / `controller` 限制；不满足时不应用任何 `pipeline_override`。 **💡 v2.3.1**

  ```jsonc
  "global_option": [
      "战斗划火柴",
      "战斗自动闪避"
  ]
  ```

- import `string[]` **💡 v2.2.0**  
    可选。导入其他 PI 文件的路径数组。文件相对路径为 interface.json 同目录下的相对路径。  
    支持导入这些文件中的 `task`、`option` 和 `preset` **💡 v2.3.0** 字段。  
    Client 会依次加载这些文件，并将它们的内容与当前文件进行合并，从而实现配置的拆分和复用。

- preset `object[]` **💡 v2.3.0**  
  可选。预设配置，为一个对象数组。每个预设是一套预定义的任务勾选状态与选项值的快照，用户可一键应用，快速切换不同使用场景。

  - name `string`  
    唯一标识符，用作预设 ID。

  - label `string`  
    显示名称，用于在用户界面中展示。支持国际化字符串（以`$`开头）。如果未设置，则显示 `name` 字段的值。可选。

  - description `string`  
    预设详细描述信息，帮助用户理解该预设的适用场景。支持文件路径、URL 或直接文本，内容支持 Markdown 格式。可选。支持国际化（以`$`开头）。

  - icon `string`  
    预设图标文件路径，相对于项目根目录。可选。支持国际化（以`$`开头）。

  - task `object[]`  
    预设包含的任务配置列表。

    - name `string`  
      必须。与顶层 `task[].name` 对应的任务名称。

    - enabled `boolean`  
      可选，默认 `true`。该任务在预设中是否勾选。

    - option `record<string, OptionValue>`  
      可选。该任务各配置项的预设值，键为顶层 `option` 中的键名。`OptionValue` 的类型取决于对应 `option` 的 `type`：

      | option.type | OptionValue 类型 | 示例 |
      |---|---|---|
      | `select` / `switch` | `string`（`case.name`） | `"x3"` / `"Yes"` |
      | `checkbox` | `string[]`（`case.name` 数组） | `["自动战斗", "自动拾取"]` |
      | `input` | `record<string, string>`（输入字段 name → 值） | `{ "章节号": "4" }` |

  **预设示例：**

  ```jsonc
  "preset": [
      {
          "name": "刷日常",
          "label": "$刷日常",
          "description": "每日例行套餐：荒原 + 心相 + 3-9 + 领奖",
          "icon": "preset_daily.png",
          "task": [
              { "name": "收取荒原", "enabled": true },
              { "name": "每日心相（意志解析）", "enabled": true },
              {
                  "name": "常规作战",
                  "enabled": true,
                  "option": {
                      "作战关卡": "3-9 厄险（百灵百验鸟）",   // select 类型：填 case.name
                      "复现次数": "x3",                       // select 类型：填 case.name
                      "刷完全部体力": "No",                   // switch 类型：填 case.name
                      "启用功能": ["自动战斗", "自动拾取"]    // checkbox 类型：填 case.name 数组
                  }
              },
              { "name": "领取奖励", "enabled": true },
              { "name": "活动：绿湖噩梦 17 艰难（活动已结束）", "enabled": false }
          ]
      },
      {
          "name": "实时辅助",
          "label": "$实时辅助",
          "description": "后台挂机辅助，仅执行自动辅助类任务",
          "task": [
              {
                  "name": "常规作战",
                  "enabled": true,
                  "option": {
                      "启用功能": ["自动战斗", "自动拾取", "自动回血"]
                  }
              }
          ]
      }
  ]
  ```

  **通过 `import` 分文件管理预设：**

  预设配置可以通过 `import` 拆分到独立文件中，方便管理和复用。例如：

  ```jsonc
  // interface.json
  {
      "import": [
          "preset_daily.json",
          "preset_realtime.json"
      ],
      "preset": [
          // 也可以在主文件中直接定义预设，会与导入的预设合并
      ]
  }
  ```

  ```jsonc
  // preset_daily.json
  {
      "preset": [
          {
              "name": "刷日常",
              "label": "$刷日常",
              "description": "每日例行套餐",
              "task": [
                  { "name": "收取荒原", "enabled": true },
                  { "name": "领取奖励", "enabled": true }
              ]
          }
      ]
  }
  ```

### Option 覆盖顺序 **💡 v2.3.0**

各级 option 生成的 `pipeline_override` 会按照以下顺序依次合并，**后合并的会覆盖先合并的同名字段**：

1. `global_option` — 全局选项，最先生效，优先级最低
2. `resource.option` — 资源包级选项
3. `controller.option` — 控制器级选项
4. `task.option` — 任务级选项，最后生效，优先级最高

即：`task.option` > `controller.option` > `resource.option` > `global_option`。

在进入上述合并顺序前，需先过滤“未激活”的 option。任何不满足当前 `controller` / `resource` 条件的 option（包括来自 `global_option`、`resource.option`、`controller.option`、`task.option` 以及嵌套 `option.option`）都不得产生 `pipeline_override`。 **💡 v2.3.1**

这样设计的理由是：越具体的配置（任务级）应当具有越高的优先级，越通用的配置（全局级）则作为兜底默认值。

**示例：**

假设 `global_option` 和某个 `task.option` 同时引用了同一个 option，且该 option 的某个 case 对同一个 pipeline 节点做了不同的 override，那么任务级的 override 将最终生效。

### 配置项示例

#### select 类型选项示例

```jsonc
{
  "option": {
    "作战关卡": {
      "type": "select",
      "label": "$选择作战关卡",
      "description": "选择要刷的关卡",
      "default_case": "3-9 厄险",
      "cases": [
        {
          "name": "3-9 厄险（百灵百验鸟）",
          "label": "$3-9厄险",
          "description": "刷百灵鸟",
          "icon": "百灵鸟.png",
          "option": [
            "使用理智药",
            "刷完xxx"
          ],
          "pipeline_override": {
            "EnterTheShow": {
              "next": "MainChapter_3"
            }
          }
        }
      ]
    }
  }
}
```

#### input 类型选项示例

```jsonc
{
  "option": {
    "自定义关卡": {
      "type": "input",
      "label": "自定义关卡",
      "description": "自己选打什么关",
      "icon": "扳手.png",
      "inputs": [
        {
          "name": "章节号",
          "label": "$章节号",
          "description": "关卡章节号，用阿拉伯数字表示",
          "default": "4",
          "pipeline_type": "string",
          "verify": "^\\d+$"
        },
        {
          "name": "超时时间",
          "label": "$超时时间",
          "description": "等待超时时间",
          "default": "20000",
          "pipeline_type": "int",
          "verify": "^\\d+$"
        }
      ],
      "pipeline_override": {
        "EnterTheShow": {
          "next": "MainChapter_{章节号}",
          "timeout": "{超时时间}"
        }
      }
    }
  }
}
```

#### checkbox 类型选项示例 💡 v2.3.0

```jsonc
{
  "option": {
    "战斗划火柴": {
      "type": "checkbox",
      "label": "$战斗划火柴",
      "description": "选择要启用的划火柴功能，可多选",
      "default_case": ["普通划火柴", "蓄力划火柴"],
      "cases": [
        {
          "name": "普通划火柴",
          "label": "$普通划火柴",
          "description": "启用普通划火柴",
          "pipeline_override": {
            "NormalMatch": {
              "enabled": true
            }
          }
        },
        {
          "name": "蓄力划火柴",
          "label": "$蓄力划火柴",
          "description": "启用蓄力划火柴",
          "pipeline_override": {
            "ChargedMatch": {
              "enabled": true
            }
          }
        },
        {
          "name": "连续划火柴",
          "label": "$连续划火柴",
          "description": "启用连续划火柴",
          "pipeline_override": {
            "ComboMatch": {
              "enabled": true
            }
          }
        }
      ]
    }
  }
}
```

> **注意：** checkbox 类型中，多个被选中 case 的 `pipeline_override` 按照 `cases` 数组中的**定义顺序**依次合并，与用户勾选的先后顺序无关。

### 国际化支持

对于所有支持国际化的字符串字段，如果字符串以 `$` 开头，则表示该字符串是国际化字符串，Client 需要从翻译文件中读取实际值再显示。

例如：

```jsonc
{
  "name": "MyDemo3",
  "label": "$MyDemo3",
  "controller": [
    {
      "name": "Android",
      "label": "$安卓端"
    }
  ]
}
```

对应的翻译文件（如 `interface_zh.json`）：

```jsonc
{
  "MyDemo3": "我的演示3",
  "安卓端": "安卓设备"
}
```

### 资源覆盖

后加载的资源中如果发现了和已加载资源同名的任务，会对任务进行合并。通常情况下，可以认为新的任务的顶级键会替换旧任务的。例如:

旧任务

```jsonc
{
    "task1": {
        "enabled": false,
        "recognition": "DirectHit",
        "next": [ "T1", "T2" ]
    }
}
```

新任务

```jsonc
{
    "task1": {
        "enabled": true,
        "action": "Click",
        "next": [ "T2", "T3" ]
    }
}
```

合并后的任务

```jsonc
{
    "task1": {
        "enabled": true,
        "recognition": "DirectHit",
        "action": "Click",
        "next": [ "T2", "T3" ]       // 直接替换，内部不会合并
    }
}
```

### 节点通知处理

MaaFramework 在任务执行过程中会通过回调函数发送节点通知，Client 需要实现相应的处理逻辑，以便向用户展示任务执行状态。

#### 回调函数签名

Client 需要注册一个回调函数来接收通知，函数签名参考 [MaaDef.h](https://github.com/MaaXYZ/MaaFramework/blob/main/include/MaaFramework/MaaDef.h)：

```c
typedef void(MAA_CALL* MaaEventCallback)(
    void* handle,
    const char* message,
    const char* details_json,
    void* trans_arg
);
```

- **message**: 消息类型标识（如 `Node.Action.Starting`、`Node.Recognition.Succeeded` 等）
- **details_json**: 包含具体数据的 JSON 字符串

#### 消息模板机制

资源作者可以在 Pipeline 中通过 `focus` 字段配置消息模板。`focus` 是一个字典，键为消息类型，值为模板字符串或模板对象。模板字符串支持文件路径、URL 或直接文本，内容支持 Markdown 格式，支持国际化（以`$`开头）。Client 收到回调后，应根据模板进行占位符替换并按指定方式展示给用户。

##### `focus` 值的两种写法

**简写（纯字符串）：** 等价于 `display: "log"`，内容仅展示在运行日志中。

```jsonc
"focus": {
    "Node.Action.Starting": "{name} 开始执行"
}
```

**完整写法（对象）：** 可通过 `display` 字段指定展示渠道。 **💡 v2.3.0**

```jsonc
"focus": {
    "Node.Action.Starting": {
        "content": "{name} 开始执行",
        "display": "toast"
    }
}
```

##### `display` 可选值 **💡 v2.3.0**

| 值 | 说明 | 行为特征 |
|----|------|----------|
| `"log"` | 运行日志（**默认**） | 追加到日志流，不打断操作 |
| `"toast"` | 应用内轻提示 | 短暂浮现后自动消失，不阻塞 |
| `"notification"` | 系统级通知 | 推送到 OS 通知中心，应用在后台时也可收到 |
| `"dialog"` | 非阻塞式对话框 | 弹出信息框，任务流水线在后台**继续执行** |
| `"modal"` | 阻塞式弹窗 | 弹出后任务**暂停等待**用户确认，适合需要人工干预的场景 |

`display` 支持数组，同一条消息可同时推送到多个渠道：

```jsonc
"focus": {
    "Node.Action.Succeeded": {
        "content": "✅ {name} 执行成功",
        "display": ["log", "toast"]
    },
    "Node.Action.Failed": {
        "content": "❌ 执行失败，请检查环境",
        "display": ["log", "modal"]
    }
}
```

**Pipeline 中的完整配置示例：**

```jsonc
{
  "NodeA": {
    "focus": {
      "Node.Recognition.Succeeded": "{name} 识别命中，准备开始执行",
      "Node.Action.Starting": {
        "content": "{name} 开始执行，任务 ID: {task_id}",
        "display": ["log", "toast"]
      },
      "Node.Action.Failed": {
        "content": "{name} 执行失败",
        "display": "modal"
      }
    }
  }
}
```

**回调函数收到的参数示例：**

```c
// message 参数:
"Node.Action.Starting"

// details_json 参数（解析后）:
{
    "task_id": 12345,
    "action_id": 11111,
    "name": "NodeA",
    "focus": {
        "Node.Recognition.Succeeded": "{name} 识别命中，准备开始执行",
        "Node.Action.Starting": {
            "content": "{name} 开始执行，任务 ID: {task_id}",
            "display": ["log", "toast"]
        },
        "Node.Action.Failed": {
            "content": "{name} 执行失败",
            "display": "modal"
        }
    }
}
```

#### Client 处理流程

1. 在回调函数中解析 `details_json` 参数
2. 检查解析后的对象中是否存在 `focus` 字段
3. 若存在，根据 `message` 参数在 `focus` 中查找对应的模板（字符串或对象）
4. 若模板为字符串，则 `content` 即为该字符串，`display` 视为 `["log"]`；若为对象，则分别读取 `content` 和 `display`
5. 使用 `details_json` 中的数据替换 `content` 中的占位符（如 `{name}`、`{task_id}`）
6. 根据 `display` 指定的渠道（可多个）将处理后的文本展示给用户 **💡 v2.3.0**

以上述示例为例，收到 `Node.Action.Starting` 时，应在日志中追加并弹出 toast：`NodeA 开始执行，任务 ID: 12345`

#### 更多消息类型

完整的回调消息列表和详细说明请参考 [回调协议](2.3-回调协议.md) 和 [MaaMsg.h](https://github.com/MaaXYZ/MaaFramework/blob/main/include/MaaFramework/MaaMsg.h)。
