import { HttpClient } from './client'; import { Element, ImageElement, UiaElement } from './element'; import { parseXpathMarker } from './xpath-marker'; import { WindowSelector, WindowInfo, ClickOptions, ClickArea, ClickOffset, TypeOptions, MoveOptions, IdleOptions, ScrollOptions, ScrollToVisibleOptions, ScrollToVisibleResult, ScrollDetectResult, Rect, ProfileStats, AutoWaitConfig, ElementList, InspectResponse, FindOptions, FindImageOptions, FindImageMatch, ImageClickOptions, AccelConfig, FlashOptions, HumanMonitorStartOptions } from './types'; import { buildWindowSelector, getRectRandomPoint, randomInt, randomFloat, getRectCenter, xpathStr, sleep } from './utils'; import { OperationLogger } from './logger'; import { Template } from './image-template'; import { computeImageClickPoint, resolveOffsetPoint, resolveAreaValue, computeRectClickPoint } from './image-click'; import { delay, setSpeedFactor, getSpeedFactor } from './sleep'; /** * Flow 类 - 管理自动化流程的上下文和操作 * * 提供窗口管理、元素查找、全局操作等功能。 * * @example * const flow = sdk.flow(); * await flow.window({ title: 'App' }); * const button = await flow.find('//Button'); * await button.click(); */ export declare class Flow { private client; private windowSelector; private _currentWindowInfo; /** 当前窗口信息(由 window() 设置) */ get currentWindowInfo(): WindowInfo | null; private screenshotManager; private autoWaitConfig; private logger; private defaultIdleOptions; private imagePrecision; private scrollToVisibleConfig; private humanMonitorConfig; private idleStack; private currentIdleXpath; private _imagePositionCache; /** 最近一次 findImageMatch 的 bestScore(0 命中时后端返回的诊断分),findImageOneAccel 读它构造错误消息 */ private _lastFindImageBestScore; private profileEnabled; private profileStartTime; private profileOperations; private humanEventSource; private humanActive; private humanWaiters; /** 上一次状态翻转时间,用于打"暂停 Xms"日志 */ private humanPauseStart; constructor(client: HttpClient, autoWaitConfig: AutoWaitConfig, logger: OperationLogger, defaultIdleOptions?: IdleOptions, // idle 默认配置,可选 imagePrecision?: number, scrollToVisibleConfig?: ScrollToVisibleOptions, humanMonitorConfig?: HumanMonitorStartOptions); /** * 工具函数集(按用途分组) * * ### 时间 / 延迟 * - `sleep(ms)` 纯延迟(Promise) * - `delay(ms)` 受 speedFactor 控制的延迟(推荐) * - `setSpeedFactor(f)` / `getSpeedFactor()` 全局速度因子 * * ### 随机 * - `randomInt(min, max)` / `randomFloat(min, max)` * - `getRectRandomPoint(rect, range)` 矩形内随机点 * - `getRectCenter(rect)` 矩形几何中心 * * ### 坐标 / ClickArea * - `computeRectClickPoint(rect, area)` 用 ClickArea 算矩形点击点 * - `computeImageClickPoint(match, area)` 用 ClickArea 算图像命中点击点 * - `resolveOffsetPoint(match, offset)` 解析 ClickOffset 表达式为坐标 * - `resolveAreaValue(v, total)` 把 ClickAreaValue(0.3 / "30%" / "10px")解析为像素 * * ### XPath 字符串 * - `xpathStr(s)` 字符串转 XPath 字面量(防引号注入) * - `buildWindowSelector(sel)` 构造 Window XPath * - `parseXpathMarker(xpath)` 解析 `:all` / `:onlyone` 后缀 * * @example * ```typescript * const pt = flow.utils.getRectRandomPoint({ x: 100, y: 200, width: 50, height: 30 }); * const c = flow.utils.getRectCenter({ x: 100, y: 200, width: 50, height: 30 }); * const cx = flow.utils.computeRectClickPoint(rect, { left: '50%' }); * ``` */ get utils(): { sleep: typeof sleep; delay: typeof delay; setSpeedFactor: typeof setSpeedFactor; getSpeedFactor: typeof getSpeedFactor; randomInt: typeof randomInt; randomFloat: typeof randomFloat; getRectRandomPoint: typeof getRectRandomPoint; getRectCenter: typeof getRectCenter; computeImageClickPoint: typeof computeImageClickPoint; resolveOffsetPoint: typeof resolveOffsetPoint; resolveAreaValue: typeof resolveAreaValue; computeRectClickPoint: typeof computeRectClickPoint; xpathStr: typeof xpathStr; buildWindowSelector: typeof buildWindowSelector; parseXpathMarker: typeof parseXpathMarker; }; /** * * @param ms 待睡眠的毫秒数 * @returns */ sleep(ms: number): Promise; /** * 列出窗口信息。 * @param selector 可选的窗口选择条件(对象),无参时返回所有窗口 * @returns 匹配的窗口信息列表 */ listWindows(selector?: WindowSelector): Promise; existsWindow(selector: string | WindowSelector): Promise; /** * 激活指定窗口并设置为当前上下文。 * 激活后可通过 find() 等方法在此窗口中查找元素。 * @param selector 窗口选择器:字符串 / WindowSelector / WindowInfo(来自 listWindows) * @returns 窗口信息(title, className, processId, processName) */ window(selector: string | WindowSelector | WindowInfo): Promise; /** * 仅激活指定窗口,不改变当前上下文(windowSelector 不变)。 * * 与 window() 的区别: * - window() — 激活窗口 + 设为当前上下文(后续 find() 在此窗口查找) * - activate() — 仅激活窗口,不改变上下文(后续操作仍在原窗口) * * @example * // 切换到窗口A并查找元素 * await flow.window({ title: '窗口A' }); * const btn = await flow.find('//Button'); * * // 仅激活窗口B(不改变上下文),查看信息后回到窗口A继续操作 * await flow.activate({ title: '窗口B' }); * await sleep(2000); * await btn.click(); // 仍在窗口A操作 */ activate(selector: string | WindowSelector): Promise; /** * 查找唯一匹配的元素(匹配多个时报错) * * 如果 XPath 匹配到多个元素,抛出错误。适用于需要精确操作的场景。 */ /** * 纯 UIA 查找,findOne 语义(匹配多个时报错) */ findElementOne(xpath: string, options?: FindOptions): Promise; /** * 纯 UIA 查找,findFirst 语义(多返回第一个) */ findElementFirst(xpath: string, options?: FindOptions): Promise; /** * 纯 UIA 查找,findAll 语义 */ findElementAll(xpath: string, options?: FindOptions): Promise; /** * 图像匹配后构造 ImageElement(内部工具)。 * 如果模板有 cropOffset,命中坐标需加上偏移还原到原图坐标。 */ private constructImageElement; /** * 纯图像匹配(accel 内部路径)——返回 Element 以保持 accel 兼容。 * @internal */ private findImageOneAccel; /** * UIA 首次查找 + 自动截图缓存模板(为下次 findImageOne 加速)。 * 有 mask 时,裁剪后保存裁剪图 + cropOffset 到 meta.json。 */ private findElementAndCache; /** * 路由层:按 accel 选项分派到 findElement* 或 findImage*。 * 无 accel → 逻辑等同 findElementOne。 * 有 accel → 检查模板:存在走 findImageOne,不存在走 findElementAndCache。 */ findOne(xpath: string, options?: FindOptions): Promise; /** * 路由层:findFirst。逻辑同 findOne,无 accel 时等同 findElementFirst。 */ findFirst(xpath: string, options?: FindOptions): Promise; /** * find 的别名(findFirst 语义)。 */ find(xpath: string, options?: FindOptions): Promise; /** * findAll。图像不支持多元素匹配,始终走 UIA。 */ findAll(xpath: string, options?: FindOptions): Promise; /** * 统一入口:根据 xpath 末尾标记分派到 findElementAll / findElementOne / findElementFirst。 * * - `//Button` → findElementFirst(默认) * - `//Button:all` → findElementAll * - `//Button:onlyone` → findElementOne */ findElement(xpath: string, options?: FindOptions): Promise; /** * 查找第 N 个匹配的元素(1-based,与 XPath position() 一致)。 * * 等价于 XPath 的 `(//Text)[2]`,但由 SDK 正确处理括号拼接, * 避免手写 `(//Text)[2]` 导致括号被破坏的问题。 * * @param xpath - XPath 表达式 * @param n - 位置索引(1-based,1=第 1 个,2=第 2 个) * @returns 第 N 个匹配的元素 * * @example * await flow.nth('//Text', 1); // 窗口中第 1 个 Text * await flow.nth('//Button', 3); // 窗口中第 3 个 Button */ nth(xpath: string, n: number): Promise; /** 返回空的 ElementList(带 position 方法) */ private emptyElementList; /** * 等待元素出现。复用 findOne 路由(支持 accel 图像加速)。 */ waitFor(xpath: string, options?: FindOptions & { timeout?: number; interval?: number; }): Promise; /** * 等待元素消失。复用 findOne 路由(支持 accel 图像加速)。 */ waitUntilGone(xpath: string, options?: FindOptions & { timeout?: number; interval?: number; }): Promise; /** * 检测元素是否存在(快照,不轮询)。复用 findOne 路由(支持 accel 图像加速)。 * @returns boolean — 存在返回 true,不存在返回 false */ exists(xpath: string, options?: FindOptions): Promise; /** * 遍历指定元素下的所有子元素,提取层级/控件类型/name/Text/rect/相对xpath。 * * 适合调试和元素结构分析:快速了解元素树的完整结构。 * * @param xpath - 目标元素 XPath * @param options - inspect 选项 * @param options.format - 返回格式:'json'(默认)返回结构化树,'txt' 返回缩进文本 * * @returns InspectResponse,包含 nodes(结构化树)或 text(格式化文本) * * @example * // JSON 格式(默认) * const result = await flow.inspect('/Window/Pane[@Name="content"]'); * console.log(result.nodes); // InspectNodeInfo 树 * console.log(result.totalChildren); // 子元素总数 * * // 文本格式 * const result = await flow.inspect('/Window/Pane', { format: 'txt' }); * console.log(result.text); // 缩进展示的元素树 */ inspect(xpath: string, options?: { format?: 'json' | 'txt'; visibleOnly?: boolean; regionFilter?: import('./types').InspectRegionFilter; }): Promise; /** * 滚动使目标元素可见——一步到位的统一滚动 API。 * * 自动处理三种场景: * 1. 目标已可见 → 直接返回 * 2. 目标已存在但 offscreen → 自动检测方向,委托 element.scrollToVisible 精调 * 3. 目标不存在 → 先在容器上按 direction 滚动直到出现,再 scrollToVisible 精调 * * @param xpath - 目标元素 XPath * @param containerXpath - 滚动容器 XPath(省略时默认与 xpath 相同) * @param options - 滚动选项 * @param options.direction - 目标不存在时的滚动方向:'up' 或 'down',默认 'down' * @param options.timeout - 总超时(ms),默认 60000 * @param options.scrollTimes - 最大滚动次数,默认 100 * @param options.autoScrollAmount - 是否自动调整滚动量,默认 true * @param options.scrollAmountRatio - 容器高度倍率(0-1),默认 0.8 * @param options.scrollInterval - 每次滚动后的等待时间(ms),默认 1000 * @param options.scrollToCenter - 是否滚动到视口中心,默认 true * @param options.centerAdjustTimes - scrollToCenter 最大调整次数,默认 5 * * @returns ScrollToVisibleResult - 包含 visible、scrolledToEnd、scrolled、targetRect 字段 * * @example * // 最简用法:一步滚动到可见 * const result = await flow.scrollToVisible(`/Document/Text[@Name='写留言']`, `/Document`); * if (!result.visible && result.scrolledToEnd) { * // 滚动到底了,可以尝试反方向 * } * * // 向上滚动找目标 * const result = await flow.scrollToVisible(target, container, { direction: 'up' }); * * // 图像加速:首次 UIA + 自动缓存,后续纯图像匹配 * const result = await flow.scrollToVisible(target, container, { accel: true }); */ scrollToVisible(xpath: string, containerXpath?: string, options?: ScrollToVisibleOptions): Promise; /** * 纯 UIA 滚动到可见(scrollToVisible 核心逻辑)。 */ scrollElementVisible(xpath: string, containerXpath?: string, options?: ScrollToVisibleOptions): Promise; /** * 纯图像滚动到可见:调用 server 端原生 scroll-find API。 * 滚动 + 截图 + 匹配 在 server 内存中完成,单次 HTTP 调用,~30fps。 * * @param template 模板(路径 / base64 / Buffer) * @param containerXpath 滚动容器 XPath * @param options 滚动选项 */ scrollImageVisible(template: Template, containerXpath: string, options?: ScrollToVisibleOptions): Promise; /** * accel 内部路径:从 tplPath 读模板调用 scrollImageVisible。 * @internal */ private scrollImageVisibleAccel; /** * 截图采样变化率检测(scrollDetectByImage)。 * 截取容器底部区域,滚动一次,对比前后变化率。 */ scrollDetectByImage(container: string, options?: { sampleRatio?: number; threshold?: number; direction?: 'up' | 'down'; scrollDelayMs?: number; rollback?: boolean; }): Promise; /** * 条件等待 */ waitUntil(condition: () => Promise, options?: { timeout?: number; interval?: number; }): Promise; /** * 全局输入文本(不针对特定元素) */ type(text: string, xpath?: string, options?: TypeOptions): Promise; /** * 按下按键 */ pressKey(key: string): Promise; /** * 执行快捷键组合(推荐方法名) * @param keys 快捷键字符串,如 "Ctrl+C", "Alt+F4" */ shortcut(keys: string): Promise; /** * 移动鼠标到指定坐标 */ moveTo(x: number, y: number, options?: MoveOptions): Promise; /** * 在指定屏幕坐标点击(移动 + 点击一步完成) * * @param x 屏幕 X 坐标 * @param y 屏幕 Y 坐标 * @param options 点击选项 * * @example * await flow.clickAt(500, 300); // 左键点击 * await flow.clickAt(500, 300, { button: 'right' }); // 右键点击 * await flow.clickAt(500, 300, { humanize: false }); // 直线移动 */ clickAt(x: number, y: number, options?: { humanize?: boolean; duration?: number; button?: 'left' | 'right'; pauseBefore?: number; pauseAfter?: number; }): Promise; /** * 在指定矩形区域内点击,支持用 ClickArea(inset 模型)指定点击位置。 * * 语义与 `flow.click(xpath, { clickArea })` 一致:先用 ClickArea 从矩形 * 各边内缩/外扩出子矩形,再在该子矩形中心(或随机点)点击。适合已知矩形 * (元素 / 图像 / 自定义计算)但需要 ClickArea 语义的场景。 * * @param rect 矩形区域(x/y 为左上角,含 width/height) * @param options.clickArea 点击区域限制(inset 模型) * @param options.offset 点击偏移表达式(优先级高于 clickArea) * @param options.randomRange 在点击点周围随机抖动的范围因子,0~1,默认 0(不抖动) * @param options.humanize 是否拟人化移动 * @param options.button 鼠标按键,默认 'left' * * @example * await flow.clickAtRect(rect); // 矩形中心 * await flow.clickAtRect(rect, { clickArea: { left: '50%' } }); // 右半区中心 * await flow.clickAtRect(rect, { offset: 'top+10px' }); // 顶边内移 10px * await flow.clickAtRect(rect, { clickArea: { left: '50%' }, randomRange: 0.2 }); */ clickAtRect(rect: Rect, options?: { clickArea?: ClickArea; offset?: ClickOffset; randomRange?: number; humanize?: boolean; duration?: number; button?: 'left' | 'right'; pauseBefore?: number; pauseAfter?: number; }): Promise; /** * 查找元素并点击 */ click(xpath: string, options?: ClickOptions): Promise; /** * 查找元素并高亮闪烁(不点击,用于调试定位) * * @param xpath 元素 XPath * @param options.timeout 闪烁持续时间(ms),默认 1000 * @param options.accel 是否启用图像加速查找 */ flash(xpath: string, options?: FlashOptions & { accel?: boolean; }): Promise; /** * 查找元素并双击 */ doubleClick(xpath: string, options?: ClickOptions): Promise; /** * 查找元素并右键点击 */ rightClick(xpath: string, options?: ClickOptions): Promise; /** * 查找元素并点击上方 * * @param xpath 元素 XPath * @param distance 距离(默认 5px)。支持:数字(像素)、"10px"、"20%" * - 百分比基准为元素**高度** * @param options 透传 ClickOptions */ clickAbove(xpath: string, distance?: number | string, options?: ClickOptions): Promise; /** * 查找元素并点击下方 * * @param xpath 元素 XPath * @param distance 距离(默认 5px)。支持:数字(像素)、"10px"、"20%" * - 百分比基准为元素**高度** * @param options 透传 ClickOptions */ clickBelow(xpath: string, distance?: number | string, options?: ClickOptions): Promise; /** * 查找元素并点击左侧 * * @param xpath 元素 XPath * @param distance 距离(默认 5px)。支持:数字(像素)、"10px"、"20%" * - 百分比基准为元素**宽度** * @param options 透传 ClickOptions */ clickLeft(xpath: string, distance?: number | string, options?: ClickOptions): Promise; /** * 查找元素并点击右侧 * * @param xpath 元素 XPath * @param distance 距离(默认 5px)。支持:数字(像素)、"10px"、"20%" * - 百分比基准为元素**宽度** * @param options 透传 ClickOptions */ clickRight(xpath: string, distance?: number | string, options?: ClickOptions): Promise; /** * 查找元素并聚焦 */ focus(xpath: string, options?: ClickOptions): Promise; /** * 查找元素、清空内容后输入新文本 */ setValue(xpath: string, text: string, options?: TypeOptions & { accel?: AccelConfig; }): Promise; /** * 纯 UIA 查找元素并点击。 * 不走 accel 图像加速,直接通过 UIA 定位。 */ clickElement(xpath: string, options?: ClickOptions): Promise; /** 纯 UIA 查找元素并双击。 */ doubleClickElement(xpath: string, options?: ClickOptions): Promise; /** 纯 UIA 查找元素并右键点击。 */ rightClickElement(xpath: string, options?: ClickOptions): Promise; /** 纯 UIA 查找元素并点击上方。 */ clickAboveElement(xpath: string, distance?: number | string, options?: ClickOptions): Promise; /** 纯 UIA 查找元素并点击下方。 */ clickBelowElement(xpath: string, distance?: number | string, options?: ClickOptions): Promise; /** 纯 UIA 查找元素并点击左侧。 */ clickLeftElement(xpath: string, distance?: number | string, options?: ClickOptions): Promise; /** 纯 UIA 查找元素并点击右侧。 */ clickRightElement(xpath: string, distance?: number | string, options?: ClickOptions): Promise; /** 纯 UIA 查找元素并聚焦。 */ focusElement(xpath: string, options?: ClickOptions): Promise; /** 纯 UIA 查找元素、清空后输入文本。 */ setValueElement(xpath: string, text: string, options?: TypeOptions): Promise; /** 纯 UIA 查找元素并输入文本。 */ typeElement(text: string, xpath: string, options?: TypeOptions): Promise; /** 纯 UIA 等待元素出现。 */ waitForElement(xpath: string, options?: FindOptions & { timeout?: number; interval?: number; }): Promise; /** 纯 UIA 等待元素消失。 */ waitUntilGoneElement(xpath: string, options?: FindOptions & { timeout?: number; interval?: number; }): Promise; /** 纯 UIA 检查元素是否存在。 */ existsElement(xpath: string, options?: FindOptions): Promise; /** * 纯图像查找并点击。 * 通过模板匹配定位元素,使用坐标点击。 * @returns void(命令-查询分离:如需匹配详情请用 findImageFirst) */ clickImage(template: Template, options?: FindImageOptions & ImageClickOptions): Promise; /** 纯图像查找并双击。 */ doubleClickImage(template: Template, options?: FindImageOptions & ImageClickOptions): Promise; /** 纯图像查找并右键点击。 */ rightClickImage(template: Template, options?: FindImageOptions & ImageClickOptions): Promise; /** 纯图像查找并点击上方。 */ clickAboveImage(template: Template, distance?: number | string, options?: FindImageOptions & ImageClickOptions): Promise; /** 纯图像查找并点击下方。 */ clickBelowImage(template: Template, distance?: number | string, options?: FindImageOptions & ImageClickOptions): Promise; /** 纯图像查找并点击左侧。 */ clickLeftImage(template: Template, distance?: number | string, options?: FindImageOptions & ImageClickOptions): Promise; /** 纯图像查找并点击右侧。 */ clickRightImage(template: Template, distance?: number | string, options?: FindImageOptions & ImageClickOptions): Promise; /** 纯图像查找并聚焦。 */ focusImage(template: Template, options?: FindImageOptions): Promise; /** 纯图像查找、清空后输入文本。 */ setValueImage(template: Template, text: string, options?: TypeOptions & FindImageOptions): Promise; /** 纯图像查找并输入文本。 */ typeImage(text: string, template: Template, options?: TypeOptions & FindImageOptions): Promise; /** * 纯图像等待匹配出现。 * @returns ImageElement(匹配到的元素,可继续操作) */ waitForImage(template: Template, options?: FindImageOptions & { timeout?: number; interval?: number; }): Promise; /** 纯图像等待匹配消失。 */ waitUntilGoneImage(template: Template, options?: FindImageOptions & { timeout?: number; interval?: number; }): Promise; /** @deprecated 使用 {@link waitUntilGoneImage} 代替 */ waitUntilImageGone(template: Template, options?: FindImageOptions & { timeout?: number; interval?: number; }): Promise; /** * 在指定元素上执行纯滚动(鼠标悬停 + 滚轮)。 * * 单次 HTTP 调用 `client.scrollMouse`,后端原生支持多次滚动 + 间隔。 * 仅负责滚动,不等待元素、不自适应。"滚到目标可见" 请用 scrollIntoView。 * * @param xpath - 鼠标悬停的元素 XPath(滚动发生在此元素上) * @param options - 滚动选项 * @param options.direction - 滚动方向:'up'(视口上移看上方)或 'down'(视口下移看下方) * @param options.amount - 总滚动量(WHEEL_DELTA 单位,120=1次滚轮),内部按 120/次拆分。优先于 times * @param options.times - 滚动次数,默认 1。amount 优先时忽略 * @param options.scrollInterval - 每次滚动间隔(ms),默认 300 * @param options.useIdle - 是否启用 pushIdle/popIdle,默认 false * * @returns 滚动结果 { scrolled: 实际滚动次数 } * * @example * await flow.scroll('/Window/Pane', { direction: 'down', amount: 1000 }); * await flow.scroll('/Window/Pane', { direction: 'up', times: 3, useIdle: true }); */ scroll(xpath: string, options: ScrollOptions): Promise<{ scrolled: number; }>; /** * 滚动边界检测:滚动一次,检测是否到底/到顶 * * 使用 UIA 原生 PropertyCondition 查询容器内指定 ControlType 的可见元素, * 比较滚动前后元素的 bound.top 变化来判断是否到达边界。 * 排除 exclude 列表后,任一元素的 bound.top 变化 > 2px → 没到底;全部不变 → 到底。 * * @param container - 滚动容器的 XPath(鼠标移到此元素中心执行滚动) * @param options - 检测选项 * @param options.controlTypes - 要监控的 ControlType 名称列表,默认 ['Text']。支持: 'Text', 'Image', 'ListItem', 'DataItem' 等。传空数组则监控所有可见元素 * @param options.direction - 滚动方向:"down"=向下滚(检测到底),"up"=向上滚(检测到顶),默认 "down" * @param options.exclude - 排除的元素 XPath 列表(如悬浮工具栏,它们随滚动自动移位,会干扰判断) * @param options.rollback - 检测后是否反向滚动恢复位置,默认 false * @param options.scrollDelayMs - 滚动后等待 UI 响应时间(ms),默认 500。某些应用有滚动动画,需等待 BoundingRectangle 更新 * * @returns ScrollDetectResult - 包含 atEnd(是否到底/到顶)等信息 * * @example * // 检测是否滚到底部(默认监控 Text 元素) * const result = await flow.scrollDetect('/Document'); * if (result.atEnd) { * console.log('已到底部'); * } * * // 监控 Text + Image 元素 * const result = await flow.scrollDetect('/Document', { controlTypes: ['Text', 'Image'] }); * * // 检测是否滚到顶部 * const result = await flow.scrollDetect('/Document', { direction: 'up' }); * * // 排除悬浮工具栏 * const result = await flow.scrollDetect('/Document', { * exclude: ['/Document//ToolBar'] * }); * * // 检测后回滚(不改变位置) * const result = await flow.scrollDetect('/Document', { rollback: true }); */ scrollDetect(container: string, options?: { controlTypes?: string[]; direction?: 'up' | 'down'; exclude?: string[]; rollback?: boolean; scrollDelayMs?: number; sampleRatio?: number; threshold?: number; accel?: AccelConfig; }): Promise; /** * 纯 UIA 滚动检测(scrollDetect 核心逻辑)。 */ scrollDetectByElement(container: string, options?: { controlTypes?: string[]; direction?: 'up' | 'down'; exclude?: string[]; rollback?: boolean; scrollDelayMs?: number; }): Promise; /** * 获取容器的 rect */ private _getContainerRect; /** * 截图 */ screenshot(path?: string): Promise; /** * 自动命名截图 */ screenshotAuto(): Promise; /** * 开始性能分析 */ startProfile(): void; /** * 停止性能分析并返回统计 */ stopProfile(): ProfileStats; /** * 启动空闲移动 * 如果已有 idle 在运行,直接替换(不入栈)。 */ idle(xpath: string, options?: IdleOptions): Promise; /** * 启动空闲移动并入栈 * 如果已有 idle 在运行,当前 xpath 自动入栈保存,然后替换为新的。 * 只有通过 pushIdle 入栈的 idle,才能使用 popIdle 回退。 */ pushIdle(xpath: string, options?: IdleOptions): Promise; /** * 停止空闲移动,并清空所有栈 */ stopIdle(): Promise; /** * 回退到上一个 idle 区域 * 停止当前 idle,弹出栈顶 xpath 并重新启动。 * 如果栈为空,则停止当前 idle。 */ popIdle(): Promise; /** * 启动人介入监控。 * * 1. POST /api/human-monitor/start 启动 server 端后台检测任务 * 2. 用全局 EventSource 订阅 /api/human-monitor/events SSE 流 * 3. onmessage 维护本地 humanActive 缓存,状态翻转时打日志并唤醒检查点 * * 连接建立时 server 立即推一次当前状态,据此初始化本地缓存。 * EventSource 断线自动重连,重连后 server 再次推当前状态校准。 * * @param options 监控选项(idleThreshold / pollInterval) */ startHumanMonitor(options?: HumanMonitorStartOptions): Promise; /** * 停止人介入监控。 * * 关闭 SSE 连接 + POST /api/human-monitor/stop 停止 server 检测。 * 唤醒所有等待中的检查点(避免死等)。 */ stopHumanMonitor(): Promise; /** * 注册 SIGINT/SIGTERM 优雅退出处理。 * * Ctrl+C 时自动:注销 preRequestHook → stopIdle → stopHumanMonitor → exit。 * 应用层只需在 `startHumanMonitor` 之后调一次 `flow.enableGracefulShutdown()`。 * * - 如果未启动监控或 idle,对应 stop 调用会被安全跳过 * - preRequestHook 先注销,避免 stopIdle 的 HTTP 请求被 humanCheckpoint 阻塞 */ enableGracefulShutdown(): void; /** * 读取本地人介入状态缓存(不发 HTTP)。 * @returns `{ monitoring, humanActive }` */ getHumanStatus(): { monitoring: boolean; humanActive: boolean; }; /** * 人介入检查点 —— 在自动化流程关键步骤间插入。 * * - 若未启动监控(`!humanEventSource`)→ 立即返回,无副作用 * - 若本地状态 `humanActive=false` → 立即返回(无人介入) * - 若 `humanActive=true` → `await Promise` 挂起等待,由 SSE onmessage * 收到 `humanActive=false` 事件时唤醒。**纯事件驱动,零轮询,零 CPU**。 * * @param options.maxWaitMs 最大等待毫秒数,0=无限等待(默认 0)。 * 超时后强制放行,避免死等。 * * @example * // 主循环每轮开头 * await flow.humanCheckpoint(); * // 点击前 * await flow.humanCheckpoint(); * await flow.click('//Button'); */ humanCheckpoint(options?: { maxWaitMs?: number; }): Promise; /** * 解析窗口选择器字符串为对象 * 支持两种格式: * 1. XPath 格式: "Window[@Name='xxx' and @ClassName='yyy']" * 2. 简单格式: "title:xxx className:yyy processName:zzz" */ private parseWindowSelector; /** * 自动等待(根据配置) */ private maybeAutoWait; /** * 截取指定屏幕区域,返回 base64 PNG */ captureScreenshot(region: { x: number; y: number; width: number; height: number; }): Promise; /** * 截取全屏,返回 base64 PNG */ captureDesktopScreenshot(): Promise; /** * 把 FindImageOptions.region 解析为屏幕坐标矩形。 * * - `'window'` 或省略 → 当前窗口矩形(须先 flow.window()) * - `'element'` → 通过 scrollContainer XPath 查找元素矩形 * - 显式 `Rect` → 直接返回 */ private resolveImageRegion; /** * 在**当前窗口区域**内查找匹配的图像位置。 * * 默认作用域是 `flow.window()` 设置的当前窗口矩形。如果没有调用过 * `flow.window(...)` 则抛 `StateError`——绝不静默 fallback 到全屏。 * 全屏查找请显式使用 {@link findImageOnDesktop}。 * * @param template 模板图像:base64 字符串 / 文件路径 / Buffer * @param options 匹配选项;`region` 可设为 `'window'`(默认)或显式 `Rect` * @returns 命中数组(按算法返回顺序) */ findImageMatch(template: Template, options?: FindImageOptions): Promise; private _imageCacheKey; /** 把归一化坐标扩展为以该点为中心、2× 模板宽高的子区域 Rect */ private _expandPositionCache; private _updatePositionCache; /** * 等价于 {@link findImageMatch},语义强调"返回所有命中"。 */ findAllImageMatches(template: Template, options?: FindImageOptions): Promise; /** @deprecated 使用 {@link findImageMatch} 代替 */ findImage(template: Template, options?: FindImageOptions): Promise; /** @deprecated 使用 {@link findAllImageMatches} 代替 */ findAllImages(template: Template, options?: FindImageOptions): Promise; /** * 串行尝试一组模板,返回第一个有命中的(含模板索引)。 */ findImageAny(templates: Template[], options?: FindImageOptions): Promise<{ match: FindImageMatch; index: number; template: Template; }>; /** @deprecated 使用 {@link findImageAny} 代替 */ findFirstImage(templates: Template[], options?: FindImageOptions): Promise<{ match: FindImageMatch; index: number; template: Template; }>; /** * 纯图像匹配,findOne 语义(期望唯一匹配,多个则抛错)。 * * @param template 模板(路径 / base64 / Buffer) * @returns ImageElement(继承 Element,支持 click/hover/type 等全部操作) * @throws ElementNotFoundError 未匹配 * @throws Error 匹配多个 */ findImageOne(template: Template, options?: FindImageOptions): Promise; /** * 纯图像匹配,findFirst 语义(取第一个匹配)。 * * @param template 模板(路径 / base64 / Buffer) * @returns ImageElement * @throws ElementNotFoundError 未匹配 */ findImageFirst(template: Template, options?: FindImageOptions): Promise; /** * 纯图像匹配,findAll 语义(返回所有匹配)。 * * @param template 模板(路径 / base64 / Buffer) * @returns ImageElement[] */ findImageAll(template: Template, options?: FindImageOptions): Promise; /** * 当前窗口区域内是否存在匹配图像。`StateError` 仍会向上抛出。 */ existsImage(template: Template, options?: FindImageOptions): Promise; /** * 在**全屏**范围内查找匹配的图像位置。可通过 `region` 限定子矩形。 * * 与 {@link findImage} 不同,本方法不依赖 `flow.window()`。 */ findImageOnDesktop(template: Template, options?: { precision?: number; algorithm?: 'segmented' | 'fft'; region?: Rect; }): Promise; /** * 全屏查找图像并点击命中位置。 */ clickImageOnDesktop(template: Template, options?: { precision?: number; algorithm?: 'segmented' | 'fft'; region?: Rect; } & ImageClickOptions): Promise; /** * 等待图像在全屏范围出现,超时抛 `TimeoutError`。 */ waitForImageOnDesktop(template: Template, options?: FindImageOptions): Promise; /** * 截屏 + 模板匹配 + 在截图上画红框标注命中位置,返回 base64 PNG。 * * 用于脚本调试:直观看到模板在屏幕上的匹配位置,保存到文件后 * 可直接打开查看。 * * @returns `{ base64, matches, width, height }` — base64 是标注后的 PNG * * @example * const viz = await flow.captureMatchVisualization('./images/btn.png'); * require('fs').writeFileSync('debug-match.png', Buffer.from(viz.base64, 'base64')); */ captureMatchVisualization(template: Template, options?: FindImageOptions & { strokeWidth?: number; }): Promise<{ base64: string; matches: FindImageMatch[]; width: number; height: number; }>; }