• 简体中文
  • 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

    共享 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 与 模型配置 文档中说明的内容完全一致。你也可以参考 模型策略 中的说明。

    自定义 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 执行规划时的具体实现。开启后,aiAct 会更注重任务拆解,并将任务 Planning 和 UI 元素定位拆解为不同的模型调用。为了兼容旧写法,'unset' 仍然可以传入,并会被按 false 处理。详情参阅 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 });
    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,
      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 - 用自然语言描述的元素定位,或使用图片作为提示词
      • opt: object - 配置对象,包含:
        • keyName: string - 必填,要按下的键,如 EnterTabEscape 等。Web 和 Computer Device 还支持 modifier shortcut,如 Control+AShift+Enter;使用 + 连接按键。内置的 Android、iOS 和 Harmony Device 仅支持单键;自定义 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('Enter', '搜索框');
    关于签名变更

    我们最近更新了 aiKeyboardPress 的 API 签名,将定位提示作为第一个参数,使得参数顺序更直观。旧的签名 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 从一开始就表示规划模式,用于引导任务拆解和专注规划的思考过程。详情请参考 aiAct 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[] —— 借助 defineAction 注册自定义动作,让规划器可以调用领域特定步骤。

    使用说明

    :::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[] —— 追加项目特有的动作,供规划器调用。

    使用说明

    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 命令。
    • 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 特有选项

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

    使用说明

    Info

    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 选项,不属于 IOSAgent 构造参数。agentFromWebDriverAgent() 可以接收它,是因为这个工厂函数在同一个对象中同时接收 IOSAgentOptIOSDeviceOpt
    • 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[] —— 通过 defineAction 扩展规划器的可用动作。

    使用说明

    • 可以使用 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 特有选项

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

    使用说明

    Info

    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(可选):向设备添加自定义操作。
    • 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 会把当前远程会话视为单个显示器返回。

    另请参阅