@midscene/core 1.12.10-beta-20260920031616.0 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/agent/agent.mjs +103 -6
- package/dist/es/agent/agent.mjs.map +1 -1
- package/dist/es/agent/index.mjs +4 -3
- package/dist/es/agent/tasks.mjs +2 -2
- package/dist/es/agent/tasks.mjs.map +1 -1
- package/dist/es/agent/test-runner-nodes.mjs +291 -40
- package/dist/es/agent/test-runner-nodes.mjs.map +1 -1
- package/dist/es/agent/utils.mjs +1 -1
- package/dist/es/device/index.mjs +15 -2
- package/dist/es/device/index.mjs.map +1 -1
- package/dist/es/dump/html-utils.mjs +37 -4
- package/dist/es/dump/html-utils.mjs.map +1 -1
- package/dist/es/dump/screenshot-store.mjs +8 -1
- package/dist/es/dump/screenshot-store.mjs.map +1 -1
- package/dist/es/report-cli.mjs +9 -4
- package/dist/es/report-cli.mjs.map +1 -1
- package/dist/es/report-generator.mjs +117 -4
- package/dist/es/report-generator.mjs.map +1 -1
- package/dist/es/report-html-template.mjs +1 -1
- package/dist/es/report.mjs +190 -41
- package/dist/es/report.mjs.map +1 -1
- package/dist/es/test-run-report.mjs.map +1 -1
- package/dist/es/test-runner/engine/case-runner.mjs +52 -0
- package/dist/es/test-runner/engine/case-runner.mjs.map +1 -0
- package/dist/es/test-runner/engine/document-runtime.mjs +273 -0
- package/dist/es/test-runner/engine/document-runtime.mjs.map +1 -0
- package/dist/es/test-runner/engine/execute-step.mjs +177 -0
- package/dist/es/test-runner/engine/execute-step.mjs.map +1 -0
- package/dist/es/test-runner/engine/execution-failure.mjs +39 -0
- package/dist/es/test-runner/engine/execution-failure.mjs.map +1 -0
- package/dist/es/test-runner/engine/index.mjs +12 -0
- package/dist/es/test-runner/engine/project-runtime.mjs +142 -0
- package/dist/es/test-runner/engine/project-runtime.mjs.map +1 -0
- package/dist/es/test-runner/engine/registry.mjs +61 -0
- package/dist/es/test-runner/engine/registry.mjs.map +1 -0
- package/dist/es/test-runner/engine/resource-operations.mjs +211 -0
- package/dist/es/test-runner/engine/resource-operations.mjs.map +1 -0
- package/dist/es/test-runner/engine/run-collected-case.mjs +193 -0
- package/dist/es/test-runner/engine/run-collected-case.mjs.map +1 -0
- package/dist/es/test-runner/engine/run-workflow-document.mjs +155 -0
- package/dist/es/test-runner/engine/run-workflow-document.mjs.map +1 -0
- package/dist/es/test-runner/engine/scheduler.mjs +27 -0
- package/dist/es/test-runner/engine/scheduler.mjs.map +1 -0
- package/dist/es/test-runner/engine/scope-teardown.mjs +16 -0
- package/dist/es/test-runner/engine/scope-teardown.mjs.map +1 -0
- package/dist/es/test-runner/engine/types.mjs +0 -0
- package/dist/es/test-runner/errors.mjs +201 -0
- package/dist/es/test-runner/errors.mjs.map +1 -0
- package/dist/es/test-runner/execution-record.mjs +83 -0
- package/dist/es/test-runner/execution-record.mjs.map +1 -0
- package/dist/es/test-runner/index.mjs +12 -0
- package/dist/es/test-runner/node/define-node.mjs +44 -0
- package/dist/es/test-runner/node/define-node.mjs.map +1 -0
- package/dist/es/test-runner/node/index.mjs +2 -0
- package/dist/es/test-runner/node/types.mjs +0 -0
- package/dist/es/test-runner/parser/document-format.mjs +21 -0
- package/dist/es/test-runner/parser/document-format.mjs.map +1 -0
- package/dist/es/test-runner/parser/normalize.mjs +87 -0
- package/dist/es/test-runner/parser/normalize.mjs.map +1 -0
- package/dist/es/test-runner/parser/types.mjs +0 -0
- package/dist/es/test-runner/project.mjs +0 -0
- package/dist/es/test-runner/reporting/agent-report.mjs +23 -0
- package/dist/es/test-runner/reporting/agent-report.mjs.map +1 -0
- package/dist/es/test-runner/reporting/execution-record.mjs +73 -0
- package/dist/es/test-runner/reporting/execution-record.mjs.map +1 -0
- package/dist/es/test-runner/reporting/merge-report.mjs +231 -0
- package/dist/es/test-runner/reporting/merge-report.mjs.map +1 -0
- package/dist/es/test-runner/reporting/sanitize.mjs +162 -0
- package/dist/es/test-runner/reporting/sanitize.mjs.map +1 -0
- package/dist/es/test-runner/reporting/test-run-report.mjs +327 -0
- package/dist/es/test-runner/reporting/test-run-report.mjs.map +1 -0
- package/dist/es/test-runner/reporting/types.mjs +0 -0
- package/dist/es/utils.mjs +1 -1
- package/dist/es/yaml/execution-context.node.mjs +6 -0
- package/dist/es/yaml/execution-context.node.mjs.map +1 -0
- package/dist/es/yaml/execution-context.stub.mjs +8 -0
- package/dist/es/yaml/execution-context.stub.mjs.map +1 -0
- package/dist/es/yaml/execution-session.mjs +90 -0
- package/dist/es/yaml/execution-session.mjs.map +1 -0
- package/dist/es/yaml/index.mjs +2 -1
- package/dist/es/yaml/legacy-yaml-execution-record.mjs +47 -0
- package/dist/es/yaml/legacy-yaml-execution-record.mjs.map +1 -0
- package/dist/es/yaml/legacy-yaml-report.mjs +52 -0
- package/dist/es/yaml/legacy-yaml-report.mjs.map +1 -0
- package/dist/es/yaml/legacy-yaml-runtime.mjs +119 -0
- package/dist/es/yaml/legacy-yaml-runtime.mjs.map +1 -0
- package/dist/es/yaml/player-state.mjs +15 -0
- package/dist/es/yaml/player-state.mjs.map +1 -0
- package/dist/es/yaml/player.mjs +214 -348
- package/dist/es/yaml/player.mjs.map +1 -1
- package/dist/es/yaml/report-policy.mjs +7 -0
- package/dist/es/yaml/report-policy.mjs.map +1 -0
- package/dist/es/yaml/test-runner-compat.mjs +301 -0
- package/dist/es/yaml/test-runner-compat.mjs.map +1 -0
- package/dist/es/yaml/test-runner-nodes.mjs +147 -0
- package/dist/es/yaml/test-runner-nodes.mjs.map +1 -0
- package/dist/es/yaml/utils.mjs +12 -1
- package/dist/es/yaml/utils.mjs.map +1 -1
- package/dist/es/yaml-runtime.mjs +7 -0
- package/dist/es/yaml.mjs.map +1 -1
- package/dist/lib/agent/agent.js +103 -6
- package/dist/lib/agent/agent.js.map +1 -1
- package/dist/lib/agent/index.js +37 -3
- package/dist/lib/agent/tasks.js +2 -2
- package/dist/lib/agent/tasks.js.map +1 -1
- package/dist/lib/agent/test-runner-nodes.js +329 -45
- package/dist/lib/agent/test-runner-nodes.js.map +1 -1
- package/dist/lib/agent/utils.js +1 -1
- package/dist/lib/device/index.js +15 -2
- package/dist/lib/device/index.js.map +1 -1
- package/dist/lib/dump/html-utils.js +43 -4
- package/dist/lib/dump/html-utils.js.map +1 -1
- package/dist/lib/dump/screenshot-store.js +8 -1
- package/dist/lib/dump/screenshot-store.js.map +1 -1
- package/dist/lib/report-cli.js +8 -3
- package/dist/lib/report-cli.js.map +1 -1
- package/dist/lib/report-generator.js +114 -1
- package/dist/lib/report-generator.js.map +1 -1
- package/dist/lib/report-html-template.js +1 -1
- package/dist/lib/report.js +187 -38
- package/dist/lib/report.js.map +1 -1
- package/dist/lib/test-run-report.js.map +1 -1
- package/dist/lib/test-runner/engine/case-runner.js +89 -0
- package/dist/lib/test-runner/engine/case-runner.js.map +1 -0
- package/dist/lib/test-runner/engine/document-runtime.js +307 -0
- package/dist/lib/test-runner/engine/document-runtime.js.map +1 -0
- package/dist/lib/test-runner/engine/execute-step.js +211 -0
- package/dist/lib/test-runner/engine/execute-step.js.map +1 -0
- package/dist/lib/test-runner/engine/execution-failure.js +79 -0
- package/dist/lib/test-runner/engine/execution-failure.js.map +1 -0
- package/dist/lib/test-runner/engine/index.js +137 -0
- package/dist/lib/test-runner/engine/index.js.map +1 -0
- package/dist/lib/test-runner/engine/project-runtime.js +176 -0
- package/dist/lib/test-runner/engine/project-runtime.js.map +1 -0
- package/dist/lib/test-runner/engine/registry.js +95 -0
- package/dist/lib/test-runner/engine/registry.js.map +1 -0
- package/dist/lib/test-runner/engine/resource-operations.js +272 -0
- package/dist/lib/test-runner/engine/resource-operations.js.map +1 -0
- package/dist/lib/test-runner/engine/run-collected-case.js +227 -0
- package/dist/lib/test-runner/engine/run-collected-case.js.map +1 -0
- package/dist/lib/test-runner/engine/run-workflow-document.js +189 -0
- package/dist/lib/test-runner/engine/run-workflow-document.js.map +1 -0
- package/dist/lib/test-runner/engine/scheduler.js +64 -0
- package/dist/lib/test-runner/engine/scheduler.js.map +1 -0
- package/dist/lib/test-runner/engine/scope-teardown.js +50 -0
- package/dist/lib/test-runner/engine/scope-teardown.js.map +1 -0
- package/dist/lib/test-runner/engine/types.js +20 -0
- package/dist/lib/test-runner/engine/types.js.map +1 -0
- package/dist/lib/test-runner/errors.js +283 -0
- package/dist/lib/test-runner/errors.js.map +1 -0
- package/dist/lib/test-runner/execution-record.js +123 -0
- package/dist/lib/test-runner/execution-record.js.map +1 -0
- package/dist/lib/test-runner/index.js +137 -0
- package/dist/lib/test-runner/index.js.map +1 -0
- package/dist/lib/test-runner/node/define-node.js +78 -0
- package/dist/lib/test-runner/node/define-node.js.map +1 -0
- package/dist/lib/test-runner/node/index.js +67 -0
- package/dist/lib/test-runner/node/index.js.map +1 -0
- package/dist/lib/test-runner/node/types.js +20 -0
- package/dist/lib/test-runner/node/types.js.map +1 -0
- package/dist/lib/test-runner/parser/document-format.js +58 -0
- package/dist/lib/test-runner/parser/document-format.js.map +1 -0
- package/dist/lib/test-runner/parser/normalize.js +124 -0
- package/dist/lib/test-runner/parser/normalize.js.map +1 -0
- package/dist/lib/test-runner/parser/types.js +20 -0
- package/dist/lib/test-runner/parser/types.js.map +1 -0
- package/dist/lib/test-runner/project.js +20 -0
- package/dist/lib/test-runner/project.js.map +1 -0
- package/dist/lib/test-runner/reporting/agent-report.js +57 -0
- package/dist/lib/test-runner/reporting/agent-report.js.map +1 -0
- package/dist/lib/test-runner/reporting/execution-record.js +107 -0
- package/dist/lib/test-runner/reporting/execution-record.js.map +1 -0
- package/dist/lib/test-runner/reporting/merge-report.js +265 -0
- package/dist/lib/test-runner/reporting/merge-report.js.map +1 -0
- package/dist/lib/test-runner/reporting/sanitize.js +202 -0
- package/dist/lib/test-runner/reporting/sanitize.js.map +1 -0
- package/dist/lib/test-runner/reporting/test-run-report.js +364 -0
- package/dist/lib/test-runner/reporting/test-run-report.js.map +1 -0
- package/dist/lib/test-runner/reporting/types.js +20 -0
- package/dist/lib/test-runner/reporting/types.js.map +1 -0
- package/dist/lib/utils.js +1 -1
- package/dist/lib/yaml/execution-context.node.js +43 -0
- package/dist/lib/yaml/execution-context.node.js.map +1 -0
- package/dist/lib/yaml/execution-context.stub.js +45 -0
- package/dist/lib/yaml/execution-context.stub.js.map +1 -0
- package/dist/lib/yaml/execution-session.js +136 -0
- package/dist/lib/yaml/execution-session.js.map +1 -0
- package/dist/lib/yaml/index.js +49 -7
- package/dist/lib/yaml/legacy-yaml-execution-record.js +81 -0
- package/dist/lib/yaml/legacy-yaml-execution-record.js.map +1 -0
- package/dist/lib/yaml/legacy-yaml-report.js +86 -0
- package/dist/lib/yaml/legacy-yaml-report.js.map +1 -0
- package/dist/lib/yaml/legacy-yaml-runtime.js +156 -0
- package/dist/lib/yaml/legacy-yaml-runtime.js.map +1 -0
- package/dist/lib/yaml/player-state.js +52 -0
- package/dist/lib/yaml/player-state.js.map +1 -0
- package/dist/lib/yaml/player.js +213 -347
- package/dist/lib/yaml/player.js.map +1 -1
- package/dist/lib/yaml/report-policy.js +41 -0
- package/dist/lib/yaml/report-policy.js.map +1 -0
- package/dist/lib/yaml/test-runner-compat.js +344 -0
- package/dist/lib/yaml/test-runner-compat.js.map +1 -0
- package/dist/lib/yaml/test-runner-nodes.js +184 -0
- package/dist/lib/yaml/test-runner-nodes.js.map +1 -0
- package/dist/lib/yaml/utils.js +18 -1
- package/dist/lib/yaml/utils.js.map +1 -1
- package/dist/lib/yaml-runtime.js +67 -0
- package/dist/lib/yaml-runtime.js.map +1 -0
- package/dist/lib/yaml.js.map +1 -1
- package/dist/types/agent/agent.d.ts +14 -1
- package/dist/types/agent/agent.d.ts.map +1 -1
- package/dist/types/agent/index.d.ts +4 -3
- package/dist/types/agent/index.d.ts.map +1 -1
- package/dist/types/agent/tasks.d.ts +1 -1
- package/dist/types/agent/tasks.d.ts.map +1 -1
- package/dist/types/agent/test-runner-nodes.d.ts +226 -0
- package/dist/types/agent/test-runner-nodes.d.ts.map +1 -1
- package/dist/types/device/index.d.ts +1 -1
- package/dist/types/device/index.d.ts.map +1 -1
- package/dist/types/dump/html-utils.d.ts +4 -0
- package/dist/types/dump/html-utils.d.ts.map +1 -1
- package/dist/types/dump/screenshot-store.d.ts +2 -1
- package/dist/types/dump/screenshot-store.d.ts.map +1 -1
- package/dist/types/report-cli.d.ts.map +1 -1
- package/dist/types/report-generator.d.ts +10 -0
- package/dist/types/report-generator.d.ts.map +1 -1
- package/dist/types/report.d.ts +3 -1
- package/dist/types/report.d.ts.map +1 -1
- package/dist/types/test-run-report.d.ts +18 -2
- package/dist/types/test-run-report.d.ts.map +1 -1
- package/dist/types/test-runner/engine/case-runner.d.ts +13 -0
- package/dist/types/test-runner/engine/case-runner.d.ts.map +1 -0
- package/dist/types/test-runner/engine/document-runtime.d.ts +4 -0
- package/dist/types/test-runner/engine/document-runtime.d.ts.map +1 -0
- package/dist/types/test-runner/engine/execute-step.d.ts +17 -0
- package/dist/types/test-runner/engine/execute-step.d.ts.map +1 -0
- package/dist/types/test-runner/engine/execution-failure.d.ts +13 -0
- package/dist/types/test-runner/engine/execution-failure.d.ts.map +1 -0
- package/dist/types/test-runner/engine/index.d.ts +13 -0
- package/dist/types/test-runner/engine/index.d.ts.map +1 -0
- package/dist/types/test-runner/engine/project-runtime.d.ts +3 -0
- package/dist/types/test-runner/engine/project-runtime.d.ts.map +1 -0
- package/dist/types/test-runner/engine/registry.d.ts +12 -0
- package/dist/types/test-runner/engine/registry.d.ts.map +1 -0
- package/dist/types/test-runner/engine/resource-operations.d.ts +32 -0
- package/dist/types/test-runner/engine/resource-operations.d.ts.map +1 -0
- package/dist/types/test-runner/engine/run-collected-case.d.ts +4 -0
- package/dist/types/test-runner/engine/run-collected-case.d.ts.map +1 -0
- package/dist/types/test-runner/engine/run-workflow-document.d.ts +4 -0
- package/dist/types/test-runner/engine/run-workflow-document.d.ts.map +1 -0
- package/dist/types/test-runner/engine/scheduler.d.ts +12 -0
- package/dist/types/test-runner/engine/scheduler.d.ts.map +1 -0
- package/dist/types/test-runner/engine/scope-teardown.d.ts +3 -0
- package/dist/types/test-runner/engine/scope-teardown.d.ts.map +1 -0
- package/dist/types/test-runner/engine/types.d.ts +226 -0
- package/dist/types/test-runner/engine/types.d.ts.map +1 -0
- package/dist/types/test-runner/errors.d.ts +79 -0
- package/dist/types/test-runner/errors.d.ts.map +1 -0
- package/dist/types/test-runner/execution-record.d.ts +43 -0
- package/dist/types/test-runner/execution-record.d.ts.map +1 -0
- package/dist/types/test-runner/index.d.ts +20 -0
- package/dist/types/test-runner/index.d.ts.map +1 -0
- package/dist/types/test-runner/node/define-node.d.ts +4 -0
- package/dist/types/test-runner/node/define-node.d.ts.map +1 -0
- package/dist/types/test-runner/node/index.d.ts +3 -0
- package/dist/types/test-runner/node/index.d.ts.map +1 -0
- package/dist/types/test-runner/node/types.d.ts +66 -0
- package/dist/types/test-runner/node/types.d.ts.map +1 -0
- package/dist/types/test-runner/parser/document-format.d.ts +8 -0
- package/dist/types/test-runner/parser/document-format.d.ts.map +1 -0
- package/dist/types/test-runner/parser/normalize.d.ts +9 -0
- package/dist/types/test-runner/parser/normalize.d.ts.map +1 -0
- package/dist/types/test-runner/parser/types.d.ts +69 -0
- package/dist/types/test-runner/parser/types.d.ts.map +1 -0
- package/dist/types/test-runner/project.d.ts +75 -0
- package/dist/types/test-runner/project.d.ts.map +1 -0
- package/dist/types/test-runner/reporting/agent-report.d.ts +9 -0
- package/dist/types/test-runner/reporting/agent-report.d.ts.map +1 -0
- package/dist/types/test-runner/reporting/execution-record.d.ts +9 -0
- package/dist/types/test-runner/reporting/execution-record.d.ts.map +1 -0
- package/dist/types/test-runner/reporting/merge-report.d.ts +15 -0
- package/dist/types/test-runner/reporting/merge-report.d.ts.map +1 -0
- package/dist/types/test-runner/reporting/sanitize.d.ts +6 -0
- package/dist/types/test-runner/reporting/sanitize.d.ts.map +1 -0
- package/dist/types/test-runner/reporting/test-run-report.d.ts +5 -0
- package/dist/types/test-runner/reporting/test-run-report.d.ts.map +1 -0
- package/dist/types/test-runner/reporting/types.d.ts +34 -0
- package/dist/types/test-runner/reporting/types.d.ts.map +1 -0
- package/dist/types/yaml/execution-context.node.d.ts +4 -0
- package/dist/types/yaml/execution-context.node.d.ts.map +1 -0
- package/dist/types/yaml/execution-context.stub.d.ts +6 -0
- package/dist/types/yaml/execution-context.stub.d.ts.map +1 -0
- package/dist/types/yaml/execution-session.d.ts +17 -0
- package/dist/types/yaml/execution-session.d.ts.map +1 -0
- package/dist/types/yaml/index.d.ts +1 -1
- package/dist/types/yaml/index.d.ts.map +1 -1
- package/dist/types/yaml/legacy-yaml-execution-record.d.ts +24 -0
- package/dist/types/yaml/legacy-yaml-execution-record.d.ts.map +1 -0
- package/dist/types/yaml/legacy-yaml-report.d.ts +19 -0
- package/dist/types/yaml/legacy-yaml-report.d.ts.map +1 -0
- package/dist/types/yaml/legacy-yaml-runtime.d.ts +23 -0
- package/dist/types/yaml/legacy-yaml-runtime.d.ts.map +1 -0
- package/dist/types/yaml/player-state.d.ts +11 -0
- package/dist/types/yaml/player-state.d.ts.map +1 -0
- package/dist/types/yaml/player.d.ts +11 -3
- package/dist/types/yaml/player.d.ts.map +1 -1
- package/dist/types/yaml/report-policy.d.ts +4 -0
- package/dist/types/yaml/report-policy.d.ts.map +1 -0
- package/dist/types/yaml/test-runner-compat.d.ts +17 -0
- package/dist/types/yaml/test-runner-compat.d.ts.map +1 -0
- package/dist/types/yaml/test-runner-nodes.d.ts +6 -0
- package/dist/types/yaml/test-runner-nodes.d.ts.map +1 -0
- package/dist/types/yaml/utils.d.ts +9 -0
- package/dist/types/yaml/utils.d.ts.map +1 -1
- package/dist/types/yaml-runtime.d.ts +13 -0
- package/dist/types/yaml-runtime.d.ts.map +1 -0
- package/dist/types/yaml.d.ts +25 -7
- package/dist/types/yaml.d.ts.map +1 -1
- package/package.json +28 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"device/index.mjs","sources":["../../../src/device/index.ts"],"sourcesContent":["import type { ModelRuntime } from '@/ai-model/models';\nimport { getMidsceneLocationSchema } from '@/common';\nimport type {\n ActionScrollParam,\n DeviceAction,\n ExecutorContext,\n LocateResultElement,\n} from '@/types';\nimport type { ElementNode } from '@midscene/shared/extractor';\nimport { getDebug } from '@midscene/shared/logger';\nimport { _keyDefinitions } from '@midscene/shared/us-keyboard-layout';\nimport { z } from 'zod';\nimport type {\n ElementCacheFeature,\n Rect,\n Size,\n UIContext,\n UITreeSnapshot,\n} from '../types';\nimport { type InputStrategy, inputStrategies } from './input-strategy';\n\nexport interface FileChooserHandler {\n accept(files: string[]): Promise<void>;\n}\n\nexport interface FileChooserRegistration {\n dispose: () => void;\n getError: () => Error | undefined | Promise<Error | undefined>;\n}\n\nexport interface MjpegStreamFrame {\n /** Raw base64-encoded image bytes WITHOUT a `data:image/...;base64,` prefix. */\n data: string;\n contentType?: string;\n}\n\nexport interface MjpegStreamHandle {\n stop(): void | Promise<void>;\n}\n\nexport interface MjpegStreamOptions {\n signal?: AbortSignal;\n onFrame(frame: MjpegStreamFrame): void;\n onError?(error: unknown): void;\n}\n\n/**\n * A cheap, not-yet-decoded handle to one screen frame from a\n * {@link DeviceFrameSource}. `ref` is platform-specific (a raw H.264 keyframe\n * buffer on Android, an already-encoded JPEG data URL on iOS/web) and must not\n * be interpreted by callers — pass it back to `decode()` to materialize.\n */\nexport interface DeviceFrameRef {\n ref: unknown;\n capturedAt: number;\n}\n\n/**\n * A continuous screen-frame source opened via\n * {@link AbstractInterface.openFrameSource}. Designed for deferred decoding:\n * grabbing `latest()` is near-zero cost, so observers can sample at a steady\n * cadence and pay any decode cost only once, for the frames they keep.\n */\nexport interface DeviceFrameSource {\n /** Latest frame handle, near-zero cost. Null until the first frame arrives. */\n latest(): DeviceFrameRef | null;\n /**\n * Materialize frame handles into `data:image/...;base64,` URLs, preserving\n * order. Possibly expensive (e.g. one ffmpeg run per unique frame on\n * Android) — call once with the sampled handles, never per tick.\n */\n decode(refs: DeviceFrameRef[]): Promise<string[]>;\n /** Release the source (stop streams/subscriptions it started). */\n stop(): Promise<void> | void;\n}\n\n/** A point in device-pixel coordinates on the screen. */\nexport interface PointerPoint {\n x: number;\n y: number;\n}\n\n/** Low-level swipe gesture implemented by touch or pointer-backed devices. */\nexport type SwipeInputPrimitive = (\n start: PointerPoint,\n end: PointerPoint,\n opts?: { duration?: number; repeat?: number },\n) => Promise<void>;\n\n/** The physical input source used to execute and describe a swipe. */\nexport type SwipeInputMode = 'touch' | 'mouse';\n\n/** A resolved swipe capability and the input semantics exposed to the model. */\nexport interface ResolvedSwipeInputPrimitive {\n swipe: SwipeInputPrimitive;\n inputMode: SwipeInputMode;\n}\n\nexport interface PointerInputPrimitives {\n tap(p: PointerPoint, opts?: { duration?: number }): Promise<void>;\n doubleClick?(p: PointerPoint): Promise<void>;\n rightClick?(p: PointerPoint): Promise<void>;\n hover?(p: PointerPoint): Promise<void>;\n longPress?(p: PointerPoint, opts?: { duration?: number }): Promise<void>;\n dragAndDrop?(from: PointerPoint, to: PointerPoint): Promise<void>;\n swipe?: SwipeInputPrimitive;\n}\n\nexport interface TouchInputPrimitives {\n swipe: SwipeInputPrimitive;\n pinch?(\n center: PointerPoint,\n opts: { startDistance: number; endDistance: number; duration: number },\n ): Promise<void>;\n}\n\nexport interface KeyboardInputPrimitives {\n keyboardPress(keyName: string, opts?: { target?: unknown }): Promise<void>;\n cursorMove?(direction: 'left' | 'right', times?: number): Promise<void>;\n typeText(\n value: string,\n opts?: {\n autoDismissKeyboard?: boolean;\n keyboardDismissStrategy?: 'esc-first' | 'back-first';\n keyboardTypeDelay?: number;\n inputStrategy?: InputStrategy;\n target?: unknown;\n replace?: boolean;\n focusOnly?: boolean;\n },\n ): Promise<void>;\n clearInput(target?: unknown): Promise<void>;\n}\n\nexport interface ScrollInputPrimitives {\n scroll(param: ActionScrollParam): Promise<void>;\n}\n\nexport interface SystemInputPrimitives {\n backButton?(): Promise<void>;\n homeButton?(): Promise<void>;\n recentAppsButton?(): Promise<void>;\n}\n\nexport interface InputPrimitives {\n pointer?: PointerInputPrimitives;\n keyboard?: KeyboardInputPrimitives;\n touch?: TouchInputPrimitives;\n scroll?: ScrollInputPrimitives;\n system?: SystemInputPrimitives;\n}\n\n/**\n * Resolve the canonical swipe capability for an input collection.\n * Touch remains preferred for devices that expose both capabilities; desktop\n * devices can fall back to a pointer-backed primary-button gesture.\n */\nexport function resolveSwipeInputPrimitive(\n input: Pick<InputPrimitives, 'pointer' | 'touch'>,\n): ResolvedSwipeInputPrimitive | undefined {\n if (input.touch?.swipe) {\n return { swipe: input.touch.swipe, inputMode: 'touch' };\n }\n if (input.pointer?.swipe) {\n return { swipe: input.pointer.swipe, inputMode: 'mouse' };\n }\n return undefined;\n}\n\nexport interface MobileInputPrimitives extends InputPrimitives {\n pointer: PointerInputPrimitives & {\n doubleClick(p: PointerPoint): Promise<void>;\n longPress(p: PointerPoint, opts?: { duration?: number }): Promise<void>;\n dragAndDrop(from: PointerPoint, to: PointerPoint): Promise<void>;\n };\n keyboard: KeyboardInputPrimitives;\n touch: TouchInputPrimitives;\n}\n\nexport interface BrowserInputPrimitives extends InputPrimitives {\n pointer: PointerInputPrimitives & {\n doubleClick(p: PointerPoint): Promise<void>;\n rightClick(p: PointerPoint): Promise<void>;\n hover(p: PointerPoint): Promise<void>;\n dragAndDrop(from: PointerPoint, to: PointerPoint): Promise<void>;\n longPress(p: PointerPoint, opts?: { duration?: number }): Promise<void>;\n };\n keyboard: KeyboardInputPrimitives;\n scroll: ScrollInputPrimitives;\n touch: TouchInputPrimitives;\n}\n\nexport interface ComputerInputPrimitives extends InputPrimitives {\n pointer: PointerInputPrimitives & {\n doubleClick(p: PointerPoint): Promise<void>;\n rightClick(p: PointerPoint): Promise<void>;\n hover(p: PointerPoint): Promise<void>;\n dragAndDrop(from: PointerPoint, to: PointerPoint): Promise<void>;\n };\n keyboard: KeyboardInputPrimitives;\n scroll: ScrollInputPrimitives;\n}\n\nexport abstract class AbstractInterface {\n abstract interfaceType: string;\n\n abstract screenshotBase64(): Promise<string>;\n abstract size(): Promise<Size>;\n abstract actionSpace(): DeviceAction[];\n\n abstract cacheFeatureForPoint?(\n center: [number, number],\n options?: {\n targetDescription?: string;\n modelRuntime?: ModelRuntime;\n },\n ): Promise<ElementCacheFeature>;\n abstract rectMatchesCacheFeature?(\n feature: ElementCacheFeature,\n ): Promise<Rect>;\n\n abstract getUITree?(): Promise<UITreeSnapshot>;\n\n abstract destroy?(): Promise<void>;\n\n abstract describe?(): string;\n abstract beforeInvokeAction?(actionName: string, param: any): Promise<void>;\n abstract afterInvokeAction?(actionName: string, param: any): Promise<void>;\n\n // for web only\n registerFileChooserListener?(\n handler: (chooser: FileChooserHandler) => Promise<void>,\n ): Promise<FileChooserRegistration>;\n\n // @deprecated do NOT extend this method\n abstract getElementsNodeTree?: () => Promise<ElementNode>;\n\n // @deprecated do NOT extend this method\n abstract url?: () => string | Promise<string>;\n\n // @deprecated do NOT extend this method\n abstract evaluateJavaScript?<T = any>(script: string): Promise<T>;\n\n /**\n * Get the current device-local time as a formatted string.\n * Prefer this for user-visible time because timestamps alone do not preserve\n * the target device's timezone when formatted on the host machine.\n */\n getDeviceLocalTimeString?(format?: string): Promise<string>;\n\n /** URL of native MJPEG stream for real-time screen preview (e.g. WDA MJPEG server) */\n mjpegStreamUrl?: string;\n\n /**\n * Optional continuous frame source for UI observation (`startObserving`).\n * Devices that maintain a continuous frame stream — scrcpy on Android, WDA\n * MJPEG on iOS, CDP screencast on web — implement this so an observer can\n * sample the screen far faster than repeated `screenshotBase64()` calls,\n * catching short-lived UI (toasts, carousels, transitions).\n *\n * The contract enables DEFERRED decoding: `latest()` returns a cheap opaque\n * handle (e.g. a raw H.264 keyframe on Android) with no per-frame decode\n * cost, and `decode()` materializes only the handles that were actually\n * sampled — once, at the end of the observation window.\n */\n openFrameSource?(): Promise<DeviceFrameSource | undefined>;\n\n /**\n * Optional in-process MJPEG frame producer. Implementations can push raw\n * base64 frames here when there is no standalone native MJPEG URL, e.g.\n * Chromium CDP Page.startScreencast for web previews.\n */\n startMjpegStream?(\n options: MjpegStreamOptions,\n ): MjpegStreamHandle | undefined | Promise<MjpegStreamHandle | undefined>;\n\n /**\n * Optional hook used after a UI action to push a fresh frame on the active\n * MJPEG stream. Set `force` after navigation to replace a transient loading\n * frame even when the screencast has already emitted one. Implementations\n * should be a no-op when no stream is active.\n */\n flushPendingVisualUpdate?(force?: boolean): Promise<void>;\n\n /**\n * Optional non-blocking variant of `flushPendingVisualUpdate`. Keyboard-\n * heavy preview interactions can schedule a coalesced refresh here without\n * stalling the input hot path; `force` preserves a requested navigation\n * refresh while work is already queued.\n */\n schedulePendingVisualUpdate?(force?: boolean): void;\n\n /**\n * Optional navigation state probe for browser-like interfaces, used to drive\n * loading indicators in playground UIs. Returning `undefined` means the\n * interface does not expose this concept.\n */\n navigationState?(): Promise<{ isLoading: boolean }>;\n\n /**\n * Low-level device input surface. Platform implementations expose transport\n * primitives here; higher-level AI actions and manual pointer dispatch should\n * adapt to this instead of duplicating platform gesture logic.\n */\n inputPrimitives?: InputPrimitives;\n}\n\n// Generic function to define actions with proper type inference\n// TRuntime allows specifying a different type for the runtime parameter (after location resolution)\n// TReturn allows specifying the return type of the action\nexport const defineAction = <\n TSchema extends z.ZodType | undefined = undefined,\n TRuntime = TSchema extends z.ZodType ? z.infer<TSchema> : undefined,\n TReturn = any,\n>(\n config: {\n name: string;\n description: string;\n interfaceAlias?: string;\n paramSchema?: TSchema;\n call: (\n param: TRuntime,\n context?: ExecutorContext,\n ) => Promise<TReturn> | TReturn;\n } & Partial<\n Omit<\n DeviceAction<TRuntime, TReturn>,\n 'name' | 'description' | 'interfaceAlias' | 'paramSchema' | 'call'\n >\n >,\n): DeviceAction<TRuntime, TReturn> => {\n return config as any; // Type assertion needed because schema validation type differs from runtime type\n};\n\nfunction pointFromLocate(\n locate: LocateResultElement | undefined,\n missingMessage: string,\n): PointerPoint {\n if (!locate) {\n throw new Error(missingMessage);\n }\n return { x: locate.center[0], y: locate.center[1] };\n}\n\nfunction defineLocatedPointAction<\n TSchema extends z.ZodType,\n TParam extends { locate: LocateResultElement },\n>(config: {\n name: string;\n description: string;\n interfaceAlias?: string;\n paramSchema: TSchema;\n sample: DeviceAction<TParam>['sample'];\n missingLocateMessage: string;\n call: (point: PointerPoint, param: TParam) => Promise<void>;\n}): DeviceAction<TParam> {\n return defineAction<TSchema, TParam>({\n name: config.name,\n description: config.description,\n interfaceAlias: config.interfaceAlias,\n paramSchema: config.paramSchema,\n sample: config.sample,\n call: async (param) => {\n await config.call(\n pointFromLocate(param.locate, config.missingLocateMessage),\n param,\n );\n },\n });\n}\n\n// Tap\nexport const actionTapParamSchema = z.object({\n locate: getMidsceneLocationSchema().describe('The element to be tapped'),\n});\nexport type ActionTapParam = {\n locate: LocateResultElement;\n};\n\nexport const defineActionTap = (\n tap: PointerInputPrimitives['tap'],\n): DeviceAction<ActionTapParam> => {\n return defineLocatedPointAction<typeof actionTapParamSchema, ActionTapParam>({\n name: 'Tap',\n description: 'Tap the element',\n interfaceAlias: 'aiTap',\n paramSchema: actionTapParamSchema,\n sample: {\n locate: { prompt: 'the \"Submit\" button' },\n },\n missingLocateMessage: 'Element not found, cannot tap',\n call: async (point) => {\n await tap(point);\n },\n });\n};\n\nexport const registerFileChooserAcceptParamSchema = z.object({\n files: z\n .union([z.string(), z.array(z.string())])\n .describe(\n \"File path(s) within the current aiAct call's fileChooserAllowedDir to use whenever a later action triggers a file chooser. fileChooserAllowedDir must be provided for this action. This setting replaces any previously registered file path(s).\",\n ),\n});\nexport type RegisterFileChooserAcceptParam = {\n files: string | string[];\n};\n\nexport const defineActionRegisterFileChooserAccept = (\n register: (files: string | string[]) => Promise<void>,\n): DeviceAction<RegisterFileChooserAcceptParam> => {\n return defineAction<\n typeof registerFileChooserAcceptParamSchema,\n RegisterFileChooserAcceptParam\n >({\n name: 'RegisterFileChooserAccept',\n description:\n 'Configure files for file chooser dialogs triggered by later actions in this aiAct',\n interfaceAlias: 'registerFileChooserAccept',\n paramSchema: registerFileChooserAcceptParamSchema,\n sample: {\n files: ['fixtures/document.pdf'],\n },\n call: async (param) => {\n await register(param.files);\n },\n });\n};\n\n// RightClick\nexport const actionRightClickParamSchema = z.object({\n locate: getMidsceneLocationSchema().describe(\n 'The element to be right clicked',\n ),\n});\nexport type ActionRightClickParam = {\n locate: LocateResultElement;\n};\n\nexport const defineActionRightClick = (\n rightClick: NonNullable<PointerInputPrimitives['rightClick']>,\n): DeviceAction<ActionRightClickParam> => {\n return defineLocatedPointAction<\n typeof actionRightClickParamSchema,\n ActionRightClickParam\n >({\n name: 'RightClick',\n description: 'Right click the element',\n interfaceAlias: 'aiRightClick',\n paramSchema: actionRightClickParamSchema,\n sample: {\n locate: { prompt: 'the file icon on the desktop' },\n },\n missingLocateMessage: 'Element not found, cannot right click',\n call: async (point) => {\n await rightClick(point);\n },\n });\n};\n\n// DoubleClick\nexport const actionDoubleClickParamSchema = z.object({\n locate: getMidsceneLocationSchema().describe(\n 'The element to be double clicked',\n ),\n});\nexport type ActionDoubleClickParam = {\n locate: LocateResultElement;\n};\n\nexport const defineActionDoubleClick = (\n doubleClick: NonNullable<PointerInputPrimitives['doubleClick']>,\n): DeviceAction<ActionDoubleClickParam> => {\n return defineLocatedPointAction<\n typeof actionDoubleClickParamSchema,\n ActionDoubleClickParam\n >({\n name: 'DoubleClick',\n description: 'Double click the element',\n interfaceAlias: 'aiDoubleClick',\n paramSchema: actionDoubleClickParamSchema,\n sample: {\n locate: { prompt: 'the folder icon' },\n },\n missingLocateMessage: 'Element not found, cannot double click',\n call: async (point) => {\n await doubleClick(point);\n },\n });\n};\n\n// Hover\nexport const actionHoverParamSchema = z.object({\n locate: getMidsceneLocationSchema().describe('The element to be hovered'),\n});\nexport type ActionHoverParam = {\n locate: LocateResultElement;\n};\n\nexport const defineActionHover = (\n hover: NonNullable<PointerInputPrimitives['hover']>,\n): DeviceAction<ActionHoverParam> => {\n return defineLocatedPointAction<\n typeof actionHoverParamSchema,\n ActionHoverParam\n >({\n name: 'Hover',\n description: 'Move the mouse to the element',\n interfaceAlias: 'aiHover',\n paramSchema: actionHoverParamSchema,\n sample: {\n locate: { prompt: 'the navigation menu item \"Products\"' },\n },\n missingLocateMessage: 'Element not found, cannot hover',\n call: async (point) => {\n await hover(point);\n },\n });\n};\n\n// Input\nconst inputLocateDescription =\n 'the position of the placeholder or text content in the target input field. If there is no content, locate the center of the input field.';\nexport const actionInputParamSchema = z.object({\n value: z\n .union([z.string(), z.number()])\n .transform((val) => String(val))\n .describe(\n 'The text to input. Provide the final content for replace mode, only the inserted characters for typeOnly mode, or an empty string when using clear mode to remove existing text.',\n ),\n locate: getMidsceneLocationSchema()\n .describe(inputLocateDescription)\n .optional(),\n mode: z\n .enum(['replace', 'clear', 'typeOnly'])\n .default('replace')\n .describe(\n 'Input mode: \"replace\" (default) - clear the field and input the value; \"typeOnly\" - type the value directly without clearing the field first, and should be set explicitly for incremental edits after moving the cursor; \"clear\" - clear the field without inputting new text.',\n ),\n autoDismissKeyboard: z\n .boolean()\n .optional()\n .describe(\n 'If true, the keyboard will be dismissed after the input is completed. Do not set it unless the user asks you to do so.',\n ),\n keyboardTypeDelay: z\n .number()\n .finite()\n .nonnegative()\n .optional()\n .describe(\n 'Delay in milliseconds between keystrokes when typing. Passed through from device/user configuration. Do not set it unless the user asks you to do so.',\n ),\n inputStrategy: z\n .enum(inputStrategies)\n .optional()\n .describe(\n 'Text input strategy: \"legacy\" (default) preserves the current platform behavior; \"sequential\" enters one Unicode code point at a time; \"bulk\" sends the complete text through one platform input operation when supported and requires keyboardTypeDelay to be omitted or set to 0. Do not set it unless the user asks you to do so.',\n ),\n});\nexport type ActionInputParam = {\n value: string;\n locate?: LocateResultElement;\n mode?: 'replace' | 'clear' | 'typeOnly' | 'append';\n autoDismissKeyboard?: boolean;\n keyboardTypeDelay?: number;\n inputStrategy?: InputStrategy;\n};\n\nexport const defineActionInput = (\n keyboard: KeyboardInputPrimitives,\n): DeviceAction<ActionInputParam> => {\n return defineAction<typeof actionInputParamSchema, ActionInputParam>({\n name: 'Input',\n description: 'Input the value into the element',\n interfaceAlias: 'aiInput',\n paramSchema: actionInputParamSchema,\n sample: {\n value: 'test@example.com',\n locate: { prompt: 'the email input field' },\n },\n call: async (param) => {\n // backward compat: convert deprecated 'append' to 'typeOnly'\n if ((param.mode as string) === 'append') {\n param.mode = 'typeOnly';\n }\n\n if (param.mode === 'clear') {\n await keyboard.clearInput(param.locate);\n return;\n }\n\n if (!param || !param.value) {\n return;\n }\n\n await keyboard.typeText(param.value, {\n target: param.locate,\n replace: param.mode !== 'typeOnly',\n autoDismissKeyboard: param.autoDismissKeyboard,\n keyboardTypeDelay: param.keyboardTypeDelay,\n inputStrategy: param.inputStrategy,\n });\n },\n });\n};\n\n// KeyboardPress\nexport const actionKeyboardPressParamSchema = z.object({\n locate: getMidsceneLocationSchema()\n .describe(\n 'The optional element to click before pressing the key. Omit this when the key should operate on the currently focused element, especially when copying or cutting an existing text selection.',\n )\n .optional(),\n keyName: z\n .string()\n .describe(\n \"The key to be pressed. Use '+' for key combinations, e.g., 'Control+A', 'Shift+Enter'\",\n ),\n});\nexport type ActionKeyboardPressParam = {\n locate?: LocateResultElement;\n keyName: string;\n};\n\nexport const defineActionKeyboardPress = (\n keyboardPress: KeyboardInputPrimitives['keyboardPress'],\n): DeviceAction<ActionKeyboardPressParam> => {\n return defineAction<\n typeof actionKeyboardPressParamSchema,\n ActionKeyboardPressParam\n >({\n name: 'KeyboardPress',\n description:\n 'Press a key or key combination, like \"Enter\", \"Tab\", \"Escape\", or \"Control+A\", \"Shift+Enter\". Do not use this to type text. Omit locate to operate on the current focus without clicking again, especially for Copy or Cut after text has been selected.',\n interfaceAlias: 'aiKeyboardPress',\n paramSchema: actionKeyboardPressParamSchema,\n sample: {\n keyName: 'Enter',\n },\n call: async (param) => {\n await keyboardPress(param.keyName, {\n target: param.locate,\n });\n },\n });\n};\n\n// Scroll\nexport const actionScrollParamSchema = z.object({\n scrollType: z\n .enum([\n 'singleAction',\n 'scrollToBottom',\n 'scrollToTop',\n 'scrollToRight',\n 'scrollToLeft',\n ])\n .default('singleAction')\n .describe(\n 'The scroll behavior: \"singleAction\" for a single scroll action, \"scrollToBottom\" for scrolling all the way to the bottom by rapidly scrolling 5-10 times (skipping intermediate content until reaching the bottom), \"scrollToTop\" for scrolling all the way to the top by rapidly scrolling 5-10 times (skipping intermediate content until reaching the top), \"scrollToRight\" for scrolling all the way to the right by rapidly scrolling multiple times, \"scrollToLeft\" for scrolling all the way to the left by rapidly scrolling multiple times',\n ),\n direction: z\n .enum(['down', 'up', 'right', 'left'])\n .default('down')\n .describe(\n 'The direction toward the off-screen content to reveal. \"down\" reveals content below the current viewport, \"up\" reveals content above, \"right\" reveals content to the right, and \"left\" reveals content to the left. This does not describe the movement direction of the content currently visible on the screen. Only effective when scrollType is \"singleAction\".',\n ),\n distance: z\n .number()\n .positive()\n .nullable()\n .optional()\n .describe(\n 'Positive requested scroll amount in screen-coordinate units. On touch platforms, it controls the length of the swipe gesture used to scroll; on web and desktop platforms, it controls or approximates the wheel scroll delta. Only effective when scrollType is \"singleAction\".',\n ),\n locate: getMidsceneLocationSchema()\n .optional()\n .describe(\n 'Describe the target element to be scrolled on, like \"the table\" or \"the list\" or \"the content area\" or \"the scrollable area\". Do NOT provide a general intent like \"scroll to find some element\"',\n ),\n});\n\nexport const defineActionScroll = (\n scroll: ScrollInputPrimitives['scroll'],\n): DeviceAction<ActionScrollParam> => {\n return defineAction<typeof actionScrollParamSchema, ActionScrollParam>({\n name: 'Scroll',\n description:\n 'Scroll a page or scrollable region to reveal off-screen content. Use Scroll when the goal is to browse content outside the current viewport. For direct gesture interactions, such as adjusting a slider or wheel picker, switching between paged cards or images, following an on-screen swipe gesture to continue or dismiss, or swiping an item to delete it, use Swipe instead if available in the current Action Space. Supports scrollToBottom/scrollToTop for boundary navigation. Default: direction `down`, scrollType `singleAction`, distance `null`.',\n interfaceAlias: 'aiScroll',\n paramSchema: actionScrollParamSchema,\n sample: {\n direction: 'down',\n scrollType: 'singleAction',\n locate: { prompt: 'the center of the product list area' },\n },\n call: async (param) => {\n await scroll(param);\n },\n });\n};\n\n// DragAndDrop\nexport const actionDragAndDropParamSchema = z.object({\n from: getMidsceneLocationSchema().describe('The position to be dragged'),\n to: getMidsceneLocationSchema().describe('The position to be dropped'),\n});\nexport type ActionDragAndDropParam = {\n from: LocateResultElement;\n to: LocateResultElement;\n};\n\nexport const defineActionDragAndDrop = (\n dragAndDrop: NonNullable<PointerInputPrimitives['dragAndDrop']>,\n): DeviceAction<ActionDragAndDropParam> => {\n return defineAction<\n typeof actionDragAndDropParamSchema,\n ActionDragAndDropParam\n >({\n name: 'DragAndDrop',\n description:\n 'Pick up a specific UI element and move it to a new position (e.g., reorder a card, move a file into a folder, sort list items). The element itself moves with your finger/mouse.',\n interfaceAlias: 'aiDragAndDrop',\n paramSchema: actionDragAndDropParamSchema,\n sample: {\n from: { prompt: 'the \"report.pdf\" file icon' },\n to: { prompt: 'the upload drop zone' },\n },\n call: async (param) => {\n const from = param.from;\n const to = param.to;\n if (!from) {\n throw new Error('missing \"from\" param for drag and drop');\n }\n if (!to) {\n throw new Error('missing \"to\" param for drag and drop');\n }\n await dragAndDrop(\n { x: from.center[0], y: from.center[1] },\n { x: to.center[0], y: to.center[1] },\n );\n },\n });\n};\n\nexport const ActionLongPressParamSchema = z.object({\n locate: getMidsceneLocationSchema().describe(\n 'The element to be long pressed',\n ),\n duration: z\n .number()\n .optional()\n .describe('Long press duration in milliseconds'),\n});\n\nexport type ActionLongPressParam = {\n locate: LocateResultElement;\n duration?: number;\n};\nexport const defineActionLongPress = (\n longPress: NonNullable<PointerInputPrimitives['longPress']>,\n): DeviceAction<ActionLongPressParam> => {\n return defineLocatedPointAction<\n typeof ActionLongPressParamSchema,\n ActionLongPressParam\n >({\n name: 'LongPress',\n description: 'Long press the element',\n interfaceAlias: 'aiLongPress',\n paramSchema: ActionLongPressParamSchema,\n sample: {\n locate: { prompt: 'the message bubble' },\n },\n missingLocateMessage: 'LongPress requires an element to be located',\n call: async (point, param) => {\n await longPress(point, { duration: param.duration });\n },\n });\n};\n\nfunction createActionSwipeParamSchema(inputMode: SwipeInputMode) {\n const movementSource = inputMode === 'touch' ? 'finger' : 'pointer';\n\n return z.object({\n start: getMidsceneLocationSchema()\n .optional()\n .describe(\n `Optional starting point of the ${movementSource} movement. Available in both relative and endpoint forms. If omitted, the center of the page is used.`,\n ),\n direction: z\n .enum(['up', 'down', 'left', 'right'])\n .optional()\n .describe(\n `${movementSource === 'finger' ? 'Finger' : 'Pointer'} movement direction. Required together with a positive distance for a relative swipe. Omit when using end.`,\n ),\n distance: z\n .number()\n .positive()\n .optional()\n .describe(\n `Positive length of the ${movementSource} movement in pixels. Required together with direction for a relative swipe. Omit when using end.`,\n ),\n end: getMidsceneLocationSchema()\n .optional()\n .describe(\n `Endpoint of the ${movementSource} movement. Use for an endpoint swipe, optionally with start. Do not provide direction or distance when using end.`,\n ),\n duration: z\n .number()\n .default(300)\n .describe('Duration of the swipe gesture in milliseconds'),\n repeat: z\n .number()\n .optional()\n .describe(\n 'The number of times to repeat the swipe gesture. 1 for default, 0 for infinite (e.g. endless swipe until the end of the page)',\n ),\n });\n}\n\n// Preserve the historical touch-oriented public schema. Desktop actions use\n// an equivalent schema with mouse-specific descriptions at definition time.\nexport const ActionSwipeParamSchema = createActionSwipeParamSchema('touch');\n\nexport type ActionSwipeParam = {\n start?: LocateResultElement;\n direction?: 'up' | 'down' | 'left' | 'right';\n distance?: number;\n end?: LocateResultElement;\n duration?: number;\n repeat?: number;\n};\n\nexport function normalizeSwipeParam(\n param: ActionSwipeParam,\n screenSize: { width: number; height: number },\n): {\n startPoint: { x: number; y: number };\n endPoint: { x: number; y: number };\n duration: number;\n repeatCount: number;\n} {\n const { width, height } = screenSize;\n const { start, end } = param;\n\n const startPoint = start\n ? { x: start.center[0], y: start.center[1] }\n : { x: width / 2, y: height / 2 };\n\n const endPoint = (() => {\n if (end) {\n if (param.direction !== undefined || param.distance !== undefined) {\n throw new Error(\n 'Invalid Swipe parameters: \"end\" cannot be combined with \"direction\" or \"distance\". Use \"end\", optionally with \"start\", for an endpoint swipe.',\n );\n }\n\n return { x: end.center[0], y: end.center[1] };\n }\n\n if (param.direction === undefined || param.distance === undefined) {\n throw new Error(\n 'Invalid Swipe parameters: a relative swipe requires both \"direction\" and a positive \"distance\".',\n );\n }\n\n if (param.distance <= 0) {\n throw new Error(\n 'Invalid Swipe parameters: \"distance\" must be a positive number.',\n );\n }\n\n return {\n x:\n startPoint.x +\n (param.direction === 'right'\n ? param.distance\n : param.direction === 'left'\n ? -param.distance\n : 0),\n y:\n startPoint.y +\n (param.direction === 'down'\n ? param.distance\n : param.direction === 'up'\n ? -param.distance\n : 0),\n };\n })();\n\n endPoint.x = Math.max(0, Math.min(endPoint.x, width));\n endPoint.y = Math.max(0, Math.min(endPoint.y, height));\n\n const duration = param.duration ?? 300;\n\n let repeatCount = typeof param.repeat === 'number' ? param.repeat : 1;\n if (repeatCount === 0) {\n repeatCount = 10;\n }\n\n return { startPoint, endPoint, duration, repeatCount };\n}\n\n/** @deprecated Use {@link normalizeSwipeParam} instead. */\nexport const normalizeMobileSwipeParam = normalizeSwipeParam;\n\nexport const defineActionSwipe = (config: {\n swipe: SwipeInputPrimitive;\n size(): Promise<Size>;\n inputMode?: SwipeInputMode;\n}): DeviceAction<ActionSwipeParam> => {\n const inputMode = config.inputMode ?? 'touch';\n return defineAction<typeof ActionSwipeParamSchema, ActionSwipeParam>({\n name: 'Swipe',\n description:\n inputMode === 'touch'\n ? 'Perform a touch gesture that directly manipulates the UI (e.g., adjust a continuous control such as a slider or wheel picker, switch between paged cards or images, follow an on-screen swipe gesture to continue or dismiss, or swipe an item to delete it). For browsing off-screen content in a page or scrollable region, use Scroll instead. Choose exactly one movement form: (1) relative swipe — provide \"direction\" and a positive \"distance\"; or (2) endpoint swipe — provide \"end\". \"start\" is optional for both forms and defaults to the center of the page. Do not combine \"end\" with \"direction\" or \"distance\".'\n : 'Perform a primary-mouse-button gesture that continuously presses and moves the pointer across the desktop UI. Use it to adjust a continuous control such as a slider or wheel picker, switch between paged cards or images, follow an on-screen swipe gesture to continue or dismiss, or swipe an item to delete it. For browsing off-screen content in a page or scrollable region, use Scroll instead. Choose exactly one movement form: (1) relative swipe — provide \"direction\" and a positive \"distance\"; or (2) endpoint swipe — provide \"end\". \"start\" is optional for both forms and defaults to the center of the page. Do not combine \"end\" with \"direction\" or \"distance\".',\n interfaceAlias: 'aiSwipe',\n paramSchema:\n inputMode === 'touch'\n ? ActionSwipeParamSchema\n : createActionSwipeParamSchema(inputMode),\n sample: {\n start: { prompt: 'center of the notification' },\n end: { prompt: 'upper edge of the screen' },\n },\n call: async (param) => {\n const { startPoint, endPoint, duration, repeatCount } =\n normalizeSwipeParam(param, await config.size());\n for (let i = 0; i < repeatCount; i++) {\n await config.swipe(startPoint, endPoint, { duration });\n }\n },\n });\n};\n\n// ClearInput\nexport const actionClearInputParamSchema = z.object({\n locate: getMidsceneLocationSchema()\n .describe('The input field to be cleared')\n .optional(),\n});\nexport type ActionClearInputParam = {\n locate?: LocateResultElement;\n};\n\nexport const defineActionClearInput = (\n clearInput: KeyboardInputPrimitives['clearInput'],\n): DeviceAction<ActionClearInputParam> => {\n return defineAction<\n typeof actionClearInputParamSchema,\n ActionClearInputParam\n >({\n name: 'ClearInput',\n description: inputLocateDescription,\n interfaceAlias: 'aiClearInput',\n paramSchema: actionClearInputParamSchema,\n sample: {\n locate: { prompt: 'the search input field' },\n },\n call: async (param) => {\n await clearInput(param.locate);\n },\n });\n};\n\n// CursorMove\nexport const actionCursorMoveParamSchema = z.object({\n direction: z\n .enum(['left', 'right'])\n .describe('The direction to move the cursor'),\n times: z\n .number()\n .int()\n .min(1)\n .default(1)\n .describe(\n 'The number of times to move the cursor in the specified direction',\n ),\n});\nexport type ActionCursorMoveParam = {\n direction: 'left' | 'right';\n times?: number;\n};\n\nexport const defineActionCursorMove = (config: {\n keyboard: Pick<KeyboardInputPrimitives, 'keyboardPress' | 'cursorMove'>;\n sleep?(timeMs: number): Promise<void>;\n}): DeviceAction<ActionCursorMoveParam> => {\n return defineAction<\n typeof actionCursorMoveParamSchema,\n ActionCursorMoveParam\n >({\n name: 'CursorMove',\n description:\n 'Move the text cursor (caret) left or right within an input field or text area. Use this to reposition the cursor without selecting text.',\n paramSchema: actionCursorMoveParamSchema,\n sample: {\n direction: 'left',\n times: 3,\n },\n call: async (param) => {\n const times = param.times ?? 1;\n if (config.keyboard.cursorMove) {\n await config.keyboard.cursorMove(param.direction, times);\n return;\n }\n\n const wait =\n config.sleep ??\n ((timeMs: number) =>\n new Promise<void>((resolve) => setTimeout(resolve, timeMs)));\n const arrowKey = param.direction === 'left' ? 'ArrowLeft' : 'ArrowRight';\n for (let i = 0; i < times; i++) {\n await config.keyboard.keyboardPress(arrowKey);\n await wait(100);\n }\n },\n });\n};\n\n// Pinch\nexport const ActionPinchParamSchema = z.object({\n locate: getMidsceneLocationSchema()\n .optional()\n .describe(\n 'The element to pinch on. If not specified, the center of the screen will be used',\n ),\n direction: z\n .enum(['in', 'out'])\n .describe(\n 'Pinch direction. \"in\" = pinch fingers together (zoom out / shrink), \"out\" = spread fingers apart (zoom in / enlarge).',\n ),\n distance: z\n .number()\n .positive()\n .optional()\n .describe(\n 'How far each finger moves in pixels. Defaults to a quarter of the shorter screen dimension.',\n ),\n duration: z\n .number()\n .default(500)\n .optional()\n .describe('Duration of the pinch gesture in milliseconds'),\n});\n\nexport type ActionPinchParam = {\n locate?: LocateResultElement;\n direction: 'in' | 'out';\n distance?: number;\n duration?: number;\n};\n\nexport const defineActionPinch = (config: {\n pinch: TouchInputPrimitives['pinch'];\n size(): Promise<Size>;\n}): DeviceAction<ActionPinchParam> | undefined => {\n if (!config.pinch) {\n return undefined;\n }\n\n return defineAction<typeof ActionPinchParamSchema, ActionPinchParam>({\n name: 'Pinch',\n description:\n 'Perform a two-finger pinch gesture. Use direction \"in\" to pinch fingers together (zoom out), or \"out\" to spread fingers apart (zoom in). Optionally specify distance for how far each finger moves.',\n interfaceAlias: 'aiPinch',\n paramSchema: ActionPinchParamSchema,\n sample: {\n locate: { prompt: 'the map area' },\n direction: 'out',\n distance: 200,\n },\n call: async (param) => {\n const { centerX, centerY, startDistance, endDistance, duration } =\n normalizePinchParam(param, await config.size());\n await config.pinch?.(\n { x: centerX, y: centerY },\n { startDistance, endDistance, duration },\n );\n },\n });\n};\n\nexport function normalizePinchParam(\n param: ActionPinchParam,\n screenSize: { width: number; height: number },\n): {\n centerX: number;\n centerY: number;\n startDistance: number;\n endDistance: number;\n duration: number;\n} {\n const { width, height } = screenSize;\n const element = param.locate;\n const centerX = element\n ? Math.round(element.center[0])\n : Math.round(width / 2);\n const centerY = element\n ? Math.round(element.center[1])\n : Math.round(height / 2);\n const duration = param.duration ?? 500;\n\n const baseDistance = Math.round(Math.min(width, height) / 4);\n const fingerDistance = param.distance ?? baseDistance;\n\n const startDistance = baseDistance;\n const endDistance =\n param.direction === 'out'\n ? baseDistance + fingerDistance\n : Math.max(10, baseDistance - fingerDistance);\n\n return { centerX, centerY, startDistance, endDistance, duration };\n}\n\nexport interface MobileInputActionContext {\n input: MobileInputPrimitives;\n size(): Promise<Size>;\n sleep?(timeMs: number): Promise<void>;\n getDefaultAutoDismissKeyboard?(): boolean | undefined;\n systemActions?: SystemInputActionOptions;\n}\n\nexport interface SystemInputActionConfig {\n name: string;\n description: string;\n interfaceAlias?: string;\n delayBeforeRunner?: number;\n delayAfterRunner?: number;\n}\n\nexport interface SystemInputActionOptions {\n backButton?: SystemInputActionConfig;\n homeButton?: SystemInputActionConfig;\n recentAppsButton?: SystemInputActionConfig;\n}\n\nexport interface InputPrimitiveActionOptions {\n size?: () => Promise<Size>;\n sleep?: (timeMs: number) => Promise<void>;\n includeSwipe?: boolean;\n includePinch?: boolean;\n systemActions?: SystemInputActionOptions;\n}\n\nfunction defineSystemInputAction(\n config: SystemInputActionConfig,\n call: () => Promise<void>,\n): DeviceAction<undefined, void> {\n return defineAction<undefined, undefined, void>({\n name: config.name,\n description: config.description,\n interfaceAlias: config.interfaceAlias,\n delayBeforeRunner: config.delayBeforeRunner,\n delayAfterRunner: config.delayAfterRunner,\n call,\n });\n}\n\nexport function defineActionsFromInputPrimitives(\n input: InputPrimitives,\n options: InputPrimitiveActionOptions = {},\n): DeviceAction<any>[] {\n const actions: Array<DeviceAction<any> | undefined> = [];\n const { pointer, keyboard, scroll, touch, system } = input;\n\n if (pointer) {\n actions.push(defineActionTap(pointer.tap));\n if (pointer.doubleClick) {\n actions.push(defineActionDoubleClick(pointer.doubleClick));\n }\n if (pointer.rightClick) {\n actions.push(defineActionRightClick(pointer.rightClick));\n }\n if (pointer.hover) {\n actions.push(defineActionHover(pointer.hover));\n }\n if (pointer.dragAndDrop) {\n actions.push(defineActionDragAndDrop(pointer.dragAndDrop));\n }\n if (pointer.longPress) {\n actions.push(defineActionLongPress(pointer.longPress));\n }\n }\n\n if (keyboard) {\n actions.push(\n defineActionInput(keyboard),\n defineActionClearInput(keyboard.clearInput),\n defineActionKeyboardPress(keyboard.keyboardPress),\n defineActionCursorMove({ keyboard, sleep: options.sleep }),\n );\n }\n\n if (scroll) {\n actions.push(defineActionScroll(scroll.scroll));\n }\n\n const swipeConfig = resolveSwipeInputPrimitive({ pointer, touch });\n if (swipeConfig && options.size && options.includeSwipe !== false) {\n actions.push(defineActionSwipe({ ...swipeConfig, size: options.size }));\n }\n\n if (touch?.pinch && options.size && options.includePinch !== false) {\n actions.push(defineActionPinch({ pinch: touch.pinch, size: options.size }));\n }\n\n if (system && options.systemActions) {\n const { systemActions } = options;\n if (system.backButton && systemActions.backButton) {\n actions.push(\n defineSystemInputAction(systemActions.backButton, system.backButton),\n );\n }\n if (system.homeButton && systemActions.homeButton) {\n actions.push(\n defineSystemInputAction(systemActions.homeButton, system.homeButton),\n );\n }\n if (system.recentAppsButton && systemActions.recentAppsButton) {\n actions.push(\n defineSystemInputAction(\n systemActions.recentAppsButton,\n system.recentAppsButton,\n ),\n );\n }\n }\n\n return actions.filter((action): action is DeviceAction<any> =>\n Boolean(action),\n );\n}\n\nexport function createDefaultMobileActions(\n context: MobileInputActionContext,\n): DeviceAction<any>[] {\n return defineActionsFromInputPrimitives(context.input, {\n size: context.size,\n sleep: context.sleep,\n systemActions: context.systemActions,\n });\n}\n\n// Sleep\nexport const ActionSleepParamSchema = z.object({\n timeMs: z\n .number()\n .default(1000)\n .optional()\n .describe('Sleep duration in milliseconds, defaults to 1000ms (1 second)'),\n});\n\nexport type ActionSleepParam = {\n timeMs?: number;\n};\n\nexport const defineActionSleep = (): DeviceAction<ActionSleepParam> => {\n return defineAction<typeof ActionSleepParamSchema, ActionSleepParam>({\n name: 'Sleep',\n description:\n 'Wait for a specified duration before continuing. Defaults to 1 second (1000ms) if not specified.',\n paramSchema: ActionSleepParamSchema,\n sample: {\n timeMs: 2000,\n },\n call: async (param) => {\n const duration = param?.timeMs ?? 1000;\n getDebug('device:common-action')(`Sleeping for ${duration}ms`);\n await new Promise((resolve) => setTimeout(resolve, duration));\n },\n });\n};\n\nexport type { DeviceAction } from '../types';\nexport type { ActionScrollParam, LocateResultElement, Size } from '../types';\nexport { getMidsceneLocationSchema } from '../common';\nexport { z };\nexport {\n inputStrategies,\n type InputStrategy,\n type ResolvedTextInputOptions,\n type SequentialTextInputHandlers,\n type TextInputOptions,\n resolveInputStrategy,\n resolveTextInputOptions,\n sendTextSequentially,\n shouldInputSequentially,\n} from './input-strategy';\nexport type {\n AndroidDeviceOpt,\n AndroidDeviceInputOpt,\n IOSDeviceOpt,\n IOSDeviceInputOpt,\n HarmonyDeviceOpt,\n HarmonyDeviceInputOpt,\n} from './device-options';\n"],"names":["resolveSwipeInputPrimitive","input","AbstractInterface","defineAction","config","pointFromLocate","locate","missingMessage","Error","defineLocatedPointAction","param","actionTapParamSchema","z","getMidsceneLocationSchema","defineActionTap","tap","point","registerFileChooserAcceptParamSchema","defineActionRegisterFileChooserAccept","register","actionRightClickParamSchema","defineActionRightClick","rightClick","actionDoubleClickParamSchema","defineActionDoubleClick","doubleClick","actionHoverParamSchema","defineActionHover","hover","inputLocateDescription","actionInputParamSchema","val","String","inputStrategies","defineActionInput","keyboard","actionKeyboardPressParamSchema","defineActionKeyboardPress","keyboardPress","actionScrollParamSchema","defineActionScroll","scroll","actionDragAndDropParamSchema","defineActionDragAndDrop","dragAndDrop","from","to","ActionLongPressParamSchema","defineActionLongPress","longPress","createActionSwipeParamSchema","inputMode","movementSource","ActionSwipeParamSchema","normalizeSwipeParam","screenSize","width","height","start","end","startPoint","endPoint","undefined","Math","duration","repeatCount","normalizeMobileSwipeParam","defineActionSwipe","i","actionClearInputParamSchema","defineActionClearInput","clearInput","actionCursorMoveParamSchema","defineActionCursorMove","times","wait","timeMs","Promise","resolve","setTimeout","arrowKey","ActionPinchParamSchema","defineActionPinch","centerX","centerY","startDistance","endDistance","normalizePinchParam","element","baseDistance","fingerDistance","defineSystemInputAction","call","defineActionsFromInputPrimitives","options","actions","pointer","touch","system","swipeConfig","systemActions","action","Boolean","createDefaultMobileActions","context","ActionSleepParamSchema","defineActionSleep","getDebug"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"device/index.mjs","sources":["../../../src/device/index.ts"],"sourcesContent":["import type { ModelRuntime } from '@/ai-model/models';\nimport { getMidsceneLocationSchema } from '@/common';\nimport type {\n ActionScrollParam,\n DeviceAction,\n ExecutorContext,\n LocateResultElement,\n} from '@/types';\nimport type { ElementNode } from '@midscene/shared/extractor';\nimport { getDebug } from '@midscene/shared/logger';\nimport { _keyDefinitions } from '@midscene/shared/us-keyboard-layout';\nimport { z } from 'zod';\nimport type {\n ElementCacheFeature,\n Rect,\n Size,\n UIContext,\n UITreeSnapshot,\n} from '../types';\nimport { type InputStrategy, inputStrategies } from './input-strategy';\n\nexport interface FileChooserHandler {\n accept(files: string[]): Promise<void>;\n}\n\nexport interface FileChooserRegistration {\n dispose: () => void;\n getError: () => Error | undefined | Promise<Error | undefined>;\n}\n\nexport interface MjpegStreamFrame {\n /** Raw base64-encoded image bytes WITHOUT a `data:image/...;base64,` prefix. */\n data: string;\n contentType?: string;\n}\n\nexport interface MjpegStreamHandle {\n stop(): void | Promise<void>;\n}\n\nexport interface MjpegStreamOptions {\n signal?: AbortSignal;\n onFrame(frame: MjpegStreamFrame): void;\n onError?(error: unknown): void;\n}\n\n/**\n * A cheap, not-yet-decoded handle to one screen frame from a\n * {@link DeviceFrameSource}. `ref` is platform-specific (a raw H.264 keyframe\n * buffer on Android, an already-encoded JPEG data URL on iOS/web) and must not\n * be interpreted by callers — pass it back to `decode()` to materialize.\n */\nexport interface DeviceFrameRef {\n ref: unknown;\n capturedAt: number;\n}\n\n/**\n * A continuous screen-frame source opened via\n * {@link AbstractInterface.openFrameSource}. Designed for deferred decoding:\n * grabbing `latest()` is near-zero cost, so observers can sample at a steady\n * cadence and pay any decode cost only once, for the frames they keep.\n */\nexport interface DeviceFrameSource {\n /** Latest frame handle, near-zero cost. Null until the first frame arrives. */\n latest(): DeviceFrameRef | null;\n /**\n * Materialize frame handles into `data:image/...;base64,` URLs, preserving\n * order. Possibly expensive (e.g. one ffmpeg run per unique frame on\n * Android) — call once with the sampled handles, never per tick.\n */\n decode(refs: DeviceFrameRef[]): Promise<string[]>;\n /** Release the source (stop streams/subscriptions it started). */\n stop(): Promise<void> | void;\n}\n\n/** A point in device-pixel coordinates on the screen. */\nexport interface PointerPoint {\n x: number;\n y: number;\n}\n\n/** Low-level swipe gesture implemented by touch or pointer-backed devices. */\nexport type SwipeInputPrimitive = (\n start: PointerPoint,\n end: PointerPoint,\n opts?: { duration?: number; repeat?: number },\n) => Promise<void>;\n\n/** The physical input source used to execute and describe a swipe. */\nexport type SwipeInputMode = 'touch' | 'mouse';\n\n/** A resolved swipe capability and the input semantics exposed to the model. */\nexport interface ResolvedSwipeInputPrimitive {\n swipe: SwipeInputPrimitive;\n inputMode: SwipeInputMode;\n}\n\nexport interface PointerInputPrimitives {\n tap(p: PointerPoint, opts?: { duration?: number }): Promise<void>;\n doubleClick?(p: PointerPoint): Promise<void>;\n rightClick?(p: PointerPoint): Promise<void>;\n hover?(p: PointerPoint): Promise<void>;\n longPress?(p: PointerPoint, opts?: { duration?: number }): Promise<void>;\n dragAndDrop?(from: PointerPoint, to: PointerPoint): Promise<void>;\n swipe?: SwipeInputPrimitive;\n}\n\nexport interface TouchInputPrimitives {\n swipe: SwipeInputPrimitive;\n pinch?(\n center: PointerPoint,\n opts: { startDistance: number; endDistance: number; duration: number },\n ): Promise<void>;\n}\n\nexport interface KeyboardInputPrimitives {\n keyboardPress(keyName: string, opts?: { target?: unknown }): Promise<void>;\n cursorMove?(direction: 'left' | 'right', times?: number): Promise<void>;\n typeText(\n value: string,\n opts?: {\n autoDismissKeyboard?: boolean;\n keyboardDismissStrategy?: 'esc-first' | 'back-first';\n keyboardTypeDelay?: number;\n inputStrategy?: InputStrategy;\n target?: unknown;\n replace?: boolean;\n focusOnly?: boolean;\n },\n ): Promise<void>;\n clearInput(target?: unknown): Promise<void>;\n}\n\nexport interface ScrollInputPrimitives {\n scroll(param: ActionScrollParam): Promise<void>;\n}\n\nexport interface SystemInputPrimitives {\n backButton?(): Promise<void>;\n homeButton?(): Promise<void>;\n recentAppsButton?(): Promise<void>;\n}\n\nexport interface InputPrimitives {\n pointer?: PointerInputPrimitives;\n keyboard?: KeyboardInputPrimitives;\n touch?: TouchInputPrimitives;\n scroll?: ScrollInputPrimitives;\n system?: SystemInputPrimitives;\n}\n\n/**\n * Resolve the canonical swipe capability for an input collection.\n * Touch remains preferred for devices that expose both capabilities; desktop\n * devices can fall back to a pointer-backed primary-button gesture.\n */\nexport function resolveSwipeInputPrimitive(\n input: Pick<InputPrimitives, 'pointer' | 'touch'>,\n): ResolvedSwipeInputPrimitive | undefined {\n if (input.touch?.swipe) {\n return { swipe: input.touch.swipe, inputMode: 'touch' };\n }\n if (input.pointer?.swipe) {\n return { swipe: input.pointer.swipe, inputMode: 'mouse' };\n }\n return undefined;\n}\n\nexport interface MobileInputPrimitives extends InputPrimitives {\n pointer: PointerInputPrimitives & {\n doubleClick(p: PointerPoint): Promise<void>;\n longPress(p: PointerPoint, opts?: { duration?: number }): Promise<void>;\n dragAndDrop(from: PointerPoint, to: PointerPoint): Promise<void>;\n };\n keyboard: KeyboardInputPrimitives;\n touch: TouchInputPrimitives;\n}\n\nexport interface BrowserInputPrimitives extends InputPrimitives {\n pointer: PointerInputPrimitives & {\n doubleClick(p: PointerPoint): Promise<void>;\n rightClick(p: PointerPoint): Promise<void>;\n hover(p: PointerPoint): Promise<void>;\n dragAndDrop(from: PointerPoint, to: PointerPoint): Promise<void>;\n longPress(p: PointerPoint, opts?: { duration?: number }): Promise<void>;\n };\n keyboard: KeyboardInputPrimitives;\n scroll: ScrollInputPrimitives;\n touch: TouchInputPrimitives;\n}\n\nexport interface ComputerInputPrimitives extends InputPrimitives {\n pointer: PointerInputPrimitives & {\n doubleClick(p: PointerPoint): Promise<void>;\n rightClick(p: PointerPoint): Promise<void>;\n hover(p: PointerPoint): Promise<void>;\n dragAndDrop(from: PointerPoint, to: PointerPoint): Promise<void>;\n };\n keyboard: KeyboardInputPrimitives;\n scroll: ScrollInputPrimitives;\n}\n\nexport abstract class AbstractInterface {\n abstract interfaceType: string;\n\n abstract screenshotBase64(): Promise<string>;\n abstract size(): Promise<Size>;\n abstract actionSpace(): DeviceAction[];\n\n abstract cacheFeatureForPoint?(\n center: [number, number],\n options?: {\n targetDescription?: string;\n modelRuntime?: ModelRuntime;\n },\n ): Promise<ElementCacheFeature>;\n abstract rectMatchesCacheFeature?(\n feature: ElementCacheFeature,\n ): Promise<Rect>;\n\n abstract getUITree?(): Promise<UITreeSnapshot>;\n\n abstract destroy?(): Promise<void>;\n\n abstract describe?(): string;\n abstract beforeInvokeAction?(actionName: string, param: any): Promise<void>;\n abstract afterInvokeAction?(actionName: string, param: any): Promise<void>;\n\n // for web only\n registerFileChooserListener?(\n handler: (chooser: FileChooserHandler) => Promise<void>,\n ): Promise<FileChooserRegistration>;\n\n // @deprecated do NOT extend this method\n abstract getElementsNodeTree?: () => Promise<ElementNode>;\n\n // @deprecated do NOT extend this method\n abstract url?: () => string | Promise<string>;\n\n // @deprecated do NOT extend this method\n abstract evaluateJavaScript?<T = any>(script: string): Promise<T>;\n\n /**\n * Get the current device-local time as a formatted string.\n * Prefer this for user-visible time because timestamps alone do not preserve\n * the target device's timezone when formatted on the host machine.\n */\n getDeviceLocalTimeString?(format?: string): Promise<string>;\n\n /** URL of native MJPEG stream for real-time screen preview (e.g. WDA MJPEG server) */\n mjpegStreamUrl?: string;\n\n /**\n * Optional continuous frame source for UI observation (`startObserving`).\n * Devices that maintain a continuous frame stream — scrcpy on Android, WDA\n * MJPEG on iOS, CDP screencast on web — implement this so an observer can\n * sample the screen far faster than repeated `screenshotBase64()` calls,\n * catching short-lived UI (toasts, carousels, transitions).\n *\n * The contract enables DEFERRED decoding: `latest()` returns a cheap opaque\n * handle (e.g. a raw H.264 keyframe on Android) with no per-frame decode\n * cost, and `decode()` materializes only the handles that were actually\n * sampled — once, at the end of the observation window.\n */\n openFrameSource?(): Promise<DeviceFrameSource | undefined>;\n\n /**\n * Optional in-process MJPEG frame producer. Implementations can push raw\n * base64 frames here when there is no standalone native MJPEG URL, e.g.\n * Chromium CDP Page.startScreencast for web previews.\n */\n startMjpegStream?(\n options: MjpegStreamOptions,\n ): MjpegStreamHandle | undefined | Promise<MjpegStreamHandle | undefined>;\n\n /**\n * Optional hook used after a UI action to push a fresh frame on the active\n * MJPEG stream. Set `force` after navigation to replace a transient loading\n * frame even when the screencast has already emitted one. Implementations\n * should be a no-op when no stream is active.\n */\n flushPendingVisualUpdate?(force?: boolean): Promise<void>;\n\n /**\n * Optional non-blocking variant of `flushPendingVisualUpdate`. Keyboard-\n * heavy preview interactions can schedule a coalesced refresh here without\n * stalling the input hot path; `force` preserves a requested navigation\n * refresh while work is already queued.\n */\n schedulePendingVisualUpdate?(force?: boolean): void;\n\n /**\n * Optional navigation state probe for browser-like interfaces, used to drive\n * loading indicators in playground UIs. Returning `undefined` means the\n * interface does not expose this concept.\n */\n navigationState?(): Promise<{ isLoading: boolean }>;\n\n /**\n * Low-level device input surface. Platform implementations expose transport\n * primitives here; higher-level AI actions and manual pointer dispatch should\n * adapt to this instead of duplicating platform gesture logic.\n */\n inputPrimitives?: InputPrimitives;\n}\n\n// Generic function to define actions with proper type inference\n// TRuntime allows specifying a different type for the runtime parameter (after location resolution)\n// TReturn allows specifying the return type of the action\nexport const defineAction = <\n TSchema extends z.ZodType | undefined = undefined,\n TRuntime = TSchema extends z.ZodType ? z.infer<TSchema> : undefined,\n TReturn = any,\n>(\n config: {\n name: string;\n description: string;\n interfaceAlias?: string;\n paramSchema?: TSchema;\n call: (\n param: TRuntime,\n context?: ExecutorContext,\n ) => Promise<TReturn> | TReturn;\n } & Partial<\n Omit<\n DeviceAction<TRuntime, TReturn>,\n 'name' | 'description' | 'interfaceAlias' | 'paramSchema' | 'call'\n >\n >,\n): DeviceAction<TRuntime, TReturn> => {\n return config as any; // Type assertion needed because schema validation type differs from runtime type\n};\n\nfunction pointFromLocate(\n locate: LocateResultElement | undefined,\n missingMessage: string,\n): PointerPoint {\n if (!locate) {\n throw new Error(missingMessage);\n }\n return { x: locate.center[0], y: locate.center[1] };\n}\n\nfunction defineLocatedPointAction<\n TSchema extends z.ZodType,\n TParam extends { locate: LocateResultElement },\n>(config: {\n name: string;\n description: string;\n interfaceAlias?: string;\n paramSchema: TSchema;\n sample: DeviceAction<TParam>['sample'];\n missingLocateMessage: string;\n call: (point: PointerPoint, param: TParam) => Promise<void>;\n}): DeviceAction<TParam> {\n return defineAction<TSchema, TParam>({\n name: config.name,\n description: config.description,\n interfaceAlias: config.interfaceAlias,\n paramSchema: config.paramSchema,\n sample: config.sample,\n call: async (param) => {\n await config.call(\n pointFromLocate(param.locate, config.missingLocateMessage),\n param,\n );\n },\n });\n}\n\n// Tap\nexport const actionTapParamSchema = z.object({\n locate: getMidsceneLocationSchema().describe('The element to be tapped'),\n});\nexport type ActionTapParam = {\n locate: LocateResultElement;\n};\n\nexport const defineActionTap = (\n tap: PointerInputPrimitives['tap'],\n): DeviceAction<ActionTapParam> => {\n return defineLocatedPointAction<typeof actionTapParamSchema, ActionTapParam>({\n name: 'Tap',\n description: 'Tap the element',\n interfaceAlias: 'aiTap',\n paramSchema: actionTapParamSchema,\n sample: {\n locate: { prompt: 'the \"Submit\" button' },\n },\n missingLocateMessage: 'Element not found, cannot tap',\n call: async (point) => {\n await tap(point);\n },\n });\n};\n\nexport const registerFileChooserAcceptParamSchema = z.object({\n files: z\n .union([z.string(), z.array(z.string())])\n .describe(\n \"File path(s) within the current aiAct call's fileChooserAllowedDir to use whenever a later action triggers a file chooser. fileChooserAllowedDir must be provided for this action. This setting replaces any previously registered file path(s).\",\n ),\n});\nexport type RegisterFileChooserAcceptParam = {\n files: string | string[];\n};\n\nexport const defineActionRegisterFileChooserAccept = (\n register: (files: string | string[]) => Promise<void>,\n): DeviceAction<RegisterFileChooserAcceptParam> => {\n return defineAction<\n typeof registerFileChooserAcceptParamSchema,\n RegisterFileChooserAcceptParam\n >({\n name: 'RegisterFileChooserAccept',\n description:\n 'Configure files for file chooser dialogs triggered by later actions in this aiAct',\n interfaceAlias: 'registerFileChooserAccept',\n paramSchema: registerFileChooserAcceptParamSchema,\n sample: {\n files: ['fixtures/document.pdf'],\n },\n call: async (param) => {\n await register(param.files);\n },\n });\n};\n\n// RightClick\nexport const actionRightClickParamSchema = z.object({\n locate: getMidsceneLocationSchema().describe(\n 'The element to be right clicked',\n ),\n});\nexport type ActionRightClickParam = {\n locate: LocateResultElement;\n};\n\nexport const defineActionRightClick = (\n rightClick: NonNullable<PointerInputPrimitives['rightClick']>,\n): DeviceAction<ActionRightClickParam> => {\n return defineLocatedPointAction<\n typeof actionRightClickParamSchema,\n ActionRightClickParam\n >({\n name: 'RightClick',\n description: 'Right click the element',\n interfaceAlias: 'aiRightClick',\n paramSchema: actionRightClickParamSchema,\n sample: {\n locate: { prompt: 'the file icon on the desktop' },\n },\n missingLocateMessage: 'Element not found, cannot right click',\n call: async (point) => {\n await rightClick(point);\n },\n });\n};\n\n// DoubleClick\nexport const actionDoubleClickParamSchema = z.object({\n locate: getMidsceneLocationSchema().describe(\n 'The element to be double clicked',\n ),\n});\nexport type ActionDoubleClickParam = {\n locate: LocateResultElement;\n};\n\nexport const defineActionDoubleClick = (\n doubleClick: NonNullable<PointerInputPrimitives['doubleClick']>,\n): DeviceAction<ActionDoubleClickParam> => {\n return defineLocatedPointAction<\n typeof actionDoubleClickParamSchema,\n ActionDoubleClickParam\n >({\n name: 'DoubleClick',\n description: 'Double click the element',\n interfaceAlias: 'aiDoubleClick',\n paramSchema: actionDoubleClickParamSchema,\n sample: {\n locate: { prompt: 'the folder icon' },\n },\n missingLocateMessage: 'Element not found, cannot double click',\n call: async (point) => {\n await doubleClick(point);\n },\n });\n};\n\n// Hover\nexport const actionHoverParamSchema = z.object({\n locate: getMidsceneLocationSchema().describe('The element to be hovered'),\n});\nexport type ActionHoverParam = {\n locate: LocateResultElement;\n};\n\nexport const defineActionHover = (\n hover: NonNullable<PointerInputPrimitives['hover']>,\n): DeviceAction<ActionHoverParam> => {\n return defineLocatedPointAction<\n typeof actionHoverParamSchema,\n ActionHoverParam\n >({\n name: 'Hover',\n description: 'Move the mouse to the element',\n interfaceAlias: 'aiHover',\n paramSchema: actionHoverParamSchema,\n sample: {\n locate: { prompt: 'the navigation menu item \"Products\"' },\n },\n missingLocateMessage: 'Element not found, cannot hover',\n call: async (point) => {\n await hover(point);\n },\n });\n};\n\n// Input\nconst inputLocateDescription =\n 'the position of the placeholder or text content in the target input field. If there is no content, locate the center of the input field.';\nexport const actionInputParamSchema = z.object({\n value: z\n .union([z.string(), z.number()])\n .transform((val) => String(val))\n .describe(\n 'The text to input. Provide the final content for replace mode, only the inserted characters for typeOnly mode, or an empty string when using clear mode to remove existing text.',\n ),\n locate: getMidsceneLocationSchema()\n .describe(inputLocateDescription)\n .optional(),\n mode: z\n .enum(['replace', 'clear', 'typeOnly'])\n .default('replace')\n .describe(\n 'Input mode: \"replace\" (default) - clear the field and input the value; \"typeOnly\" - type the value directly without clearing the field first, and should be set explicitly for incremental edits after moving the cursor; \"clear\" - clear the field without inputting new text.',\n ),\n autoDismissKeyboard: z\n .boolean()\n .optional()\n .describe(\n 'If true, the keyboard will be dismissed after the input is completed. Do not set it unless the user asks you to do so.',\n ),\n keyboardTypeDelay: z\n .number()\n .finite()\n .nonnegative()\n .optional()\n .describe(\n 'Delay in milliseconds between keystrokes when typing. Passed through from device/user configuration. Do not set it unless the user asks you to do so.',\n ),\n inputStrategy: z\n .enum(inputStrategies)\n .optional()\n .describe(\n 'Text input strategy: \"legacy\" (default) preserves the current platform behavior; \"sequential\" enters one Unicode code point at a time; \"bulk\" sends the complete text through one platform input operation when supported and requires keyboardTypeDelay to be omitted or set to 0. Do not set it unless the user asks you to do so.',\n ),\n});\nexport type ActionInputParam = {\n value: string;\n locate?: LocateResultElement;\n mode?: 'replace' | 'clear' | 'typeOnly' | 'append';\n autoDismissKeyboard?: boolean;\n keyboardTypeDelay?: number;\n inputStrategy?: InputStrategy;\n};\n\nexport const defineActionInput = (\n keyboard: KeyboardInputPrimitives,\n): DeviceAction<ActionInputParam> => {\n return defineAction<typeof actionInputParamSchema, ActionInputParam>({\n name: 'Input',\n description: 'Input the value into the element',\n interfaceAlias: 'aiInput',\n paramSchema: actionInputParamSchema,\n sample: {\n value: 'test@example.com',\n locate: { prompt: 'the email input field' },\n },\n call: async (param) => {\n // backward compat: convert deprecated 'append' to 'typeOnly'\n if ((param.mode as string) === 'append') {\n param.mode = 'typeOnly';\n }\n\n if (param.mode === 'clear') {\n await keyboard.clearInput(param.locate);\n return;\n }\n\n if (!param || !param.value) {\n return;\n }\n\n await keyboard.typeText(param.value, {\n target: param.locate,\n replace: param.mode !== 'typeOnly',\n autoDismissKeyboard: param.autoDismissKeyboard,\n keyboardTypeDelay: param.keyboardTypeDelay,\n inputStrategy: param.inputStrategy,\n });\n },\n });\n};\n\n// KeyboardPress\nexport const actionKeyboardPressParamSchema = z.object({\n locate: getMidsceneLocationSchema()\n .describe(\n 'The optional element to click before pressing the key. Omit this when the key should operate on the currently focused element, especially when copying or cutting an existing text selection.',\n )\n .optional(),\n keyName: z\n .string()\n .describe(\n \"The key to be pressed. Use '+' for key combinations, e.g., 'Control+A', 'Shift+Enter'\",\n ),\n});\nexport type ActionKeyboardPressParam = {\n locate?: LocateResultElement;\n keyName: string;\n};\n\nexport const defineActionKeyboardPress = (\n keyboardPress: KeyboardInputPrimitives['keyboardPress'],\n): DeviceAction<ActionKeyboardPressParam> => {\n return defineAction<\n typeof actionKeyboardPressParamSchema,\n ActionKeyboardPressParam\n >({\n name: 'KeyboardPress',\n description:\n 'Press a key or key combination, like \"Enter\", \"Tab\", \"Escape\", or \"Control+A\", \"Shift+Enter\". Do not use this to type text. Omit locate to operate on the current focus without clicking again, especially for Copy or Cut after text has been selected.',\n interfaceAlias: 'aiKeyboardPress',\n paramSchema: actionKeyboardPressParamSchema,\n sample: {\n keyName: 'Enter',\n },\n call: async (param) => {\n await keyboardPress(param.keyName, {\n target: param.locate,\n });\n },\n });\n};\n\n// Scroll\nexport const actionScrollParamSchema = z.object({\n scrollType: z\n .enum([\n 'singleAction',\n 'scrollToBottom',\n 'scrollToTop',\n 'scrollToRight',\n 'scrollToLeft',\n ])\n .default('singleAction')\n .describe(\n 'The scroll behavior: \"singleAction\" for a single scroll action, \"scrollToBottom\" for scrolling all the way to the bottom by rapidly scrolling 5-10 times (skipping intermediate content until reaching the bottom), \"scrollToTop\" for scrolling all the way to the top by rapidly scrolling 5-10 times (skipping intermediate content until reaching the top), \"scrollToRight\" for scrolling all the way to the right by rapidly scrolling multiple times, \"scrollToLeft\" for scrolling all the way to the left by rapidly scrolling multiple times',\n ),\n direction: z\n .enum(['down', 'up', 'right', 'left'])\n .default('down')\n .describe(\n 'The direction toward the off-screen content to reveal. \"down\" reveals content below the current viewport, \"up\" reveals content above, \"right\" reveals content to the right, and \"left\" reveals content to the left. This does not describe the movement direction of the content currently visible on the screen. Only effective when scrollType is \"singleAction\".',\n ),\n distance: z\n .number()\n .positive()\n .nullable()\n .optional()\n .describe(\n 'Positive requested scroll amount in screen-coordinate units. On touch platforms, it controls the length of the swipe gesture used to scroll; on web and desktop platforms, it controls or approximates the wheel scroll delta. Only effective when scrollType is \"singleAction\".',\n ),\n locate: getMidsceneLocationSchema()\n .optional()\n .describe(\n 'Describe the target element to be scrolled on, like \"the table\" or \"the list\" or \"the content area\" or \"the scrollable area\". Do NOT provide a general intent like \"scroll to find some element\"',\n ),\n});\n\nexport const defineActionScroll = (\n scroll: ScrollInputPrimitives['scroll'],\n): DeviceAction<ActionScrollParam> => {\n return defineAction<typeof actionScrollParamSchema, ActionScrollParam>({\n name: 'Scroll',\n description:\n 'Scroll a page or scrollable region to reveal off-screen content. Use Scroll when the goal is to browse content outside the current viewport. For direct gesture interactions, such as adjusting a slider or wheel picker, switching between paged cards or images, following an on-screen swipe gesture to continue or dismiss, or swiping an item to delete it, use Swipe instead if available in the current Action Space. Supports scrollToBottom/scrollToTop for boundary navigation. Default: direction `down`, scrollType `singleAction`, distance `null`.',\n interfaceAlias: 'aiScroll',\n paramSchema: actionScrollParamSchema,\n sample: {\n direction: 'down',\n scrollType: 'singleAction',\n locate: { prompt: 'the center of the product list area' },\n },\n call: async (param) => {\n await scroll(param);\n },\n });\n};\n\n// DragAndDrop\nexport const actionDragAndDropParamSchema = z.object({\n from: getMidsceneLocationSchema().describe('The position to be dragged'),\n to: getMidsceneLocationSchema().describe('The position to be dropped'),\n});\nexport type ActionDragAndDropParam = {\n from: LocateResultElement;\n to: LocateResultElement;\n};\n\nexport const defineActionDragAndDrop = (\n dragAndDrop: NonNullable<PointerInputPrimitives['dragAndDrop']>,\n): DeviceAction<ActionDragAndDropParam> => {\n return defineAction<\n typeof actionDragAndDropParamSchema,\n ActionDragAndDropParam\n >({\n name: 'DragAndDrop',\n description:\n 'Pick up a specific UI element and move it to a new position (e.g., reorder a card, move a file into a folder, sort list items). The element itself moves with your finger/mouse.',\n interfaceAlias: 'aiDragAndDrop',\n paramSchema: actionDragAndDropParamSchema,\n sample: {\n from: { prompt: 'the \"report.pdf\" file icon' },\n to: { prompt: 'the upload drop zone' },\n },\n call: async (param) => {\n const from = param.from;\n const to = param.to;\n if (!from) {\n throw new Error('missing \"from\" param for drag and drop');\n }\n if (!to) {\n throw new Error('missing \"to\" param for drag and drop');\n }\n await dragAndDrop(\n { x: from.center[0], y: from.center[1] },\n { x: to.center[0], y: to.center[1] },\n );\n },\n });\n};\n\nexport const ActionLongPressParamSchema = z.object({\n locate: getMidsceneLocationSchema().describe(\n 'The element to be long pressed',\n ),\n duration: z\n .number()\n .optional()\n .describe('Long press duration in milliseconds'),\n});\n\nexport type ActionLongPressParam = {\n locate: LocateResultElement;\n duration?: number;\n};\nexport const defineActionLongPress = (\n longPress: NonNullable<PointerInputPrimitives['longPress']>,\n): DeviceAction<ActionLongPressParam> => {\n return defineLocatedPointAction<\n typeof ActionLongPressParamSchema,\n ActionLongPressParam\n >({\n name: 'LongPress',\n description: 'Long press the element',\n interfaceAlias: 'aiLongPress',\n paramSchema: ActionLongPressParamSchema,\n sample: {\n locate: { prompt: 'the message bubble' },\n },\n missingLocateMessage: 'LongPress requires an element to be located',\n call: async (point, param) => {\n await longPress(point, { duration: param.duration });\n },\n });\n};\n\nfunction createActionSwipeParamSchema(inputMode: SwipeInputMode) {\n const movementSource = inputMode === 'touch' ? 'finger' : 'pointer';\n\n return z.object({\n start: getMidsceneLocationSchema()\n .optional()\n .describe(\n `Optional starting point of the ${movementSource} movement. Available in both relative and endpoint forms. If omitted, the center of the page is used.`,\n ),\n direction: z\n .enum(['up', 'down', 'left', 'right'])\n .optional()\n .describe(\n `${movementSource === 'finger' ? 'Finger' : 'Pointer'} movement direction. Required together with a positive distance for a relative swipe. Omit when using end.`,\n ),\n distance: z\n .number()\n .positive()\n .optional()\n .describe(\n `Positive length of the ${movementSource} movement in pixels. Required together with direction for a relative swipe. Omit when using end.`,\n ),\n end: getMidsceneLocationSchema()\n .optional()\n .describe(\n `Endpoint of the ${movementSource} movement. Use for an endpoint swipe, optionally with start. Do not provide direction or distance when using end.`,\n ),\n duration: z\n .number()\n .default(300)\n .describe('Duration of the swipe gesture in milliseconds'),\n repeat: z\n .number()\n .optional()\n .describe(\n 'The number of times to repeat the swipe gesture. 1 for default, 0 for infinite (e.g. endless swipe until the end of the page)',\n ),\n });\n}\n\n// Preserve the historical touch-oriented public schema. Desktop actions use\n// an equivalent schema with mouse-specific descriptions at definition time.\nexport const ActionSwipeParamSchema = createActionSwipeParamSchema('touch');\n\nexport type ActionSwipeParam = {\n start?: LocateResultElement;\n direction?: 'up' | 'down' | 'left' | 'right';\n distance?: number;\n end?: LocateResultElement;\n duration?: number;\n repeat?: number;\n};\n\nexport function normalizeSwipeParam(\n param: ActionSwipeParam,\n screenSize: { width: number; height: number },\n): {\n startPoint: { x: number; y: number };\n endPoint: { x: number; y: number };\n duration: number;\n repeatCount: number;\n} {\n const { width, height } = screenSize;\n const { start, end } = param;\n\n const startPoint = start\n ? { x: start.center[0], y: start.center[1] }\n : { x: width / 2, y: height / 2 };\n\n const endPoint = (() => {\n if (end) {\n if (param.direction !== undefined || param.distance !== undefined) {\n throw new Error(\n 'Invalid Swipe parameters: \"end\" cannot be combined with \"direction\" or \"distance\". Use \"end\", optionally with \"start\", for an endpoint swipe.',\n );\n }\n\n return { x: end.center[0], y: end.center[1] };\n }\n\n if (param.direction === undefined || param.distance === undefined) {\n throw new Error(\n 'Invalid Swipe parameters: a relative swipe requires both \"direction\" and a positive \"distance\".',\n );\n }\n\n if (param.distance <= 0) {\n throw new Error(\n 'Invalid Swipe parameters: \"distance\" must be a positive number.',\n );\n }\n\n return {\n x:\n startPoint.x +\n (param.direction === 'right'\n ? param.distance\n : param.direction === 'left'\n ? -param.distance\n : 0),\n y:\n startPoint.y +\n (param.direction === 'down'\n ? param.distance\n : param.direction === 'up'\n ? -param.distance\n : 0),\n };\n })();\n\n endPoint.x = Math.max(0, Math.min(endPoint.x, width));\n endPoint.y = Math.max(0, Math.min(endPoint.y, height));\n\n const duration = param.duration ?? 300;\n\n let repeatCount = typeof param.repeat === 'number' ? param.repeat : 1;\n if (repeatCount === 0) {\n repeatCount = 10;\n }\n\n return { startPoint, endPoint, duration, repeatCount };\n}\n\n/** @deprecated Use {@link normalizeSwipeParam} instead. */\nexport const normalizeMobileSwipeParam = normalizeSwipeParam;\n\nexport const defineActionSwipe = (config: {\n swipe: SwipeInputPrimitive;\n size(): Promise<Size>;\n inputMode?: SwipeInputMode;\n}): DeviceAction<ActionSwipeParam> => {\n const inputMode = config.inputMode ?? 'touch';\n return defineAction<typeof ActionSwipeParamSchema, ActionSwipeParam>({\n name: 'Swipe',\n description:\n inputMode === 'touch'\n ? 'Perform a touch gesture that directly manipulates the UI (e.g., adjust a continuous control such as a slider or wheel picker, switch between paged cards or images, follow an on-screen swipe gesture to continue or dismiss, or swipe an item to delete it). For browsing off-screen content in a page or scrollable region, use Scroll instead. Choose exactly one movement form: (1) relative swipe — provide \"direction\" and a positive \"distance\"; or (2) endpoint swipe — provide \"end\". \"start\" is optional for both forms and defaults to the center of the page. Do not combine \"end\" with \"direction\" or \"distance\".'\n : 'Perform a primary-mouse-button gesture that continuously presses and moves the pointer across the desktop UI. Use it to adjust a continuous control such as a slider or wheel picker, switch between paged cards or images, follow an on-screen swipe gesture to continue or dismiss, or swipe an item to delete it. For browsing off-screen content in a page or scrollable region, use Scroll instead. Choose exactly one movement form: (1) relative swipe — provide \"direction\" and a positive \"distance\"; or (2) endpoint swipe — provide \"end\". \"start\" is optional for both forms and defaults to the center of the page. Do not combine \"end\" with \"direction\" or \"distance\".',\n interfaceAlias: 'aiSwipe',\n paramSchema:\n inputMode === 'touch'\n ? ActionSwipeParamSchema\n : createActionSwipeParamSchema(inputMode),\n sample: {\n start: { prompt: 'center of the notification' },\n end: { prompt: 'upper edge of the screen' },\n },\n call: async (param) => {\n const { startPoint, endPoint, duration, repeatCount } =\n normalizeSwipeParam(param, await config.size());\n for (let i = 0; i < repeatCount; i++) {\n await config.swipe(startPoint, endPoint, { duration });\n }\n },\n });\n};\n\n// ClearInput\nexport const actionClearInputParamSchema = z.object({\n locate: getMidsceneLocationSchema()\n .describe('The input field to be cleared')\n .optional(),\n});\nexport type ActionClearInputParam = {\n locate?: LocateResultElement;\n};\n\nexport const defineActionClearInput = (\n clearInput: KeyboardInputPrimitives['clearInput'],\n): DeviceAction<ActionClearInputParam> => {\n return defineAction<\n typeof actionClearInputParamSchema,\n ActionClearInputParam\n >({\n name: 'ClearInput',\n description: inputLocateDescription,\n interfaceAlias: 'aiClearInput',\n paramSchema: actionClearInputParamSchema,\n sample: {\n locate: { prompt: 'the search input field' },\n },\n call: async (param) => {\n await clearInput(param.locate);\n },\n });\n};\n\n// CursorMove\nexport const actionCursorMoveParamSchema = z.object({\n direction: z\n .enum(['left', 'right'])\n .describe('The direction to move the cursor'),\n times: z\n .number()\n .int()\n .min(1)\n .default(1)\n .describe(\n 'The number of times to move the cursor in the specified direction',\n ),\n});\nexport type ActionCursorMoveParam = {\n direction: 'left' | 'right';\n times?: number;\n};\n\nexport const defineActionCursorMove = (config: {\n keyboard: Pick<KeyboardInputPrimitives, 'keyboardPress' | 'cursorMove'>;\n sleep?(timeMs: number): Promise<void>;\n}): DeviceAction<ActionCursorMoveParam> => {\n return defineAction<\n typeof actionCursorMoveParamSchema,\n ActionCursorMoveParam\n >({\n name: 'CursorMove',\n description:\n 'Move the text cursor (caret) left or right within an input field or text area. Use this to reposition the cursor without selecting text.',\n paramSchema: actionCursorMoveParamSchema,\n sample: {\n direction: 'left',\n times: 3,\n },\n call: async (param) => {\n const times = param.times ?? 1;\n if (config.keyboard.cursorMove) {\n await config.keyboard.cursorMove(param.direction, times);\n return;\n }\n\n const wait =\n config.sleep ??\n ((timeMs: number) =>\n new Promise<void>((resolve) => setTimeout(resolve, timeMs)));\n const arrowKey = param.direction === 'left' ? 'ArrowLeft' : 'ArrowRight';\n for (let i = 0; i < times; i++) {\n await config.keyboard.keyboardPress(arrowKey);\n await wait(100);\n }\n },\n });\n};\n\n// Pinch\nexport const ActionPinchParamSchema = z.object({\n locate: getMidsceneLocationSchema()\n .optional()\n .describe(\n 'The element to pinch on. If not specified, the center of the screen will be used',\n ),\n direction: z\n .enum(['in', 'out'])\n .describe(\n 'Pinch direction. \"in\" = pinch fingers together (zoom out / shrink), \"out\" = spread fingers apart (zoom in / enlarge).',\n ),\n distance: z\n .number()\n .positive()\n .optional()\n .describe(\n 'How far each finger moves in pixels. Defaults to a quarter of the shorter screen dimension.',\n ),\n duration: z\n .number()\n .default(500)\n .optional()\n .describe('Duration of the pinch gesture in milliseconds'),\n});\n\nexport type ActionPinchParam = {\n locate?: LocateResultElement;\n direction: 'in' | 'out';\n distance?: number;\n duration?: number;\n};\n\nexport const defineActionPinch = (config: {\n pinch: TouchInputPrimitives['pinch'];\n size(): Promise<Size>;\n}): DeviceAction<ActionPinchParam> | undefined => {\n if (!config.pinch) {\n return undefined;\n }\n\n return defineAction<typeof ActionPinchParamSchema, ActionPinchParam>({\n name: 'Pinch',\n description:\n 'Perform a two-finger pinch gesture. Use direction \"in\" to pinch fingers together (zoom out), or \"out\" to spread fingers apart (zoom in). Optionally specify distance for how far each finger moves.',\n interfaceAlias: 'aiPinch',\n paramSchema: ActionPinchParamSchema,\n sample: {\n locate: { prompt: 'the map area' },\n direction: 'out',\n distance: 200,\n },\n call: async (param) => {\n const { centerX, centerY, startDistance, endDistance, duration } =\n normalizePinchParam(param, await config.size());\n await config.pinch?.(\n { x: centerX, y: centerY },\n { startDistance, endDistance, duration },\n );\n },\n });\n};\n\nexport function normalizePinchParam(\n param: ActionPinchParam,\n screenSize: { width: number; height: number },\n): {\n centerX: number;\n centerY: number;\n startDistance: number;\n endDistance: number;\n duration: number;\n} {\n const { width, height } = screenSize;\n const element = param.locate;\n const centerX = element\n ? Math.round(element.center[0])\n : Math.round(width / 2);\n const centerY = element\n ? Math.round(element.center[1])\n : Math.round(height / 2);\n const duration = param.duration ?? 500;\n\n const baseDistance = Math.round(Math.min(width, height) / 4);\n const fingerDistance = param.distance ?? baseDistance;\n\n const startDistance = baseDistance;\n const endDistance =\n param.direction === 'out'\n ? baseDistance + fingerDistance\n : Math.max(10, baseDistance - fingerDistance);\n\n return { centerX, centerY, startDistance, endDistance, duration };\n}\n\nexport interface MobileInputActionContext {\n input: MobileInputPrimitives;\n size(): Promise<Size>;\n sleep?(timeMs: number): Promise<void>;\n getDefaultAutoDismissKeyboard?(): boolean | undefined;\n systemActions?: SystemInputActionOptions;\n}\n\nexport interface SystemInputActionConfig {\n name: string;\n description: string;\n interfaceAlias?: string;\n delayBeforeRunner?: number;\n delayAfterRunner?: number;\n}\n\nexport interface SystemInputActionOptions {\n backButton?: SystemInputActionConfig;\n homeButton?: SystemInputActionConfig;\n recentAppsButton?: SystemInputActionConfig;\n}\n\nexport interface InputPrimitiveActionOptions {\n size?: () => Promise<Size>;\n sleep?: (timeMs: number) => Promise<void>;\n includeSwipe?: boolean;\n includePinch?: boolean;\n systemActions?: SystemInputActionOptions;\n}\n\nfunction defineSystemInputAction(\n config: SystemInputActionConfig,\n call: () => Promise<void>,\n): DeviceAction<undefined, void> {\n return defineAction<undefined, undefined, void>({\n name: config.name,\n description: config.description,\n interfaceAlias: config.interfaceAlias,\n delayBeforeRunner: config.delayBeforeRunner,\n delayAfterRunner: config.delayAfterRunner,\n call,\n });\n}\n\nexport function defineActionsFromInputPrimitives(\n input: InputPrimitives,\n options: InputPrimitiveActionOptions = {},\n): DeviceAction<any>[] {\n const actions: Array<DeviceAction<any> | undefined> = [];\n const { pointer, keyboard, scroll, touch, system } = input;\n\n if (pointer) {\n actions.push(defineActionTap(pointer.tap));\n if (pointer.doubleClick) {\n actions.push(defineActionDoubleClick(pointer.doubleClick));\n }\n if (pointer.rightClick) {\n actions.push(defineActionRightClick(pointer.rightClick));\n }\n if (pointer.hover) {\n actions.push(defineActionHover(pointer.hover));\n }\n if (pointer.dragAndDrop) {\n actions.push(defineActionDragAndDrop(pointer.dragAndDrop));\n }\n if (pointer.longPress) {\n actions.push(defineActionLongPress(pointer.longPress));\n }\n }\n\n if (keyboard) {\n actions.push(\n defineActionInput(keyboard),\n defineActionClearInput(keyboard.clearInput),\n defineActionKeyboardPress(keyboard.keyboardPress),\n defineActionCursorMove({ keyboard, sleep: options.sleep }),\n );\n }\n\n if (scroll) {\n actions.push(defineActionScroll(scroll.scroll));\n }\n\n const swipeConfig = resolveSwipeInputPrimitive({ pointer, touch });\n if (swipeConfig && options.size && options.includeSwipe !== false) {\n actions.push(defineActionSwipe({ ...swipeConfig, size: options.size }));\n }\n\n if (touch?.pinch && options.size && options.includePinch !== false) {\n actions.push(defineActionPinch({ pinch: touch.pinch, size: options.size }));\n }\n\n if (system && options.systemActions) {\n const { systemActions } = options;\n if (system.backButton && systemActions.backButton) {\n actions.push(\n defineSystemInputAction(systemActions.backButton, system.backButton),\n );\n }\n if (system.homeButton && systemActions.homeButton) {\n actions.push(\n defineSystemInputAction(systemActions.homeButton, system.homeButton),\n );\n }\n if (system.recentAppsButton && systemActions.recentAppsButton) {\n actions.push(\n defineSystemInputAction(\n systemActions.recentAppsButton,\n system.recentAppsButton,\n ),\n );\n }\n }\n\n return actions.filter((action): action is DeviceAction<any> =>\n Boolean(action),\n );\n}\n\nexport function createDefaultMobileActions(\n context: MobileInputActionContext,\n): DeviceAction<any>[] {\n return defineActionsFromInputPrimitives(context.input, {\n size: context.size,\n sleep: context.sleep,\n systemActions: context.systemActions,\n });\n}\n\n// Sleep\nexport const ActionSleepParamSchema = z.object({\n timeMs: z\n .number()\n .default(1000)\n .optional()\n .describe('Sleep duration in milliseconds, defaults to 1000ms (1 second)'),\n});\n\nexport type ActionSleepParam = {\n timeMs?: number;\n};\n\nexport const defineActionSleep = (\n abortSignal?: AbortSignal,\n): DeviceAction<ActionSleepParam> => {\n return defineAction<typeof ActionSleepParamSchema, ActionSleepParam>({\n name: 'Sleep',\n description:\n 'Wait for a specified duration before continuing. Defaults to 1 second (1000ms) if not specified.',\n paramSchema: ActionSleepParamSchema,\n sample: {\n timeMs: 2000,\n },\n call: async (param) => {\n const duration = param?.timeMs ?? 1000;\n getDebug('device:common-action')(`Sleeping for ${duration}ms`);\n abortSignal?.throwIfAborted();\n await new Promise<void>((resolve, reject) => {\n const abort = () => {\n clearTimeout(timer);\n reject(abortSignal?.reason);\n };\n const timer = setTimeout(() => {\n abortSignal?.removeEventListener('abort', abort);\n resolve();\n }, duration);\n abortSignal?.addEventListener('abort', abort, { once: true });\n });\n },\n });\n};\n\nexport type { DeviceAction } from '../types';\nexport type { ActionScrollParam, LocateResultElement, Size } from '../types';\nexport { getMidsceneLocationSchema } from '../common';\nexport { z };\nexport {\n inputStrategies,\n type InputStrategy,\n type ResolvedTextInputOptions,\n type SequentialTextInputHandlers,\n type TextInputOptions,\n resolveInputStrategy,\n resolveTextInputOptions,\n sendTextSequentially,\n shouldInputSequentially,\n} from './input-strategy';\nexport type {\n AndroidDeviceOpt,\n AndroidDeviceInputOpt,\n IOSDeviceOpt,\n IOSDeviceInputOpt,\n HarmonyDeviceOpt,\n HarmonyDeviceInputOpt,\n} from './device-options';\n"],"names":["resolveSwipeInputPrimitive","input","AbstractInterface","defineAction","config","pointFromLocate","locate","missingMessage","Error","defineLocatedPointAction","param","actionTapParamSchema","z","getMidsceneLocationSchema","defineActionTap","tap","point","registerFileChooserAcceptParamSchema","defineActionRegisterFileChooserAccept","register","actionRightClickParamSchema","defineActionRightClick","rightClick","actionDoubleClickParamSchema","defineActionDoubleClick","doubleClick","actionHoverParamSchema","defineActionHover","hover","inputLocateDescription","actionInputParamSchema","val","String","inputStrategies","defineActionInput","keyboard","actionKeyboardPressParamSchema","defineActionKeyboardPress","keyboardPress","actionScrollParamSchema","defineActionScroll","scroll","actionDragAndDropParamSchema","defineActionDragAndDrop","dragAndDrop","from","to","ActionLongPressParamSchema","defineActionLongPress","longPress","createActionSwipeParamSchema","inputMode","movementSource","ActionSwipeParamSchema","normalizeSwipeParam","screenSize","width","height","start","end","startPoint","endPoint","undefined","Math","duration","repeatCount","normalizeMobileSwipeParam","defineActionSwipe","i","actionClearInputParamSchema","defineActionClearInput","clearInput","actionCursorMoveParamSchema","defineActionCursorMove","times","wait","timeMs","Promise","resolve","setTimeout","arrowKey","ActionPinchParamSchema","defineActionPinch","centerX","centerY","startDistance","endDistance","normalizePinchParam","element","baseDistance","fingerDistance","defineSystemInputAction","call","defineActionsFromInputPrimitives","options","actions","pointer","touch","system","swipeConfig","systemActions","action","Boolean","createDefaultMobileActions","context","ActionSleepParamSchema","defineActionSleep","abortSignal","getDebug","reject","abort","clearTimeout","timer"],"mappings":"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"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import * as __rspack_external_node_fs_5ea92f0c from "node:fs";
|
|
2
2
|
import { open as promises_open } from "node:fs/promises";
|
|
3
|
+
import { StringDecoder } from "node:string_decoder";
|
|
3
4
|
import { antiEscapeScriptTag, escapeScriptTag } from "@midscene/shared/utils";
|
|
5
|
+
import { TEST_RUN_REPORT_SCRIPT_TYPE } from "../test-run-report.mjs";
|
|
4
6
|
var __webpack_modules__ = {
|
|
5
7
|
"node:fs" (module) {
|
|
6
8
|
module.exports = __rspack_external_node_fs_5ea92f0c;
|
|
@@ -94,6 +96,34 @@ function generateAgentReportComment(report) {
|
|
|
94
96
|
function htmlScriptCloseTag() {
|
|
95
97
|
return String.fromCharCode(60) + "/script>";
|
|
96
98
|
}
|
|
99
|
+
function generateTestRunReportScriptTag(dump) {
|
|
100
|
+
return [
|
|
101
|
+
String.fromCharCode(60),
|
|
102
|
+
'script type="',
|
|
103
|
+
TEST_RUN_REPORT_SCRIPT_TYPE,
|
|
104
|
+
'">\n',
|
|
105
|
+
escapeScriptTag(JSON.stringify(dump)),
|
|
106
|
+
'\n',
|
|
107
|
+
htmlScriptCloseTag(),
|
|
108
|
+
'\n'
|
|
109
|
+
].join('');
|
|
110
|
+
}
|
|
111
|
+
function extractTestRunReportDumpSync(filePath) {
|
|
112
|
+
let result;
|
|
113
|
+
const openTag = `${String.fromCharCode(60)}script`;
|
|
114
|
+
streamScanTags(filePath, openTag, htmlScriptCloseTag(), (content)=>{
|
|
115
|
+
const tagEnd = content.indexOf('>');
|
|
116
|
+
const attributes = content.slice(0, tagEnd);
|
|
117
|
+
const type = attributes.match(/(?:^|\s)type\s*=\s*["']([^"']+)["']/i)?.[1];
|
|
118
|
+
if (type !== TEST_RUN_REPORT_SCRIPT_TYPE) return false;
|
|
119
|
+
if (result) throw new Error(`Report contains multiple Midscene Test dumps: ${filePath}`);
|
|
120
|
+
const parsed = JSON.parse(unescapeContent(content.slice(tagEnd + 1)));
|
|
121
|
+
if (parsed?.schemaVersion !== 1 || parsed?.kind !== 'test-runner' || !Array.isArray(parsed.projects)) throw new Error(`Invalid Midscene Test report dump: ${filePath}`);
|
|
122
|
+
result = parsed;
|
|
123
|
+
return false;
|
|
124
|
+
});
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
97
127
|
const STREAMING_CHUNK_SIZE = 65536;
|
|
98
128
|
class StreamingTagMatcher {
|
|
99
129
|
push(nextChunk) {
|
|
@@ -147,14 +177,16 @@ function streamScanTags(filePath, openTag, closeTag, onMatch) {
|
|
|
147
177
|
const fd = (0, external_node_fs_.openSync)(filePath, 'r');
|
|
148
178
|
const fileSize = (0, external_node_fs_.statSync)(filePath).size;
|
|
149
179
|
const buffer = Buffer.alloc(STREAMING_CHUNK_SIZE);
|
|
180
|
+
const decoder = new StringDecoder('utf8');
|
|
150
181
|
const matcher = new StreamingTagMatcher(openTag, closeTag, onMatch);
|
|
151
182
|
let position = 0;
|
|
152
183
|
try {
|
|
153
184
|
while(position < fileSize){
|
|
154
185
|
const bytesRead = (0, external_node_fs_.readSync)(fd, buffer, 0, STREAMING_CHUNK_SIZE, position);
|
|
155
186
|
position += bytesRead;
|
|
156
|
-
if (matcher.push(buffer.
|
|
187
|
+
if (matcher.push(decoder.write(buffer.subarray(0, bytesRead)))) return;
|
|
157
188
|
}
|
|
189
|
+
matcher.push(decoder.end());
|
|
158
190
|
} finally{
|
|
159
191
|
(0, external_node_fs_.closeSync)(fd);
|
|
160
192
|
}
|
|
@@ -162,14 +194,15 @@ function streamScanTags(filePath, openTag, closeTag, onMatch) {
|
|
|
162
194
|
async function streamScanTagsAsync(filePath, openTag, closeTag, onMatch) {
|
|
163
195
|
const handle = await promises_open(filePath, 'r');
|
|
164
196
|
const buffer = Buffer.alloc(STREAMING_CHUNK_SIZE);
|
|
197
|
+
const decoder = new StringDecoder('utf8');
|
|
165
198
|
let position = 0;
|
|
166
199
|
const matcher = new StreamingTagMatcher(openTag, closeTag, onMatch);
|
|
167
200
|
try {
|
|
168
201
|
while(true){
|
|
169
202
|
const { bytesRead } = await handle.read(buffer, 0, STREAMING_CHUNK_SIZE, position);
|
|
170
|
-
if (0 === bytesRead) return;
|
|
203
|
+
if (0 === bytesRead) return void matcher.push(decoder.end());
|
|
171
204
|
position += bytesRead;
|
|
172
|
-
if (matcher.push(buffer.
|
|
205
|
+
if (matcher.push(decoder.write(buffer.subarray(0, bytesRead)))) return;
|
|
173
206
|
}
|
|
174
207
|
} finally{
|
|
175
208
|
await handle.close();
|
|
@@ -409,6 +442,6 @@ function generateDumpScriptTag(json, attributes) {
|
|
|
409
442
|
if (attributes && Object.keys(attributes).length > 0) attrString = ' ' + Object.entries(attributes).map(([k, v])=>k + '="' + encodeURIComponent(v) + '"').join(' ');
|
|
410
443
|
return '<script type="midscene_web_dump"' + attrString + '>' + escapeContent(json) + closeTag;
|
|
411
444
|
}
|
|
412
|
-
export { DATA_SCREENSHOT_MODE_ATTR, STREAMING_CHUNK_SIZE, collectImageScriptIds, escapeContent, extractAllDumpScriptsSync, extractImageByIdSync, extractLastDumpScriptSync, generateAgentReportComment, generateDumpScriptTag, generateImageScriptTag, getBaseUrlFixScript, parseDumpScript, parseDumpScriptAttributes, parseImageScripts, streamDumpScriptsSync, streamImageScriptsToFile, streamScanTags, streamScanTagsAsync, unescapeContent };
|
|
445
|
+
export { DATA_SCREENSHOT_MODE_ATTR, STREAMING_CHUNK_SIZE, collectImageScriptIds, escapeContent, extractAllDumpScriptsSync, extractImageByIdSync, extractLastDumpScriptSync, extractTestRunReportDumpSync, generateAgentReportComment, generateDumpScriptTag, generateImageScriptTag, generateTestRunReportScriptTag, getBaseUrlFixScript, parseDumpScript, parseDumpScriptAttributes, parseImageScripts, streamDumpScriptsSync, streamImageScriptsToFile, streamScanTags, streamScanTagsAsync, unescapeContent };
|
|
413
446
|
|
|
414
447
|
//# sourceMappingURL=html-utils.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dump/html-utils.mjs","sources":["../../../src/dump/html-utils.ts"],"sourcesContent":["import { closeSync, openSync, readSync, statSync } from 'node:fs';\nimport { open as openAsync } from 'node:fs/promises';\nimport { antiEscapeScriptTag, escapeScriptTag } from '@midscene/shared/utils';\nimport type { AIUsageInfo, IReportActionDump, ModelBrief } from '../types';\n\nexport const escapeContent = escapeScriptTag;\nexport const unescapeContent = antiEscapeScriptTag;\n\nfunction cleanHtmlCommentValue(value: unknown): string {\n return String(value ?? 'N/A')\n .replace(/\\0/g, '')\n .replace(/--/g, '- -');\n}\n\nfunction formatModelBriefForAgent(brief: ModelBrief): string {\n const intent = brief.intent || 'default';\n const model = brief.name || 'unknown';\n const description = brief.modelDescription\n ? ` (${brief.modelDescription})`\n : '';\n return `${intent}: ${model}${description}`;\n}\n\nfunction hasUsageModelInfo(usage?: AIUsageInfo): usage is AIUsageInfo {\n return Boolean(\n usage &&\n (usage.model_name ||\n usage.response_model_name ||\n usage.model_description ||\n usage.intent),\n );\n}\n\nfunction formatUsageModelForAgent(source: string, usage: AIUsageInfo): string {\n const intent = usage.intent || source;\n const model = usage.model_name || usage.response_model_name || 'unknown';\n const description = usage.model_description\n ? ` (${usage.model_description})`\n : '';\n return `${intent}: ${model}${description}`;\n}\n\nfunction collectUsageModelsForAgent(report: IReportActionDump): string {\n const models = new Map<string, string>();\n const executions = Array.isArray(report.executions) ? report.executions : [];\n for (const execution of executions) {\n const tasks = Array.isArray(execution.tasks) ? execution.tasks : [];\n for (const task of tasks) {\n const taskWithUsage = task as {\n usage?: AIUsageInfo;\n searchAreaUsage?: AIUsageInfo;\n };\n const usages: Array<[string, AIUsageInfo | undefined]> = [\n ['main', taskWithUsage.usage],\n ['searchArea', taskWithUsage.searchAreaUsage],\n ];\n\n for (const [source, usage] of usages) {\n if (!hasUsageModelInfo(usage)) {\n continue;\n }\n const formatted = formatUsageModelForAgent(source, usage);\n models.set(formatted, formatted);\n }\n }\n }\n\n return Array.from(models.values()).join('; ');\n}\n\nexport function generateAgentReportComment(report: IReportActionDump): string {\n const executions = Array.isArray(report.executions) ? report.executions : [];\n const taskCount = executions.reduce(\n (sum, execution) =>\n sum + (Array.isArray(execution.tasks) ? execution.tasks.length : 0),\n 0,\n );\n const modelBriefs = report.modelBriefs?.length\n ? report.modelBriefs.map(formatModelBriefForAgent).join('; ')\n : collectUsageModelsForAgent(report) || 'No model metadata recorded';\n\n const lines = [\n 'For Agent Analysis:',\n `Report: ${cleanHtmlCommentValue(report.groupName)}`,\n `SDK: ${cleanHtmlCommentValue(report.sdkVersion)}`,\n `Device: ${cleanHtmlCommentValue(report.deviceType)}`,\n `Executions: ${executions.length}; Tasks: ${taskCount}`,\n `Models: ${cleanHtmlCommentValue(modelBriefs)}`,\n 'Structured report JSON is stored in script[type=\"midscene_web_dump\"] tags near this comment.',\n 'Screenshots are stored as script[type=\"midscene-image\"] tags or files referenced by screenshot refs.',\n 'For AI analysis, inspect each execution task for type, status, timing, param, output, usage, searchAreaUsage, recorder, and screenshot refs.',\n 'Use the Markdown export when available; it contains the same report context plus image links for agent review.',\n ];\n\n return `\\n<!--\\n${lines.join('\\n')}\\n-->\\n`;\n}\n\nfunction htmlScriptCloseTag(): string {\n // biome-ignore lint/style/useTemplate: keep this token runtime-built for inline report bundles\n return String.fromCharCode(60) + '/script>';\n}\n\n/** Chunk size for streaming file operations (64KB) */\nexport const STREAMING_CHUNK_SIZE = 64 * 1024;\n\n/**\n * Callback for processing matched tags during streaming.\n * @param content - The content between open and close tags\n * @returns true to stop scanning, false to continue\n */\ntype TagMatchCallback = (content: string) => boolean;\n\nclass StreamingTagMatcher {\n private leftover = '';\n private capturing = false;\n private currentContent = '';\n\n constructor(\n private readonly openTag: string,\n private readonly closeTag: string,\n private readonly onMatch: TagMatchCallback,\n ) {}\n\n push(nextChunk: string): boolean {\n const chunk = this.leftover + nextChunk;\n this.leftover = '';\n let searchStart = 0;\n\n while (searchStart < chunk.length) {\n if (!this.capturing) {\n const startIndex = chunk.indexOf(this.openTag, searchStart);\n if (startIndex === -1) {\n const retainedSuffixLength = Math.max(0, this.openTag.length - 1);\n this.leftover = retainedSuffixLength\n ? chunk.slice(-retainedSuffixLength)\n : '';\n return false;\n }\n this.capturing = true;\n searchStart = startIndex + this.openTag.length;\n }\n\n const endIndex = chunk.indexOf(this.closeTag, searchStart);\n if (endIndex !== -1) {\n this.currentContent += chunk.slice(searchStart, endIndex);\n if (this.onMatch(this.currentContent)) return true;\n this.capturing = false;\n this.currentContent = '';\n searchStart = endIndex + this.closeTag.length;\n continue;\n }\n\n const retainedSuffixLength = Math.max(0, this.closeTag.length - 1);\n const contentEnd = Math.max(\n searchStart,\n chunk.length - retainedSuffixLength,\n );\n this.currentContent += chunk.slice(searchStart, contentEnd);\n this.leftover = chunk.slice(contentEnd);\n return false;\n }\n\n return false;\n }\n}\n\n/**\n * Stream through a file and find tags matching the pattern.\n * Memory usage: O(chunk_size + tag_size), not O(file_size).\n *\n * @param filePath - Absolute path to the file\n * @param openTag - Opening tag to search for\n * @param closeTag - Closing tag\n * @param onMatch - Callback for each matched tag content\n */\nexport function streamScanTags(\n filePath: string,\n openTag: string,\n closeTag: string,\n onMatch: TagMatchCallback,\n): void {\n const fd = openSync(filePath, 'r');\n const fileSize = statSync(filePath).size;\n const buffer = Buffer.alloc(STREAMING_CHUNK_SIZE);\n const matcher = new StreamingTagMatcher(openTag, closeTag, onMatch);\n let position = 0;\n\n try {\n while (position < fileSize) {\n const bytesRead = readSync(fd, buffer, 0, STREAMING_CHUNK_SIZE, position);\n position += bytesRead;\n if (matcher.push(buffer.toString('utf-8', 0, bytesRead))) return;\n }\n } finally {\n closeSync(fd);\n }\n}\n\n/** Asynchronously stream tags without blocking the Node.js event loop. */\nexport async function streamScanTagsAsync(\n filePath: string,\n openTag: string,\n closeTag: string,\n onMatch: TagMatchCallback,\n): Promise<void> {\n const handle = await openAsync(filePath, 'r');\n const buffer = Buffer.alloc(STREAMING_CHUNK_SIZE);\n let position = 0;\n const matcher = new StreamingTagMatcher(openTag, closeTag, onMatch);\n\n try {\n while (true) {\n const { bytesRead } = await handle.read(\n buffer,\n 0,\n STREAMING_CHUNK_SIZE,\n position,\n );\n if (bytesRead === 0) return;\n position += bytesRead;\n if (matcher.push(buffer.toString('utf-8', 0, bytesRead))) return;\n }\n } finally {\n await handle.close();\n }\n}\n\n/**\n * Synchronously extract a specific image's base64 data from an HTML file by its id.\n * Uses streaming to avoid loading the entire file into memory.\n *\n * @param htmlPath - Absolute path to the HTML file\n * @param imageId - The id of the image to extract\n * @returns The base64 data string, or null if not found\n */\nexport function extractImageByIdSync(\n htmlPath: string,\n imageId: string,\n): string | null {\n const targetTag = `<script type=\"midscene-image\" data-id=\"${imageId}\">`;\n const closeTag = htmlScriptCloseTag();\n\n let result: string | null = null;\n\n streamScanTags(htmlPath, targetTag, closeTag, (content) => {\n result = unescapeContent(content);\n return true; // Stop after first match\n });\n\n return result;\n}\n\n/**\n * Stream image script tags from source file directly to output file.\n * Memory usage: O(single_image_size), not O(all_images_size).\n *\n * @param srcFilePath - Source HTML file path\n * @param destFilePath - Destination file path to append to\n * @param writtenImageIds - IDs already written to the destination\n */\nexport function streamImageScriptsToFile(\n srcFilePath: string,\n destFilePath: string,\n writtenImageIds: Set<string> = new Set(),\n): void {\n const { appendFileSync } = require('node:fs');\n const openTag = '<script type=\"midscene-image\"';\n const closeTag = htmlScriptCloseTag();\n\n streamScanTags(srcFilePath, openTag, closeTag, (content) => {\n const imageId = parseImageScriptId(content);\n if (!imageId || writtenImageIds.has(imageId)) {\n return false;\n }\n // Write complete tag immediately to destination, don't accumulate\n appendFileSync(destFilePath, `${openTag}${content}${closeTag}\\n`);\n writtenImageIds.add(imageId);\n return false; // Continue scanning for more tags\n });\n}\n\nfunction parseImageScriptId(contentAfterType: string): string | null {\n const match = /^ data-id=\"([a-z0-9_-]{1,128})\">/i.exec(contentAfterType);\n return match?.[1] ?? null;\n}\n\n/** Collect IDs of real inline image script tags without blocking on a large report. */\nexport async function collectImageScriptIds(\n filePath: string,\n): Promise<Set<string>> {\n const imageIds = new Set<string>();\n const openTag = '<script type=\"midscene-image\"';\n const closeTag = htmlScriptCloseTag();\n\n await streamScanTagsAsync(filePath, openTag, closeTag, (content) => {\n const imageId = parseImageScriptId(content);\n if (imageId) imageIds.add(imageId);\n return false;\n });\n return imageIds;\n}\n\n/**\n * Extract the LAST dump script content from HTML file using streaming.\n * Memory usage: O(dump_size), not O(file_size).\n *\n * @param filePath - Absolute path to the HTML file\n * @returns The dump script content (trimmed), or empty string if not found\n */\nexport function extractLastDumpScriptSync(filePath: string): string {\n const openTagPrefix = '<script type=\"midscene_web_dump\"';\n const closeTag = htmlScriptCloseTag();\n\n let lastContent = '';\n\n // Custom streaming to handle the special case where open tag has variable attributes\n const fd = openSync(filePath, 'r');\n const fileSize = statSync(filePath).size;\n const buffer = Buffer.alloc(STREAMING_CHUNK_SIZE);\n\n let position = 0;\n let leftover = '';\n let capturing = false;\n let currentContent = '';\n\n try {\n while (position < fileSize) {\n const bytesRead = readSync(fd, buffer, 0, STREAMING_CHUNK_SIZE, position);\n const chunk = leftover + buffer.toString('utf-8', 0, bytesRead);\n position += bytesRead;\n\n let searchStart = 0;\n\n while (searchStart < chunk.length) {\n if (!capturing) {\n const startIdx = chunk.indexOf(openTagPrefix, searchStart);\n if (startIdx !== -1) {\n // Find the end of the opening tag (the '>' character)\n const tagEndIdx = chunk.indexOf('>', startIdx);\n if (tagEndIdx !== -1) {\n capturing = true;\n currentContent = chunk.slice(tagEndIdx + 1);\n const endIdx = currentContent.indexOf(closeTag);\n if (endIdx !== -1) {\n lastContent = currentContent.slice(0, endIdx).trim();\n capturing = false;\n currentContent = '';\n searchStart = tagEndIdx + 1 + endIdx + closeTag.length;\n } else {\n leftover = currentContent.slice(-closeTag.length);\n currentContent = currentContent.slice(0, -closeTag.length);\n break;\n }\n } else {\n leftover = chunk.slice(startIdx);\n break;\n }\n } else {\n leftover = chunk.slice(-openTagPrefix.length);\n break;\n }\n } else {\n const endIdx = chunk.indexOf(closeTag, searchStart);\n if (endIdx !== -1) {\n currentContent += chunk.slice(searchStart, endIdx);\n lastContent = currentContent.trim();\n capturing = false;\n currentContent = '';\n searchStart = endIdx + closeTag.length;\n } else {\n currentContent += chunk.slice(searchStart, -closeTag.length);\n leftover = chunk.slice(-closeTag.length);\n break;\n }\n }\n }\n }\n } finally {\n closeSync(fd);\n }\n\n return lastContent;\n}\n\n/**\n * Extract ALL dump script contents from an HTML file using streaming.\n * Each entry includes the full opening tag (for attribute extraction) and the content.\n *\n * @param filePath - Absolute path to the HTML file\n * @returns Array of { openTag, content } for each dump script found\n */\nexport function extractAllDumpScriptsSync(\n filePath: string,\n): { openTag: string; content: string }[] {\n const results: { openTag: string; content: string }[] = [];\n streamDumpScriptsSync(filePath, (dumpScript) => {\n results.push(dumpScript);\n return false;\n });\n return results;\n}\n\n/**\n * Stream ALL dump scripts from an HTML file.\n * Calls onMatch for each dump script and keeps memory bounded to a single\n * dump script payload instead of accumulating every dump in memory.\n *\n * @param filePath - Absolute path to the HTML file\n * @param onMatch - Callback for each dump script; return true to stop early\n */\nexport function streamDumpScriptsSync(\n filePath: string,\n onMatch: (dumpScript: { openTag: string; content: string }) => boolean,\n): void {\n const openTagPrefix = '<script type=\"midscene_web_dump\"';\n const closeTag = htmlScriptCloseTag();\n\n const fd = openSync(filePath, 'r');\n const fileSize = statSync(filePath).size;\n const buffer = Buffer.alloc(STREAMING_CHUNK_SIZE);\n\n let position = 0;\n let leftover = '';\n let capturing = false;\n let currentContent = '';\n let currentOpenTag = '';\n\n try {\n while (position < fileSize) {\n const bytesRead = readSync(fd, buffer, 0, STREAMING_CHUNK_SIZE, position);\n const chunk = leftover + buffer.toString('utf-8', 0, bytesRead);\n position += bytesRead;\n\n let searchStart = 0;\n\n while (searchStart < chunk.length) {\n if (!capturing) {\n const startIdx = chunk.indexOf(openTagPrefix, searchStart);\n if (startIdx !== -1) {\n const tagEndIdx = chunk.indexOf('>', startIdx);\n if (tagEndIdx !== -1) {\n capturing = true;\n currentOpenTag = chunk.slice(startIdx, tagEndIdx + 1);\n currentContent = chunk.slice(tagEndIdx + 1);\n const endIdx = currentContent.indexOf(closeTag);\n if (endIdx !== -1) {\n const shouldStop = onMatch({\n openTag: currentOpenTag,\n content: currentContent.slice(0, endIdx).trim(),\n });\n if (shouldStop) return;\n capturing = false;\n currentContent = '';\n currentOpenTag = '';\n searchStart = tagEndIdx + 1 + endIdx + closeTag.length;\n } else {\n leftover = currentContent.slice(-closeTag.length);\n currentContent = currentContent.slice(0, -closeTag.length);\n break;\n }\n } else {\n leftover = chunk.slice(startIdx);\n break;\n }\n } else {\n leftover = chunk.slice(-openTagPrefix.length);\n break;\n }\n } else {\n const endIdx = chunk.indexOf(closeTag, searchStart);\n if (endIdx !== -1) {\n currentContent += chunk.slice(searchStart, endIdx);\n const shouldStop = onMatch({\n openTag: currentOpenTag,\n content: currentContent.trim(),\n });\n if (shouldStop) return;\n capturing = false;\n currentContent = '';\n currentOpenTag = '';\n searchStart = endIdx + closeTag.length;\n } else {\n currentContent += chunk.slice(searchStart, -closeTag.length);\n leftover = chunk.slice(-closeTag.length);\n break;\n }\n }\n }\n }\n } finally {\n closeSync(fd);\n }\n}\n\nexport function parseImageScripts(html: string): Record<string, string> {\n const imageMap: Record<string, string> = {};\n const regex =\n /<script type=\"midscene-image\" data-id=\"([^\"]+)\">([\\s\\S]*?)<\\/script>/g;\n\n for (const match of html.matchAll(regex)) {\n const [, id, content] = match;\n imageMap[id] = unescapeContent(content);\n }\n\n return imageMap;\n}\n\nexport function parseDumpScript(html: string): string {\n // Use string search instead of regex to avoid ReDoS vulnerability\n // Find the LAST dump script tag (template may contain similar patterns in bundled JS)\n const scriptOpenTag = '<script type=\"midscene_web_dump\"';\n const closeTag = htmlScriptCloseTag();\n\n // Find the last occurrence of the opening tag\n const lastOpenIndex = html.lastIndexOf(scriptOpenTag);\n if (lastOpenIndex === -1) {\n throw new Error('No dump script found in HTML');\n }\n\n // Find the end of the opening tag (the '>' character)\n const tagEndIndex = html.indexOf('>', lastOpenIndex);\n if (tagEndIndex === -1) {\n throw new Error('No dump script found in HTML');\n }\n\n // Find the closing tag after the opening tag\n const closeIndex = html.indexOf(closeTag, tagEndIndex);\n if (closeIndex === -1) {\n throw new Error('No dump script found in HTML');\n }\n\n // Extract content between opening and closing tags\n const content = html.substring(tagEndIndex + 1, closeIndex);\n return unescapeContent(content);\n}\n\nexport function parseDumpScriptAttributes(\n html: string,\n): Record<string, string> {\n const regex = /<script type=\"midscene_web_dump\"([^>]*)>/;\n const match = regex.exec(html);\n\n if (!match) {\n return {};\n }\n\n const attrString = match[1];\n const attributes: Record<string, string> = {};\n const attrRegex = /(\\w+)=\"([^\"]*)\"/g;\n\n for (const attrMatch of attrString.matchAll(attrRegex)) {\n const [, key, value] = attrMatch;\n if (key !== 'type') {\n attributes[key] = decodeURIComponent(value);\n }\n }\n\n return attributes;\n}\n\nexport function generateImageScriptTag(id: string, data: string): string {\n // Do not use template string here, will cause bundle error with <script\n const closeTag = htmlScriptCloseTag();\n return (\n // biome-ignore lint/style/useTemplate: <explanation>\n '<script type=\"midscene-image\" data-id=\"' +\n id +\n '\">' +\n escapeContent(data) +\n closeTag\n );\n}\n\n/**\n * Inline script that fixes relative URL resolution for directory-mode reports.\n *\n * Problem: when a static server (e.g. `npx serve`) serves `name/index.html`\n * at URL `/name` (without trailing slash), relative paths like\n * `./screenshots/xxx.png` resolve to `/screenshots/xxx.png` instead of\n * `/name/screenshots/xxx.png`.\n *\n * Fix: dynamically insert a <base> tag so relative URLs resolve correctly.\n */\n// Do not use template string here, will cause bundle error with <script\n//\n// The closing script tag is built at runtime so bundlers cannot inline the\n// token that would prematurely close the report template's inline app bundle.\n//\n// Do NOT replace this with a string constant, hex escape (\\x3c), or literal\n// string concatenation. Bundlers may optimise those forms back to the unsafe\n// token.\nlet _baseUrlFixScript: string;\nexport function getBaseUrlFixScript(): string {\n if (!_baseUrlFixScript) {\n const close = htmlScriptCloseTag();\n _baseUrlFixScript =\n // biome-ignore lint/style/useTemplate: see above\n '\\n<script>(function(){' +\n 'var p=window.location.pathname;' +\n 'if(p.endsWith(\"/\")||/\\\\.\\\\w+$/.test(p))return;' +\n 'var b=document.createElement(\"base\");' +\n 'b.href=p+\"/\";' +\n 'document.head.insertBefore(b,document.head.firstChild)' +\n '})()' +\n close +\n '\\n';\n }\n return _baseUrlFixScript;\n}\n\n/**\n * Dump-script attribute that records how the report file stores screenshots.\n * Written by the report generator and the merger, read back when deciding\n * whether a report is in directory mode. Kept here, next to dump-tag\n * generation, so the writer and reader share one source of truth.\n */\nexport const DATA_SCREENSHOT_MODE_ATTR = 'data-screenshot-mode';\n\nexport function generateDumpScriptTag(\n json: string,\n attributes?: Record<string, string | number | boolean>,\n): string {\n const closeTag = htmlScriptCloseTag();\n let attrString = '';\n if (attributes && Object.keys(attributes).length > 0) {\n // Do not use template string here, will cause bundle error with <script\n attrString =\n // biome-ignore lint/style/useTemplate: <explanation>\n ' ' +\n Object.entries(attributes)\n // biome-ignore lint/style/useTemplate: <explanation>\n .map(([k, v]) => k + '=\"' + encodeURIComponent(v) + '\"')\n .join(' ');\n }\n\n // Do not use template string here, will cause bundle error with <script\n return (\n // biome-ignore lint/style/useTemplate: <explanation>\n '<script type=\"midscene_web_dump\"' +\n attrString +\n '>' +\n escapeContent(json) +\n closeTag\n );\n}\n"],"names":["escapeContent","escapeScriptTag","unescapeContent","antiEscapeScriptTag","cleanHtmlCommentValue","value","String","formatModelBriefForAgent","brief","intent","model","description","hasUsageModelInfo","usage","Boolean","formatUsageModelForAgent","source","collectUsageModelsForAgent","report","models","Map","executions","Array","execution","tasks","task","taskWithUsage","usages","formatted","generateAgentReportComment","taskCount","sum","modelBriefs","lines","htmlScriptCloseTag","STREAMING_CHUNK_SIZE","StreamingTagMatcher","nextChunk","chunk","searchStart","startIndex","retainedSuffixLength","Math","endIndex","contentEnd","openTag","closeTag","onMatch","streamScanTags","filePath","fd","openSync","fileSize","statSync","buffer","Buffer","matcher","position","bytesRead","readSync","closeSync","streamScanTagsAsync","handle","openAsync","extractImageByIdSync","htmlPath","imageId","targetTag","result","content","streamImageScriptsToFile","srcFilePath","destFilePath","writtenImageIds","Set","appendFileSync","require","parseImageScriptId","contentAfterType","match","collectImageScriptIds","imageIds","extractLastDumpScriptSync","openTagPrefix","lastContent","leftover","capturing","currentContent","endIdx","startIdx","tagEndIdx","extractAllDumpScriptsSync","results","streamDumpScriptsSync","dumpScript","currentOpenTag","shouldStop","parseImageScripts","html","imageMap","regex","id","parseDumpScript","scriptOpenTag","lastOpenIndex","Error","tagEndIndex","closeIndex","parseDumpScriptAttributes","attrString","attributes","attrRegex","attrMatch","key","decodeURIComponent","generateImageScriptTag","data","_baseUrlFixScript","getBaseUrlFixScript","close","DATA_SCREENSHOT_MODE_ATTR","generateDumpScriptTag","json","Object","k","v","encodeURIComponent"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dump/html-utils.mjs","sources":["../../../src/dump/html-utils.ts"],"sourcesContent":["import { closeSync, openSync, readSync, statSync } from 'node:fs';\nimport { open as openAsync } from 'node:fs/promises';\nimport { StringDecoder } from 'node:string_decoder';\nimport { antiEscapeScriptTag, escapeScriptTag } from '@midscene/shared/utils';\nimport {\n TEST_RUN_REPORT_SCRIPT_TYPE,\n type TestRunReportDump,\n} from '../test-run-report';\nimport type { AIUsageInfo, IReportActionDump, ModelBrief } from '../types';\n\nexport const escapeContent = escapeScriptTag;\nexport const unescapeContent = antiEscapeScriptTag;\n\nfunction cleanHtmlCommentValue(value: unknown): string {\n return String(value ?? 'N/A')\n .replace(/\\0/g, '')\n .replace(/--/g, '- -');\n}\n\nfunction formatModelBriefForAgent(brief: ModelBrief): string {\n const intent = brief.intent || 'default';\n const model = brief.name || 'unknown';\n const description = brief.modelDescription\n ? ` (${brief.modelDescription})`\n : '';\n return `${intent}: ${model}${description}`;\n}\n\nfunction hasUsageModelInfo(usage?: AIUsageInfo): usage is AIUsageInfo {\n return Boolean(\n usage &&\n (usage.model_name ||\n usage.response_model_name ||\n usage.model_description ||\n usage.intent),\n );\n}\n\nfunction formatUsageModelForAgent(source: string, usage: AIUsageInfo): string {\n const intent = usage.intent || source;\n const model = usage.model_name || usage.response_model_name || 'unknown';\n const description = usage.model_description\n ? ` (${usage.model_description})`\n : '';\n return `${intent}: ${model}${description}`;\n}\n\nfunction collectUsageModelsForAgent(report: IReportActionDump): string {\n const models = new Map<string, string>();\n const executions = Array.isArray(report.executions) ? report.executions : [];\n for (const execution of executions) {\n const tasks = Array.isArray(execution.tasks) ? execution.tasks : [];\n for (const task of tasks) {\n const taskWithUsage = task as {\n usage?: AIUsageInfo;\n searchAreaUsage?: AIUsageInfo;\n };\n const usages: Array<[string, AIUsageInfo | undefined]> = [\n ['main', taskWithUsage.usage],\n ['searchArea', taskWithUsage.searchAreaUsage],\n ];\n\n for (const [source, usage] of usages) {\n if (!hasUsageModelInfo(usage)) {\n continue;\n }\n const formatted = formatUsageModelForAgent(source, usage);\n models.set(formatted, formatted);\n }\n }\n }\n\n return Array.from(models.values()).join('; ');\n}\n\nexport function generateAgentReportComment(report: IReportActionDump): string {\n const executions = Array.isArray(report.executions) ? report.executions : [];\n const taskCount = executions.reduce(\n (sum, execution) =>\n sum + (Array.isArray(execution.tasks) ? execution.tasks.length : 0),\n 0,\n );\n const modelBriefs = report.modelBriefs?.length\n ? report.modelBriefs.map(formatModelBriefForAgent).join('; ')\n : collectUsageModelsForAgent(report) || 'No model metadata recorded';\n\n const lines = [\n 'For Agent Analysis:',\n `Report: ${cleanHtmlCommentValue(report.groupName)}`,\n `SDK: ${cleanHtmlCommentValue(report.sdkVersion)}`,\n `Device: ${cleanHtmlCommentValue(report.deviceType)}`,\n `Executions: ${executions.length}; Tasks: ${taskCount}`,\n `Models: ${cleanHtmlCommentValue(modelBriefs)}`,\n 'Structured report JSON is stored in script[type=\"midscene_web_dump\"] tags near this comment.',\n 'Screenshots are stored as script[type=\"midscene-image\"] tags or files referenced by screenshot refs.',\n 'For AI analysis, inspect each execution task for type, status, timing, param, output, usage, searchAreaUsage, recorder, and screenshot refs.',\n 'Use the Markdown export when available; it contains the same report context plus image links for agent review.',\n ];\n\n return `\\n<!--\\n${lines.join('\\n')}\\n-->\\n`;\n}\n\nfunction htmlScriptCloseTag(): string {\n // biome-ignore lint/style/useTemplate: keep this token runtime-built for inline report bundles\n return String.fromCharCode(60) + '/script>';\n}\n\n/** Avoid raw close tokens: this writer itself is bundled into report HTML. */\nexport function generateTestRunReportScriptTag(\n dump: TestRunReportDump,\n): string {\n return [\n String.fromCharCode(60),\n 'script type=\"',\n TEST_RUN_REPORT_SCRIPT_TYPE,\n '\">\\n',\n escapeScriptTag(JSON.stringify(dump)),\n '\\n',\n htmlScriptCloseTag(),\n '\\n',\n ].join('');\n}\n\nexport function extractTestRunReportDumpSync(\n filePath: string,\n): TestRunReportDump | undefined {\n let result: TestRunReportDump | undefined;\n // Consume executable scripts as whole elements: the embedded viewer itself\n // contains report-writer source with data-tag strings that are not payloads.\n const openTag = `${String.fromCharCode(60)}script`;\n streamScanTags(filePath, openTag, htmlScriptCloseTag(), (content) => {\n const tagEnd = content.indexOf('>');\n const attributes = content.slice(0, tagEnd);\n const type = attributes.match(/(?:^|\\s)type\\s*=\\s*[\"']([^\"']+)[\"']/i)?.[1];\n if (type !== TEST_RUN_REPORT_SCRIPT_TYPE) return false;\n if (result)\n throw new Error(\n `Report contains multiple Midscene Test dumps: ${filePath}`,\n );\n const parsed = JSON.parse(unescapeContent(content.slice(tagEnd + 1)));\n if (\n parsed?.schemaVersion !== 1 ||\n parsed?.kind !== 'test-runner' ||\n !Array.isArray(parsed.projects)\n )\n throw new Error(`Invalid Midscene Test report dump: ${filePath}`);\n result = parsed;\n return false;\n });\n return result;\n}\n\n/** Chunk size for streaming file operations (64KB) */\nexport const STREAMING_CHUNK_SIZE = 64 * 1024;\n\n/**\n * Callback for processing matched tags during streaming.\n * @param content - The content between open and close tags\n * @returns true to stop scanning, false to continue\n */\ntype TagMatchCallback = (content: string) => boolean;\n\nclass StreamingTagMatcher {\n private leftover = '';\n private capturing = false;\n private currentContent = '';\n\n constructor(\n private readonly openTag: string,\n private readonly closeTag: string,\n private readonly onMatch: TagMatchCallback,\n ) {}\n\n push(nextChunk: string): boolean {\n const chunk = this.leftover + nextChunk;\n this.leftover = '';\n let searchStart = 0;\n\n while (searchStart < chunk.length) {\n if (!this.capturing) {\n const startIndex = chunk.indexOf(this.openTag, searchStart);\n if (startIndex === -1) {\n const retainedSuffixLength = Math.max(0, this.openTag.length - 1);\n this.leftover = retainedSuffixLength\n ? chunk.slice(-retainedSuffixLength)\n : '';\n return false;\n }\n this.capturing = true;\n searchStart = startIndex + this.openTag.length;\n }\n\n const endIndex = chunk.indexOf(this.closeTag, searchStart);\n if (endIndex !== -1) {\n this.currentContent += chunk.slice(searchStart, endIndex);\n if (this.onMatch(this.currentContent)) return true;\n this.capturing = false;\n this.currentContent = '';\n searchStart = endIndex + this.closeTag.length;\n continue;\n }\n\n const retainedSuffixLength = Math.max(0, this.closeTag.length - 1);\n const contentEnd = Math.max(\n searchStart,\n chunk.length - retainedSuffixLength,\n );\n this.currentContent += chunk.slice(searchStart, contentEnd);\n this.leftover = chunk.slice(contentEnd);\n return false;\n }\n\n return false;\n }\n}\n\n/**\n * Stream through a file and find tags matching the pattern.\n * Memory usage: O(chunk_size + tag_size), not O(file_size).\n *\n * @param filePath - Absolute path to the file\n * @param openTag - Opening tag to search for\n * @param closeTag - Closing tag\n * @param onMatch - Callback for each matched tag content\n */\nexport function streamScanTags(\n filePath: string,\n openTag: string,\n closeTag: string,\n onMatch: TagMatchCallback,\n): void {\n const fd = openSync(filePath, 'r');\n const fileSize = statSync(filePath).size;\n const buffer = Buffer.alloc(STREAMING_CHUNK_SIZE);\n const decoder = new StringDecoder('utf8');\n const matcher = new StreamingTagMatcher(openTag, closeTag, onMatch);\n let position = 0;\n\n try {\n while (position < fileSize) {\n const bytesRead = readSync(fd, buffer, 0, STREAMING_CHUNK_SIZE, position);\n position += bytesRead;\n if (matcher.push(decoder.write(buffer.subarray(0, bytesRead)))) return;\n }\n matcher.push(decoder.end());\n } finally {\n closeSync(fd);\n }\n}\n\n/** Asynchronously stream tags without blocking the Node.js event loop. */\nexport async function streamScanTagsAsync(\n filePath: string,\n openTag: string,\n closeTag: string,\n onMatch: TagMatchCallback,\n): Promise<void> {\n const handle = await openAsync(filePath, 'r');\n const buffer = Buffer.alloc(STREAMING_CHUNK_SIZE);\n const decoder = new StringDecoder('utf8');\n let position = 0;\n const matcher = new StreamingTagMatcher(openTag, closeTag, onMatch);\n\n try {\n while (true) {\n const { bytesRead } = await handle.read(\n buffer,\n 0,\n STREAMING_CHUNK_SIZE,\n position,\n );\n if (bytesRead === 0) {\n matcher.push(decoder.end());\n return;\n }\n position += bytesRead;\n if (matcher.push(decoder.write(buffer.subarray(0, bytesRead)))) return;\n }\n } finally {\n await handle.close();\n }\n}\n\n/**\n * Synchronously extract a specific image's base64 data from an HTML file by its id.\n * Uses streaming to avoid loading the entire file into memory.\n *\n * @param htmlPath - Absolute path to the HTML file\n * @param imageId - The id of the image to extract\n * @returns The base64 data string, or null if not found\n */\nexport function extractImageByIdSync(\n htmlPath: string,\n imageId: string,\n): string | null {\n const targetTag = `<script type=\"midscene-image\" data-id=\"${imageId}\">`;\n const closeTag = htmlScriptCloseTag();\n\n let result: string | null = null;\n\n streamScanTags(htmlPath, targetTag, closeTag, (content) => {\n result = unescapeContent(content);\n return true; // Stop after first match\n });\n\n return result;\n}\n\n/**\n * Stream image script tags from source file directly to output file.\n * Memory usage: O(single_image_size), not O(all_images_size).\n *\n * @param srcFilePath - Source HTML file path\n * @param destFilePath - Destination file path to append to\n * @param writtenImageIds - IDs already written to the destination\n */\nexport function streamImageScriptsToFile(\n srcFilePath: string,\n destFilePath: string,\n writtenImageIds: Set<string> = new Set(),\n): void {\n const { appendFileSync } = require('node:fs');\n const openTag = '<script type=\"midscene-image\"';\n const closeTag = htmlScriptCloseTag();\n\n streamScanTags(srcFilePath, openTag, closeTag, (content) => {\n const imageId = parseImageScriptId(content);\n if (!imageId || writtenImageIds.has(imageId)) {\n return false;\n }\n // Write complete tag immediately to destination, don't accumulate\n appendFileSync(destFilePath, `${openTag}${content}${closeTag}\\n`);\n writtenImageIds.add(imageId);\n return false; // Continue scanning for more tags\n });\n}\n\nfunction parseImageScriptId(contentAfterType: string): string | null {\n const match = /^ data-id=\"([a-z0-9_-]{1,128})\">/i.exec(contentAfterType);\n return match?.[1] ?? null;\n}\n\n/** Collect IDs of real inline image script tags without blocking on a large report. */\nexport async function collectImageScriptIds(\n filePath: string,\n): Promise<Set<string>> {\n const imageIds = new Set<string>();\n const openTag = '<script type=\"midscene-image\"';\n const closeTag = htmlScriptCloseTag();\n\n await streamScanTagsAsync(filePath, openTag, closeTag, (content) => {\n const imageId = parseImageScriptId(content);\n if (imageId) imageIds.add(imageId);\n return false;\n });\n return imageIds;\n}\n\n/**\n * Extract the LAST dump script content from HTML file using streaming.\n * Memory usage: O(dump_size), not O(file_size).\n *\n * @param filePath - Absolute path to the HTML file\n * @returns The dump script content (trimmed), or empty string if not found\n */\nexport function extractLastDumpScriptSync(filePath: string): string {\n const openTagPrefix = '<script type=\"midscene_web_dump\"';\n const closeTag = htmlScriptCloseTag();\n\n let lastContent = '';\n\n // Custom streaming to handle the special case where open tag has variable attributes\n const fd = openSync(filePath, 'r');\n const fileSize = statSync(filePath).size;\n const buffer = Buffer.alloc(STREAMING_CHUNK_SIZE);\n\n let position = 0;\n let leftover = '';\n let capturing = false;\n let currentContent = '';\n\n try {\n while (position < fileSize) {\n const bytesRead = readSync(fd, buffer, 0, STREAMING_CHUNK_SIZE, position);\n const chunk = leftover + buffer.toString('utf-8', 0, bytesRead);\n position += bytesRead;\n\n let searchStart = 0;\n\n while (searchStart < chunk.length) {\n if (!capturing) {\n const startIdx = chunk.indexOf(openTagPrefix, searchStart);\n if (startIdx !== -1) {\n // Find the end of the opening tag (the '>' character)\n const tagEndIdx = chunk.indexOf('>', startIdx);\n if (tagEndIdx !== -1) {\n capturing = true;\n currentContent = chunk.slice(tagEndIdx + 1);\n const endIdx = currentContent.indexOf(closeTag);\n if (endIdx !== -1) {\n lastContent = currentContent.slice(0, endIdx).trim();\n capturing = false;\n currentContent = '';\n searchStart = tagEndIdx + 1 + endIdx + closeTag.length;\n } else {\n leftover = currentContent.slice(-closeTag.length);\n currentContent = currentContent.slice(0, -closeTag.length);\n break;\n }\n } else {\n leftover = chunk.slice(startIdx);\n break;\n }\n } else {\n leftover = chunk.slice(-openTagPrefix.length);\n break;\n }\n } else {\n const endIdx = chunk.indexOf(closeTag, searchStart);\n if (endIdx !== -1) {\n currentContent += chunk.slice(searchStart, endIdx);\n lastContent = currentContent.trim();\n capturing = false;\n currentContent = '';\n searchStart = endIdx + closeTag.length;\n } else {\n currentContent += chunk.slice(searchStart, -closeTag.length);\n leftover = chunk.slice(-closeTag.length);\n break;\n }\n }\n }\n }\n } finally {\n closeSync(fd);\n }\n\n return lastContent;\n}\n\n/**\n * Extract ALL dump script contents from an HTML file using streaming.\n * Each entry includes the full opening tag (for attribute extraction) and the content.\n *\n * @param filePath - Absolute path to the HTML file\n * @returns Array of { openTag, content } for each dump script found\n */\nexport function extractAllDumpScriptsSync(\n filePath: string,\n): { openTag: string; content: string }[] {\n const results: { openTag: string; content: string }[] = [];\n streamDumpScriptsSync(filePath, (dumpScript) => {\n results.push(dumpScript);\n return false;\n });\n return results;\n}\n\n/**\n * Stream ALL dump scripts from an HTML file.\n * Calls onMatch for each dump script and keeps memory bounded to a single\n * dump script payload instead of accumulating every dump in memory.\n *\n * @param filePath - Absolute path to the HTML file\n * @param onMatch - Callback for each dump script; return true to stop early\n */\nexport function streamDumpScriptsSync(\n filePath: string,\n onMatch: (dumpScript: { openTag: string; content: string }) => boolean,\n): void {\n const openTagPrefix = '<script type=\"midscene_web_dump\"';\n const closeTag = htmlScriptCloseTag();\n\n const fd = openSync(filePath, 'r');\n const fileSize = statSync(filePath).size;\n const buffer = Buffer.alloc(STREAMING_CHUNK_SIZE);\n\n let position = 0;\n let leftover = '';\n let capturing = false;\n let currentContent = '';\n let currentOpenTag = '';\n\n try {\n while (position < fileSize) {\n const bytesRead = readSync(fd, buffer, 0, STREAMING_CHUNK_SIZE, position);\n const chunk = leftover + buffer.toString('utf-8', 0, bytesRead);\n position += bytesRead;\n\n let searchStart = 0;\n\n while (searchStart < chunk.length) {\n if (!capturing) {\n const startIdx = chunk.indexOf(openTagPrefix, searchStart);\n if (startIdx !== -1) {\n const tagEndIdx = chunk.indexOf('>', startIdx);\n if (tagEndIdx !== -1) {\n capturing = true;\n currentOpenTag = chunk.slice(startIdx, tagEndIdx + 1);\n currentContent = chunk.slice(tagEndIdx + 1);\n const endIdx = currentContent.indexOf(closeTag);\n if (endIdx !== -1) {\n const shouldStop = onMatch({\n openTag: currentOpenTag,\n content: currentContent.slice(0, endIdx).trim(),\n });\n if (shouldStop) return;\n capturing = false;\n currentContent = '';\n currentOpenTag = '';\n searchStart = tagEndIdx + 1 + endIdx + closeTag.length;\n } else {\n leftover = currentContent.slice(-closeTag.length);\n currentContent = currentContent.slice(0, -closeTag.length);\n break;\n }\n } else {\n leftover = chunk.slice(startIdx);\n break;\n }\n } else {\n leftover = chunk.slice(-openTagPrefix.length);\n break;\n }\n } else {\n const endIdx = chunk.indexOf(closeTag, searchStart);\n if (endIdx !== -1) {\n currentContent += chunk.slice(searchStart, endIdx);\n const shouldStop = onMatch({\n openTag: currentOpenTag,\n content: currentContent.trim(),\n });\n if (shouldStop) return;\n capturing = false;\n currentContent = '';\n currentOpenTag = '';\n searchStart = endIdx + closeTag.length;\n } else {\n currentContent += chunk.slice(searchStart, -closeTag.length);\n leftover = chunk.slice(-closeTag.length);\n break;\n }\n }\n }\n }\n } finally {\n closeSync(fd);\n }\n}\n\nexport function parseImageScripts(html: string): Record<string, string> {\n const imageMap: Record<string, string> = {};\n const regex =\n /<script type=\"midscene-image\" data-id=\"([^\"]+)\">([\\s\\S]*?)<\\/script>/g;\n\n for (const match of html.matchAll(regex)) {\n const [, id, content] = match;\n imageMap[id] = unescapeContent(content);\n }\n\n return imageMap;\n}\n\nexport function parseDumpScript(html: string): string {\n // Use string search instead of regex to avoid ReDoS vulnerability\n // Find the LAST dump script tag (template may contain similar patterns in bundled JS)\n const scriptOpenTag = '<script type=\"midscene_web_dump\"';\n const closeTag = htmlScriptCloseTag();\n\n // Find the last occurrence of the opening tag\n const lastOpenIndex = html.lastIndexOf(scriptOpenTag);\n if (lastOpenIndex === -1) {\n throw new Error('No dump script found in HTML');\n }\n\n // Find the end of the opening tag (the '>' character)\n const tagEndIndex = html.indexOf('>', lastOpenIndex);\n if (tagEndIndex === -1) {\n throw new Error('No dump script found in HTML');\n }\n\n // Find the closing tag after the opening tag\n const closeIndex = html.indexOf(closeTag, tagEndIndex);\n if (closeIndex === -1) {\n throw new Error('No dump script found in HTML');\n }\n\n // Extract content between opening and closing tags\n const content = html.substring(tagEndIndex + 1, closeIndex);\n return unescapeContent(content);\n}\n\nexport function parseDumpScriptAttributes(\n html: string,\n): Record<string, string> {\n const regex = /<script type=\"midscene_web_dump\"([^>]*)>/;\n const match = regex.exec(html);\n\n if (!match) {\n return {};\n }\n\n const attrString = match[1];\n const attributes: Record<string, string> = {};\n const attrRegex = /(\\w+)=\"([^\"]*)\"/g;\n\n for (const attrMatch of attrString.matchAll(attrRegex)) {\n const [, key, value] = attrMatch;\n if (key !== 'type') {\n attributes[key] = decodeURIComponent(value);\n }\n }\n\n return attributes;\n}\n\nexport function generateImageScriptTag(id: string, data: string): string {\n // Do not use template string here, will cause bundle error with <script\n const closeTag = htmlScriptCloseTag();\n return (\n // biome-ignore lint/style/useTemplate: <explanation>\n '<script type=\"midscene-image\" data-id=\"' +\n id +\n '\">' +\n escapeContent(data) +\n closeTag\n );\n}\n\n/**\n * Inline script that fixes relative URL resolution for directory-mode reports.\n *\n * Problem: when a static server (e.g. `npx serve`) serves `name/index.html`\n * at URL `/name` (without trailing slash), relative paths like\n * `./screenshots/xxx.png` resolve to `/screenshots/xxx.png` instead of\n * `/name/screenshots/xxx.png`.\n *\n * Fix: dynamically insert a <base> tag so relative URLs resolve correctly.\n */\n// Do not use template string here, will cause bundle error with <script\n//\n// The closing script tag is built at runtime so bundlers cannot inline the\n// token that would prematurely close the report template's inline app bundle.\n//\n// Do NOT replace this with a string constant, hex escape (\\x3c), or literal\n// string concatenation. Bundlers may optimise those forms back to the unsafe\n// token.\nlet _baseUrlFixScript: string;\nexport function getBaseUrlFixScript(): string {\n if (!_baseUrlFixScript) {\n const close = htmlScriptCloseTag();\n _baseUrlFixScript =\n // biome-ignore lint/style/useTemplate: see above\n '\\n<script>(function(){' +\n 'var p=window.location.pathname;' +\n 'if(p.endsWith(\"/\")||/\\\\.\\\\w+$/.test(p))return;' +\n 'var b=document.createElement(\"base\");' +\n 'b.href=p+\"/\";' +\n 'document.head.insertBefore(b,document.head.firstChild)' +\n '})()' +\n close +\n '\\n';\n }\n return _baseUrlFixScript;\n}\n\n/**\n * Dump-script attribute that records how the report file stores screenshots.\n * Written by the report generator and the merger, read back when deciding\n * whether a report is in directory mode. Kept here, next to dump-tag\n * generation, so the writer and reader share one source of truth.\n */\nexport const DATA_SCREENSHOT_MODE_ATTR = 'data-screenshot-mode';\n\nexport function generateDumpScriptTag(\n json: string,\n attributes?: Record<string, string | number | boolean>,\n): string {\n const closeTag = htmlScriptCloseTag();\n let attrString = '';\n if (attributes && Object.keys(attributes).length > 0) {\n // Do not use template string here, will cause bundle error with <script\n attrString =\n // biome-ignore lint/style/useTemplate: <explanation>\n ' ' +\n Object.entries(attributes)\n // biome-ignore lint/style/useTemplate: <explanation>\n .map(([k, v]) => k + '=\"' + encodeURIComponent(v) + '\"')\n .join(' ');\n }\n\n // Do not use template string here, will cause bundle error with <script\n return (\n // biome-ignore lint/style/useTemplate: <explanation>\n '<script type=\"midscene_web_dump\"' +\n attrString +\n '>' +\n escapeContent(json) +\n closeTag\n );\n}\n"],"names":["escapeContent","escapeScriptTag","unescapeContent","antiEscapeScriptTag","cleanHtmlCommentValue","value","String","formatModelBriefForAgent","brief","intent","model","description","hasUsageModelInfo","usage","Boolean","formatUsageModelForAgent","source","collectUsageModelsForAgent","report","models","Map","executions","Array","execution","tasks","task","taskWithUsage","usages","formatted","generateAgentReportComment","taskCount","sum","modelBriefs","lines","htmlScriptCloseTag","generateTestRunReportScriptTag","dump","TEST_RUN_REPORT_SCRIPT_TYPE","JSON","extractTestRunReportDumpSync","filePath","result","openTag","streamScanTags","content","tagEnd","attributes","type","Error","parsed","STREAMING_CHUNK_SIZE","StreamingTagMatcher","nextChunk","chunk","searchStart","startIndex","retainedSuffixLength","Math","endIndex","contentEnd","closeTag","onMatch","fd","openSync","fileSize","statSync","buffer","Buffer","decoder","StringDecoder","matcher","position","bytesRead","readSync","closeSync","streamScanTagsAsync","handle","openAsync","extractImageByIdSync","htmlPath","imageId","targetTag","streamImageScriptsToFile","srcFilePath","destFilePath","writtenImageIds","Set","appendFileSync","require","parseImageScriptId","contentAfterType","match","collectImageScriptIds","imageIds","extractLastDumpScriptSync","openTagPrefix","lastContent","leftover","capturing","currentContent","endIdx","startIdx","tagEndIdx","extractAllDumpScriptsSync","results","streamDumpScriptsSync","dumpScript","currentOpenTag","shouldStop","parseImageScripts","html","imageMap","regex","id","parseDumpScript","scriptOpenTag","lastOpenIndex","tagEndIndex","closeIndex","parseDumpScriptAttributes","attrString","attrRegex","attrMatch","key","decodeURIComponent","generateImageScriptTag","data","_baseUrlFixScript","getBaseUrlFixScript","close","DATA_SCREENSHOT_MODE_ATTR","generateDumpScriptTag","json","Object","k","v","encodeURIComponent"],"mappings":"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"}
|
|
@@ -75,6 +75,9 @@ function resolveScreenshotSource(refInput, options) {
|
|
|
75
75
|
return resolveImageSource(ref, options);
|
|
76
76
|
}
|
|
77
77
|
class ReportImageStore {
|
|
78
|
+
enableFileCopies() {
|
|
79
|
+
this.alsoWriteFileCopy = true;
|
|
80
|
+
}
|
|
78
81
|
async persist(screenshot) {
|
|
79
82
|
await this.ensureExistingInlineImageIdsLoaded();
|
|
80
83
|
const shouldWriteFileCopy = 'directory' === this.mode || this.alsoWriteFileCopy;
|
|
@@ -143,7 +146,11 @@ class ReportImageStore {
|
|
|
143
146
|
await this.existingInlineImageIdsPromise;
|
|
144
147
|
}
|
|
145
148
|
async persistToSharedFileIfNeeded(screenshot, options) {
|
|
146
|
-
const
|
|
149
|
+
const fileName = `${screenshot.id}.${screenshot.extension}`;
|
|
150
|
+
const { relativePath, absolutePath } = this.writtenFileIds.has(screenshot.id) ? {
|
|
151
|
+
relativePath: `./screenshots/${fileName}`,
|
|
152
|
+
absolutePath: join(this.screenshotsDir, fileName)
|
|
153
|
+
} : await this.writeImageFileIfNeeded({
|
|
147
154
|
id: screenshot.id,
|
|
148
155
|
extension: screenshot.extension,
|
|
149
156
|
rawBase64: screenshot.rawBase64
|