• 简体中文
  • API 参考

    本页汇总通用 Agent API,以及各平台专属的构造函数、选项、操作和辅助方法。

    本页记录 API 契约。安装、端到端工作流和故障排查请参考对应指南。平台 Agent 默认继承共享 Agent API;平台章节只记录对应环境的构造方式、选项、能力差异和工具。

    本页保留少量完整示例,帮助理解相关 API 如何组合使用。更完整的接入流程和最佳实践请参考各章节末尾的指南链接。

    领域内容
    共享 Agent APIAgent 选项、交互、提取、观察、工作流、报告、共享类型和报告工具
    Web 浏览器Puppeteer、Playwright 和 Chrome Bridge API
    AndroidAndroid Device、Agent、工厂函数和工具 API
    iOSiOS Device、Agent、工厂函数和工具 API
    HarmonyOSHarmonyOS Device、Agent、工厂函数和工具 API
    桌面端本机桌面和 RDP API
    运行时配置运行产物、语言、Playground 网络和 Debug 日志等全局环境变量

    共享 Agent API

    Agent 选项与配置

    Midscene 针对每个不同环境都有对应的 Agent。每个 Agent 的构造函数都接受一组共享的配置项(设备、报告、缓存、AI 配置、钩子等),然后再叠加平台专属的配置,比如浏览器里的导航控制或 Android 的 ADB 配置。

    你可以通过下面的链接查看各 Agent 的导入路径和平台专属参数:

    参数

    这些 Agent 有一些相同的构造参数:

    • generateReport: boolean: 如果为 true,则生成报告文件。默认值为 true。
    • persistExecutionDump: boolean: 如果为 true,Midscene 还会在报告旁边额外写出每次执行对应的 JSON dump 文件。默认值为 false。这个选项要求 generateReport 保持为 true
    • reportFileName: string: 报告输出名称,默认值由 midscene 内部生成。它在不同 outputFormat 下含义不同:
      • single-html(默认):按文件名处理。Midscene 会在 midscene_run/report/ 下写入 <reportFileName>.html(如果已带 .html 后缀则保持不变)。
      • html-and-external-assets:按目录名处理。Midscene 会在 midscene_run/report/<reportFileName>/ 下写入 index.html 与相关静态资源。
    • autoPrintReportMsg: boolean: 如果为 true,则打印报告消息。默认值为 true。
    • cache?: false | { id: string; strategy?: 'read-only' | 'read-write' | 'write-only'; cacheDir?: string }
      • false:完全禁用缓存。
      • id:必填的缓存 ID。
      • strategy:可选缓存策略。默认值为 'read-write'
      • cacheDir:可选缓存目录路径。配置后,缓存文件会写入该目录,而不是 <MIDSCENE_RUN_DIR>/cache。相对路径会基于当前工作目录解析,而不是基于 MIDSCENE_RUN_DIR。这样可以把缓存、日志和报告目录拆开。
    • cacheId: string | undefined(已废弃):仅用于向后兼容。推荐使用 cache.id
    • aiActContext: string: 调用 agent.aiAct() 时,发送给 AI 模型的背景知识,比如 "有 cookie 对话框时先关闭它",默认值为空。此前名为 aiActionContext,旧名称仍然兼容。
    • modelConfig: Record<string, string | number>:当前 Agent 的模型配置。传入该参数后,当前 Agent 不再读取系统环境变量中的模型配置。详细用法见下文。
    • replanningCycleLimit: number: aiAct 的最大重规划次数。标准模型默认 20,UI-TARS 模型默认 40,AutoGLM 模型默认 100。推荐通过 Agent 入参设置;MIDSCENE_REPLANNING_CYCLE_LIMIT 环境变量仅作兼容读取。
    • waitAfterAction: number: 每次动作执行后的等待时间(毫秒)。这让 UI 有时间稳定,然后再执行下一个动作。默认值为 300 毫秒。
    • useDeviceTime: boolean:是否使用目标设备的本地时间记录任务时间。目标接口必须实现 getDeviceLocalTimeString。如果未实现,Midscene 会输出警告并改用运行环境的系统时间。默认值为 false
    • onTaskStartTip: (tip: string) => void | Promise<void>:可选回调,在每个子任务执行开始前收到一条可读的任务描述提示。默认值为 undefined。
    • createOpenAIClient: (openai, options) => Promise<OpenAI | undefined>:可选的 OpenAI 客户端包装函数,可用于接入可观测性工具或自定义中间件。详细示例见下文。
    • onLLMUsage: (usage: AIUsageInfo) => void:可选回调。每次大模型调用的用量信息就绪后,Midscene 会调用一次该函数,可用于实时统计用量和成本。
    • outputFormat: 'single-html' | 'html-and-external-assets': 控制报告的生成格式。'single-html'(默认)将所有截图作为 base64 内嵌到单个 HTML 文件中,并把 reportFileName 作为 HTML 文件名。'html-and-external-assets' 将截图保存为独立的 PNG 文件到子目录,并把 reportFileName 作为该目录名,适用于报告文件过大的场景。注意:使用 'html-and-external-assets' 时,报告必须通过 HTTP 服务器或 CDN 地址访问,无法直接使用 file:// 协议打开。这是因为浏览器的 CORS(跨源资源共享)限制会阻止从 file 协议加载相对路径的本地图片。如需在本地测试,可在报告目录下启动简易的 HTTP 服务器。进入报告目录后运行以下命令之一:
      • 使用 Node.js:npx serve
      • 使用 Python:python -m http.serverpython3 -m http.server 然后通过 http://localhost:3000(或终端显示的端口)访问报告。
    • screenshotShrinkFactor: number: 控制截图的缩放比例,以减少发送给 AI 模型的图像大小,从而减少 token 消耗。默认值为 1(不缩放)。如果将其设置为 2,则截图的宽高将缩小为原来的一半,面积缩小为原来的四分之一。你可以根据实际情况调整这个值,以在图像清晰度和 token 消耗之间找到最佳平衡点。
      • 对于移动端设备,将 screenshotShrinkFactor 设置为 2 可以在保持清晰度的同时减少 token 的消耗,但不建议设置的值超过 3,否则可能会导致图像过于模糊,影响 AI 模型的理解。
      • 对于 Web 页面,如果页面内容比较复杂或包含大量细节,不建议设置过高的 screenshotShrinkFactor,以避免截图过于模糊。通常也可以通过 Puppeteer 或 Playwright 的 deviceScaleFactor 在更上游控制截图尺寸。
    Info

    screenshotShrinkFactordeviceScaleFactor 的区别:

    • screenshotShrinkFactor 是 Midscene 自定义的参数,用于控制拿到浏览器、手机等设备的截图后,是否对其进行尺寸压缩。目的是减少 token 消耗、加快模型响应速度。但过度压缩会导致图片模糊,影响模型理解。

    • deviceScaleFactor 是 Puppeteer 和 Playwright 自带的参数,用于配置高清屏适配(现在很多设备都是高清屏了)时将一个 CSS 逻辑像素渲染为几倍的物理像素。这也就是为什么 deviceScaleFactor 和实际设备的缩放比例不一致时,非 headless 模式下可能会出现页面闪烁。同时,这一缩放逻辑也决定了 Puppeteer/Playwright 的截图尺寸(基于物理像素)。相比于 screenshotShrinkFactor,它是在更上游的生产端控制了截图尺寸。

    二者是否可以同时使用?

    • Web 场景:二者同时使用的意义不大,应该优先使用 deviceScaleFactor,直接在生产端控制截图尺寸。
      • 一种特殊情况:你期望配置 deviceScaleFactor 来避免浏览器闪烁,但同时又不期望发送给模型的截图过大,此时可以同时使用 screenshotShrinkFactor 控制发送给模型时的图片压缩。
    • 移动端等非 Web 场景:因为没有 deviceScaleFactor 参数可用,所以只能通过 screenshotShrinkFactor 来控制模型消费时使用的截图尺寸。

    设备 CLI 可以按单次调用传入这些 Agent 行为参数。把 API 的 camelCase 参数名转换成不带平台前缀的 kebab-case flag,例如 waitAfterAction -> --wait-after-action。各平台 CLI 入口请参考 Skills

    自定义模型

    modelConfig: Record<string, string | number> 可选。它允许你通过代码配置模型,而不是通过环境变量。

    如果在 Agent 初始化时提供了 modelConfig系统环境变量中的模型配置将全部被忽略,仅使用该对象中的值。 这里可配置的 key / value 与 模型配置 文档中说明的内容完全一致。Default、Planning 和 Insight 模型的职责请参考模型策略

    自定义 OpenAI 客户端

    createOpenAIClient: (openai, options) => Promise<OpenAI | undefined> 可选。它允许你包装 OpenAI 客户端实例,用于集成可观测性工具(如 LangSmith、Langfuse)或应用自定义中间件。

    参数说明:

    • openai: OpenAI - Midscene 创建的基础 OpenAI 客户端实例,已包含所有必要配置(API 密钥、基础 URL、代理等)
    • options: Record<string, unknown> - OpenAI 初始化选项,包括:
      • baseURL?: string - API 接入地址
      • apiKey?: string - API 密钥
      • dangerouslyAllowBrowser: boolean - 在 Midscene 中始终为 true
      • 其他 OpenAI 配置选项

    返回值:

    • 返回包装后的 OpenAI 客户端实例,或返回 undefined 表示使用原始实例

    规划与交互

    这些是 Midscene 中各类 Agent 的主要 API。

    agent.ai()agent.aiAct() 会根据自然语言自动规划并执行多个步骤。agent.aiTap()agent.aiInput() 等即时操作 API 直接执行指定动作,AI 模型只负责定位等底层任务。

    aiAct()ai()

    这个方法允许你通过自然语言描述一系列 UI 操作步骤。Midscene 会自动规划这些步骤并执行。

    向后兼容

    这个接口在之前版本里也被写为 aiAction(),当前的版本兼容两种写法。为了保持代码的一致性,建议使用新的 aiAct() 方法。

    • 类型
    function aiAct(
      prompt: string | object,
      options?: {
        cacheable?: boolean;
        deepThink?: 'unset' | true | false;
        deepLocate?: boolean;
        fileChooserAccept?: string | string[];
        fileChooserAllowedDir?: string;
        abortSignal?: AbortSignal;
        context?: string;
      },
    ): Promise<string | undefined>;
    function ai(prompt: string, options?: Object): Promise<string | undefined>; // 简写形式
    • 参数:

      • prompt: string | object - 用自然语言描述的操作内容,或使用图片作为提示词
      • options?: object - 可选,一个配置对象,包含:
        • cacheable?: boolean - 当启用 缓存功能 时,是否允许缓存当前 API 调用结果。默认值为 true
        • deepThink?: 'unset' | true | false - 控制 Midscene 在 aiAct 执行规划时的具体实现。详见 deepThink 规划模式
        • deepLocate?: boolean - 是否开启深度定位。默认值为 false。
        • context?: string - 本次调用的额外上下文。详见单次调用上下文
        • fileChooserAccept?: string | string[] - 当文件选择器弹出时,指定对应的文件路径。可以是单个文件路径或路径数组。仅在 web 页面(Playwright、Puppeteer 或 Chrome extension Bridge mode)中可用。该选项不受 fileChooserAllowedDir 限制。
          • 注意:如果文件输入框不支持多文件(没有 multiple 属性),但是传入了多个文件,会抛出错误。
          • 注意:如果点击触发了文件选择器但没有传入 fileChooserAccept 参数,文件选择器会被忽略,页面可以继续正常操作。
          • 注意:Chrome extension Bridge mode 上传本地文件时,需要在 chrome://extensions > Midscene > “Details” 中开启插件的 “Allow access to file URLs” 权限。开启后请从目标 http(s):// 页面重新连接桥接模式。
          • 注意:Chrome extension Bridge mode 不支持目录上传输入框(webkitdirectory / directory)。如需上传目录,请使用 Playwright。
        • fileChooserAllowedDir?: string:显式授权当前 aiAct 通过提示词上传文件时可访问的目录。未配置时,模型规划的文件上传会被拒绝。配置后,提示词中的相对路径会基于此目录解析,并校验是否位于此目录下;绝对路径则直接校验是否位于此目录下。位于此目录下的 symlink 也允许上传。建议将其设置为测试用例的 fixtures 目录,以避免 AI 幻觉或页面提示词攻击导致 aiAct 上传敏感文件。
        • abortSignal?: AbortSignal - 可选的 AbortSignal,用于中止 aiAct 的执行。当信号被触发时,Midscene 会停止当前的规划循环并抛出错误。适用于实现超时控制或用户主动取消操作的场景。
    • 返回值:

      • 返回执行完成后的规划输出文本;如果规划没有产生输出,则返回 undefined。执行失败时会抛出错误。
    • 示例:

    // 基本用法
    await agent.aiAct('在搜索框中输入 "JavaScript",然后点击搜索按钮');
    
    // 使用 .ai 简写形式
    await agent.ai(
      '点击页面顶部的登录按钮,然后在用户名输入框中输入 "test@example.com"',
    );
    
    // 使用 abortSignal 设置超时
    const controller = new AbortController();
    setTimeout(() => controller.abort('timeout'), 30000); // 30 秒超时
    await agent.aiAct('填写表单并提交', {
      abortSignal: controller.signal,
    });
    
    // 对于复杂任务,可以启用 deepThink 参数
    await agent.aiAct('完成 github 账号注册的表单填写。地区必须选择「加拿大」。确保表单上没有遗漏的字段,确保所有的表单项能够通过校验。 只需要填写表单项即可,不需要发起真实的账号注册。 最终请返回表单上实际填写的字段内容', { deepThink: true });
    deepThink 规划模式

    deepThink 控制 aiAct 的规划实现:

    • 默认情况下,aiAct 会在同一次规划请求中完成下一步规划和目标元素定位。
    • 设置为 true 后,aiAct 会更注重任务拆解,并将任务规划和元素定位分为不同的模型调用。复杂任务可能因此更加稳定,但模型调用次数和延迟也会增加。

    deepThink 支持 'unset' | true | false'unset' 是兼容旧写法的取值,与 false 的行为相同。

    deepThink 不控制模型原生思考。相关环境变量请参考模型原生思考

    aiAct 提示词中上传文件

    如需让 aiAct 上传提示词中提到的文件,请为该次调用显式传入 fileChooserAllowedDir。建议将其设置为测试用例的 fixtures 目录。Midscene 会在打开文件选择器的操作之前规划文件选择器配置;相对路径和绝对路径都会先解析,再校验解析结果是否仍在所选目录内。

    const agent = new PlaywrightAgent(page);
    
    await agent.aiAct(
      '先点击“上传头像”按钮并上传 avatar.png;然后点击“上传封面”按钮并上传 cover.png。上传完成后,请确认页面头像显示一只猫、封面显示一片海滩。',
      { fileChooserAllowedDir: './fixtures' },
    );

    如果一次 aiAct 需要上传多个文件,请在提示词中把每个相对路径与对应的上传操作明确写出。后出现的路径会覆盖此前配置的文件选择器路径。不要将提示词中的文件路径与 options.fileChooserAccept 混用:模型在规划过程中生成的后续文件选择器配置可能覆盖该 option 的值。

    此能力仅适用于 web 页面(Playwright、Puppeteer 和 Chrome extension Bridge mode)。当 fixture 不在当前工作目录下,或需要让同一提示词在本地和 CI 环境中复用时,请配置 fileChooserAllowedDir

    在 Chrome extension Bridge mode 中上传本地文件时,需要在 chrome://extensions > Midscene > “Details” 中开启插件的 “Allow access to file URLs” 权限。开启后请切回目标 http(s):// 页面,再重新连接桥接模式。

    Info

    在实际运行时,Midscene 会将用户指令规划(Planning)成多个步骤,然后逐步执行。如果 Midscene 认为无法执行,将抛出一个错误。

    为了获得最佳效果,请尽可能提供清晰、详细的步骤描述。

    关联文档:

    aiTap()

    点击某个元素

    • 类型
    function aiTap(locate: string | object, options?: object): Promise<void>;
    • 参数:

      • locate: string | object - 用自然语言描述的元素定位,或使用图片作为提示词
      • options?: object - 可选,一个配置对象,包含:
        • deepLocate?: boolean - 是否开启深度定位。该参数原来叫 deepThink,现已更名为 deepLocate。默认值为 false。
        • context?: string - 本次调用的额外上下文。详见单次调用上下文
        • xpath?: string - 目标元素的 xpath 路径,用于执行当前操作。如果提供了这个 xpath,Midscene 会优先使用该 xpath 来找到元素,然后依次使用缓存和 AI 模型。默认值为空
        • cacheable?: boolean - 当启用 缓存功能 时,是否允许缓存当前 API 调用结果。默认值为 true
        • fileChooserAccept?: string | string[] - 当文件选择器弹出时,指定对应的文件路径。可以是单个文件路径或路径数组。仅在 web 页面(Playwright、Puppeteer 或 Chrome extension Bridge mode)中可用。
          • 注意:如果文件输入框不支持多文件(没有 multiple 属性),但是传入了多个文件,会抛出错误。
          • 注意:如果点击触发了文件选择器但没有传入 fileChooserAccept 参数,文件选择器会被忽略,页面可以继续正常操作。
          • 注意:Chrome extension Bridge mode 不支持目录上传输入框(webkitdirectory / directory)。如需上传目录,请使用 Playwright。
    • 返回值:

      • Promise<void>
    • 示例:

    await agent.aiTap('页面顶部的登录按钮');
    
    // 使用 deepLocate 功能精确定位元素
    await agent.aiTap('页面顶部的登录按钮', { deepLocate: true });
    
    // 文件上传:点击上传按钮并选择文件
    await agent.aiTap('选择文件按钮', { fileChooserAccept: ['./document.pdf'] });
    await agent.aiTap('上传图片', { fileChooserAccept: ['./image1.jpg', './image2.png'] });

    aiHover()

    在 Web 页面和桌面端(@midscene/computer)中可用,在移动端(Android、iOS 或 HarmonyOS)下不可用。

    鼠标悬停某个元素上。

    • 类型
    function aiHover(locate: string | object, options?: object): Promise<void>;
    • 参数:

      • locate: string | object - 用自然语言描述的元素定位,或使用图片作为提示词
      • options?: object - 可选,一个配置对象,包含:
        • deepLocate?: boolean - 是否开启深度定位。该参数原来叫 deepThink,现已更名为 deepLocate。默认值为 false。
        • context?: string - 本次调用的额外上下文。详见单次调用上下文
        • xpath?: string - 目标元素的 xpath 路径,用于执行当前操作。如果提供了这个 xpath,Midscene 会优先使用该 xpath 来找到元素,然后依次使用缓存和 AI 模型。默认值为空
        • cacheable?: boolean - 当启用 缓存功能 时,是否允许缓存当前 API 调用结果。默认值为 true
    • 返回值:

      • Promise<void>
    • 示例:

    await agent.aiHover('页面顶部的登录按钮');

    aiInput()

    在某个元素中输入文本。

    • 类型
    // 推荐用法:定位提示在前,其他选项在 opt 中
    function aiInput(
      locate: string | object,
      opt: {
        value: string | number;
        deepLocate?: boolean;
        xpath?: string;
        cacheable?: boolean;
        autoDismissKeyboard?: boolean;
        keyboardTypeDelay?: number;
        mode?: 'replace' | 'clear' | 'typeOnly';
      },
    ): Promise<void>;
    
    // 兼容用法:保留向后兼容性
    function aiInput(
      value: string | number,
      locate: string | object,
      options?: object,
    ): Promise<void>;
    • 参数:

      推荐用法

      • locate: string | object - 用自然语言描述的元素定位,或使用图片作为提示词
      • opt: object - 配置对象,包含:
        • value: string | number - 必填,要输入的文本内容。
          • mode'replace' 时:文本将替换输入框中的所有现有内容。
          • mode'typeOnly' 时:直接输入文本,不会先清空输入框。
          • mode'clear' 时:会忽略文本内容,仅清空输入框。
        • deepLocate?: boolean - 是否开启深度定位。该参数原来叫 deepThink,现已更名为 deepLocate。默认值为 false。
        • context?: string - 本次调用的额外上下文。详见单次调用上下文
        • xpath?: string - 目标元素的 xpath 路径,用于执行当前操作。如果提供了这个 xpath,Midscene 会优先使用该 xpath 来找到元素,然后依次使用缓存和 AI 模型。默认值为空
        • cacheable?: boolean - 当启用 缓存功能 时,是否允许缓存当前 API 调用结果。默认值为 true
        • autoDismissKeyboard?: boolean - 如果为 true,则键盘会在输入文本后自动关闭,仅在 Android/iOS/HarmonyOS 中有效。默认值为 true。
        • keyboardTypeDelay?: number - 输入文本时每个按键之间的延迟(毫秒)。设置后,文本将逐字符输入,每个字符之间等待指定的延迟。适用于输入框在快速输入下丢字的场景。
        • mode?: 'replace' | 'clear' | 'typeOnly' - 输入模式。(默认值: 'replace')
          • 'replace': 先清空输入框,然后输入文本。
          • 'typeOnly': 直接输入文本,不会先清空输入框。
          • 'clear': 清空输入框,不会输入新的文本。

      兼容用法(已过时,但仍然支持):

      • value: string | number - 要输入的文本内容。
      • locate: string | object - 用自然语言描述的元素定位,或使用图片作为提示词
      • options?: object - 可选的配置对象,类型与推荐用法中的 opt 类型相同。
    • 返回值:

      • Promise<void>
    • 示例:

    // 推荐用法
    await agent.aiInput('搜索框', { value: 'Hello World' });
    
    // 兼容用法(不推荐)
    await agent.aiInput('Hello World', '搜索框');
    关于签名变更

    我们最近更新了 aiInput 的 API 签名,将定位提示作为第一个参数,使得参数顺序更直观。旧的签名 aiInput(value, locate, options) 仍然完全兼容,但建议新代码使用推荐的签名。

    aiClearInput()

    清空输入框内容。适合作为一个独立步骤使用:在输入前先清空,或只需删除现有文本而暂时不输入新内容。

    • 类型
    function aiClearInput(
      locate: string | object,
      opt?: {
        deepLocate?: boolean;
        xpath?: string;
        cacheable?: boolean;
      },
    ): Promise<void>;
    • 参数:

      • locate: string | object - 要清空的输入框的自然语言描述,或通过图像提示
      • opt?: object - 可选配置对象:
        • deepLocate?: boolean - 是否开启深度定位。默认 false
        • context?: string - 本次调用的额外上下文。详见单次调用上下文
        • xpath?: string - 要操作元素的 xpath,默认空。
        • cacheable?: boolean - 启用缓存功能时是否缓存,默认 true
    • 返回值:

      • 返回 Promise<void>
    • 示例:

    // 清空搜索框
    await agent.aiClearInput('搜索框');
    
    // 先清空再输入新值
    await agent.aiClearInput('邮箱输入框');
    await agent.aiInput('邮箱输入框', { value: 'user@example.com' });
    Info

    aiClearInputaiInput 的取舍

    aiInput(locate, { value: '...' }) 默认会先清空输入框(mode: 'replace')。只有在需要把清空当成独立一步时(例如测试空值校验,或想把清空和输入拆成两步分别控制)才使用 aiClearInput

    aiKeyboardPress()

    按下键盘上的某个键。

    • 类型
    // 推荐用法:定位提示在前,其他选项在 opt 中
    function aiKeyboardPress(
      locate: string | object | undefined,
      opt: {
        keyName: string;
        deepLocate?: boolean;
        xpath?: string;
        cacheable?: boolean;
      },
    ): Promise<void>;
    
    // 兼容用法:保留向后兼容性
    function aiKeyboardPress(
      key: string,
      locate?: string | object,
      options?: object,
    ): Promise<void>;
    • 参数:

      推荐用法

      • locate: string | object | undefined - 可选的元素定位描述,或使用图片作为提示词。传入 undefined 时,不执行 AI 定位和预先点击,按键会直接作用于当前获得焦点的元素。
      • opt: object - 配置对象,包含:
        • keyName: string - 必填,要按下的键,如 EnterTabEscape 等;输入文本请使用 aiInput()。Web 和 Computer Device 支持 Control+AShift+Enter 等 modifier shortcut,使用 + 连接按键。可在共享按键名称定义中查看非移动端的候选名称,实际支持情况以平台为准。内置的 Android、iOS 和 Harmony Device 仅支持单键。Harmony 支持一组保守的具名键以及 A-Z0-9? 等没有独立键码的输出字符不属于按键名。不支持的按键和移动端组合键会抛出错误;自定义 Device 的支持情况取决于其实现。
        • deepLocate?: boolean - 是否开启深度定位。该参数原来叫 deepThink,现已更名为 deepLocate。默认值为 false。
        • context?: string - 本次调用的额外上下文。详见单次调用上下文
        • xpath?: string - 目标元素的 xpath 路径,用于执行当前操作。如果提供了这个 xpath,Midscene 会优先使用该 xpath 来找到元素,然后依次使用缓存和 AI 模型。默认值为空
        • cacheable?: boolean - 当启用 缓存功能 时,是否允许缓存当前 API 调用结果。默认值为 true

      兼容用法(已过时,但仍然支持):

      • key: string - 要按下的键,如 EnterTabEscape 等。
      • locate?: string | object - 用自然语言描述的元素定位,或使用图片作为提示词
      • options?: object - 可选的配置对象,类型与推荐用法中的 opt 类型相同。
    • 返回值:

      • Promise<void>
    • 示例:

    // 推荐用法
    await agent.aiKeyboardPress('搜索框', { keyName: 'Enter' });
    await agent.aiKeyboardPress('搜索框', { keyName: 'Control+A' });
    
    // 对当前获得焦点的元素执行纯键盘操作
    await agent.aiKeyboardPress(undefined, { keyName: 'Control+X' });
    
    // 兼容用法(不推荐)
    await agent.aiKeyboardPress('Enter', '搜索框');
    关于签名变更

    我们最近更新了 aiKeyboardPress 的 API 签名,将可选的定位提示作为第一个参数,使得参数顺序更直观。如果快捷键应该直接作用于当前焦点,请传入 undefined,这样不会执行定位或点击。此时仍需提供有效的模型配置,系统会在执行 action 前完成配置校验,但不会向模型发起定位请求。旧的签名 aiKeyboardPress(key, locate, options) 仍然完全兼容,但建议新代码使用推荐的签名。

    aiScroll()

    滚动页面或某个元素。

    • 类型
    // 推荐用法:定位提示在前,其他选项在 opt 中
    function aiScroll(
      locate: string | object | undefined,
      opt: {
        scrollType?: 'singleAction' | 'scrollToBottom' | 'scrollToTop' | 'scrollToRight' | 'scrollToLeft';
        direction?: 'down' | 'up' | 'left' | 'right';
        distance?: number | null;
        deepLocate?: boolean;
        xpath?: string;
        cacheable?: boolean;
      },
    ): Promise<void>;
    
    // 兼容用法:保留向后兼容性
    function aiScroll(
      scrollParam: PlanningActionParamScroll,
      locate?: string | object,
      options?: object,
    ): Promise<void>;
    • 参数:

      推荐用法

      • locate: string | object | undefined - 用自然语言描述的元素定位,或使用图片作为提示词。如果未传入或为 undefined,Midscene 会在当前鼠标位置滚动。
      • opt: object - 配置对象,包含:
        • scrollType?: 'singleAction' | 'scrollToBottom' | 'scrollToTop' | 'scrollToRight' | 'scrollToLeft' - 滚动类型,默认值为 singleAction
        • direction?: 'down' | 'up' | 'left' | 'right' - 滚动方向,默认值为 down。仅在 scrollTypesingleAction 时生效。不论是 Android 还是 Web,这里的滚动方向都是指页面哪个方向的内容会进入屏幕。比如当滚动方向是 down 时,页面下方被隐藏的内容会从屏幕底部开始逐渐向上露出。
        • distance?: number | null - 滚动距离,单位为像素。设置为 null 表示由 Midscene 自动决定。
        • deepLocate?: boolean - 是否开启深度定位。该参数原来叫 deepThink,现已更名为 deepLocate。默认值为 false。
        • context?: string - 本次调用的额外上下文。详见单次调用上下文
        • xpath?: string - 目标元素的 xpath 路径,用于执行当前操作。如果提供了这个 xpath,Midscene 会优先使用该 xpath 来找到元素,然后依次使用缓存和 AI 模型。默认值为空
        • cacheable?: boolean - 当启用 缓存功能 时,是否允许缓存当前 API 调用结果。默认值为 true

      兼容用法(已过时,但仍然支持):

      • scrollParam: PlanningActionParamScroll - 滚动参数(包含 scrollType、direction、distance)。
      • locate?: string | object - 用自然语言描述的元素定位,或使用图片作为提示词
      • options?: object - 可选的配置对象,类型与推荐用法中的 opt 类型相同。
    • 返回值:

      • Promise<void>
    • 示例:

    // 推荐用法
    await agent.aiScroll('表单区域', {
      scrollType: 'singleAction',
      direction: 'up',
      distance: 100,
    });
    
    // 兼容用法(不推荐)
    await agent.aiScroll(
      { scrollType: 'singleAction', direction: 'up', distance: 100 },
      '表单区域',
    );
    关于签名变更

    我们最近更新了 aiScroll 的 API 签名,将定位提示作为第一个参数,使得参数顺序更直观。旧的签名 aiScroll(scrollParam, locate, options) 仍然完全兼容,但建议新代码使用推荐的签名。

    aiPinch()

    执行双指缩放手势,用于放大或缩小。支持 Android、iOS 和 Web(基于 Chromium 的浏览器)。

    • 类型
    function aiPinch(
      locate: string | object | undefined,
      opt: {
        direction: 'in' | 'out';
        distance?: number;
        duration?: number;
        deepLocate?: boolean;
        xpath?: string;
        cacheable?: boolean;
      },
    ): Promise<void>;
    • 参数:

      • locate: string | object | undefined - 用自然语言描述的缩放目标元素,或使用图片作为提示词。如果未传入,缩放将在屏幕中心执行。
      • opt: object - 配置对象,包含:
        • direction: 'in' | 'out' - 必填。 "in" = 双指收拢(缩小),"out" = 双指张开(放大)。
        • distance?: number - 每根手指移动的距离(像素)。默认值为屏幕较短边的四分之一。
        • duration?: number - 缩放手势持续时间(毫秒),默认值为 500
        • deepLocate?: boolean - 是否开启深度定位。默认值为 false。
        • context?: string - 本次调用的额外上下文。详见单次调用上下文
        • xpath?: string - 目标元素的 xpath 路径。默认值为空。
        • cacheable?: boolean - 当启用缓存功能时,是否允许缓存。默认值为 true。
    • 返回值:

      • Promise<void>
    • 示例:

    // 在地图上放大(双指张开)
    await agent.aiPinch('地图区域', { direction: 'out', distance: 200 });
    
    // 在屏幕中心缩小(双指收拢)
    await agent.aiPinch(undefined, { direction: 'in' });
    
    // 自定义持续时间放大
    await agent.aiPinch('图片', { direction: 'out', distance: 300, duration: 1000 });
    平台支持
    • Android:通过 yadb-pinch 命令实现。
    • iOS:通过 W3C Actions API 双触摸指针实现。
    • Web:通过 CDP 触摸事件实现。Puppeteer/Playwright 需设置 enableTouchEventsInActionSpace: true。Playwright 仅支持 Chromium 内核浏览器。
    • HarmonyOS:不支持。uitest 框架未提供多触点 API。

    aiLongPress()

    长按(按住不放)某个元素,常用于唤起右键/上下文菜单、触发选中模式或其他长按手势。

    • 类型
    function aiLongPress(
      locate: string | object,
      opt?: {
        duration?: number;
        deepLocate?: boolean;
        xpath?: string;
        cacheable?: boolean;
      },
    ): Promise<void>;
    • 参数:

      • locate: string | object - 要长按的元素的自然语言描述,或通过图像提示
      • opt?: object - 可选配置对象:
        • duration?: number - 按住时长(毫秒)。Android 默认 2000,iOS 默认 1000,Web 默认 500。HarmonyOS 使用系统长按时长并忽略此选项。
        • deepLocate?: boolean - 是否启用深度定位,默认 false
        • context?: string - 本次调用的额外上下文。详见单次调用上下文
        • xpath?: string - 要操作元素的 xpath,默认空。
        • cacheable?: boolean - 启用缓存功能时是否缓存,默认 true
    • 返回值:

      • 返回 Promise<void>
    • 示例:

    // 长按首页的第一篇文章以唤起菜单
    await agent.aiLongPress('首页的第一篇文章');
    
    // 自定义长按时长
    await agent.aiLongPress('消息气泡', { duration: 2000 });
    平台支持
    • AndroidiOSHarmonyOSWeb(基于 Chromium 的浏览器,通过触摸事件实现)。HarmonyOS 会忽略 duration 选项,因为底层 uitest API 不支持自定义按住时长。

    aiDoubleClick()

    双击某个元素。

    • 类型
    function aiDoubleClick(locate: string | object, options?: object): Promise<void>;
    • 参数:

      • locate: string | object - 用自然语言描述的元素定位,或使用图片作为提示词
      • options?: object - 可选,一个配置对象,包含:
        • deepLocate?: boolean - 是否开启深度定位。该参数原来叫 deepThink,现已更名为 deepLocate。默认值为 false。
        • context?: string - 本次调用的额外上下文。详见单次调用上下文
        • xpath?: string - 目标元素的 xpath 路径,用于执行当前操作。如果提供了这个 xpath,Midscene 会优先使用该 xpath 来找到元素,然后依次使用缓存和 AI 模型。默认值为空
        • cacheable?: boolean - 当启用 缓存功能 时,是否允许缓存当前 API 调用结果。默认值为 true
    • 返回值:

      • Promise<void>
    • 示例:

    await agent.aiDoubleClick('页面顶部的文件名称');
    
    // 使用 deepLocate 功能精确定位元素
    await agent.aiDoubleClick('页面顶部的文件名称', { deepLocate: true });

    aiRightClick()

    可用于 web 页面和 PC 桌面端(@midscene/computer),不可用于移动设备(Android、iOS 或 HarmonyOS)。

    右键点击某个元素。请注意,Midscene 在右键点击后无法与浏览器原生上下文菜单交互。这个接口通常用于已经监听了右键点击事件的元素。

    • 类型
    function aiRightClick(locate: string | object, options?: object): Promise<void>;
    • 参数:

      • locate: string | object - 用自然语言描述的元素定位,或使用图片作为提示词
      • options?: object - 可选,一个配置对象,包含:
        • deepLocate?: boolean - 是否开启深度定位。该参数原来叫 deepThink,现已更名为 deepLocate。默认值为 false。
        • context?: string - 本次调用的额外上下文。详见单次调用上下文
        • xpath?: string - 目标元素的 xpath 路径,用于执行当前操作。如果提供了这个 xpath,Midscene 会优先使用该 xpath 来找到元素,然后依次使用缓存和 AI 模型。默认值为空
        • cacheable?: boolean - 当启用 缓存功能 时,是否允许缓存当前 API 调用结果。默认值为 true
    • 返回值:

      • Promise<void>
    • 示例:

    await agent.aiRightClick('页面顶部的文件名称');
    
    // 使用 deepLocate 功能精确定位元素
    await agent.aiRightClick('页面顶部的文件名称', { deepLocate: true });

    提取、定位与断言

    aiAsk()

    使用此方法,你可以针对当前页面,直接向 AI 模型发起提问,并获得字符串形式的回答。

    aiAsk()aiString() 完全等价。

    • 类型
    function aiAsk(prompt: string | object, options?: object): Promise<string>;
    • 参数:

      • prompt: string | object - 用自然语言描述的询问内容,或使用图片作为提示词
      • options?: object - 可选,一个配置对象,包含:
        • domIncluded?: boolean | 'visible-only' - 是否向模型发送精简后的 DOM 信息,一般用于提取 UI 中不可见的属性,比如图片的链接。如果设置为 'visible-only',则只发送可见的元素。默认值为 false。
        • screenshotIncluded?: boolean - 是否向模型发送截图。默认值为 true。
        • context?: string - 本次调用的额外上下文。详见单次调用上下文
    • 返回值:

      • 返回一个 Promise。返回 AI 模型的回答。
    • 示例:

    const result = await agent.aiAsk('当前页面的应该怎么进行测试?');
    console.log(result); // 输出 AI 模型的回答

    除了 aiAsk 方法,你还可以使用 aiQuery 方法,直接从 UI 提取结构化的数据。

    aiQuery()

    使用此方法,你可以直接从 UI 提取结构化的数据。只需在 dataDemand 中描述期望的数据格式(如字符串、数字、JSON、数组等),Midscene 即返回相应结果。

    • 类型
    function aiQuery<T>(dataDemand: string | object, options?: object): Promise<T>;
    • 参数:

      • dataDemand: string | object:描述预期的返回值和格式。
      • options?: object - 可选,一个配置对象,包含:
        • domIncluded?: boolean | 'visible-only' - 是否向模型发送精简后的 DOM 信息,一般用于提取 UI 中不可见的属性,比如图片的链接。如果设置为 'visible-only',则只发送可见的元素。默认值为 false。
        • screenshotIncluded?: boolean - 是否向模型发送截图。默认值为 true。
        • context?: string - 本次调用的额外上下文。详见单次调用上下文
    • 返回值:

      • 返回值可以是任何合法的基本类型,比如字符串、数字、JSON、数组等。
      • 你只需在 dataDemand 中描述它,Midscene 就会给你满足格式的返回。
    • 示例:

    const dataA = await agent.aiQuery({
      time: '左上角展示的日期和时间,string',
      userInfo: '用户信息,{name: string}',
      tableFields: '表格的字段名,string[]',
      tableDataRecord: '表格中的数据记录,{id: string, [fieldName]: string}[]',
    });
    
    // 你也可以用纯字符串描述预期的返回值格式:
    
    // dataB 将是一个字符串数组
    const dataB = await agent.aiQuery('string[],列表中的任务名称');
    
    // dataC 将是一个包含对象的数组
    const dataC = await agent.aiQuery(
      '{name: string, age: string}[], 表格中的数据记录',
    );
    
    // 使用 domIncluded 功能提取 UI 中不可见的属性
    const dataD = await agent.aiQuery(
      '{name: string, age: string, avatarUrl: string}[], 表格中的数据记录',
      { domIncluded: true },
    );

    此外,我们还提供了 aiBoolean(), aiNumber(), aiString() 三个便捷方法,用于直接提取布尔值、数字和字符串。

    aiBoolean()

    从 UI 中提取一个布尔值。

    • 类型
    function aiBoolean(prompt: string | object, options?: object): Promise<boolean>;
    • 参数:

      • prompt: string - 用自然语言描述的期望值,或使用图片作为提示词
      • options?: object - 可选,一个配置对象,包含:
        • domIncluded?: boolean | 'visible-only' - 是否向模型发送精简后的 DOM 信息,一般用于提取 UI 中不可见的属性,比如图片的链接。如果设置为 'visible-only',则只发送可见的元素。默认值为 false。
        • screenshotIncluded?: boolean - 是否向模型发送截图。默认值为 true。
        • context?: string - 本次调用的额外上下文。详见单次调用上下文
    • 返回值:

      • 返回一个 Promise。当 AI 返回结果时解析为布尔值。
    • 示例:

    const boolA = await agent.aiBoolean('是否存在登录对话框');
    
    // 使用 domIncluded 功能提取 UI 中不可见的属性
    const boolB = await agent.aiBoolean('忘记密码按钮是否存在链接', {
      domIncluded: true,
    });

    aiNumber()

    从 UI 中提取一个数字。

    • 类型
    function aiNumber(prompt: string | object, options?: object): Promise<number>;
    • 参数:

      • prompt: string | object - 用自然语言描述的期望值,或使用图片作为提示词
      • options?: object - 可选,一个配置对象,包含:
        • domIncluded?: boolean | 'visible-only' - 是否向模型发送精简后的 DOM 信息,一般用于提取 UI 中不可见的属性,比如图片的链接。如果设置为 'visible-only',则只发送可见的元素。默认值为 false。
        • screenshotIncluded?: boolean - 是否向模型发送截图。默认值为 true。
        • context?: string - 本次调用的额外上下文。详见单次调用上下文
    • 返回值:

      • 返回一个 Promise。当 AI 返回结果时解析为数字。
    • 示例:

    const numberA = await agent.aiNumber('账户剩余的积分');
    
    // 使用 domIncluded 功能提取 UI 中不可见的属性
    const numberB = await agent.aiNumber('账户剩余的积分元素的 value 值', {
      domIncluded: true,
    });

    aiString()

    从 UI 中提取一个字符串。

    aiString()aiAsk() 完全等价。

    • 类型
    function aiString(prompt: string | object, options?: object): Promise<string>;
    • 参数:

      • prompt: string | object - 用自然语言描述的期望值,或使用图片作为提示词
      • options?: object - 可选,一个配置对象,包含:
        • domIncluded?: boolean | 'visible-only' - 是否向模型发送精简后的 DOM 信息,一般用于提取 UI 中不可见的属性,比如图片的链接。如果设置为 'visible-only',则只发送可见的元素。默认值为 false。
        • screenshotIncluded?: boolean - 是否向模型发送截图。默认值为 true。
        • context?: string - 本次调用的额外上下文。详见单次调用上下文
    • 返回值:

      • 返回一个 Promise。当 AI 返回结果时解析为字符串。
    • 示例:

    const stringA = await agent.aiString('当前列表的第一条记录的名称');
    
    // 使用 domIncluded 功能提取 UI 中不可见的属性
    const stringB = await agent.aiString('当前列表的第一条记录的跳转链接', {
      domIncluded: true,
    });

    aiLocate()

    通过自然语言描述一个元素的定位。

    • 类型
    function aiLocate(
      locate: string | object,
      options?: object,
    ): Promise<{
      rect: {
        left: number;
        top: number;
        width: number;
        height: number;
      };
      center: [number, number];
      dpr?: number; // 仅 Web:设备像素比
    }>;
    • 参数:

      • locate: string | object - 用自然语言描述的元素定位,或使用图片作为提示词
      • options?: object - 可选,一个配置对象,包含:
        • deepLocate?: boolean - 是否开启深度定位。该参数原来叫 deepThink,现已更名为 deepLocate。默认值为 false。
        • context?: string - 本次调用的额外上下文。详见单次调用上下文
        • xpath?: string - 目标元素的 xpath 路径,用于执行当前操作。如果提供了这个 xpath,Midscene 会优先使用该 xpath 来找到元素,然后依次使用缓存和 AI 模型。默认值为空
        • cacheable?: boolean - 当启用 缓存功能 时,是否允许缓存当前 API 调用结果。默认值为 true
    • 返回值:

      • 返回一个 Promise。当元素定位成功时解析为元素定位信息。
      • rect 在大多数定位链路里表示命中的目标元素边界。
      • 有些模型只支持按点定位,不支持按元素边界定位。在这种情况下,例如 AutoGLM,这里的 rect 会退化成一个包含元素中心的 8x8 小方块,而不是真实的元素边界。
      • 由于 rect 的表现会明显受底层模型能力影响,不建议对这个字段建立过强的边界语义依赖。
      • 如果你想获得更稳定的点击位置,推荐优先使用 center 字段。
      • dpr 是仅供 Web 使用的兼容字段,表示截图物理像素与 CSS 逻辑像素的比例。其他 Agent 类型不保证提供该字段。
    • 示例:

    const locateInfo = await agent.aiLocate('页面顶部的登录按钮');
    console.log(locateInfo);

    aiAssert()

    通过自然语言描述一个断言条件,让 AI 判断该条件是否为真。当条件不满足时,SDK 会抛出错误,并在错误信息中追加 AI 返回的详细原因。

    • 类型
    function aiAssert(
      assertion: string | object,
      errorMsg?: string,
      options?: object,
    ): Promise<void>;
    • 参数:

      • assertion: string | object - 用自然语言描述的断言条件,或使用图片作为提示词
      • errorMsg?: string - 当断言失败时附加的可选错误提示信息。
      • options?: object - 可选,一个配置对象,包含:
        • domIncluded?: boolean | 'visible-only' - 是否向模型发送精简后的 DOM 信息,一般用于提取 UI 中不可见的属性,比如图片的链接。如果设置为 'visible-only',则只发送可见的元素。默认值为 false。
        • screenshotIncluded?: boolean - 是否向模型发送截图。默认值为 true。
        • context?: string - 本次调用的额外上下文。详见单次调用上下文
    • 返回值:

      • 返回一个 Promise。当断言成功时解析为 void;若断言失败,则抛出一个错误,错误信息包含 errorMsg 以及 AI 生成的原因。
    • 示例:

    await agent.aiAssert('"Sauce Labs Onesie" 的价格是 7.99');
    Info

    断言在测试脚本中非常重要。为了降低因 AI 幻觉导致错误断言的风险(例如遗漏错误),你也可以使用 .aiQuery 加上常规的 JavaScript 断言来替代 .aiAssert

    例如,你可以这样替代上面的断言代码:

    const items = await agent.aiQuery(
      '{name: string, price: number}[], 返回商品名称和价格列表',
    );
    const onesieItem = items.find((item) => item.name === 'Sauce Labs Onesie');
    expect(onesieItem).toBeTruthy();
    expect(onesieItem.price).toBe(7.99);

    观察与等待

    startObserving()

    startObserving() 会持续记录屏幕。它适合检查短暂出现的 UI,例如 toast、横幅和页面切换。

    调用流程很简单:先开始录制,再执行页面操作,最后调用 stop()stop() 返回 UIObservation。你可以查询或断言录制期间的画面。

    function startObserving(options?: {
      intervalMs?: number; // 采样间隔。默认 1,000 ms,最小 200 ms
      maxFrames?: number; // 最多保留的帧数。默认 30
      watchdogMs?: number; // 最长录制时间。默认 300,000 ms;0 表示不限制
    }): Promise<UIObserver>;

    UIObserver 负责录制:

    • observer.bufferedFrameCount: number - 录制过程中当前缓冲的帧数。
    • observer.stop(): Promise<UIObservation> - 停止录制,并返回 UIObservation

    UIObservation 保存已经录下的画面。调用 stop() 后,这些画面不再变化。

    你可以调用 aiQuery()aiBoolean()aiNumber()aiString()aiAsk()aiAssert()。这些方法只读取录制画面,不读取当前页面的 DOM。

    因此,这些方法不支持 domIncluded。TypeScript 会检查这个错误。JavaScript 在运行时传入该参数,Midscene 也会抛出错误。

    UIObservation 还包含 frameCountstartedAtendedAt。使用完后,可以调用 observation.dispose() 清理临时图片。agent.destroy() 也会清理尚未释放的图片。

    const observer = await agent.startObserving();
    await agent.aiAct('提交表单');
    const observation = await observer.stop();
    await observation.aiAssert('过程中弹出了成功提示 toast');
    const toastCount = await observation.aiNumber('共出现了多少次成功 toast?');
    await observation.dispose();

    采样和资源占用:

    • Midscene 会优先使用连续帧源。Android 使用 scrcpy(scrcpyConfig.enabled),iOS 使用 WDA MJPEG(wdaMjpegFrameSource.enabled),Web 使用 CDP screencast。无法使用连续帧源时,Midscene 会定时截图。移动端此时的采样频率较低。
    • data URL 形式的帧会在采样时写入文件。UIObserver 不会把所有图片长期保存在内存中。其他帧会在录制停止时分批解码并保存。查询或断言时,Midscene 才读取图片。
    • 模型会接收最多 maxFrames 帧,以及最后一张截图。增大 intervalMs 可以减少采样帧,减小 maxFrames 可以限制帧数。观察帧会显示在报告时间线中,并带有 Observed 标签。
    • iOS 的观察帧来自 MJPEG 流,分辨率和质量较低。最后一张截图仍使用完整质量。

    aiWaitFor()

    等待某个条件达成。为控制 AI 服务成本,相邻两次检查的开始时间至少间隔 checkIntervalMs 毫秒。

    • 类型
    function aiWaitFor(
      assertion: string,
      options?: {
        timeoutMs?: number;
        checkIntervalMs?: number;
        context?: string;
      },
    ): Promise<void>;
    • 参数:

      • assertion: string - 用自然语言描述的断言条件
      • options?: object - 可选的配置对象
        • timeoutMs?: number - 超时时间(毫秒,默认为 15000)。每轮检查开始时都会记录时间,只要该时间点仍在超时窗口内,就会进入下一轮检查;否则视为超时
        • checkIntervalMs?: number - 相邻两次检查开始时间的最小间隔(毫秒),默认值为 3000
        • context?: string - 本次调用的额外上下文。详见单次调用上下文
    • 返回值:

      • 返回一个 Promise。当断言成功时解析为 void;若超时,则抛出错误。
    • 示例:

    // 基本用法
    await agent.aiWaitFor('界面上至少有一个耳机的信息');
    
    // 使用自定义配置
    await agent.aiWaitFor('购物车图标显示数量为 2', {
      timeoutMs: 30000, // 等待 30 秒
      checkIntervalMs: 5000, // 每 5 秒检查一次
    });
    Info

    考虑到 AI 服务的时间消耗,.aiWaitFor 并不是一个特别高效的方法。使用一个普通的 sleep 可能是替代 waitFor 的另一种方式。

    工作流执行与上下文

    runYaml()

    执行一个 YAML 格式的自动化脚本。脚本中的 tasks 部分会被解析和执行,并返回所有 .aiQuery 调用的结果。

    • 类型
    function runYaml(yamlScriptContent: string): Promise<{ result: any }>;
    • 参数:

      • yamlScriptContent: string - YAML 格式的脚本内容
    • 返回值:

      • 返回一个包含 result 属性的对象,其中包含所有 aiQuery 调用的结果
    • 示例:

    const { result } = await agent.runYaml(`
    tasks:
      - name: search weather
        flow:
          - ai: input 'weather today' in input box, click search button
          - sleep: 3000
    
      - name: query weather
        flow:
          - aiQuery: "the result shows the weather info, {description: string}"
    `);
    console.log(result);
    Info

    更多关于 YAML 脚本的信息,请参考 Automate with Scripts in YAML

    runGherkinScenario()

    运行一个 Gherkin Scenario,并把其中的步骤映射为 Midscene Agent 调用。

    Beta

    此 API 从 Midscene 1.10 开始支持,目前仍处于 Beta 阶段。未来 API 可能发生变化。

    • 类型
    function runGherkinScenario(
      scenarioText: string,
      options?: {
        context?: string;
        abortSignal?: AbortSignal;
        deepThink?: 'unset' | true | false;
        deepLocate?: boolean;
      },
    ): Promise<void>;
    • 参数:

      • scenarioText: string - 一个 Gherkin scenario,或者一组不带 Scenario: 头部的 Gherkin 步骤
      • options?: object - 可选的运行选项
        • context?: string - 本次运行使用的临时上下文
        • abortSignal?: AbortSignal - 用于中止本次运行的可选信号
        • deepThink?: 'unset' | true | false - 传给 GivenWhen 步骤对应的 aiAct
        • deepLocate?: boolean - 传给 GivenWhen 步骤对应的 aiAct
    • 返回值:

      • Promise<void> - 所有步骤执行完成后 resolve。如果某个步骤失败,错误文案会包含 Gherkin 行号、原始步骤,以及当时正在执行的 Midscene 语义动作。
    • 示例:

    await agent.runGherkinScenario(`
    Scenario: 添加待办事项
      Given 待办事项页面已经打开
      When 我添加一条名为“买牛奶”的待办事项
      Then 待办事项列表中应该包含“买牛奶”
    `);

    关于支持规则、限制、缓存行为和 YAML 用法,请参考 BDD 风格脚本(Gherkin)

    setAIActContext()

    设置在调用 agent.aiAct()agent.ai() 时,发送给 AI 模型的背景知识。这个设置会覆盖之前的设置。

    对于即时操作类型的 API,比如 aiTap(),这个设置不会生效。

    • 类型
    function setAIActContext(aiActContext: string): void;
    • 参数:

      • aiActContext: string - 要发送给 AI 模型的背景知识。aiActionContext 旧参数名依然可用。
    • 示例:

    await agent.setAIActContext('如果 “使用cookie” 对话框存在,先关闭它');
    Note

    agent.setAIActionContext() 已被弃用,请改用 agent.setAIActContext()。弃用的方法仍作为兼容别名保留。

    evaluateJavaScript()

    仅 Web Agent 可用。

    这个方法允许你在 web 页面上下文中执行一段 JavaScript 代码,并返回执行结果。

    • 类型
    function evaluateJavaScript(script: string): Promise<any>;
    • 参数:

      • script: string - 要执行的 JavaScript 代码。
    • 返回值:

      • 返回执行结果。
    • 示例:

    const result = await agent.evaluateJavaScript('document.title');
    console.log(result);

    freezePageContext()

    冻结当前页面上下文,使后续所有的操作都复用同一个页面快照,避免多次重复获取页面状态。在执行大量并发操作时,它可以显著提升性能。

    一些注意点:

    • 通常情况下,你不需要使用这个方法,除非你确定“页面状态获取”是脚本性能瓶颈。
    • 需要及时调用 agent.unfreezePageContext() 来恢复实时页面状态。
    • 不要在交互类操作中使用这个方法,它会让 AI 模型无法感知到页面的最新状态,产生令人困惑的错误。
    • 类型
    function freezePageContext(): Promise<void>;
    • 返回值:

      • Promise<void>
    • 示例:

    // 冻结页面上下文,确保多个操作看到相同的页面状态
    await agent.freezePageContext();
    
    // 执行一些操作...
    const results = await Promise.all([
      agent.aiQuery('Username input box value'),
      agent.aiQuery('Password input box value'),
      agent.aiLocate('Login button'),
    ]);
    console.log(results);
    
    // 解冻页面上下文
    await agent.unfreezePageContext();
    Info

    在报告中,使用冻结上下文的操作会在 Insight tab 中显示 🧊 图标。

    unfreezePageContext()

    解冻页面上下文,恢复使用实时的页面状态。

    • 类型
    function unfreezePageContext(): Promise<void>;
    • 返回值:

      • Promise<void>

    报告、指标与生命周期

    recordToReport()

    默认在报告文件中记录当前截图并添加描述,也可以记录调用方传入的截图。

    • 类型
    interface RecordToReportOptions {
      content?: string;
      /** @deprecated 请改用 screenshots。 */
      screenshotBase64?: string;
      screenshots?: {
        /**
         * PNG/JPEG data URI,或裸 PNG base64 body。
         */
        base64: string;
        description?: string;
      }[];
    }
    
    function recordToReport(
      title?: string,
      options?: RecordToReportOptions,
    ): Promise<void>;
    • 参数:

      • title?: string - 可选,截图的标题,如果未提供,则标题为 'untitled'。
      • options?: RecordToReportOptions - 可选,一个配置对象,包含:
        • content?: string - 截图的描述。
        • screenshots?: Array<{ base64: string; description?: string }> - 在同一个报告条目下记录一张或多张传入的截图。设置该选项后,Midscene 不会再自动截图。base64 推荐使用 PNG/JPEG data URI,例如 data:image/png;base64,...;也可以传裸 base64 body,此时会按 PNG 处理。
    • 兼容性:

      screenshotBase64?: string 仍然作为向后兼容的单截图覆盖参数被接受。新代码建议使用 screenshots: [{ base64 }]screenshotsscreenshotBase64 二者只能传入一个。

    • 返回值:

      • Promise<void>
    • 示例:

    await agent.recordToReport('登录页面', {
      content: '用户 A',
    });
    
    const before = await page.screenshot({ encoding: 'base64' });
    const after = await page.screenshot({ encoding: 'base64' });
    await agent.recordToReport('结算页对比', {
      content: '对比提交前后的页面状态。',
      screenshots: [
        {
          base64: `data:image/png;base64,${before}`,
          description: '提交前',
        },
        {
          base64: `data:image/png;base64,${after}`,
          description: '提交后',
        },
      ],
    });

    _unstableLogContent()

    从报告文件中获取日志内容。日志内容的结构可能会在未来发生变化。

    • 类型
    function _unstableLogContent(): object;
    • 返回值:

      • 返回一个对象,包含日志内容。
    • 示例:

    const logContent = agent._unstableLogContent();
    console.log(logContent);

    大模型用量指标

    Midscene 会记录每一次大模型调用的 token 用量。你可以在运行时从 agent 读取聚合后的总量,这对于配合 Langfuse 等工具做成本可观测性非常有用。

    metrics

    一个 getter,返回自 agent 创建以来累计的大模型用量快照。

    • 类型
    interface UsageBucket {
      promptTokens: number;
      completionTokens: number;
      totalTokens: number;
      calls: number;
    }
    
    interface MidsceneUsageMetrics {
      totalPromptTokens: number;
      totalCompletionTokens: number;
      totalTokens: number;
      totalCachedInput: number;
      totalTimeCostMs: number;
      calls: number;
      // 按调用意图拆分:`planning`、`insight`、`default`。
      byIntent: Record<string, UsageBucket>;
      // 按模型名称拆分。
      byModel: Record<string, UsageBucket>;
    }
    • 示例
    await agent.aiAct('搜索耳机');
    const usage = agent.metrics;
    console.log(usage.totalTokens, usage.byIntent, usage.byModel);

    onLLMUsage 选项

    如需实时追踪,可在构造 agent 时传入 onLLMUsage 回调。每次大模型调用的用量一旦就绪即触发一次,回调参数为原始用量信息(token 数、模型名、意图、请求 id 等)。

    const agent = new PuppeteerAgent(page, {
      onLLMUsage: (usage) => {
        langfuse.event({ name: usage.intent, value: usage.total_tokens });
      },
    });

    destroy()

    完成 Agent 报告的收尾(finalization),并释放 Agent 持有的资源。

    • 类型
    function destroy(): Promise<void>;
    • 行为:
      • 停止正在运行的 observer。
      • 调用底层 interface 的可选 destroy() 方法,并等待清理完成。
      • 等待报告写入完成,再完成报告收尾。最后,将最终路径写入 .reportFile。如果没有生成报告,则写入 undefined
      • 首次调用完成后,再次调用不会重复清理。
      • 如果 interface 清理失败,Midscene 仍会尝试完成报告收尾,然后抛出清理错误。

    调用该方法后,请勿继续使用这个 Agent。

    资源清理范围

    Agent.destroy() 会停止 Agent、完成报告收尾,并释放 Midscene 持有的控制资源。 它通常不会关闭目标页面或断开物理设备,但部分平台会结束相应的自动化会话或连接。 具体行为请参见各平台的 destroy() 说明。

    属性

    .reportFile

    当前报告文件的路径。它的类型是 string | null | undefined

    首次更新报告前,该属性没有可用值。以下情况也不会生成报告路径:关闭报告生成功能、Agent 在没有文件系统访问能力的浏览器环境中运行,或 Agent 没有产生 execution。

    报告路径可用后,报告内容仍可能继续更新。使用最终报告前,请调用 await agent.destroy()。该方法会等待报告写入完成,完成报告的 收尾,并将最终路径写入 .reportFile

    单次调用上下文

    每个 agent.ai* 方法的 options 都支持可选的 context 字符串。它可以为单次请求补充业务知识、用户状态、术语或其他背景,而不必把这些信息写入主 prompt。

    await agent.aiAssert('当前页面是新版本样式', undefined, {
      context:
        '新版本的 Tab 栏位于页面顶部,且页面主色调为红色;旧版本的 Tab 栏位于页面底部,且页面主色调为绿色。只有同时满足新版本的两项条件时,才视为新版本。',
    });

    context 仅对本次方法调用生效。对于 aiAct,单次调用的 context 优先于 Agent 级别的 aiActContext(显式传入空字符串也会覆盖)。其他 AI 方法不会自动继承 aiActContext

    共享类型

    定位选项:深度定位(deepLocate

    deepLocate 是一个可选参数,适用于所有需要元素定位的 API(aiActaiTapaiHoveraiInputaiKeyboardPressaiScrollaiDoubleClickaiRightClickaiLocate 等)。

    开启后,Midscene 会调用 AI 模型两次以精确定位元素,从而提升准确性。这在目标元素面积较小、难以和周围元素区分时非常有用。对于新一代模型(如 Qwen3.x / Doubao 2.0 / Gemini 3.5),大多数场景下带来的收益不明显,建议按需开启。

    • 默认值false
    // 开启 deepLocate 精确定位较难识别的元素
    await agent.aiTap('右上角购物车图标', { deepLocate: true });
    Note

    历史上,deepThink 这个名字在不同 API 中承担过两种含义:

    • aiAct() 中,deepThink 从一开始就表示规划模式,用于引导任务拆解和专注规划的思考过程。详情请参考 deepThink 规划模式
    • aiTapaiHover 等单步操作方法中,旧的 deepThink 表示定位增强,等同于现在的 deepLocate

    为了区分这两种语义,自 v1.5.1 起,deepThink 只用于表示规划模式,定位增强统一命名为 deepLocate

    • 对于 aiAct(),可以同时使用 deepThinkdeepLocatedeepThink 控制规划模式,deepLocate 控制本节描述的深度定位。
    • 对于 aiTapaiHover 等单步操作方法,如果需要提升定位精确度,推荐使用语义更清晰的 deepLocate 参数;旧的 deepThink 参数仍然兼容,语义等同于 deepLocate。 :::

    使用图片的提示词输入

    你可以在提示词中使用图片作为补充,来描述无法通过自然语言表达的内容。

    使用图片作为提示词时,提示词的参数格式如下:

    {
      // 提示词文本,其中可提及需要使用的图片
      prompt: string,
      // 提示词中提到的图片
      images?: {
        // 图片名称,需要和提示词文本中提到的图片名称对应
        name: string,
        // 图片 url,可以是本地图片路径、Base64 字符串,或者图片的 http 链接
        url: string
      }[]
      // 开启该选项后,http 格式的图片链接会被转化为 Base64 编码发送给大模型,适用于图片链接不是公开可访问的情况。
      convertHttpImage2Base64?: boolean
    }
    • 示例一:使用图片描述点击位置
    await agent.aiTap({
      prompt: '指定 logo',
      images: [
        {
          name: '指定 logo',
          url: 'https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png',
        },
      ],
    });
    • 示例二:使用图片进行页面断言
    await agent.aiAssert({
      prompt: '页面上是否存在指定 logo',
      images: [
        {
          name: '指定 logo',
          url: 'https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png',
        },
      ],
    });
    • 示例三:使用图片引导操作(aiAct
    await agent.aiAct({
      prompt: '点击与参考 logo 一致的图标',
      images: [
        {
          name: '指定 logo',
          url: 'https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png',
        },
      ],
    });

    图片尺寸的注意事项

    请遵守模型提供商对图片体积和尺寸的限制。过大或过小的图片都可能被拒绝,准确限制请以模型提供商的文档为准。

    报告工具

    ReportMergingTool

    每个自动化工作流都可以生成独立报告。ReportMergingTool 可以将这些报告合并,便于统一查看和管理。合并结果可能是独立的 HTML 文件,也可能是包含 index.html 和外部截图资源的目录。

    new ReportMergingTool()

    创建一个报告合并工具实例。

    • 示例:
    import { ReportMergingTool } from '@midscene/core/report';
    
    const reportMergingTool = new ReportMergingTool();

    .append()

    将自动化报告添加到待合并列表中。通常在每个自动化工作流结束后调用此方法。

    • 类型
    type SkippedReportFileAttributes = Omit<
      ReportFileAttributes,
      'testStatus'
    > & {
      testStatus: 'skipped';
    };
    
    type ReportFileWithAttributes =
      | {
          reportFilePath: string;
          reportAttributes: ReportFileAttributes;
        }
      | {
          reportFilePath?: undefined;
          reportAttributes: SkippedReportFileAttributes;
        };
    
    function append(reportInfo: ReportFileWithAttributes): void;
    • 参数:

      • reportInfo: ReportFileWithAttributes - 报告信息对象,包含:
        • reportFilePath: string | undefined - 报告文件的路径,通常是 agent.reportFile。只有当 reportAttributes.testStatus'skipped' 时,才能省略该字段。其他状态缺少路径时,append() 会抛出错误
        • reportAttributes: object - 报告属性
          • testId: string - 自动化工作流的唯一标识符
          • testTitle: string - 自动化工作流标题
          • testDescription: string - 自动化工作流描述
          • testDuration: number - 自动化工作流执行时长(毫秒)
          • testStatus: 'passed' | 'failed' | 'timedOut' | 'skipped' | 'interrupted' - 自动化状态
    • 返回值:

      • void
    • 示例:

    import type { TestStatus } from '@midscene/core';
    
    // 在 afterEach 钩子中添加报告
    afterEach(async (ctx) => {
      let workflowStatus: TestStatus = 'passed';
      if (ctx.task.result?.state === 'skip') {
        workflowStatus = 'skipped';
      } else if (ctx.task.result?.errors?.[0]?.message.includes('timed out')) {
        workflowStatus = 'timedOut';
      } else if (ctx.task.result?.state === 'fail') {
        workflowStatus = 'failed';
      }
    
      // 读取最终路径前,先完成报告的 finalization。
      await agent.destroy();
      const reportFilePath = agent.reportFile;
      const reportAttributes = {
        testId: ctx.task.name,
        testTitle: ctx.task.name,
        testDescription: '自动化工作流描述',
        testDuration: Date.now() - startTime,
      };
    
      if (!reportFilePath) {
        if (workflowStatus !== 'skipped') {
          throw new Error('Midscene 报告未生成');
        }
    
        reportMergingTool.append({
          reportAttributes: {
            ...reportAttributes,
            testStatus: 'skipped',
          },
        });
        return;
      }
    
      reportMergingTool.append({
        reportFilePath,
        reportAttributes: {
          ...reportAttributes,
          testStatus: workflowStatus,
        },
      });
    });

    调用 .mergeReports() 前,应完成所有报告相关 Agent 的收尾。

    .mergeReports()

    执行报告合并操作,将所有添加的报告合并为一份报告。

    • 类型
    function mergeReports(
      reportFileName?: 'AUTO' | string,
      opts?: {
        rmOriginalReports?: boolean;
        overwrite?: boolean;
        outputDir?: string;
      },
    ): string | null;
    • 参数:

      • reportFileName?: 'AUTO' | string - 合并后的报告文件名
        • 默认为 'AUTO',自动生成文件名
        • 可以指定自定义文件名(不需要 .html 后缀)
      • opts?: object - 可选配置对象
        • rmOriginalReports?: boolean - 是否删除原始报告文件,默认为 false
        • overwrite?: boolean - 如果目标文件已存在是否覆盖,默认为 false
        • outputDir?: string - 合并报告的输出目录。相对路径从当前工作目录开始解析。默认输出到 midscene_run/report/
    • 返回值:

      • 成功时返回合并报告的入口 HTML 路径
      • 如果没有添加任何报告,返回 null
      • 所有源报告均使用 single-html 时,输出路径为 <outputDir>/<reportFileName>.html
      • 任一源报告使用 html-and-external-assets 时,输出路径为 <outputDir>/<reportFileName>/index.html,同时输出截图资源
    • 示例:

    // 基本用法 - 使用自动生成的文件名
    afterAll(() => {
      reportMergingTool.mergeReports();
    });
    
    // 指定自定义文件名
    afterAll(() => {
      reportMergingTool.mergeReports('my-automation-report');
    });
    
    // 合并后删除原始报告
    afterAll(() => {
      reportMergingTool.mergeReports('my-automation-report', {
        rmOriginalReports: true,
      });
    });
    
    // 覆盖已存在的报告文件
    afterAll(() => {
      reportMergingTool.mergeReports('my-automation-report', {
        overwrite: true,
      });
    });
    
    // 将合并报告写入自定义目录
    afterAll(() => {
      reportMergingTool.mergeReports('my-automation-report', {
        outputDir: './test-results',
      });
    });

    .clear()

    清空待合并的报告列表。如果需要在同一个实例中进行多次合并操作,可以使用此方法清空之前的报告列表。

    • 类型
    function clear(): void;
    • 返回值:

      • void
    • 示例:

    reportMergingTool.mergeReports('first-batch');
    reportMergingTool.clear(); // 清空列表
    // 继续添加新的报告...

    Web 浏览器(@midscene/web

    当你需要自定义 Midscene 的浏览器自动化 Agent,或查阅 Web 专属构造参数时,请参考本篇。关于通用参数(报告、Hook、缓存等),请阅读API 参考(通用)

    Web Action Space(动作空间)

    PuppeteerAgent、PlaywrightAgent 和 Chrome Bridge 共用一套 Action Space,Midscene Agent 在规划任务时可以使用这些操作:

    • Tap —— 左键点击元素。
    • RightClick —— 右键点击元素。
    • DoubleClick —— 双击元素。
    • Hover —— 悬停目标元素。
    • Input —— 输入文本,支持 replace/typeOnly/clear 模式(appendtypeOnly 的已废弃别名)。
    • KeyboardPress —— 按下指定键(可在按键前先聚焦目标)。
    • Scroll —— 以元素为起点或从屏幕中央滚动,支持滚动到顶/底/左/右。
    • DragAndDrop —— 从一个元素拖拽到另一个元素。
    • LongPress —— 长按目标元素,可选自定义时长。
    • Swipe —— 触摸式滑动(开启 enableTouchEventsInActionSpace 时可用)。
    • Pinch —— 双指缩放手势,用于放大/缩小(开启 enableTouchEventsInActionSpace 时可用;Playwright 仅支持 Chromium 内核浏览器)。
    • ClearInput —— 清空输入框内容。
    • Navigate —— 在当前标签页打开指定 URL。
    • Reload —— 刷新当前页面。
    • GoBack —— 浏览器后退。

    生命周期与所有权

    Puppeteer 和 Playwright Page/Browser Agent 继承通用的 destroy() 方法。调用该方法会完成 Midscene 报告收尾, 并清理 Agent 持有的资源。该方法不会关闭 Agent 使用的 Page、Browser 或 BrowserContext。

    PuppeteerPageAgent / PuppeteerAgent

    当你需要在 Puppeteer 控制的浏览器里复用 Midscene 的 AI 操作能力时使用。

    PuppeteerPageAgent 绑定单个 Puppeteer PagePuppeteerAgent 仍作为兼容别名保留。

    导入

    import { PuppeteerPageAgent } from '@midscene/web/puppeteer';

    构造器

    const agent = new PuppeteerPageAgent(page, {
      // 浏览器特有配置...
    });

    浏览器特有选项

    除了通用 Agent 参数,Puppeteer 还提供:

    • forceSameTabNavigation: boolean —— 限制始终在当前标签页内导航,默认 true
    • waitForNavigationTimeout: number —— 当操作触发页面跳转时的最长等待时间,默认 5000(设为 0 表示不等待)。
    • waitForNetworkIdleTimeout: number —— 每次操作后等待网络空闲的时间,默认 2000(设为 0 关闭)。
    • enableTouchEventsInActionSpace: boolean —— 在动作空间里增加触摸手势(如滑动),用于需要触摸事件的页面,默认 false
    • keyboardTypeDelay: number —— 透传给 Puppeteer page.keyboard.type 的每字符延迟(毫秒)。默认值为 undefined,表示 Midscene 不传该选项,使用 Puppeteer 自身默认值。通常无需配置;只有当受控输入框在快速输入下出现丢字等特殊情况时,再调大该值(例如 80)。
    • forceChromeSelectRendering: boolean —— 强制 select 元素使用 Chrome 的 base-select 样式,避免系统原生样式导致截图/元素提取不可见;需要 Puppeteer > 24.6.0。默认值为 true;如需关闭(例如旧版 Chrome/Puppeteer)可设为 false
    • customActions: DeviceAction[] —— 添加额外的自定义动作,让 Agent 可以调用你定义的领域特定动作。

    使用说明

    :::info

    • 每个页面一个 Agent:默认情况下(forceSameTabNavigation: true)Midscene 会拦截新标签并在当前页打开,便于调试;若想保留浏览器原生的新标签行为可设为 false,并自行给每个页面创建新的 PuppeteerAgent。如果需要同一个 Agent 管理浏览器级别的页面切换,请使用 PuppeteerBrowserAgent
    • PuppeteerAgent / PuppeteerPageAgent 为了兼容性仍保持 page-scoped 语义,不会暴露浏览器级别的页面切换能力;需要时请显式选择 PuppeteerBrowserAgent
    • 更多交互方法请参考 API 参考(通用)

    PuppeteerBrowserAgent

    当一个 Midscene Agent 需要管理 Puppeteer 浏览器内的页面切换时,使用 PuppeteerBrowserAgent。它绑定 browser 实例,维护一个 active page,并且可以选择自动跟随新打开的页面。

    const agent = new PuppeteerBrowserAgent(browser, page, {
      autoFollowNewPage: true,
    });
    • 构造函数:new PuppeteerBrowserAgent(browser, page, options?) —— 你要显式指定初始 active page 时使用。
    • 工厂方法:PuppeteerBrowserAgent.create(browser, options?) —— 你希望 Midscene 自动选择或创建初始 active page 时使用。它会优先使用 initialPage,否则复用浏览器里的第一个页面,或者创建一个新页面。
    • initialPage: Page —— 工厂方法使用的初始 Puppeteer 页面。
    • autoFollowNewPage: boolean —— 浏览器打开新页面时是否自动切换 active page,默认 false
    • newPageTimeout: number —— waitForNewPage 的超时时间,默认 5000
    • activePage: Page —— Browser Agent 当前控制的页面。
    • pages() —— 列出绑定 browser 中的页面。
    • newPage() —— 创建新页面并将其设为 active page。
    • setActivePage(page: Page) —— 显式指定 Browser Agent 接下来控制哪个 Puppeteer 页面。
    • waitForNewPage(action?, options?) —— 等待新打开的页面,但不会隐式切换 active page。

    另请参阅

    PlaywrightPageAgent / PlaywrightAgent

    在 Playwright 浏览器中使用 Midscene 以支持带 AI 的测试或自动化流程。

    PlaywrightPageAgent 绑定单个 Playwright PagePlaywrightAgent 仍作为兼容别名保留。

    导入

    import { PlaywrightPageAgent } from '@midscene/web/playwright';

    构造器

    const agent = new PlaywrightPageAgent(page, {
      // 浏览器特有配置...
    });

    浏览器特有选项

    • forceSameTabNavigation: boolean —— 强制在当前标签页内执行,默认 true
    • waitForNavigationTimeout: number —— 等待导航完成的时间,默认 5000(设为 0 关闭)。
    • waitForNetworkIdleTimeout: number —— 每次操作后等待网络空闲的时间,默认 2000(设为 0 关闭)。
    • enableTouchEventsInActionSpace: boolean —— 在动作空间里增加触摸手势(如滑动),用于需要触摸事件的页面,默认 false
    • keyboardTypeDelay: number —— 透传给 Playwright page.keyboard.type 的每字符延迟(毫秒)。默认值为 undefined,表示 Midscene 不传该选项,使用 Playwright 自身默认值。通常无需配置;只有当受控输入框在快速输入下出现丢字等特殊情况时,再调大该值(例如 80)。
    • forceChromeSelectRendering: boolean —— 强制 select 元素使用 Chrome 的 base-select 样式,避免系统原生样式导致截图/元素提取不可见;需要 Playwright ≥ 1.52.0。默认值为 true;如需关闭(例如旧版 Chrome/Playwright)可设为 false
    • customActions: DeviceAction[] —— 添加额外的自定义动作,让 Agent 可以调用你定义的领域特定动作。

    使用说明

    Info
    • 每个页面一个 Agent:默认 forceSameTabNavigationtrue,Midscene 会拦截新标签确保稳定性;如需浏览器原生新标签行为请设为 false,并自行给每个页面创建新的 PlaywrightAgent。如果需要同一个 Agent 管理 browser context 级别的页面切换,请使用 PlaywrightBrowserAgent
    • PlaywrightAgent / PlaywrightPageAgent 为了兼容性仍保持 page-scoped 语义,不会暴露浏览器级别的页面切换能力;需要时请显式选择 PlaywrightBrowserAgent
    • 更多交互方法请参考 API 参考(通用)

    PlaywrightBrowserAgent

    当一个 Midscene Agent 需要管理 Playwright browser context 内的页面切换时,使用 PlaywrightBrowserAgent。它绑定 browser context,维护一个 active page,并且可以选择自动跟随新打开的页面。

    const agent = new PlaywrightBrowserAgent(context, page, {
      autoFollowNewPage: true,
    });
    • 构造函数:new PlaywrightBrowserAgent(context, page, options?) —— 你要显式指定初始 active page 时使用。
    • 工厂方法:PlaywrightBrowserAgent.create(context, options?) —— 你希望 Midscene 自动选择或创建初始 active page 时使用。它会优先使用 initialPage,否则复用 context 里的第一个页面,或者创建一个新页面。
    • initialPage: Page —— 工厂方法使用的初始 Playwright 页面。
    • autoFollowNewPage: boolean —— context 打开新页面时是否自动切换 active page,默认 false
    • newPageTimeout: number —— waitForNewPage 的超时时间,默认 5000
    • activePage: Page —— Browser Agent 当前控制的页面。
    • pages() —— 列出绑定 browser context 中的页面。
    • newPage() —— 创建新页面并将其设为 active page。
    • setActivePage(page: Page) —— 显式指定 Browser Agent 接下来控制哪个 Playwright 页面。
    • waitForNewPage(action?, options?) —— 等待新打开的页面,但不会隐式切换 active page。

    另请参阅

    Chrome Bridge Agent

    Bridge mode 允许 Midscene 通过扩展控制当前桌面 Chrome 标签页,而无需再启动独立的自动化浏览器。

    导入

    import { AgentOverChromeBridge } from '@midscene/web/bridge-mode';

    构造器

    const agent = new AgentOverChromeBridge({
      allowRemoteAccess: false,
      // 其他桥接配置...
    });

    桥接配置

    • closeNewTabsAfterDisconnect?: boolean —— 是否在销毁时自动关闭桥接创建的新标签页,默认 false
    • allowRemoteAccess?: boolean —— 是否允许远程机器连接,默认 false(监听 127.0.0.1)。
    • host?: string —— 自定义 Bridge Server 的监听地址,优先级高于 allowRemoteAccess
    • port?: number —— Bridge Server 端口,默认 3766
    • enableWaterFlowAnimation?: boolean —— Midscene 控制页面时是否显示蓝色动态边框和鼠标指示,默认 true;如需截取不含视觉覆盖层的截图,可设为 false

    完整安装与能力说明,见 Chrome 插件桥接模式

    使用说明

    Info

    请先调用 connectCurrentTabconnectNewTabWithUrl 再执行其他操作。每个 AgentOverChromeBridge 只能连接一个标签页;destroy 之后需要重新创建实例。

    方法

    connectCurrentTab()

    function connectCurrentTab(options?: {
      forceSameTabNavigation?: boolean;
    }): Promise<void>;
    • options.forceSameTabNavigation(默认 true)会拦截新标签并在当前页打开,方便调试;若想保留新标签行为可设为 false,但需要为每个新标签创建新的 Agent。
    • 连接当前激活标签页,成功后返回 Promise<void>,如果扩展未允许连接会报错。

    connectNewTabWithUrl()

    function connectNewTabWithUrl(
      url: string,
      options?: { forceSameTabNavigation?: boolean },
    ): Promise<void>;
    • url —— 新标签页要打开的地址。
    • options —— 与 connectCurrentTab 相同。
    • 打开新标签并连接成功后返回 Promise<void>

    destroy()

    function destroy(closeNewTabsAfterDisconnect?: boolean): Promise<void>;
    • closeNewTabsAfterDisconnect —— 运行时覆盖构造器配置,为 true 时销毁时关闭桥接创建的新标签页。
    • 包含通用 Agent 清理行为,其中包括报告收尾。
    • 清理桥接连接、本地服务和 Agent 报告后,返回 Promise<void>

    另请参阅

    Android(@midscene/android

    当你需要自定义设备行为、把 Midscene 接入框架,或排查 adb 问题时,请查阅本节。关于通用构造函数(报告、Hook、缓存等)的参数说明,请参考平台无关的 API 参考

    Android Action Space(动作空间)

    AndroidDevice 使用以下动作空间,Midscene Agent 在规划任务时可以使用这些操作:

    • Tap —— 点击元素。
    • DoubleClick —— 双击元素。
    • Input —— 输入文本,支持 replace/typeOnly/clear 模式(appendtypeOnly 的已废弃别名)。支持可选参数 autoDismissKeyboardkeyboardTypeDelay
    • Scroll —— 以元素为起点或从屏幕中央向上/下/左/右滚动,支持滚动到顶/底/左/右。
    • DragAndDrop —— 从一个元素拖拽到另一个元素。
    • KeyboardPress —— 按下指定键位。
    • LongPress —— 长按目标元素,可选自定义时长。
    • PullGesture —— 上拉或下拉(如下拉刷新),可选距离与持续时间。
    • Pinch —— 双指缩放手势。scale > 1 放大,scale < 1 缩小。
    • ClearInput —— 清空输入框内容。
    • Launch —— 打开网页或 package/.Activity
    • Terminate —— 按包名强制停止应用。
    • RunAdbShell —— 执行原始 adb shell 命令。此动作默认启用。将 exposeRunAdbShellAction 设为 false,可从动作空间中移除此动作。
    • AndroidBackButton —— 触发系统返回。
    • AndroidHomeButton —— 回到桌面。
    • AndroidRecentAppsButton —— 打开多任务/最近应用。

    AndroidDevice

    创建一个可供 AndroidAgent 驱动的 adb 设备实例。

    导入

    import {
      AndroidDevice,
      getConnectedDevices,
      getConnectedDevicesWithDetails,
    } from '@midscene/android';

    构造函数

    const device = new AndroidDevice(deviceId, {
      // 设备参数...
    });

    设备选项

    • deviceId: string —— 来自 adb devicesgetConnectedDevices() 的值。
    • autoDismissKeyboard?: boolean —— 输入完成后自动隐藏键盘,默认 true
    • keyboardDismissStrategy?: 'esc-first' | 'back-first' —— 关闭键盘的顺序,默认 'esc-first'
    • keyboardTypeDelay?: number —— 输入文本时每个按键之间的延迟(毫秒)。设置后,文本将逐字符输入,每个字符之间等待指定的延迟,而不是一次性发送整串文本。适用于输入过快导致丢字的设备或输入框。仅对 input text 路径生效;使用 yadb 时忽略此选项。
    • androidAdbPath?: string —— adb 可执行文件的自定义路径。
    • remoteAdbHost?: string / remoteAdbPort?: number —— 指向远程 adb server。
    • imeStrategy?: 'always-yadb' | 'yadb-for-non-ascii' —— 控制何时调用 yadb 进行文本输入,默认 'yadb-for-non-ascii'
      • 'yadb-for-non-ascii'(默认)—— 对 Unicode 字符(包括 Latin Unicode 如 ö、é、ñ)、中文、日文以及格式化符号(如 %s、%d)使用 yadb。纯 ASCII 文本使用更快的原生 adb input text
      • 'always-yadb' —— 对所有文本输入始终使用 yadb,提供最大兼容性,但对纯 ASCII 文本稍慢。
    • screenshotStrategy?: 'auto' | 'always-yadb' —— 控制截图方式,默认 'auto'
      • 'auto'(默认)—— 先尝试 adb.takeScreenshot,失败后回退到 shell screencap;若 screencap 执行失败,则改用 yadb 工具。scrcpy 仅当 scrcpyConfig.enabled 开启时才会优先尝试。该策略不会分析截图内容:即使某个截图方法执行成功但返回全黑图片,也不会自动切换到 yadb;只有当前一种截图方法执行失败时,才会尝试下一种方法。
      • 'always-yadb' —— 绕过默认的 auto 截图流程(adb.takeScreenshotscreencap,以及开启时的 scrcpy),直接使用 yadb 截图。适用于 screencap 对安全页面(FLAG_SECURE)截出黑屏、但 yadb 能正常截取的场景——能否截取取决于 Android 版本、ROM、root/hook 环境和设备配置,请以实际设备测试结果为准。yadb 只能截取默认显示器(displayId=0);如果同时设置该策略和非零 displayId,Midscene 会抛出错误。也可通过环境变量 MIDSCENE_ANDROID_SCREENSHOT_STRATEGY=always-yadb 设置。
    • displayId?: number —— 在设备镜像多个屏幕时,选择特定虚拟屏幕。
    • exposeRunAdbShellAction?: boolean —— 是否在动作空间中暴露内置的 RunAdbShell 动作。默认值:true。设为 false 后,AI 规划器、YAML 脚本和 agent.runAdbShell() 都无法执行 ADB shell 命令。
    • customActions?: DeviceAction[] —— 添加额外的自定义动作,让 Agent 可以调用你定义的领域特定动作。
    • screenshotResizeScale?: number —— 已废弃。 此选项已移除,不再生效。如需控制发送给 AI 模型的截图尺寸,请使用 AgentOpt 中的 screenshotShrinkFactor
    • minScreenshotBufferSize?: number —— 截图 buffer 大小校验阈值,单位为字节;低于该值的 buffer 会被视为截图采集失败或已损坏。默认 1024(1KB)。设置为 0 仅跳过此大小校验;Midscene 仍会拒绝空 buffer 和无效图片格式。
    • alwaysRefreshScreenInfo?: boolean —— 每一步都重新查询旋转角度与屏幕尺寸,默认 false

    scrcpy 配置和状态方法

    • scrcpyConfig?: object —— scrcpy 截图配置,默认关闭。

      • enabled?: boolean —— 是否启用 scrcpy 截图,默认 false
      • maxSize?: number —— 视频流最大宽度或高度,默认 0,表示不缩放。
      • videoBitRate?: number —— H.264 编码码率,单位为 bps,默认 100000000
      • idleTimeoutMs?: number —— 空闲连接的断开时间,单位为毫秒,默认 30000;设为 0 时禁用。
    • device.getScrcpyStatus() —— 返回 enabledconnectedlastErrorretryAfter

    • device.retryScrcpy(): Promise<void> —— 跳过冷却时间,立即重试 scrcpy 连接。

    使用说明

    • 可以使用 getConnectedDevices() 发现设备,udidadb devices 输出一致。
    • 可以使用 remoteAdbHost/remoteAdbPort 连接远程 adb;如果 adb 不在 PATH 中,可设置 androidAdbPath

    destroy()

    function destroy(): Promise<void>;

    释放 AndroidDevice 持有的资源,其中包括正在运行的 scrcpy 连接。同时,清除 ADB 状态。该方法可以重复调用。调用完成后,这个 Device 实例不能再执行 ADB 命令,但物理设备仍与 ADB server 保持连接。

    调用 AndroidAgent.destroy() 时,会自动执行该方法。每个 AndroidDevice 实例只属于一个 AndroidAgent

    AndroidAgent

    将 Midscene 的 AI 规划能力绑定到 AndroidDevice,实现 UI 自动化。

    导入

    import { AndroidAgent } from '@midscene/android';

    构造函数

    const agent = new AndroidAgent(device, {
      // 通用 Agent 参数...
    });

    Android 特有选项

    • appNameMapping?: Record<string, string> —— 将友好的应用名称映射到包名。当你在 launch(target) 里传入应用名称时,Agent 会在此映射中查找对应的包名;若未找到映射,则按原样尝试启动 target
    • 其余字段与通用构造参数一致,包括 generateReportreportFileNameaiActContextmodelConfigcachecreateOpenAIClientonTaskStartTip 等。

    使用说明

    Info
    • 一个设备连接对应一个 Agent。
    • customActions 用于向 AndroidDevice 添加额外的自定义动作。可以将它传给 Device 构造函数或 agentFromAdbDevice()
    • launchterminaterunAdbShell 等 Android 专属辅助函数也可在 YAML 脚本中使用,语法见 Android 平台特定动作
    • 通用交互方法请查阅 API 参考(通用)

    Android 特有方法

    agent.launch()

    启动网页或原生 Android activity/package。

    function launch(target: string): Promise<void>;
    • target: string —— 可以是网页 URL,也可以是 package/.Activity 形式的字符串,例如 com.android.settings/.Settings,也可以是应用包名、URL 或应用名称。若传入应用名称且在 appNameMapping 中存在映射,将自动解析为对应包名;若未找到映射,则直接按 target 启动。

    agent.runAdbShell()

    通过连接的设备运行原始的 adb shell 命令。传入的内容只需要包含 shell 命令本身,不要包含 adb shell 前缀。

    function runAdbShell(command: string, opt?: { timeout?: number }): Promise<string>;
    • command: string —— 原样传递给 adb shell 的命令。例如要传 input tap 100 200,不要传 adb shell input tap 100 200
    • opt.timeout?: number —— 可选的命令执行超时时间,单位为毫秒。

    此方法会调用 RunAdbShell 动作。创建 AndroidDevice 时,如果将 exposeRunAdbShellAction 设为 false,此方法将不可用。

    const result = await agent.runAdbShell('dumpsys battery', { timeout: 60 * 1000 });
    console.log(result);
    
    await agent.runAdbShell('input tap 100 200');

    agent.terminate()

    终止(强制停止)正在运行的 Android 应用。

    function terminate(uri: string): Promise<void>;
    • uri: string —— 应用包名、appNameMapping 中的应用名称,或 package/.Activity(仅使用包名部分)。
    await agent.terminate('com.android.settings');

    导航辅助

    • agent.back(): Promise<void> —— 触发 Android 系统的返回操作。
    • agent.home(): Promise<void> —— 返回桌面。
    • agent.recentApps(): Promise<void> —— 打开多任务/最近应用界面。

    Android 工厂函数和工具

    agentFromAdbDevice()

    从任意已连接的 adb 设备创建 AndroidAgent

    function agentFromAdbDevice(
      deviceId?: string,
      opts?: AndroidAgentOpt & AndroidDeviceOpt,
    ): Promise<AndroidAgent>;
    • deviceId?: string —— 连接特定设备;留空表示使用“第一个可用设备”。
    • opts?: AndroidAgentOpt & AndroidDeviceOpt —— Agent 选项与 AndroidDevice 设置。未传 deviceId 时,Midscene 会通过 adb 自动发现设备。可在 opts 中设置 androidAdbPathremoteAdbHostremoteAdbPort,指定用于发现和连接设备的 adb。

    getConnectedDevices()

    列举 Midscene 可驱动的 adb 设备。

    function getConnectedDevices(
      deviceOptions?: AndroidDeviceOpt,
    ): Promise<Array<{
      udid: string;
      state: string;
      port?: number;
    }>>;

    deviceOptions 是可选参数。省略时,Midscene 使用默认 adb 配置。通过 androidAdbPath 指定 adb 可执行文件,或通过 remoteAdbHostremoteAdbPort 连接远程 adb server:

    const devices = await getConnectedDevices({
      androidAdbPath: '/absolute/path/to/adb',
      remoteAdbHost: '192.168.1.10',
      remoteAdbPort: 5038,
    });

    getConnectedDevicesWithDetails()

    getConnectedDevices() 功能类似,并额外返回设备品牌、型号、分辨率和屏幕密度。无法获取的字段为 undefined

    function getConnectedDevicesWithDetails(
      deviceOptions?: AndroidDeviceOpt,
    ): Promise<Array<{
      udid: string;
      state: string;
      port?: number;
      model?: string;
      brand?: string;
      resolution?: string;
      density?: number;
    }>>;

    相关阅读

    iOS(@midscene/ios

    当你需要自定义 iOS 设备行为、将 Midscene 接入依赖 WebDriverAgent 的工作流,或排查 WDA 请求问题时,请查阅本节。关于通用构造函数(报告、Hook、缓存等),请参考平台无关的 API 参考

    iOS Action Space(动作空间)

    IOSDevice 使用以下动作空间,Midscene Agent 在规划任务时可以使用这些操作:

    • Tap —— 点击元素。
    • DoubleClick —— 双击元素。
    • Input —— 输入文本,支持 replace/typeOnly/clear 模式(appendtypeOnly 的已废弃别名)。支持可选参数 autoDismissKeyboardkeyboardTypeDelay
    • Scroll —— 以元素为起点或从屏幕中央向上/下/左/右滚动,支持滚动到顶/底/左/右。
    • DragAndDrop —— 从一个元素拖拽到另一个元素。
    • KeyboardPress —— 按下指定键位。
    • LongPress —— 长按目标元素,可选自定义时长。
    • Pinch —— 双指缩放手势。scale > 1 放大,scale < 1 缩小。
    • ClearInput —— 清空输入框内容。
    • Launch —— 打开网页、Bundle ID 或 URL Scheme。
    • Terminate —— 通过 Bundle ID 关闭正在运行的 iOS 应用。
    • RunWdaRequest —— 直接调用 WebDriverAgent REST 接口。
    • IOSHomeButton —— 执行 iOS 系统 Home 操作。
    • IOSAppSwitcher —— 打开 iOS 多任务视图。

    IOSDevice

    创建一个由 WebDriverAgent 支撑、供 IOSAgent 驱动的设备连接。

    导入

    import { IOSDevice } from '@midscene/ios';

    构造函数

    const device = new IOSDevice({
      // 设备参数...
    });

    设备选项

    • wdaPort?: number —— WebDriverAgent 端口,默认 8100
    • wdaHost?: string —— WebDriverAgent host,默认 'localhost'
    • iOSDeviceClassOverride?: string —— 使用 agentFromWebDriverAgent() 或 iOS Playground 时替换默认 IOSDevice 的 npm module path。目标模块必须导出 IOSDevice class 或 default class。
    • sessionId?: string —— 复用已有的 WebDriverAgent session ID。传入后,Midscene 不再创建新的 WDA session;清理时只会从这个外部 WebDriver session 分离,不会删除它。
    • wdaMjpegPort?: number —— WDA MJPEG 服务端口,用于实时画面流,默认 9100
    • wdaMjpegFrameSource?: { enabled?: boolean } —— 使用 WDA 的 MJPEG stream 作为 agent.startObserving() 的连续帧源。默认关闭;关闭时,观察逻辑会退回到连续调用 screenshotBase64()
    • autoDismissKeyboard?: boolean —— 文本输入后自动隐藏键盘,默认 true
    • keyboardTypeDelay?: number —— 输入文本时每个按键之间的延迟(毫秒)。设置后,通过 WDA 的 /wda/keys 接口逐字符输入,每个字符之间等待指定的延迟。适用于输入框在快速输入下丢字的场景。
    • customActions?: DeviceAction<any>[] —— 添加额外的自定义动作,让 Agent 可以调用你定义的领域特定动作。

    使用说明

    • 请确认已开启开发者模式且 WDA 能访问设备;真机转发端口时可借助 iproxy
    • 通过 wdaHost/wdaPort 可指向远程设备或自建的 WDA。
    • 多设备并发时,请为每个设备设置不同的 wdaPortwdaMjpegPort,避免 WDA 命令和 MJPEG stream 端口冲突。
    • 通用交互方法请查阅 API 参考(通用)

    destroy()

    function destroy(): Promise<void>;

    尝试停止 MJPEG frame source、删除 WebDriverAgent session,并停止 WDA manager。 清理失败时会记录日志,但 Promise 不会因此被拒绝。该方法可以重复调用。调用完成 后,不能再使用这个 IOSDevice 实例。

    调用 IOSAgent.destroy() 时,会自动执行该方法。每个 IOSDevice 实例只属于一个 IOSAgent

    IOSAgent

    将 Midscene 的 AI 规划能力绑定到 IOSDevice,通过 WebDriverAgent 实现 UI 自动化。

    导入

    import { IOSAgent } from '@midscene/ios';

    构造函数

    const agent = new IOSAgent(device, {
      // 通用 Agent 参数...
    });

    iOS 特有选项

    • appNameMapping?: Record<string, string> —— 将友好的应用名称映射到 Bundle Identifier。当你在 launch(target)terminate(bundleId) 里传入应用名称时,Agent 会在此映射中查找对应的 Bundle ID;若未找到映射,则按原样使用 target。用户提供的 appNameMapping 优先级高于默认映射。
    • 其余字段与通用构造参数一致,包括 generateReportreportFileNameaiActContextmodelConfigcachecreateOpenAIClientonTaskStartTip 等。

    使用说明

    Info
    • 一个设备连接对应一个 Agent。
    • customActions 用于向 IOSDevice 添加额外的自定义动作。可以将它传给 Device 构造函数或 agentFromWebDriverAgent()
    • launchterminaterunWdaRequest 等 iOS 专属辅助函数也可在 YAML 脚本中使用,语法见 iOS 平台特定动作
    • 通用交互方法请查阅 API 参考(通用)

    iOS 特有方法

    agent.launch()

    打开网页、原生应用或自定义 Scheme。

    function launch(target: string): Promise<void>;
    • target: string —— 目标地址(网页 URL、Bundle Identifier、URL scheme、tel/mailto 等)或应用名称。若传入应用名称且在 appNameMapping 中存在映射,将自动解析为对应 Bundle ID;若未找到映射,则直接按 target 启动。
    await agent.launch('https://www.apple.com');
    await agent.launch('com.apple.Preferences');
    await agent.launch('myapp://profile/user/123');
    await agent.launch('tel:+1234567890');

    agent.terminate()

    通过 Bundle ID 终止(关闭)正在运行的 iOS 应用。

    function terminate(bundleId: string): Promise<void>;
    • bundleId: string —— 要终止的应用的 Bundle Identifier(如 com.apple.Preferences)。若传入应用名称且在 appNameMapping 中存在映射,将自动解析为对应 Bundle ID。
    await agent.terminate('com.apple.Preferences');
    await agent.terminate('com.apple.mobilesafari');

    agent.runWdaRequest()

    当你需要更底层的控制能力时,执行原始的 WebDriverAgent REST 请求。

    function runWdaRequest(params: {
      method: 'GET' | 'POST' | 'DELETE' | 'PUT';
      endpoint: string;
      data?: Record<string, any>;
    }): Promise<any>;
    • params.method —— HTTP 动词。支持值为 GETPOSTDELETEPUT
    • params.endpoint —— WebDriverAgent 接口路径。
    • params.data —— 可选的 JSON 请求体。
    const screen = await agent.runWdaRequest({
      method: 'GET',
      endpoint: '/wda/screen',
    });
    
    await agent.runWdaRequest({
      method: 'POST',
      endpoint: '/session/test/wda/pressButton',
      data: { name: 'home' },
    });

    如果直接调用设备实例上的 IOSDevice.runWdaRequest(),请使用位置参数签名 runWdaRequest(method, endpoint, data?)

    应用间导航

    • agent.home(): Promise<void> —— 回到主屏。
    • agent.appSwitcher(): Promise<void> —— 打开多任务视图。

    iOS 工厂函数和工具

    agentFromWebDriverAgent()

    连接 WebDriverAgent 并返回可用的 IOSAgent。

    function agentFromWebDriverAgent(
      opts?: IOSAgentOpt & IOSDeviceOpt,
    ): Promise<IOSAgent>;
    • opts?: IOSAgentOpt & IOSDeviceOpt —— 在一个对象中同时传入 iOS Agent 选项与 IOSDevice 的配置。
    • 设置 MIDSCENE_IOS_DEVICE_CLASS_OVERRIDE 可通过环境变量应用同一个设备 class override。显式传入的选项优先级高于环境变量。
    import { agentFromWebDriverAgent } from '@midscene/ios';
    
    const agent = await agentFromWebDriverAgent({
      wdaHost: 'localhost',
      wdaPort: 8100,
      iOSDeviceClassOverride: '@your-scope/ios-device',
      aiActContext: 'Accept permission dialogs automatically.',
    });

    相关阅读

    HarmonyOS(@midscene/harmony

    当你需要自定义设备行为、把 Midscene 接入框架,或排查 HDC 问题时,请查阅本节。关于通用构造函数(报告、Hook、缓存等)的参数说明,请参考平台无关的 API 参考

    HarmonyOS Action Space(动作空间)

    HarmonyDevice 使用以下动作空间,Midscene Agent 在规划任务时可以使用这些操作:

    • Tap —— 点击元素。
    • DoubleClick —— 双击元素。
    • Input —— 输入文本,支持 replace/typeOnly/clear 模式。
    • Scroll —— 以元素为起点或从屏幕中央向上/下/左/右滚动,支持滚动到顶/底/左/右。
    • DragAndDrop —— 从一个元素拖拽到另一个元素。
    • KeyboardPress —— 按下指定键位。
    • LongPress —— 长按目标元素,可选自定义时长。
    • ClearInput —— 清空输入框内容。
    • Pinch —— 不支持。HarmonyOS uitest 框架未提供多触点输入 API。
    • Launch —— 打开 HarmonyOS 应用(bundle name)。
    • Terminate —— 按 bundle name 强制停止应用。
    • RunHdcShell —— 执行原始 hdc shell 命令。
    • HarmonyBackButton —— 触发系统返回。
    • HarmonyHomeButton —— 回到桌面。
    • HarmonyRecentAppsButton —— 打开多任务/最近应用。

    HarmonyDevice

    创建一个可供 HarmonyAgent 驱动的 HDC 设备实例。

    导入

    import { HarmonyDevice, getConnectedDevices } from '@midscene/harmony';

    构造函数

    const device = new HarmonyDevice(deviceId, {
      // 设备参数...
    });

    设备选项

    • deviceId: string —— 来自 hdc list targetsgetConnectedDevices() 的值。
    • hdcPath?: string —— HDC 可执行文件的自定义路径。若未设置,将依次从 HDC_HOME 环境变量和常见安装路径中查找。
    • autoDismissKeyboard?: boolean —— 输入完成后自动隐藏键盘,默认 true
    • keyboardDismissStrategy?: 'esc-first' | 'back-first' —— 自动隐藏键盘时优先使用的按键,默认 'esc-first'。HarmonyOS 只发送该策略的首选按键:'esc-first' 发送 ESC,'back-first' 发送 Back。
    • keyboardTypeDelay?: number —— 输入文本时每个按键之间的延迟(毫秒)。设置后,通过 uitest uiInput inputText 逐字符输入,每个字符之间等待指定的延迟。适用于输入框在快速输入下丢字的场景。
    • screenshotResizeScale?: number —— 已废弃。 此选项已移除,不再生效。如需控制发送给 AI 模型的截图尺寸,请使用 AgentOpt 中的 screenshotShrinkFactor
    • customActions?: DeviceAction[] —— 添加额外的自定义动作,让 Agent 可以调用你定义的领域特定动作。

    使用说明

    • 可以使用 getConnectedDevices() 发现设备,返回的 deviceIdhdc list targets 输出一致。
    • 如果 HDC 不在系统 PATH 中,可通过 HDC_HOME 环境变量或 hdcPath 选项指定路径。

    destroy()

    function destroy(): Promise<void>;

    释放 HarmonyDevice 持有的 HDC 状态和屏幕信息缓存。该方法可以重复调用。调用 完成后,这个 Device 实例不能再执行 HDC 命令,但物理设备仍与 HDC 保持连接。

    调用 HarmonyAgent.destroy() 时,会自动执行该方法。每个 HarmonyDevice 实例只属于一个 HarmonyAgent

    HarmonyAgent

    将 Midscene 的 AI 规划能力绑定到 HarmonyDevice,实现 UI 自动化。

    导入

    import { HarmonyAgent } from '@midscene/harmony';

    构造函数

    const agent = new HarmonyAgent(device, {
      // 通用 Agent 参数...
    });

    HarmonyOS 特有选项

    • appNameMapping?: Record<string, string> —— 将友好的应用名称映射到 bundle name。当你在 launch(target) 里传入应用名称时,Agent 会在此映射中查找对应的 bundle name;若未找到映射,则按原样尝试启动 target
    • 其余字段与通用构造参数一致,包括 generateReportreportFileNameaiActContextmodelConfigcachecreateOpenAIClientonTaskStartTip 等。

    使用说明

    Info
    • 一个设备连接对应一个 Agent。
    • customActions 用于向 HarmonyDevice 添加额外的自定义动作。可以将它传给 Device 构造函数或 agentFromHdcDevice()
    • launchterminaterunHdcShell 等 HarmonyOS 专属辅助函数也可在 YAML 脚本中使用,语法见 HarmonyOS 平台特定动作
    • 通用交互方法请查阅 API 参考(通用)

    HarmonyOS 特有方法

    agent.launch()

    启动 HarmonyOS 应用。

    function launch(uri: string): Promise<void>;
    • uri: string —— 可以是应用 bundle name(如 com.huawei.hmos.settings),也可以是在 appNameMapping 中注册的应用名称。如果传入 http://https:// 开头的 URL,将通过浏览器打开。
    await agent.launch('com.huawei.hmos.settings'); // 打开系统设置
    await agent.launch('com.huawei.hmos.camera');    // 打开相机

    agent.runHdcShell()

    通过连接的设备运行原始的 hdc shell 命令。

    function runHdcShell(command: string): Promise<string>;
    • command: string —— 原样传递给 hdc shell 的命令。
    const result = await agent.runHdcShell('hidumper -s RenderService -a screen');
    console.log(result);

    agent.terminate()

    终止(强制停止)正在运行的 HarmonyOS 应用。

    function terminate(uri: string): Promise<void>;
    • uri: string —— 应用 bundle name、appNameMapping 中的应用名称,或 bundle/Ability(仅使用 bundle name 部分)。
    await agent.terminate('com.huawei.hmos.settings');

    导航辅助

    • agent.back(): Promise<void> —— 触发 HarmonyOS 系统的返回操作。
    • agent.home(): Promise<void> —— 返回桌面。
    • agent.recentApps(): Promise<void> —— 打开多任务/最近应用界面。

    HarmonyOS 工厂函数和工具

    agentFromHdcDevice()

    从任意已连接的 HDC 设备创建 HarmonyAgent

    function agentFromHdcDevice(
      deviceId?: string,
      opts?: HarmonyAgentOpt & HarmonyDeviceOpt,
    ): Promise<HarmonyAgent>;
    • deviceId?: string —— 连接特定设备;留空表示使用"第一个可用设备"。
    • opts?: HarmonyAgentOpt & HarmonyDeviceOpt —— 在一个对象中合并 Agent 选项与 HarmonyDevice 的设置。
    import { agentFromHdcDevice } from '@midscene/harmony';
    
    const agent = await agentFromHdcDevice('0123456789ABCDEF'); // 传入 deviceId
    // 或者使用第一个可用设备:
    // const agent = await agentFromHdcDevice();

    getConnectedDevices()

    列举 Midscene 可驱动的 HDC 设备。

    function getConnectedDevices(
      hdcPath?: string,
    ): Promise<Array<{ deviceId: string }>>;
    import { getConnectedDevices } from '@midscene/harmony';
    
    const devices = await getConnectedDevices();
    console.log(devices); // [{ deviceId: '0123456789ABCDEF' }]

    相关阅读

    桌面端(@midscene/computer

    本页记录了 @midscene/computer 提供的 PC 桌面特定 API。

    有关适用于所有平台的通用 API,请参阅 通用 API 参考

    Agent 工厂函数

    agentForComputer(opts?): Promise<ComputerAgent>

    创建用于本机桌面自动化的 agent。

    向后兼容:agentFromComputer 仍可作为别名使用。

    agentForRDPComputer(opts): Promise<ComputerAgent<RDPDevice>>

    创建用于通过 RDP 控制远程 Windows 桌面的 agent。

    参数:

    interface BaseComputerAgentOpt {
      // Agent 选项(继承自 AgentOpt)
      aiActContext?: string;
      cache?: false | CacheConfig;
      // ... 其他 AgentOpt 属性
    
      customActions?: DeviceAction<any>[];
      keyboardTypeDelay?: number;
    }
    
    interface LocalComputerAgentOpt extends BaseComputerAgentOpt {
    
      // 本机桌面选项
      displayId?: string;
      keyboardDriver?: 'applescript' | 'libnut';
      headless?: boolean;
      xvfbResolution?: string;
    }
    
    interface RDPComputerAgentOpt extends BaseComputerAgentOpt {
      host: string;
      port?: number;
      username?: string;
      password?: string;
      domain?: string;
      localAddress?: string;
      adminSession?: boolean;
      ignoreCertificate?: boolean;
      securityProtocol?: 'auto' | 'tls' | 'nla' | 'rdp';
      desktopWidth?: number;
      desktopHeight?: number;
    }

    本机桌面选项

    • displayId(可选):指定要控制的显示器。使用 ComputerDevice.listDisplays() 获取可用显示器。
    • customActions(可选):添加额外的自定义动作,让 Agent 可以调用你定义的领域特定动作。
    • keyboardDriver?: 'applescript' | 'libnut':macOS 的键盘事件后端。'applescript' 是兼容性更好的默认选项;目标应用支持时,也可以使用输入速度更快的 'libnut'
    • headless(可选,仅 Linux):设为 true 时通过 Xvfb 启动虚拟显示器,使桌面自动化能在无物理显示器的 Linux 服务器和 CI 环境中运行。也可通过环境变量 MIDSCENE_COMPUTER_HEADLESS_LINUX=true 设置。
    • xvfbResolution(可选):Xvfb 虚拟显示器的分辨率,默认为 '1920x1080x24'

    键盘输入选项

    • keyboardTypeDelay(可选):每次按键之间的最小延迟,单位为毫秒。设为正数后,本机和 RDP Computer Agent 会逐个 Unicode 字符发送文本,不再使用默认的整串输入。本机 Computer 会发送真实按键事件;未设置或设为 0 时,仍使用剪贴板粘贴,以免受到当前输入法的干扰。通过 aiInput() 传入的动作级 keyboardTypeDelay 优先于 Agent 级默认值,也可以传 0,只对当前动作恢复剪贴板输入。

    Agent 级选项同样会作用于 agent.ai() 自动规划生成的 Input 动作,因此不要求用户单独调用 aiInput()

    const agent = await agentForComputer({ keyboardTypeDelay: 80 });
    
    await agent.ai('把账号信息输入表单');
    
    // 只覆盖这一次确定性输入,恢复整串粘贴。
    await agent.aiInput('备注输入框', {
      value: '整串粘贴的内容',
      keyboardTypeDelay: 0,
    });

    RDP 选项

    • host:远程 Windows 主机名或 IP。
    • port:RDP 端口,默认是 3389
    • username / password:远程会话凭据。
    • domain:可选的 Windows 域。
    • localAddress:RDP TCP 连接使用的本地源 IP。运行 Midscene 的机器有多条出站路由时使用。
    • adminSession:当服务端允许时,请求远程管理员会话。
    • ignoreCertificate:用于跳过自签名证书校验。
    • securityProtocol:可选 'auto''tls''nla''rdp'
    • desktopWidth / desktopHeight:请求指定远程桌面分辨率。
    示例:在无头 Linux CI 中测试 Electron 应用

    使用 @midscene/computer 在无头 Linux CI 中测试 Obsidian(Electron 应用)的完整示例:https://github.com/web-infra-dev/midscene-example/tree/main/computer/electron-demo

    示例:

    import { agentForComputer } from '@midscene/computer';
    
    // 连接到主显示器
    const agent = await agentForComputer({
      aiActContext: '你正在自动化一个桌面应用。',
    });
    
    // 连接到特定显示器
    const displays = await ComputerDevice.listDisplays();
    const agent2 = await agentForComputer({
      displayId: displays[1].id,
    });

    示例:通过 RDP 连接远程 Windows 桌面

    import { agentForRDPComputer } from '@midscene/computer';
    
    const agent = await agentForRDPComputer({
      aiActContext:
        'You are controlling a remote Windows desktop over the RDP protocol.',
      host: '10.75.166.249',
      port: 3389,
      username: 'Admin',
      password: 'replace-with-your-password',
      // 可选:把 TCP 连接绑定到这个本地源 IP。
      localAddress: '10.75.166.10',
      ignoreCertificate: true,
    });
    
    await agent.aiWaitFor('The remote Windows desktop is visible');
    await agent.aiAct('Click the Windows Start button');
    await agent.aiAct('Open Settings');
    示例:通过 RDP 控制远程 Windows 桌面

    一个可直接运行的 Demo:连接远程 Windows,打开「设置」并进入「Windows 更新」,最后输出一份结构化报告:https://github.com/web-infra-dev/midscene-example/tree/main/computer/rdp-demo

    当运行 Midscene 的机器存在多条出站路由,且 RDP 服务器要求从指定本地源 IP 访问时,可以使用 localAddress。这里传入的是 IP 地址,不是网卡名。

    ComputerDevice

    ComputerDevice.listDisplays(): Promise<DisplayInfo[]>

    列出所有可用显示器。

    返回:

    interface DisplayInfo {
      id: string;
      name: string;
      primary?: boolean;
    }

    示例:

    import { ComputerDevice } from '@midscene/computer';
    
    const displays = await ComputerDevice.listDisplays();
    console.log('可用显示器:', displays);
    // [
    //   { id: '0', name: '内置显示器', primary: true },
    //   { id: '1', name: '外接显示器', primary: false }
    // ]

    checkComputerEnvironment(): Promise<EnvironmentCheck>

    检查计算机环境是否正确配置。

    返回:

    interface EnvironmentCheck {
      available: boolean;
      error?: string;
      platform: string;
      displays: number;
    }

    示例:

    import { checkComputerEnvironment } from '@midscene/computer';
    
    const env = await checkComputerEnvironment();
    console.log('环境检查:', env);
    
    if (!env.available) {
      console.error('环境错误:', env.error);
    }

    ComputerDevice.destroy()

    function destroy(): Promise<void>;

    释放本机输入驱动。如果存在 Agent 创建的 Xvfb 实例,也会将它停止。该方法可以 重复调用。调用完成后,不能再使用这个 ComputerDevice 实例。

    RDPDevice.destroy()

    function destroy(): Promise<void>;

    断开 RDP backend,并清除连接状态。该方法可以重复调用。调用完成后,不能再使用 这个 RDPDevice 实例。

    调用 ComputerAgent.destroy() 时,会自动执行对应的 Device 方法。agentForComputer()agentForRDPComputer() 返回的 Agent 都遵循该行为。

    ComputerAgent

    ComputerAgent 类继承自 PageAgent<ComputerDevice>,并继承所有通用 agent 方法:

    • aiAct(action: string):使用 AI 执行操作
    • aiQuery(query: string):使用 AI 提取信息
    • aiAssert(assertion: string):使用 AI 断言条件
    • aiWaitFor(condition: string):等待条件
    • aiLocate(description: string):定位元素
    • 更多...

    定位元素后,还可以使用即时操作(Instant Action)进行直接、确定性的控制:

    • aiTap()aiDoubleClick()aiRightClick()aiHover():鼠标操作
    • aiInput()aiClearInput()aiKeyboardPress():键盘操作
    • aiScroll():滚动操作

    详见 通用 API 参考

    桌面端操作与限制

    ComputerDevice 支持以下操作:

    鼠标操作

    Tap(点击)

    在目标位置单击。

    await agent.aiAct('点击文件菜单');
    await agent.aiAct('点击屏幕中心');

    DoubleClick(双击)

    在目标位置双击。

    await agent.aiAct('双击桌面图标');

    RightClick(右键)

    右键点击打开上下文菜单。

    await agent.aiAct('右键点击桌面');
    await agent.aiAct('右键点击文件');

    MouseMove(移动鼠标 / 悬停)

    将鼠标移动到目标元素上,也就是鼠标悬停(hover),例如用于触发悬停菜单或提示框。

    // 自然语言形式(移动鼠标 / 悬停)
    await agent.aiAct('移动鼠标到菜单项');
    
    // 即时操作:一次调用完成定位并悬停
    await agent.aiHover('菜单项「Products」');

    DragAndDrop(拖放)

    从一个位置拖动并放到另一个位置。

    await agent.aiAct('将文件拖到文件夹');

    键盘操作

    KeyboardPress(按键)

    按键盘按键,可选修饰键。

    支持的按键:

    • 普通键:a-z0-9EnterEscapeSpaceTab
    • 方向键:ArrowUpArrowDownArrowLeftArrowRight
    • 功能键:F1-F12
    • 修饰键:Command/Cmd(macOS)、Control/CtrlAltShiftWin(Windows)
    • 媒体键:VolumeUpVolumeDownMute

    示例:

    // 简单按键
    await agent.aiAct('按 Enter');
    await agent.aiAct('按 Escape');
    
    // 组合键(平台特定)
    if (process.platform === 'darwin') {
      // macOS
      await agent.aiAct('按 Cmd+Space');  // 打开 Spotlight
      await agent.aiAct('按 Cmd+Tab');    // 应用切换器
      await agent.aiAct('按 Cmd+C');      // 复制
      await agent.aiAct('按 Cmd+V');      // 粘贴
    } else {
      // Windows/Linux
      await agent.aiAct('按 Windows 键'); // 开始菜单
      await agent.aiAct('按 Alt+Tab');    // 应用切换器
      await agent.aiAct('按 Ctrl+C');     // 复制
      await agent.aiAct('按 Ctrl+V');     // 粘贴
    }
    
    // 方向键
    await agent.aiAct('按 ArrowDown');
    await agent.aiAct('按 ArrowUp');
    
    // 功能键
    await agent.aiAct('按 F5');  // 刷新

    Input(输入)

    在输入框中输入文本。

    await agent.aiAct('在搜索框输入 "你好世界"');
    await agent.aiAct('输入 "我的文档.txt"');

    ClearInput(清空输入)

    清空输入框内容。

    await agent.aiAct('清空文本框');

    滚动操作

    滚动屏幕或特定区域。

    // 滚动方向
    await agent.aiAct('向下滚动');
    await agent.aiAct('向上滚动');
    await agent.aiAct('向左滚动');
    await agent.aiAct('向右滚动');
    
    // 滚动到位置
    await agent.aiAct('滚动到顶部');
    await agent.aiAct('滚动到底部');

    显示器操作

    ListDisplays(列出显示器)

    获取所有已连接显示器的信息。

    const displays = await ComputerDevice.listDisplays();

    使用 RDP 时,ListDisplays 会把当前远程会话视为单个显示器返回。

    运行时配置

    以下环境变量控制全局运行时行为,而不是模型请求。Agent 的 modelConfig 对象不支持这些参数。

    名称类型默认值说明
    MIDSCENE_RUN_DIR字符串midscene_run报告、日志、模型调用记录和其他运行产物的保存目录。支持绝对路径,也支持相对于当前工作目录的路径。
    MIDSCENE_PREFERRED_LANGUAGE字符串Asia/Shanghai 时区为 Chinese,其他时区为 English模型响应的首选语言。
    MIDSCENE_PLAYGROUND_HOST字符串127.0.0.1Playground 服务器监听的网络接口。远程设备、虚拟机、容器或其他计算机需要连接时,请设为对方可访问的接口地址。
    DEBUG字符串未设置启用额外的 Debug 日志命名空间。支持的选择器请参考 Debug 日志

    设置 MIDSCENE_PLAYGROUND_HOST=0.0.0.0 会监听所有网络接口。请仅在可信网络中使用该值。

    Debug 日志

    DEBUG 设置为以下选择器之一:

    取值描述
    midscene:ai:profile:stats使用逗号分隔的格式打印模型延迟和 Token 使用量。
    midscene:ai:profile:detail打印详细的 Token 使用量日志。
    midscene:ai:call打印 AI 响应详情。
    midscene:android:adb打印 Android ADB 命令调用详情。
    midscene:*打印全部 Midscene Debug 日志。

    即使未设置 DEBUG,Midscene 也会将日志保存在 <MIDSCENE_RUN_DIR>/log 目录中。Debug 日志可能包含模型输入、输出或截图。分享前请先检查内容,也不要将日志提交到源码仓库。

    模型连接检查、调用记录和 Tracing 集成请参考模型调试与可观测性

    另请参阅