@metamask/device-mcp 0.3.3 → 0.4.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/CHANGELOG.md +11 -1
- package/README.md +110 -21
- package/dist/android/NOTICE.md +65 -0
- package/dist/android/device-mcp-android-snapshot-helper-0.4.0.apk +0 -0
- package/dist/android/device-mcp-android-snapshot-helper-0.4.0.apk.idsig +0 -0
- package/dist/android/device-mcp-android-snapshot-helper-0.4.0.manifest.json +10 -0
- package/dist/backends/adb-backend.cjs +325 -17
- package/dist/backends/adb-backend.cjs.map +1 -1
- package/dist/backends/adb-backend.d.cts.map +1 -1
- package/dist/backends/adb-backend.d.mts.map +1 -1
- package/dist/backends/adb-backend.mjs +326 -18
- package/dist/backends/adb-backend.mjs.map +1 -1
- package/dist/backends/android-instrumentation/adb.cjs +28 -0
- package/dist/backends/android-instrumentation/adb.cjs.map +1 -0
- package/dist/backends/android-instrumentation/adb.d.cts +24 -0
- package/dist/backends/android-instrumentation/adb.d.cts.map +1 -0
- package/dist/backends/android-instrumentation/adb.d.mts +24 -0
- package/dist/backends/android-instrumentation/adb.d.mts.map +1 -0
- package/dist/backends/android-instrumentation/adb.mjs +25 -0
- package/dist/backends/android-instrumentation/adb.mjs.map +1 -0
- package/dist/backends/android-instrumentation/artifact.cjs +139 -0
- package/dist/backends/android-instrumentation/artifact.cjs.map +1 -0
- package/dist/backends/android-instrumentation/artifact.d.cts +28 -0
- package/dist/backends/android-instrumentation/artifact.d.cts.map +1 -0
- package/dist/backends/android-instrumentation/artifact.d.mts +28 -0
- package/dist/backends/android-instrumentation/artifact.d.mts.map +1 -0
- package/dist/backends/android-instrumentation/artifact.mjs +154 -0
- package/dist/backends/android-instrumentation/artifact.mjs.map +1 -0
- package/dist/backends/android-instrumentation/errors.cjs +79 -0
- package/dist/backends/android-instrumentation/errors.cjs.map +1 -0
- package/dist/backends/android-instrumentation/errors.d.cts +68 -0
- package/dist/backends/android-instrumentation/errors.d.cts.map +1 -0
- package/dist/backends/android-instrumentation/errors.d.mts +68 -0
- package/dist/backends/android-instrumentation/errors.d.mts.map +1 -0
- package/dist/backends/android-instrumentation/errors.mjs +73 -0
- package/dist/backends/android-instrumentation/errors.mjs.map +1 -0
- package/dist/backends/android-instrumentation/installer.cjs +235 -0
- package/dist/backends/android-instrumentation/installer.cjs.map +1 -0
- package/dist/backends/android-instrumentation/installer.d.cts +123 -0
- package/dist/backends/android-instrumentation/installer.d.cts.map +1 -0
- package/dist/backends/android-instrumentation/installer.d.mts +123 -0
- package/dist/backends/android-instrumentation/installer.d.mts.map +1 -0
- package/dist/backends/android-instrumentation/installer.mjs +225 -0
- package/dist/backends/android-instrumentation/installer.mjs.map +1 -0
- package/dist/backends/android-instrumentation/signer.cjs +435 -0
- package/dist/backends/android-instrumentation/signer.cjs.map +1 -0
- package/dist/backends/android-instrumentation/signer.d.cts +40 -0
- package/dist/backends/android-instrumentation/signer.d.cts.map +1 -0
- package/dist/backends/android-instrumentation/signer.d.mts +40 -0
- package/dist/backends/android-instrumentation/signer.d.mts.map +1 -0
- package/dist/backends/android-instrumentation/signer.mjs +431 -0
- package/dist/backends/android-instrumentation/signer.mjs.map +1 -0
- package/dist/backends/android-instrumentation/snapshot.cjs +159 -0
- package/dist/backends/android-instrumentation/snapshot.cjs.map +1 -0
- package/dist/backends/android-instrumentation/snapshot.d.cts +67 -0
- package/dist/backends/android-instrumentation/snapshot.d.cts.map +1 -0
- package/dist/backends/android-instrumentation/snapshot.d.mts +67 -0
- package/dist/backends/android-instrumentation/snapshot.d.mts.map +1 -0
- package/dist/backends/android-instrumentation/snapshot.mjs +154 -0
- package/dist/backends/android-instrumentation/snapshot.mjs.map +1 -0
- package/dist/backends/appium-backend.cjs +18 -1
- package/dist/backends/appium-backend.cjs.map +1 -1
- package/dist/backends/appium-backend.d.cts.map +1 -1
- package/dist/backends/appium-backend.d.mts.map +1 -1
- package/dist/backends/appium-backend.mjs +18 -1
- package/dist/backends/appium-backend.mjs.map +1 -1
- package/dist/backends/idb-backend.cjs +127 -31
- package/dist/backends/idb-backend.cjs.map +1 -1
- package/dist/backends/idb-backend.d.cts.map +1 -1
- package/dist/backends/idb-backend.d.mts.map +1 -1
- package/dist/backends/idb-backend.mjs +128 -32
- package/dist/backends/idb-backend.mjs.map +1 -1
- package/dist/tools/swipe.cjs +3 -1
- package/dist/tools/swipe.cjs.map +1 -1
- package/dist/tools/swipe.d.cts.map +1 -1
- package/dist/tools/swipe.d.mts.map +1 -1
- package/dist/tools/swipe.mjs +3 -1
- package/dist/tools/swipe.mjs.map +1 -1
- package/dist/utils/output-path.cjs +13 -0
- package/dist/utils/output-path.cjs.map +1 -1
- package/dist/utils/output-path.d.cts +10 -0
- package/dist/utils/output-path.d.cts.map +1 -1
- package/dist/utils/output-path.d.mts +10 -0
- package/dist/utils/output-path.d.mts.map +1 -1
- package/dist/utils/output-path.mjs +12 -0
- package/dist/utils/output-path.mjs.map +1 -1
- package/dist/utils/platform.cjs +65 -7
- package/dist/utils/platform.cjs.map +1 -1
- package/dist/utils/platform.d.cts +33 -0
- package/dist/utils/platform.d.cts.map +1 -1
- package/dist/utils/platform.d.mts +33 -0
- package/dist/utils/platform.d.mts.map +1 -1
- package/dist/utils/platform.mjs +63 -7
- package/dist/utils/platform.mjs.map +1 -1
- package/package.json +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adb-backend.cjs","sourceRoot":"","sources":["../../src/backends/adb-backend.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from 'node:child_process';\nimport type { ChildProcess } from 'node:child_process';\nimport { readFile } from 'node:fs/promises';\n\nimport type {\n DeviceBackend,\n DeviceButton,\n DeviceInfo,\n SnapshotResult,\n ScreenshotResult,\n ScreenshotFileResult,\n ScreenshotOptions,\n LogsResult,\n TapResult,\n AppStateResult,\n ElementQuery,\n UIElement,\n WindowSize,\n} from './types.js';\nimport { ACCEPT_LABELS, DENY_LABELS } from '../utils/alert-labels.js';\nimport {\n findElement,\n describeElement,\n computeSwipeEnd,\n} from '../utils/element.js';\nimport { execStrict, isCommandAvailable } from '../utils/exec.js';\nimport {\n hardenArtifactFile,\n resolveArtifactPath,\n} from '../utils/output-path.js';\n\nexport class AdbBackend implements DeviceBackend {\n readonly kind = 'adb' as const;\n\n readonly platform = 'android' as const;\n\n readonly #serial: string;\n\n #recordingProcess: ChildProcess | null = null;\n\n #recordingPath: string | null = null;\n\n readonly #recordingRemotePath = '/sdcard/device-mcp-recording.mp4';\n\n constructor(serial: string) {\n this.#serial = serial;\n }\n\n async #adb(args: string[]): Promise<string> {\n return execStrict('adb', ['-s', this.#serial, ...args]);\n }\n\n async ensureConnected(): Promise<void> {\n if (!(await isCommandAvailable('adb'))) {\n throw new Error(\n 'adb is not installed or not on $PATH.\\n' +\n 'Install Android SDK platform-tools.',\n );\n }\n const state = await this.#adb(['get-state']);\n if (state.trim() !== 'device') {\n throw new Error(\n `Device ${this.#serial} is not ready (state: ${state.trim()})`,\n );\n }\n }\n\n async getDeviceInfo(): Promise<DeviceInfo> {\n const [model, version] = await Promise.all([\n this.#adb(['shell', 'getprop', 'ro.product.model']),\n this.#adb(['shell', 'getprop', 'ro.build.version.release']),\n ]);\n return {\n platform: 'android',\n deviceId: this.#serial,\n name: model.trim(),\n osVersion: version.trim(),\n state: 'device',\n };\n }\n\n async snapshot(): Promise<SnapshotResult> {\n const raw = await this.#dumpUiHierarchy();\n const hierarchy = parseAndroidHierarchy(raw);\n return {\n platform: 'android',\n hierarchy,\n raw,\n timestamp: Date.now(),\n };\n }\n\n async tapElement(query: ElementQuery): Promise<TapResult> {\n const snapshot = await this.snapshot();\n const element = findElement(snapshot.hierarchy, query);\n if (!element) {\n throw new Error(\n `Element not found: ${JSON.stringify(query)}\\n` +\n 'Use device_snapshot to inspect the current UI hierarchy.',\n );\n }\n\n const x = Math.round(element.frame.x + element.frame.width / 2);\n const y = Math.round(element.frame.y + element.frame.height / 2);\n await this.tapCoordinates(x, y);\n\n return {\n success: true,\n x,\n y,\n targetDescription: describeElement(element),\n };\n }\n\n async getElementText(query: ElementQuery): Promise<string> {\n const snapshot = await this.snapshot();\n const element = findElement(snapshot.hierarchy, query);\n if (!element) {\n throw new Error(\n `Element not found: ${JSON.stringify(query)}\\n` +\n 'Use device_snapshot to inspect the current UI hierarchy.',\n );\n }\n return element.label ?? element.value ?? '';\n }\n\n async tapCoordinates(x: number, y: number): Promise<void> {\n await this.#adb(['shell', 'input', 'tap', String(x), String(y)]);\n }\n\n async typeText(text: string): Promise<void> {\n // Android keyevent 29+shift = Ctrl+A (select all), 67 = DEL\n await this.#adb(['shell', 'input', 'keyevent', 'KEYCODE_MOVE_HOME']);\n await this.#adb([\n 'shell',\n 'input',\n 'keyevent',\n '--longpress',\n 'KEYCODE_SHIFT_LEFT',\n 'KEYCODE_MOVE_END',\n ]);\n await this.#adb(['shell', 'input', 'keyevent', '67']);\n // Android input text requires escaping spaces and special chars\n const escaped = text.replace(/ /gu, '%s').replace(/[&|;<>]/gu, '\\\\$&');\n await this.#adb(['shell', 'input', 'text', escaped]);\n }\n\n async swipe(\n direction: 'up' | 'down' | 'left' | 'right',\n startX?: number,\n startY?: number,\n distance?: number,\n ): Promise<void> {\n const d = distance ?? 500;\n const sx = startX ?? 540;\n const sy = startY ?? 960;\n const [endX, endY] = computeSwipeEnd(sx, sy, direction, d);\n const durationMs = 300;\n\n await this.#adb([\n 'shell',\n 'input',\n 'swipe',\n String(sx),\n String(sy),\n String(endX),\n String(endY),\n String(durationMs),\n ]);\n }\n\n async waitForElement(\n query: ElementQuery,\n timeoutMs = 10_000,\n intervalMs = 500,\n ): Promise<UIElement> {\n const start = Date.now();\n while (Date.now() - start < timeoutMs) {\n const snapshot = await this.snapshot();\n const element = findElement(snapshot.hierarchy, query);\n if (element) {\n return element;\n }\n await new Promise((resolve) => setTimeout(resolve, intervalMs));\n }\n throw new Error(\n `Timed out waiting for element: ${JSON.stringify(query)} (${timeoutMs}ms)`,\n );\n }\n\n async getAppState(bundleId: string): Promise<AppStateResult> {\n const raw = await this.#adb(['shell', 'dumpsys', 'activity', 'processes']);\n\n const pidMatch = raw.match(\n new RegExp(`PID #(\\\\d+):.*${bundleId.replace(/\\./gu, '\\\\.')}`, 'u'),\n );\n\n if (pidMatch) {\n return {\n bundleId,\n state: 'Running',\n pid: parseInt(pidMatch[1], 10),\n };\n }\n\n const pmResult = await this.#adb([\n 'shell',\n 'pm',\n 'list',\n 'packages',\n bundleId,\n ]);\n if (pmResult.includes(bundleId)) {\n return { bundleId, state: 'Installed (not running)' };\n }\n\n return { bundleId, state: 'Not Installed' };\n }\n\n async #dumpUiHierarchy(): Promise<string> {\n const remotePath = '/sdcard/window_dump.xml';\n await this.#adb(['shell', 'uiautomator', 'dump', remotePath]);\n const xml = await this.#adb(['shell', 'cat', remotePath]);\n await this.#adb(['shell', 'rm', '-f', remotePath]);\n return xml;\n }\n\n screenshot(\n outputPath?: string,\n options?: { encode?: true },\n ): Promise<ScreenshotResult>;\n\n screenshot(\n outputPath: string | undefined,\n options: { encode: false },\n ): Promise<ScreenshotFileResult>;\n\n screenshot(\n outputPath?: string,\n options?: ScreenshotOptions,\n ): Promise<ScreenshotResult | ScreenshotFileResult>;\n\n async screenshot(\n outputPath?: string,\n options?: ScreenshotOptions,\n ): Promise<ScreenshotResult | ScreenshotFileResult> {\n const localPath = resolveArtifactPath(outputPath, 'screenshot');\n await this.#adb(['shell', 'screencap', '-p', '/sdcard/screenshot.png']);\n await execStrict('adb', [\n '-s',\n this.#serial,\n 'pull',\n '/sdcard/screenshot.png',\n localPath,\n ]);\n await this.#adb(['shell', 'rm', '-f', '/sdcard/screenshot.png']);\n hardenArtifactFile(localPath);\n if (options?.encode === false) {\n return { data: undefined, format: 'png', path: localPath };\n }\n const data = await readFile(localPath, 'base64');\n return { data, format: 'png', path: localPath };\n }\n\n async openApp(bundleId: string): Promise<void> {\n await this.#adb([\n 'shell',\n 'monkey',\n '-p',\n bundleId,\n '-c',\n 'android.intent.category.LAUNCHER',\n '1',\n ]);\n }\n\n async closeApp(bundleId: string): Promise<void> {\n await this.#adb(['shell', 'am', 'force-stop', bundleId]);\n }\n\n async pressButton(button: DeviceButton): Promise<void> {\n // Android keyevent codes\n const keyMap: Record<DeviceButton, string> = {\n home: '3',\n back: '4',\n enter: '66',\n lock: '26',\n };\n await this.#adb(['shell', 'input', 'keyevent', keyMap[button]]);\n }\n\n async dismissKeyboard(): Promise<void> {\n await this.#adb(['shell', 'input', 'keyevent', '111']);\n }\n\n async dismissAlert(accept: boolean): Promise<void> {\n const snap = await this.snapshot();\n const candidates = accept ? ACCEPT_LABELS : DENY_LABELS;\n\n for (const label of candidates) {\n const element = findElement(snap.hierarchy, { text: label });\n if (element) {\n const cx = Math.round(element.frame.x + element.frame.width / 2);\n const cy = Math.round(element.frame.y + element.frame.height / 2);\n await this.tapCoordinates(cx, cy);\n return;\n }\n }\n\n throw new Error(`No alert button found. Tried: ${candidates.join(', ')}`);\n }\n\n async getLogs(durationSeconds = 30, filter?: string): Promise<LogsResult> {\n const args = ['shell', 'logcat', '-d', '-t', String(durationSeconds)];\n if (filter) {\n args.push('-e', filter);\n }\n const raw = await this.#adb(args);\n const entries = raw\n .trim()\n .split('\\n')\n .filter(Boolean)\n .map((line) => {\n const match = line.match(/^(\\d{2}-\\d{2}\\s+\\S+)\\s+(\\w)\\/.*?:\\s*(.*)/u);\n if (match) {\n return {\n timestamp: match[1],\n level: match[2],\n message: match[3],\n };\n }\n return { timestamp: '', level: 'info', message: line };\n });\n return { entries, source: 'logcat' };\n }\n\n async longPress(query: ElementQuery, durationMs = 1000): Promise<TapResult> {\n const snap = await this.snapshot();\n const element = findElement(snap.hierarchy, query);\n if (!element) {\n throw new Error(\n `Element not found: ${JSON.stringify(query)}\\n` +\n 'Use device_snapshot to inspect the current UI hierarchy.',\n );\n }\n\n const cx = Math.round(element.frame.x + element.frame.width / 2);\n const cy = Math.round(element.frame.y + element.frame.height / 2);\n await this.#adb([\n 'shell',\n 'input',\n 'swipe',\n String(cx),\n String(cy),\n String(cx),\n String(cy),\n String(durationMs),\n ]);\n\n return {\n success: true,\n x: cx,\n y: cy,\n targetDescription: describeElement(element),\n };\n }\n\n async scrollToElement(\n query: ElementQuery,\n direction: 'up' | 'down' = 'down',\n maxAttempts = 10,\n ): Promise<UIElement> {\n let previousRaw = '';\n\n for (let i = 0; i < maxAttempts; i++) {\n const snap = await this.snapshot();\n const element = findElement(snap.hierarchy, query);\n if (element) {\n return element;\n }\n if (snap.raw === previousRaw) {\n break;\n }\n previousRaw = snap.raw;\n await this.swipe(direction);\n }\n throw new Error(\n `Element not found after scrolling: ${JSON.stringify(query)}\\n` +\n 'Use device_snapshot to inspect the current UI hierarchy.',\n );\n }\n\n async getAlertText(): Promise<string> {\n const snap = await this.snapshot();\n const texts = collectAndroidAlertTexts(snap.hierarchy);\n if (texts.length === 0) {\n throw new Error(\n 'No alert is currently displayed.\\n' +\n 'Use device_snapshot to inspect the current UI hierarchy.',\n );\n }\n return texts.join('\\n');\n }\n\n async getWindowSize(): Promise<WindowSize> {\n const raw = await this.#adb(['shell', 'wm', 'size']);\n const match = raw.match(/(\\d+)x(\\d+)/u);\n if (match) {\n return {\n width: parseInt(match[1], 10),\n height: parseInt(match[2], 10),\n };\n }\n throw new Error(`Unable to parse window size from: ${raw.trim()}`);\n }\n\n async getContexts(): Promise<string[]> {\n return ['NATIVE_APP'];\n }\n\n async setContext(context: string): Promise<void> {\n if (context === 'NATIVE_APP') {\n return;\n }\n throw new Error(\n 'Context switching requires Appium backend. ADB only supports NATIVE_APP.',\n );\n }\n\n async getClipboard(): Promise<string> {\n throw new Error(\n 'Clipboard access via ADB is not supported on modern Android. ' +\n 'Use the Appium backend for clipboard operations.',\n );\n }\n\n async setClipboard(_text: string): Promise<void> {\n throw new Error(\n 'Clipboard access via ADB is not supported on modern Android. ' +\n 'Use the Appium backend for clipboard operations.',\n );\n }\n\n async startScreenRecording(outputPath?: string): Promise<void> {\n if (this.#recordingProcess) {\n throw new Error('Screen recording is already in progress');\n }\n this.#recordingPath = resolveArtifactPath(outputPath, 'recording');\n this.#recordingProcess = spawn('adb', [\n '-s',\n this.#serial,\n 'shell',\n 'screenrecord',\n this.#recordingRemotePath,\n ]);\n this.#recordingProcess.on('error', () => {\n this.#recordingProcess = null;\n this.#recordingPath = null;\n });\n }\n\n async stopScreenRecording(): Promise<string> {\n if (!this.#recordingProcess || !this.#recordingPath) {\n throw new Error('No screen recording in progress');\n }\n const localPath = this.#recordingPath;\n this.#recordingProcess.kill('SIGINT');\n await new Promise((resolve) => setTimeout(resolve, 2000));\n this.#recordingProcess = null;\n this.#recordingPath = null;\n\n await execStrict('adb', [\n '-s',\n this.#serial,\n 'pull',\n this.#recordingRemotePath,\n localPath,\n ]);\n await this.#adb(['shell', 'rm', '-f', this.#recordingRemotePath]);\n hardenArtifactFile(localPath);\n return localPath;\n }\n}\n\nfunction collectAndroidAlertTexts(elements: UIElement[]): string[] {\n const texts: string[] = [];\n for (const el of elements) {\n const isDialog =\n el.type.includes('Dialog') || el.type.includes('AlertDialog');\n if (isDialog) {\n collectTextValues(el.children ?? [], texts);\n return texts;\n }\n if (el.children) {\n const found = collectAndroidAlertTexts(el.children);\n if (found.length > 0) {\n return found;\n }\n }\n }\n return texts;\n}\n\nfunction collectTextValues(elements: UIElement[], texts: string[]): void {\n for (const el of elements) {\n if (el.type.includes('TextView') && el.value) {\n texts.push(el.value);\n }\n if (el.children) {\n collectTextValues(el.children, texts);\n }\n }\n}\n\nexport function parseAndroidHierarchy(xml: string): UIElement[] {\n const stack: UIElement[][] = [[]];\n\n // Matches all <node ...>, </node>, and <node .../> tags in document order\n const tagRegex = /<(\\/?)node(?:\\s+([^>]*?))?\\s*(\\/?)>/gu;\n let match: RegExpExecArray | null;\n\n while ((match = tagRegex.exec(xml)) !== null) {\n const isClosing = match[1] === '/';\n const attrs = match[2];\n const isSelfClosing = match[3] === '/';\n\n if (isClosing) {\n if (stack.length > 1) {\n stack.pop();\n }\n } else if (isSelfClosing && attrs) {\n const element = parseNodeAttributes(attrs);\n if (element) {\n stack[stack.length - 1].push(element);\n }\n } else if (attrs) {\n const element = parseNodeAttributes(attrs);\n if (element) {\n element.children = [];\n stack[stack.length - 1].push(element);\n stack.push(element.children);\n }\n }\n }\n\n return stack[0];\n}\n\nexport function parseNodeAttributes(attrs: string): UIElement | null {\n const get = (name: string): string | undefined => {\n const attrMatch = attrs.match(new RegExp(`${name}=\"([^\"]*)\"`, 'u'));\n return attrMatch?.[1] || undefined;\n };\n\n const boundsStr = get('bounds');\n if (!boundsStr) {\n return null;\n }\n\n // bounds format: [x1,y1][x2,y2]\n const boundsMatch = boundsStr.match(/\\[(\\d+),(\\d+)\\]\\[(\\d+),(\\d+)\\]/u);\n if (!boundsMatch) {\n return null;\n }\n\n const x1 = parseInt(boundsMatch[1], 10);\n const y1 = parseInt(boundsMatch[2], 10);\n const x2 = parseInt(boundsMatch[3], 10);\n const y2 = parseInt(boundsMatch[4], 10);\n\n return {\n type: get('class') ?? 'Unknown',\n label: get('content-desc') || undefined,\n value: get('text') || undefined,\n identifier: get('resource-id') || undefined,\n frame: { x: x1, y: y1, width: x2 - x1, height: y2 - y1 },\n enabled: get('enabled') !== 'false',\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"adb-backend.cjs","sourceRoot":"","sources":["../../src/backends/adb-backend.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from 'node:child_process';\nimport type { ChildProcess } from 'node:child_process';\nimport { randomUUID } from 'node:crypto';\nimport { readFile } from 'node:fs/promises';\n\nimport { isUntrustedHelperError } from './android-instrumentation/errors.js';\nimport {\n assertInstalledHelperTrusted,\n ensureHelperInstalled,\n INSTRUMENTATION_NOT_FOUND_SIGNATURE,\n} from './android-instrumentation/installer.js';\nimport {\n HELPER_INSTRUMENTATION,\n reassembleInstrumentationXml,\n} from './android-instrumentation/snapshot.js';\nimport type {\n DeviceBackend,\n DeviceButton,\n DeviceInfo,\n SnapshotResult,\n ScreenshotResult,\n ScreenshotFileResult,\n ScreenshotOptions,\n LogsResult,\n TapResult,\n AppStateResult,\n ElementQuery,\n UIElement,\n WindowSize,\n} from './types.js';\nimport { ACCEPT_LABELS, DENY_LABELS } from '../utils/alert-labels.js';\nimport {\n findElement,\n describeElement,\n computeSwipeEnd,\n} from '../utils/element.js';\nimport { exec, execStrict, isCommandAvailable } from '../utils/exec.js';\nimport {\n hardenArtifactFile,\n resolveArtifactPath,\n} from '../utils/output-path.js';\n\n// `uiautomator dump` exits 0 and prints its success banner even when it fails, so\n// the XML payload is the only trustworthy signal.\nconst DUMP_ATTEMPTS = 3;\n\nconst DUMP_RETRY_DELAY_MS = 750;\n\n// Healthy dumps take ~150ms-2s, and even a failing one returns in ~2s.\nconst DUMP_TIMEOUT_MS = 15_000;\n\n// Caps all attempts plus backoff, so worst case does not scale with retries.\nconst DUMP_DEADLINE_MS = 25_000;\n\n// Writable by the shell user without scoped-storage restrictions.\nconst DUMP_REMOTE_DIR = '/data/local/tmp';\n\n// The instrumentation helper skips uiautomator's idle wait, so it must be\n// bounded by its own timeout rather than relying on \"eventually idle\".\nconst INSTRUMENT_TIMEOUT_MS = 8_000;\n\n// `am instrument` streams the whole hierarchy as base64 status records; a deep\n// tree can produce a few hundred KB, so raise the exec buffer above the default\n// 10MB guard to keep an overflow from masquerading as a helper crash.\nconst INSTRUMENT_MAX_BUFFER = 16 * 1024 * 1024;\n\n// In `auto` mode, try a single fast uiautomator dump first (it wins instantly\n// on idle screens) before paying for the instrumentation path.\nconst AUTO_PROBE_DUMP_ATTEMPTS = 1;\n\n/**\n * How `#dumpUiHierarchy` chooses between the uiautomator dump and the\n * instrumentation helper.\n *\n * - `auto` (default): one quick dump, then the helper, then the remaining dump\n * retries as a last resort. Fast on idle screens, self-healing on churn.\n * - `instrument`: helper only.\n * - `dump`: stock uiautomator dump only (the original behavior).\n */\ntype AdbSnapshotMode = 'auto' | 'instrument' | 'dump';\n\n/**\n * Resolve the snapshot mode from the environment.\n *\n * @returns The configured mode, defaulting to `auto` for unset/unknown values.\n */\nfunction resolveSnapshotMode(): AdbSnapshotMode {\n const raw = process.env.DEVICE_MCP_ADB_SNAPSHOT?.trim().toLowerCase();\n if (raw === 'instrument' || raw === 'dump' || raw === 'auto') {\n return raw;\n }\n return 'auto';\n}\n\n/**\n * Check that a dump produced complete hierarchy XML.\n *\n * `<hierarchy` alone sits at the start of the file, so requiring the closing tag\n * too is what rejects a truncated write before it reaches the tolerant regex\n * parser. An empty screen still emits a complete root, keeping \"no elements\"\n * distinguishable from a failed capture.\n *\n * @param xml - Raw stdout captured from the dumped file.\n * @returns True when the payload is complete hierarchy XML.\n */\nfunction isValidHierarchyXml(xml: string): boolean {\n return xml.includes('<hierarchy') && xml.includes('</hierarchy>');\n}\n\n/**\n * Build a single-line diagnostic from a dump's output streams.\n *\n * @param stderr - Standard error captured from the dump.\n * @param stdout - Standard output captured from the dump.\n * @returns A description of why the dump was rejected.\n */\nfunction summarizeDumpOutput(stderr: string, stdout: string): string {\n const detail = [stderr, stdout]\n .map((stream) => stream.trim())\n .filter(Boolean)\n .join(' | ');\n return detail\n ? `uiautomator produced no usable hierarchy (${detail})`\n : 'uiautomator produced no usable hierarchy';\n}\n\n/**\n * Pick remediation advice that matches how the dump actually failed, so an\n * unreachable device is not misreported as an animation problem.\n *\n * @param failures - The collected per-attempt failure details.\n * @returns Actionable guidance for the caller.\n */\nfunction describeDumpRemediation(failures: string): string {\n const detail = failures.toLowerCase();\n\n if (detail.includes('not found') || detail.includes('device offline')) {\n return (\n 'The device was unreachable during the dump. Confirm it is still connected ' +\n 'with `adb devices` and that the serial is correct.'\n );\n }\n\n return (\n 'The most common cause is that uiautomator never observed an idle window ' +\n '(\"ERROR: could not get idle state.\"), which means something on screen is ' +\n 'animating or redrawing continuously.\\n' +\n 'Things to check:\\n' +\n ' - Disable animations: adb shell settings put global window_animation_scale 0 ' +\n '(also transition_animation_scale and animator_duration_scale).\\n' +\n ' - Animation scales do NOT stop JS-driven React Native animations ' +\n '(Animated.loop with useNativeDriver: false), indeterminate ProgressBars, ' +\n 'shimmer/skeleton loaders, or video/SurfaceView playback. Look for one of those ' +\n 'on the current screen and use device_screenshot to confirm what is on it.'\n );\n}\n\nexport class AdbBackend implements DeviceBackend {\n readonly kind = 'adb' as const;\n\n readonly platform = 'android' as const;\n\n readonly #serial: string;\n\n #recordingProcess: ChildProcess | null = null;\n\n #recordingPath: string | null = null;\n\n readonly #recordingRemotePath = '/sdcard/device-mcp-recording.mp4';\n\n // Optimistic per-process cache: once the helper is confirmed installed we\n // skip the `pm list packages` probe on every snapshot. A mid-session\n // uninstall/reboot is recovered reactively when `am instrument` reports the\n // instrumentation is missing.\n #helperInstalled = false;\n\n // Serializes snapshot captures so overlapping `am instrument` runs (e.g. a\n // tap that triggers a nested snapshot) do not race on the single helper.\n #captureQueue: Promise<unknown> = Promise.resolve();\n\n constructor(serial: string) {\n this.#serial = serial;\n }\n\n async #adb(args: string[]): Promise<string> {\n return execStrict('adb', ['-s', this.#serial, ...args]);\n }\n\n async ensureConnected(): Promise<void> {\n if (!(await isCommandAvailable('adb'))) {\n throw new Error(\n 'adb is not installed or not on $PATH.\\n' +\n 'Install Android SDK platform-tools.',\n );\n }\n const state = await this.#adb(['get-state']);\n if (state.trim() !== 'device') {\n throw new Error(\n `Device ${this.#serial} is not ready (state: ${state.trim()})`,\n );\n }\n }\n\n async getDeviceInfo(): Promise<DeviceInfo> {\n const [model, version] = await Promise.all([\n this.#adb(['shell', 'getprop', 'ro.product.model']),\n this.#adb(['shell', 'getprop', 'ro.build.version.release']),\n ]);\n return {\n platform: 'android',\n deviceId: this.#serial,\n name: model.trim(),\n osVersion: version.trim(),\n state: 'device',\n };\n }\n\n async snapshot(): Promise<SnapshotResult> {\n const raw = await this.#serializeCapture(async () =>\n this.#dumpUiHierarchy(),\n );\n const hierarchy = parseAndroidHierarchy(raw);\n return {\n platform: 'android',\n hierarchy,\n raw,\n timestamp: Date.now(),\n };\n }\n\n async tapElement(query: ElementQuery): Promise<TapResult> {\n const snapshot = await this.snapshot();\n const element = findElement(snapshot.hierarchy, query);\n if (!element) {\n throw new Error(\n `Element not found: ${JSON.stringify(query)}\\n` +\n 'Use device_snapshot to inspect the current UI hierarchy.',\n );\n }\n\n const x = Math.round(element.frame.x + element.frame.width / 2);\n const y = Math.round(element.frame.y + element.frame.height / 2);\n await this.tapCoordinates(x, y);\n\n return {\n success: true,\n x,\n y,\n targetDescription: describeElement(element),\n };\n }\n\n async getElementText(query: ElementQuery): Promise<string> {\n const snapshot = await this.snapshot();\n const element = findElement(snapshot.hierarchy, query);\n if (!element) {\n throw new Error(\n `Element not found: ${JSON.stringify(query)}\\n` +\n 'Use device_snapshot to inspect the current UI hierarchy.',\n );\n }\n return element.label ?? element.value ?? '';\n }\n\n async tapCoordinates(x: number, y: number): Promise<void> {\n await this.#adb(['shell', 'input', 'tap', String(x), String(y)]);\n }\n\n async typeText(text: string): Promise<void> {\n // Android keyevent 29+shift = Ctrl+A (select all), 67 = DEL\n await this.#adb(['shell', 'input', 'keyevent', 'KEYCODE_MOVE_HOME']);\n await this.#adb([\n 'shell',\n 'input',\n 'keyevent',\n '--longpress',\n 'KEYCODE_SHIFT_LEFT',\n 'KEYCODE_MOVE_END',\n ]);\n await this.#adb(['shell', 'input', 'keyevent', '67']);\n // Android input text requires escaping spaces and special chars\n const escaped = text.replace(/ /gu, '%s').replace(/[&|;<>]/gu, '\\\\$&');\n await this.#adb(['shell', 'input', 'text', escaped]);\n }\n\n async swipe(\n direction: 'up' | 'down' | 'left' | 'right',\n startX?: number,\n startY?: number,\n distance?: number,\n ): Promise<void> {\n const d = distance ?? 500;\n const sx = startX ?? 540;\n const sy = startY ?? 960;\n const [endX, endY] = computeSwipeEnd(sx, sy, direction, d);\n const durationMs = 300;\n\n await this.#adb([\n 'shell',\n 'input',\n 'swipe',\n String(sx),\n String(sy),\n String(endX),\n String(endY),\n String(durationMs),\n ]);\n }\n\n async waitForElement(\n query: ElementQuery,\n timeoutMs = 10_000,\n intervalMs = 500,\n ): Promise<UIElement> {\n const start = Date.now();\n while (Date.now() - start < timeoutMs) {\n const snapshot = await this.snapshot();\n const element = findElement(snapshot.hierarchy, query);\n if (element) {\n return element;\n }\n await new Promise((resolve) => setTimeout(resolve, intervalMs));\n }\n throw new Error(\n `Timed out waiting for element: ${JSON.stringify(query)} (${timeoutMs}ms)`,\n );\n }\n\n async getAppState(bundleId: string): Promise<AppStateResult> {\n const raw = await this.#adb(['shell', 'dumpsys', 'activity', 'processes']);\n\n const pidMatch = raw.match(\n new RegExp(`PID #(\\\\d+):.*${bundleId.replace(/\\./gu, '\\\\.')}`, 'u'),\n );\n\n if (pidMatch) {\n return {\n bundleId,\n state: 'Running',\n pid: parseInt(pidMatch[1], 10),\n };\n }\n\n const pmResult = await this.#adb([\n 'shell',\n 'pm',\n 'list',\n 'packages',\n bundleId,\n ]);\n if (pmResult.includes(bundleId)) {\n return { bundleId, state: 'Installed (not running)' };\n }\n\n return { bundleId, state: 'Not Installed' };\n }\n\n /**\n * Run a capture after any in-flight capture completes, so concurrent\n * snapshots never overlap an `am instrument` run on the shared helper.\n *\n * @param capture - The capture operation to serialize.\n * @returns The capture result.\n */\n async #serializeCapture<Result>(\n capture: () => Promise<Result>,\n ): Promise<Result> {\n const run = this.#captureQueue.then(capture, capture);\n this.#captureQueue = run.then(\n () => undefined,\n () => undefined,\n );\n return run;\n }\n\n async #dumpUiHierarchy(): Promise<string> {\n const mode = resolveSnapshotMode();\n const failures: string[] = [];\n const deadline = Date.now() + DUMP_DEADLINE_MS;\n\n if (mode === 'dump') {\n const xml = await this.#tryDumpViaUiautomator(\n deadline,\n DUMP_ATTEMPTS,\n failures,\n );\n if (xml !== null) {\n return xml;\n }\n throw new Error(this.#composeDumpFailure(failures));\n }\n\n if (mode === 'instrument') {\n const xml = await this.#dumpViaInstrumentation(deadline, failures);\n if (xml !== null) {\n return xml;\n }\n throw new Error(this.#composeDumpFailure(failures));\n }\n\n // auto: a single fast dump wins instantly on idle screens; otherwise the\n // instrumentation helper handles churn; the remaining dump retries are the\n // last resort so a helper-side problem never leaves us worse than before.\n const quick = await this.#tryDumpViaUiautomator(\n deadline,\n AUTO_PROBE_DUMP_ATTEMPTS,\n failures,\n );\n if (quick !== null) {\n return quick;\n }\n\n const instrumented = await this.#dumpViaInstrumentation(deadline, failures);\n if (instrumented !== null) {\n return instrumented;\n }\n\n const fallback = await this.#tryDumpViaUiautomator(\n deadline,\n DUMP_ATTEMPTS,\n failures,\n );\n if (fallback !== null) {\n return fallback;\n }\n\n throw new Error(this.#composeDumpFailure(failures));\n }\n\n /**\n * Capture the hierarchy with the instrumentation helper, installing it first\n * when needed.\n *\n * @param deadline - Absolute time (ms) the overall snapshot must finish by.\n * @param failures - Accumulator for diagnostic messages across dump paths.\n * @returns The hierarchy XML, or null when the helper path could not produce\n * a valid capture (the caller then falls back to uiautomator dump).\n */\n async #dumpViaInstrumentation(\n deadline: number,\n failures: string[],\n ): Promise<string | null> {\n try {\n const xml = await this.#runInstrumentation(deadline);\n if (isValidHierarchyXml(xml)) {\n return xml;\n }\n failures.push('instrument: output was not complete hierarchy XML');\n } catch (error) {\n // A signer-trust failure means a possibly-malicious app squats the\n // helper's package name. Fail closed: propagate, never fall back to dump.\n if (isUntrustedHelperError(error)) {\n throw error;\n }\n const message = error instanceof Error ? error.message : String(error);\n\n // A mid-session uninstall/reboot invalidates the optimistic cache. Retry\n // once through the ensure-install path before giving up on this path.\n if (\n this.#helperInstalled &&\n message.includes(INSTRUMENTATION_NOT_FOUND_SIGNATURE)\n ) {\n this.#helperInstalled = false;\n try {\n const xml = await this.#runInstrumentation(deadline);\n if (isValidHierarchyXml(xml)) {\n return xml;\n }\n failures.push(\n 'instrument: output was not complete hierarchy XML after reinstall',\n );\n } catch (retryError) {\n if (isUntrustedHelperError(retryError)) {\n throw retryError;\n }\n failures.push(\n `instrument: ${retryError instanceof Error ? retryError.message : String(retryError)}`,\n );\n }\n } else {\n failures.push(`instrument: ${message}`);\n }\n }\n return null;\n }\n\n /**\n * Ensure the helper is installed, then run it and reassemble its output.\n *\n * @param deadline - Absolute time (ms) the overall snapshot must finish by.\n * @returns The reassembled hierarchy XML.\n */\n async #runInstrumentation(deadline: number): Promise<string> {\n if (!this.#helperInstalled) {\n // Install/upgrade the helper by versionCode. Safe to cache per process:\n // the install is idempotent and NOT a trust decision.\n await ensureHelperInstalled(this.#serial);\n this.#helperInstalled = true;\n }\n\n // Trust invariant: re-verify the installed signer BEFORE EVERY am instrument,\n // never cached. Package name / versionCode are attacker-controlled, so an\n // actor who can install packages could swap a previously-trusted helper for\n // a malicious same-package instrumentation between snapshots. Caching this as\n // a boolean would degrade the signature pin to a one-time check; verifying\n // per use (pm path + pull + hash) keeps it a per-use invariant. Throws\n // UntrustedHelperError on a signer mismatch (caller fails closed).\n await assertInstalledHelperTrusted(this.#serial);\n\n const remainingMs = deadline - Date.now();\n const timeoutMs = Math.max(\n 1_000,\n Math.min(INSTRUMENT_TIMEOUT_MS, remainingMs),\n );\n\n const { stdout } = await exec(\n 'adb',\n [\n '-s',\n this.#serial,\n 'shell',\n 'am',\n 'instrument',\n '-w',\n '-e',\n 'waitForIdleTimeoutMs',\n '0',\n '-e',\n 'timeoutMs',\n String(timeoutMs),\n HELPER_INSTRUMENTATION,\n ],\n // Bound the exec above the helper's own timeout, and raise the buffer so\n // a large tree is not misreported as a crash.\n { timeoutMs: timeoutMs + 5_000, maxBuffer: INSTRUMENT_MAX_BUFFER },\n );\n\n return reassembleInstrumentationXml(stdout);\n }\n\n /**\n * Run the uiautomator dump retry loop, swallowing failure into null.\n *\n * @param deadline - Absolute time (ms) the overall snapshot must finish by.\n * @param maxAttempts - How many dump attempts to make.\n * @param failures - Accumulator for diagnostic messages across dump paths.\n * @returns The hierarchy XML, or null when every attempt failed.\n */\n async #tryDumpViaUiautomator(\n deadline: number,\n maxAttempts: number,\n failures: string[],\n ): Promise<string | null> {\n try {\n return await this.#dumpViaUiautomator(deadline, maxAttempts, failures);\n } catch {\n // Failures are already recorded in the accumulator.\n return null;\n }\n }\n\n /**\n * The stock `uiautomator dump` capture with bounded retries.\n *\n * `uiautomator dump` exits 0 and prints its success banner even when it fails,\n * so the XML payload is the only trustworthy signal.\n *\n * @param deadline - Absolute time (ms) the overall snapshot must finish by.\n * @param maxAttempts - How many dump attempts to make.\n * @param failures - Accumulator for diagnostic messages across dump paths.\n * @returns The hierarchy XML.\n * @throws If every attempt failed to produce complete hierarchy XML.\n */\n async #dumpViaUiautomator(\n deadline: number,\n maxAttempts: number,\n failures: string[],\n ): Promise<string> {\n const startFailures = failures.length;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n const remainingMs = deadline - Date.now();\n if (remainingMs <= 0) {\n failures.push(\n `dump attempt ${attempt}: skipped, ${DUMP_DEADLINE_MS}ms budget exhausted`,\n );\n break;\n }\n\n // Unique per attempt: on a shared path a failed dump leaves the previous\n // file behind, so `cat` silently returns an earlier screen's hierarchy.\n const remotePath = `${DUMP_REMOTE_DIR}/device-mcp-dump-${randomUUID()}.xml`;\n\n try {\n const dump = await exec(\n 'adb',\n ['-s', this.#serial, 'shell', 'uiautomator', 'dump', remotePath],\n // Never outlive the overall deadline.\n { timeoutMs: Math.min(DUMP_TIMEOUT_MS, remainingMs) },\n );\n const xml = await this.#adb(['shell', 'cat', remotePath]);\n\n if (isValidHierarchyXml(xml)) {\n return xml;\n }\n\n failures.push(\n `dump attempt ${attempt}: ${summarizeDumpOutput(dump.stderr, dump.stdout)}`,\n );\n } catch (error) {\n failures.push(\n `dump attempt ${attempt}: ${error instanceof Error ? error.message : String(error)}`,\n );\n } finally {\n // Never let cleanup mask the dump outcome.\n await this.#adb(['shell', 'rm', '-f', remotePath]).catch(\n () => undefined,\n );\n }\n\n const backoffMs = DUMP_RETRY_DELAY_MS * attempt;\n if (attempt < maxAttempts && Date.now() + backoffMs < deadline) {\n await new Promise((resolve) => {\n setTimeout(resolve, backoffMs);\n });\n }\n }\n\n const attemptCount = failures.length - startFailures;\n throw new Error(\n `uiautomator failed to capture the UI hierarchy after ${attemptCount} attempts.`,\n );\n }\n\n /**\n * Build the final error when every snapshot path has failed.\n *\n * @param failures - The collected per-path failure details.\n * @returns A single actionable error message.\n */\n #composeDumpFailure(failures: string[]): string {\n const detail = failures.join('\\n');\n const remediation = describeDumpRemediation(detail);\n return `Failed to capture the UI hierarchy.\\n${detail}\\n${remediation}`;\n }\n\n screenshot(\n outputPath?: string,\n options?: { encode?: true },\n ): Promise<ScreenshotResult>;\n\n screenshot(\n outputPath: string | undefined,\n options: { encode: false },\n ): Promise<ScreenshotFileResult>;\n\n screenshot(\n outputPath?: string,\n options?: ScreenshotOptions,\n ): Promise<ScreenshotResult | ScreenshotFileResult>;\n\n async screenshot(\n outputPath?: string,\n options?: ScreenshotOptions,\n ): Promise<ScreenshotResult | ScreenshotFileResult> {\n const localPath = resolveArtifactPath(outputPath, 'screenshot');\n // Unique per capture so a failed screencap can never be pulled as a stale\n // image from an earlier call.\n const remotePath = `${DUMP_REMOTE_DIR}/device-mcp-screenshot-${randomUUID()}.png`;\n try {\n await this.#adb(['shell', 'screencap', '-p', remotePath]);\n await execStrict('adb', [\n '-s',\n this.#serial,\n 'pull',\n remotePath,\n localPath,\n ]);\n } finally {\n await this.#adb(['shell', 'rm', '-f', remotePath]).catch(() => undefined);\n }\n hardenArtifactFile(localPath);\n if (options?.encode === false) {\n return { data: undefined, format: 'png', path: localPath };\n }\n const data = await readFile(localPath, 'base64');\n return { data, format: 'png', path: localPath };\n }\n\n async openApp(bundleId: string): Promise<void> {\n await this.#adb([\n 'shell',\n 'monkey',\n '-p',\n bundleId,\n '-c',\n 'android.intent.category.LAUNCHER',\n '1',\n ]);\n }\n\n async closeApp(bundleId: string): Promise<void> {\n await this.#adb(['shell', 'am', 'force-stop', bundleId]);\n }\n\n async pressButton(button: DeviceButton): Promise<void> {\n // Android keyevent codes\n const keyMap: Record<DeviceButton, string> = {\n home: '3',\n back: '4',\n enter: '66',\n lock: '26',\n };\n await this.#adb(['shell', 'input', 'keyevent', keyMap[button]]);\n }\n\n async dismissKeyboard(): Promise<void> {\n await this.#adb(['shell', 'input', 'keyevent', '111']);\n }\n\n async dismissAlert(accept: boolean): Promise<void> {\n const snap = await this.snapshot();\n const candidates = accept ? ACCEPT_LABELS : DENY_LABELS;\n\n for (const label of candidates) {\n const element = findElement(snap.hierarchy, { text: label });\n if (element) {\n const cx = Math.round(element.frame.x + element.frame.width / 2);\n const cy = Math.round(element.frame.y + element.frame.height / 2);\n await this.tapCoordinates(cx, cy);\n return;\n }\n }\n\n throw new Error(`No alert button found. Tried: ${candidates.join(', ')}`);\n }\n\n async getLogs(durationSeconds = 30, filter?: string): Promise<LogsResult> {\n const args = ['shell', 'logcat', '-d', '-t', String(durationSeconds)];\n if (filter) {\n args.push('-e', filter);\n }\n const raw = await this.#adb(args);\n const entries = raw\n .trim()\n .split('\\n')\n .filter(Boolean)\n .map((line) => {\n const match = line.match(/^(\\d{2}-\\d{2}\\s+\\S+)\\s+(\\w)\\/.*?:\\s*(.*)/u);\n if (match) {\n return {\n timestamp: match[1],\n level: match[2],\n message: match[3],\n };\n }\n return { timestamp: '', level: 'info', message: line };\n });\n return { entries, source: 'logcat' };\n }\n\n async longPress(query: ElementQuery, durationMs = 1000): Promise<TapResult> {\n const snap = await this.snapshot();\n const element = findElement(snap.hierarchy, query);\n if (!element) {\n throw new Error(\n `Element not found: ${JSON.stringify(query)}\\n` +\n 'Use device_snapshot to inspect the current UI hierarchy.',\n );\n }\n\n const cx = Math.round(element.frame.x + element.frame.width / 2);\n const cy = Math.round(element.frame.y + element.frame.height / 2);\n await this.#adb([\n 'shell',\n 'input',\n 'swipe',\n String(cx),\n String(cy),\n String(cx),\n String(cy),\n String(durationMs),\n ]);\n\n return {\n success: true,\n x: cx,\n y: cy,\n targetDescription: describeElement(element),\n };\n }\n\n async scrollToElement(\n query: ElementQuery,\n direction: 'up' | 'down' = 'down',\n maxAttempts = 10,\n ): Promise<UIElement> {\n let previousRaw = '';\n\n for (let i = 0; i < maxAttempts; i++) {\n const snap = await this.snapshot();\n const element = findElement(snap.hierarchy, query);\n if (element) {\n return element;\n }\n if (snap.raw === previousRaw) {\n break;\n }\n previousRaw = snap.raw;\n await this.swipe(direction);\n }\n throw new Error(\n `Element not found after scrolling: ${JSON.stringify(query)}\\n` +\n 'Use device_snapshot to inspect the current UI hierarchy.',\n );\n }\n\n async getAlertText(): Promise<string> {\n const snap = await this.snapshot();\n const texts = collectAndroidAlertTexts(snap.hierarchy);\n if (texts.length === 0) {\n throw new Error(\n 'No alert is currently displayed.\\n' +\n 'Use device_snapshot to inspect the current UI hierarchy.',\n );\n }\n return texts.join('\\n');\n }\n\n async getWindowSize(): Promise<WindowSize> {\n const raw = await this.#adb(['shell', 'wm', 'size']);\n const match = raw.match(/(\\d+)x(\\d+)/u);\n if (match) {\n return {\n width: parseInt(match[1], 10),\n height: parseInt(match[2], 10),\n };\n }\n throw new Error(`Unable to parse window size from: ${raw.trim()}`);\n }\n\n async getContexts(): Promise<string[]> {\n return ['NATIVE_APP'];\n }\n\n async setContext(context: string): Promise<void> {\n if (context === 'NATIVE_APP') {\n return;\n }\n throw new Error(\n 'Context switching requires Appium backend. ADB only supports NATIVE_APP.',\n );\n }\n\n async getClipboard(): Promise<string> {\n throw new Error(\n 'Clipboard access via ADB is not supported on modern Android. ' +\n 'Use the Appium backend for clipboard operations.',\n );\n }\n\n async setClipboard(_text: string): Promise<void> {\n throw new Error(\n 'Clipboard access via ADB is not supported on modern Android. ' +\n 'Use the Appium backend for clipboard operations.',\n );\n }\n\n async startScreenRecording(outputPath?: string): Promise<void> {\n if (this.#recordingProcess) {\n throw new Error('Screen recording is already in progress');\n }\n this.#recordingPath = resolveArtifactPath(outputPath, 'recording');\n this.#recordingProcess = spawn('adb', [\n '-s',\n this.#serial,\n 'shell',\n 'screenrecord',\n this.#recordingRemotePath,\n ]);\n this.#recordingProcess.on('error', () => {\n this.#recordingProcess = null;\n this.#recordingPath = null;\n });\n }\n\n async stopScreenRecording(): Promise<string> {\n if (!this.#recordingProcess || !this.#recordingPath) {\n throw new Error('No screen recording in progress');\n }\n const localPath = this.#recordingPath;\n this.#recordingProcess.kill('SIGINT');\n await new Promise((resolve) => setTimeout(resolve, 2000));\n this.#recordingProcess = null;\n this.#recordingPath = null;\n\n await execStrict('adb', [\n '-s',\n this.#serial,\n 'pull',\n this.#recordingRemotePath,\n localPath,\n ]);\n await this.#adb(['shell', 'rm', '-f', this.#recordingRemotePath]);\n hardenArtifactFile(localPath);\n return localPath;\n }\n}\n\nfunction collectAndroidAlertTexts(elements: UIElement[]): string[] {\n const texts: string[] = [];\n for (const el of elements) {\n const isDialog =\n el.type.includes('Dialog') || el.type.includes('AlertDialog');\n if (isDialog) {\n collectTextValues(el.children ?? [], texts);\n return texts;\n }\n if (el.children) {\n const found = collectAndroidAlertTexts(el.children);\n if (found.length > 0) {\n return found;\n }\n }\n }\n return texts;\n}\n\nfunction collectTextValues(elements: UIElement[], texts: string[]): void {\n for (const el of elements) {\n if (el.type.includes('TextView') && el.value) {\n texts.push(el.value);\n }\n if (el.children) {\n collectTextValues(el.children, texts);\n }\n }\n}\n\nexport function parseAndroidHierarchy(xml: string): UIElement[] {\n const stack: UIElement[][] = [[]];\n\n // Matches all <node ...>, </node>, and <node .../> tags in document order\n const tagRegex = /<(\\/?)node(?:\\s+([^>]*?))?\\s*(\\/?)>/gu;\n let match: RegExpExecArray | null;\n\n while ((match = tagRegex.exec(xml)) !== null) {\n const isClosing = match[1] === '/';\n const attrs = match[2];\n const isSelfClosing = match[3] === '/';\n\n if (isClosing) {\n if (stack.length > 1) {\n stack.pop();\n }\n } else if (isSelfClosing && attrs) {\n const element = parseNodeAttributes(attrs);\n if (element) {\n stack[stack.length - 1].push(element);\n }\n } else if (attrs) {\n const element = parseNodeAttributes(attrs);\n if (element) {\n element.children = [];\n stack[stack.length - 1].push(element);\n stack.push(element.children);\n }\n }\n }\n\n return stack[0];\n}\n\nexport function parseNodeAttributes(attrs: string): UIElement | null {\n const get = (name: string): string | undefined => {\n const attrMatch = attrs.match(new RegExp(`${name}=\"([^\"]*)\"`, 'u'));\n return attrMatch?.[1] || undefined;\n };\n\n const boundsStr = get('bounds');\n if (!boundsStr) {\n return null;\n }\n\n // bounds format: [x1,y1][x2,y2]\n const boundsMatch = boundsStr.match(/\\[(\\d+),(\\d+)\\]\\[(\\d+),(\\d+)\\]/u);\n if (!boundsMatch) {\n return null;\n }\n\n const x1 = parseInt(boundsMatch[1], 10);\n const y1 = parseInt(boundsMatch[2], 10);\n const x2 = parseInt(boundsMatch[3], 10);\n const y2 = parseInt(boundsMatch[4], 10);\n\n return {\n type: get('class') ?? 'Unknown',\n label: get('content-desc') || undefined,\n value: get('text') || undefined,\n identifier: get('resource-id') || undefined,\n frame: { x: x1, y: y1, width: x2 - x1, height: y2 - y1 },\n enabled: get('enabled') !== 'false',\n };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adb-backend.d.cts","sourceRoot":"","sources":["../../src/backends/adb-backend.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"adb-backend.d.cts","sourceRoot":"","sources":["../../src/backends/adb-backend.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adb-backend.d.mts","sourceRoot":"","sources":["../../src/backends/adb-backend.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"adb-backend.d.mts","sourceRoot":"","sources":["../../src/backends/adb-backend.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,13 +9,103 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
9
9
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
10
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
11
|
};
|
|
12
|
-
var _AdbBackend_instances, _AdbBackend_serial, _AdbBackend_recordingProcess, _AdbBackend_recordingPath, _AdbBackend_recordingRemotePath, _AdbBackend_adb, _AdbBackend_dumpUiHierarchy;
|
|
12
|
+
var _AdbBackend_instances, _AdbBackend_serial, _AdbBackend_recordingProcess, _AdbBackend_recordingPath, _AdbBackend_recordingRemotePath, _AdbBackend_helperInstalled, _AdbBackend_captureQueue, _AdbBackend_adb, _AdbBackend_serializeCapture, _AdbBackend_dumpUiHierarchy, _AdbBackend_dumpViaInstrumentation, _AdbBackend_runInstrumentation, _AdbBackend_tryDumpViaUiautomator, _AdbBackend_dumpViaUiautomator, _AdbBackend_composeDumpFailure;
|
|
13
13
|
import { spawn } from "node:child_process";
|
|
14
|
+
import { randomUUID } from "node:crypto";
|
|
14
15
|
import { readFile } from "node:fs/promises";
|
|
16
|
+
import { isUntrustedHelperError } from "./android-instrumentation/errors.mjs";
|
|
17
|
+
import { assertInstalledHelperTrusted, ensureHelperInstalled, INSTRUMENTATION_NOT_FOUND_SIGNATURE } from "./android-instrumentation/installer.mjs";
|
|
18
|
+
import { HELPER_INSTRUMENTATION, reassembleInstrumentationXml } from "./android-instrumentation/snapshot.mjs";
|
|
15
19
|
import { ACCEPT_LABELS, DENY_LABELS } from "../utils/alert-labels.mjs";
|
|
16
20
|
import { findElement, describeElement, computeSwipeEnd } from "../utils/element.mjs";
|
|
17
|
-
import { execStrict, isCommandAvailable } from "../utils/exec.mjs";
|
|
21
|
+
import { exec, execStrict, isCommandAvailable } from "../utils/exec.mjs";
|
|
18
22
|
import { hardenArtifactFile, resolveArtifactPath } from "../utils/output-path.mjs";
|
|
23
|
+
// `uiautomator dump` exits 0 and prints its success banner even when it fails, so
|
|
24
|
+
// the XML payload is the only trustworthy signal.
|
|
25
|
+
const DUMP_ATTEMPTS = 3;
|
|
26
|
+
const DUMP_RETRY_DELAY_MS = 750;
|
|
27
|
+
// Healthy dumps take ~150ms-2s, and even a failing one returns in ~2s.
|
|
28
|
+
const DUMP_TIMEOUT_MS = 15000;
|
|
29
|
+
// Caps all attempts plus backoff, so worst case does not scale with retries.
|
|
30
|
+
const DUMP_DEADLINE_MS = 25000;
|
|
31
|
+
// Writable by the shell user without scoped-storage restrictions.
|
|
32
|
+
const DUMP_REMOTE_DIR = '/data/local/tmp';
|
|
33
|
+
// The instrumentation helper skips uiautomator's idle wait, so it must be
|
|
34
|
+
// bounded by its own timeout rather than relying on "eventually idle".
|
|
35
|
+
const INSTRUMENT_TIMEOUT_MS = 8000;
|
|
36
|
+
// `am instrument` streams the whole hierarchy as base64 status records; a deep
|
|
37
|
+
// tree can produce a few hundred KB, so raise the exec buffer above the default
|
|
38
|
+
// 10MB guard to keep an overflow from masquerading as a helper crash.
|
|
39
|
+
const INSTRUMENT_MAX_BUFFER = 16 * 1024 * 1024;
|
|
40
|
+
// In `auto` mode, try a single fast uiautomator dump first (it wins instantly
|
|
41
|
+
// on idle screens) before paying for the instrumentation path.
|
|
42
|
+
const AUTO_PROBE_DUMP_ATTEMPTS = 1;
|
|
43
|
+
/**
|
|
44
|
+
* Resolve the snapshot mode from the environment.
|
|
45
|
+
*
|
|
46
|
+
* @returns The configured mode, defaulting to `auto` for unset/unknown values.
|
|
47
|
+
*/
|
|
48
|
+
function resolveSnapshotMode() {
|
|
49
|
+
const raw = process.env.DEVICE_MCP_ADB_SNAPSHOT?.trim().toLowerCase();
|
|
50
|
+
if (raw === 'instrument' || raw === 'dump' || raw === 'auto') {
|
|
51
|
+
return raw;
|
|
52
|
+
}
|
|
53
|
+
return 'auto';
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Check that a dump produced complete hierarchy XML.
|
|
57
|
+
*
|
|
58
|
+
* `<hierarchy` alone sits at the start of the file, so requiring the closing tag
|
|
59
|
+
* too is what rejects a truncated write before it reaches the tolerant regex
|
|
60
|
+
* parser. An empty screen still emits a complete root, keeping "no elements"
|
|
61
|
+
* distinguishable from a failed capture.
|
|
62
|
+
*
|
|
63
|
+
* @param xml - Raw stdout captured from the dumped file.
|
|
64
|
+
* @returns True when the payload is complete hierarchy XML.
|
|
65
|
+
*/
|
|
66
|
+
function isValidHierarchyXml(xml) {
|
|
67
|
+
return xml.includes('<hierarchy') && xml.includes('</hierarchy>');
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Build a single-line diagnostic from a dump's output streams.
|
|
71
|
+
*
|
|
72
|
+
* @param stderr - Standard error captured from the dump.
|
|
73
|
+
* @param stdout - Standard output captured from the dump.
|
|
74
|
+
* @returns A description of why the dump was rejected.
|
|
75
|
+
*/
|
|
76
|
+
function summarizeDumpOutput(stderr, stdout) {
|
|
77
|
+
const detail = [stderr, stdout]
|
|
78
|
+
.map((stream) => stream.trim())
|
|
79
|
+
.filter(Boolean)
|
|
80
|
+
.join(' | ');
|
|
81
|
+
return detail
|
|
82
|
+
? `uiautomator produced no usable hierarchy (${detail})`
|
|
83
|
+
: 'uiautomator produced no usable hierarchy';
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Pick remediation advice that matches how the dump actually failed, so an
|
|
87
|
+
* unreachable device is not misreported as an animation problem.
|
|
88
|
+
*
|
|
89
|
+
* @param failures - The collected per-attempt failure details.
|
|
90
|
+
* @returns Actionable guidance for the caller.
|
|
91
|
+
*/
|
|
92
|
+
function describeDumpRemediation(failures) {
|
|
93
|
+
const detail = failures.toLowerCase();
|
|
94
|
+
if (detail.includes('not found') || detail.includes('device offline')) {
|
|
95
|
+
return ('The device was unreachable during the dump. Confirm it is still connected ' +
|
|
96
|
+
'with `adb devices` and that the serial is correct.');
|
|
97
|
+
}
|
|
98
|
+
return ('The most common cause is that uiautomator never observed an idle window ' +
|
|
99
|
+
'("ERROR: could not get idle state."), which means something on screen is ' +
|
|
100
|
+
'animating or redrawing continuously.\n' +
|
|
101
|
+
'Things to check:\n' +
|
|
102
|
+
' - Disable animations: adb shell settings put global window_animation_scale 0 ' +
|
|
103
|
+
'(also transition_animation_scale and animator_duration_scale).\n' +
|
|
104
|
+
' - Animation scales do NOT stop JS-driven React Native animations ' +
|
|
105
|
+
'(Animated.loop with useNativeDriver: false), indeterminate ProgressBars, ' +
|
|
106
|
+
'shimmer/skeleton loaders, or video/SurfaceView playback. Look for one of those ' +
|
|
107
|
+
'on the current screen and use device_screenshot to confirm what is on it.');
|
|
108
|
+
}
|
|
19
109
|
export class AdbBackend {
|
|
20
110
|
constructor(serial) {
|
|
21
111
|
_AdbBackend_instances.add(this);
|
|
@@ -25,6 +115,14 @@ export class AdbBackend {
|
|
|
25
115
|
_AdbBackend_recordingProcess.set(this, null);
|
|
26
116
|
_AdbBackend_recordingPath.set(this, null);
|
|
27
117
|
_AdbBackend_recordingRemotePath.set(this, '/sdcard/device-mcp-recording.mp4');
|
|
118
|
+
// Optimistic per-process cache: once the helper is confirmed installed we
|
|
119
|
+
// skip the `pm list packages` probe on every snapshot. A mid-session
|
|
120
|
+
// uninstall/reboot is recovered reactively when `am instrument` reports the
|
|
121
|
+
// instrumentation is missing.
|
|
122
|
+
_AdbBackend_helperInstalled.set(this, false);
|
|
123
|
+
// Serializes snapshot captures so overlapping `am instrument` runs (e.g. a
|
|
124
|
+
// tap that triggers a nested snapshot) do not race on the single helper.
|
|
125
|
+
_AdbBackend_captureQueue.set(this, Promise.resolve());
|
|
28
126
|
__classPrivateFieldSet(this, _AdbBackend_serial, serial, "f");
|
|
29
127
|
}
|
|
30
128
|
async ensureConnected() {
|
|
@@ -51,7 +149,7 @@ export class AdbBackend {
|
|
|
51
149
|
};
|
|
52
150
|
}
|
|
53
151
|
async snapshot() {
|
|
54
|
-
const raw = await __classPrivateFieldGet(this, _AdbBackend_instances, "m", _AdbBackend_dumpUiHierarchy).call(this);
|
|
152
|
+
const raw = await __classPrivateFieldGet(this, _AdbBackend_instances, "m", _AdbBackend_serializeCapture).call(this, async () => __classPrivateFieldGet(this, _AdbBackend_instances, "m", _AdbBackend_dumpUiHierarchy).call(this));
|
|
55
153
|
const hierarchy = parseAndroidHierarchy(raw);
|
|
56
154
|
return {
|
|
57
155
|
platform: 'android',
|
|
@@ -158,15 +256,22 @@ export class AdbBackend {
|
|
|
158
256
|
}
|
|
159
257
|
async screenshot(outputPath, options) {
|
|
160
258
|
const localPath = resolveArtifactPath(outputPath, 'screenshot');
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
'
|
|
166
|
-
'
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
259
|
+
// Unique per capture so a failed screencap can never be pulled as a stale
|
|
260
|
+
// image from an earlier call.
|
|
261
|
+
const remotePath = `${DUMP_REMOTE_DIR}/device-mcp-screenshot-${randomUUID()}.png`;
|
|
262
|
+
try {
|
|
263
|
+
await __classPrivateFieldGet(this, _AdbBackend_instances, "m", _AdbBackend_adb).call(this, ['shell', 'screencap', '-p', remotePath]);
|
|
264
|
+
await execStrict('adb', [
|
|
265
|
+
'-s',
|
|
266
|
+
__classPrivateFieldGet(this, _AdbBackend_serial, "f"),
|
|
267
|
+
'pull',
|
|
268
|
+
remotePath,
|
|
269
|
+
localPath,
|
|
270
|
+
]);
|
|
271
|
+
}
|
|
272
|
+
finally {
|
|
273
|
+
await __classPrivateFieldGet(this, _AdbBackend_instances, "m", _AdbBackend_adb).call(this, ['shell', 'rm', '-f', remotePath]).catch(() => undefined);
|
|
274
|
+
}
|
|
170
275
|
hardenArtifactFile(localPath);
|
|
171
276
|
if (options?.encode === false) {
|
|
172
277
|
return { data: undefined, format: 'png', path: localPath };
|
|
@@ -356,14 +461,217 @@ export class AdbBackend {
|
|
|
356
461
|
return localPath;
|
|
357
462
|
}
|
|
358
463
|
}
|
|
359
|
-
_AdbBackend_serial = new WeakMap(), _AdbBackend_recordingProcess = new WeakMap(), _AdbBackend_recordingPath = new WeakMap(), _AdbBackend_recordingRemotePath = new WeakMap(), _AdbBackend_instances = new WeakSet(), _AdbBackend_adb = async function _AdbBackend_adb(args) {
|
|
464
|
+
_AdbBackend_serial = new WeakMap(), _AdbBackend_recordingProcess = new WeakMap(), _AdbBackend_recordingPath = new WeakMap(), _AdbBackend_recordingRemotePath = new WeakMap(), _AdbBackend_helperInstalled = new WeakMap(), _AdbBackend_captureQueue = new WeakMap(), _AdbBackend_instances = new WeakSet(), _AdbBackend_adb = async function _AdbBackend_adb(args) {
|
|
360
465
|
return execStrict('adb', ['-s', __classPrivateFieldGet(this, _AdbBackend_serial, "f"), ...args]);
|
|
466
|
+
}, _AdbBackend_serializeCapture =
|
|
467
|
+
/**
|
|
468
|
+
* Run a capture after any in-flight capture completes, so concurrent
|
|
469
|
+
* snapshots never overlap an `am instrument` run on the shared helper.
|
|
470
|
+
*
|
|
471
|
+
* @param capture - The capture operation to serialize.
|
|
472
|
+
* @returns The capture result.
|
|
473
|
+
*/
|
|
474
|
+
async function _AdbBackend_serializeCapture(capture) {
|
|
475
|
+
const run = __classPrivateFieldGet(this, _AdbBackend_captureQueue, "f").then(capture, capture);
|
|
476
|
+
__classPrivateFieldSet(this, _AdbBackend_captureQueue, run.then(() => undefined, () => undefined), "f");
|
|
477
|
+
return run;
|
|
361
478
|
}, _AdbBackend_dumpUiHierarchy = async function _AdbBackend_dumpUiHierarchy() {
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
const
|
|
365
|
-
|
|
366
|
-
|
|
479
|
+
const mode = resolveSnapshotMode();
|
|
480
|
+
const failures = [];
|
|
481
|
+
const deadline = Date.now() + DUMP_DEADLINE_MS;
|
|
482
|
+
if (mode === 'dump') {
|
|
483
|
+
const xml = await __classPrivateFieldGet(this, _AdbBackend_instances, "m", _AdbBackend_tryDumpViaUiautomator).call(this, deadline, DUMP_ATTEMPTS, failures);
|
|
484
|
+
if (xml !== null) {
|
|
485
|
+
return xml;
|
|
486
|
+
}
|
|
487
|
+
throw new Error(__classPrivateFieldGet(this, _AdbBackend_instances, "m", _AdbBackend_composeDumpFailure).call(this, failures));
|
|
488
|
+
}
|
|
489
|
+
if (mode === 'instrument') {
|
|
490
|
+
const xml = await __classPrivateFieldGet(this, _AdbBackend_instances, "m", _AdbBackend_dumpViaInstrumentation).call(this, deadline, failures);
|
|
491
|
+
if (xml !== null) {
|
|
492
|
+
return xml;
|
|
493
|
+
}
|
|
494
|
+
throw new Error(__classPrivateFieldGet(this, _AdbBackend_instances, "m", _AdbBackend_composeDumpFailure).call(this, failures));
|
|
495
|
+
}
|
|
496
|
+
// auto: a single fast dump wins instantly on idle screens; otherwise the
|
|
497
|
+
// instrumentation helper handles churn; the remaining dump retries are the
|
|
498
|
+
// last resort so a helper-side problem never leaves us worse than before.
|
|
499
|
+
const quick = await __classPrivateFieldGet(this, _AdbBackend_instances, "m", _AdbBackend_tryDumpViaUiautomator).call(this, deadline, AUTO_PROBE_DUMP_ATTEMPTS, failures);
|
|
500
|
+
if (quick !== null) {
|
|
501
|
+
return quick;
|
|
502
|
+
}
|
|
503
|
+
const instrumented = await __classPrivateFieldGet(this, _AdbBackend_instances, "m", _AdbBackend_dumpViaInstrumentation).call(this, deadline, failures);
|
|
504
|
+
if (instrumented !== null) {
|
|
505
|
+
return instrumented;
|
|
506
|
+
}
|
|
507
|
+
const fallback = await __classPrivateFieldGet(this, _AdbBackend_instances, "m", _AdbBackend_tryDumpViaUiautomator).call(this, deadline, DUMP_ATTEMPTS, failures);
|
|
508
|
+
if (fallback !== null) {
|
|
509
|
+
return fallback;
|
|
510
|
+
}
|
|
511
|
+
throw new Error(__classPrivateFieldGet(this, _AdbBackend_instances, "m", _AdbBackend_composeDumpFailure).call(this, failures));
|
|
512
|
+
}, _AdbBackend_dumpViaInstrumentation =
|
|
513
|
+
/**
|
|
514
|
+
* Capture the hierarchy with the instrumentation helper, installing it first
|
|
515
|
+
* when needed.
|
|
516
|
+
*
|
|
517
|
+
* @param deadline - Absolute time (ms) the overall snapshot must finish by.
|
|
518
|
+
* @param failures - Accumulator for diagnostic messages across dump paths.
|
|
519
|
+
* @returns The hierarchy XML, or null when the helper path could not produce
|
|
520
|
+
* a valid capture (the caller then falls back to uiautomator dump).
|
|
521
|
+
*/
|
|
522
|
+
async function _AdbBackend_dumpViaInstrumentation(deadline, failures) {
|
|
523
|
+
try {
|
|
524
|
+
const xml = await __classPrivateFieldGet(this, _AdbBackend_instances, "m", _AdbBackend_runInstrumentation).call(this, deadline);
|
|
525
|
+
if (isValidHierarchyXml(xml)) {
|
|
526
|
+
return xml;
|
|
527
|
+
}
|
|
528
|
+
failures.push('instrument: output was not complete hierarchy XML');
|
|
529
|
+
}
|
|
530
|
+
catch (error) {
|
|
531
|
+
// A signer-trust failure means a possibly-malicious app squats the
|
|
532
|
+
// helper's package name. Fail closed: propagate, never fall back to dump.
|
|
533
|
+
if (isUntrustedHelperError(error)) {
|
|
534
|
+
throw error;
|
|
535
|
+
}
|
|
536
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
537
|
+
// A mid-session uninstall/reboot invalidates the optimistic cache. Retry
|
|
538
|
+
// once through the ensure-install path before giving up on this path.
|
|
539
|
+
if (__classPrivateFieldGet(this, _AdbBackend_helperInstalled, "f") &&
|
|
540
|
+
message.includes(INSTRUMENTATION_NOT_FOUND_SIGNATURE)) {
|
|
541
|
+
__classPrivateFieldSet(this, _AdbBackend_helperInstalled, false, "f");
|
|
542
|
+
try {
|
|
543
|
+
const xml = await __classPrivateFieldGet(this, _AdbBackend_instances, "m", _AdbBackend_runInstrumentation).call(this, deadline);
|
|
544
|
+
if (isValidHierarchyXml(xml)) {
|
|
545
|
+
return xml;
|
|
546
|
+
}
|
|
547
|
+
failures.push('instrument: output was not complete hierarchy XML after reinstall');
|
|
548
|
+
}
|
|
549
|
+
catch (retryError) {
|
|
550
|
+
if (isUntrustedHelperError(retryError)) {
|
|
551
|
+
throw retryError;
|
|
552
|
+
}
|
|
553
|
+
failures.push(`instrument: ${retryError instanceof Error ? retryError.message : String(retryError)}`);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
else {
|
|
557
|
+
failures.push(`instrument: ${message}`);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
return null;
|
|
561
|
+
}, _AdbBackend_runInstrumentation =
|
|
562
|
+
/**
|
|
563
|
+
* Ensure the helper is installed, then run it and reassemble its output.
|
|
564
|
+
*
|
|
565
|
+
* @param deadline - Absolute time (ms) the overall snapshot must finish by.
|
|
566
|
+
* @returns The reassembled hierarchy XML.
|
|
567
|
+
*/
|
|
568
|
+
async function _AdbBackend_runInstrumentation(deadline) {
|
|
569
|
+
if (!__classPrivateFieldGet(this, _AdbBackend_helperInstalled, "f")) {
|
|
570
|
+
// Install/upgrade the helper by versionCode. Safe to cache per process:
|
|
571
|
+
// the install is idempotent and NOT a trust decision.
|
|
572
|
+
await ensureHelperInstalled(__classPrivateFieldGet(this, _AdbBackend_serial, "f"));
|
|
573
|
+
__classPrivateFieldSet(this, _AdbBackend_helperInstalled, true, "f");
|
|
574
|
+
}
|
|
575
|
+
// Trust invariant: re-verify the installed signer BEFORE EVERY am instrument,
|
|
576
|
+
// never cached. Package name / versionCode are attacker-controlled, so an
|
|
577
|
+
// actor who can install packages could swap a previously-trusted helper for
|
|
578
|
+
// a malicious same-package instrumentation between snapshots. Caching this as
|
|
579
|
+
// a boolean would degrade the signature pin to a one-time check; verifying
|
|
580
|
+
// per use (pm path + pull + hash) keeps it a per-use invariant. Throws
|
|
581
|
+
// UntrustedHelperError on a signer mismatch (caller fails closed).
|
|
582
|
+
await assertInstalledHelperTrusted(__classPrivateFieldGet(this, _AdbBackend_serial, "f"));
|
|
583
|
+
const remainingMs = deadline - Date.now();
|
|
584
|
+
const timeoutMs = Math.max(1000, Math.min(INSTRUMENT_TIMEOUT_MS, remainingMs));
|
|
585
|
+
const { stdout } = await exec('adb', [
|
|
586
|
+
'-s',
|
|
587
|
+
__classPrivateFieldGet(this, _AdbBackend_serial, "f"),
|
|
588
|
+
'shell',
|
|
589
|
+
'am',
|
|
590
|
+
'instrument',
|
|
591
|
+
'-w',
|
|
592
|
+
'-e',
|
|
593
|
+
'waitForIdleTimeoutMs',
|
|
594
|
+
'0',
|
|
595
|
+
'-e',
|
|
596
|
+
'timeoutMs',
|
|
597
|
+
String(timeoutMs),
|
|
598
|
+
HELPER_INSTRUMENTATION,
|
|
599
|
+
],
|
|
600
|
+
// Bound the exec above the helper's own timeout, and raise the buffer so
|
|
601
|
+
// a large tree is not misreported as a crash.
|
|
602
|
+
{ timeoutMs: timeoutMs + 5000, maxBuffer: INSTRUMENT_MAX_BUFFER });
|
|
603
|
+
return reassembleInstrumentationXml(stdout);
|
|
604
|
+
}, _AdbBackend_tryDumpViaUiautomator =
|
|
605
|
+
/**
|
|
606
|
+
* Run the uiautomator dump retry loop, swallowing failure into null.
|
|
607
|
+
*
|
|
608
|
+
* @param deadline - Absolute time (ms) the overall snapshot must finish by.
|
|
609
|
+
* @param maxAttempts - How many dump attempts to make.
|
|
610
|
+
* @param failures - Accumulator for diagnostic messages across dump paths.
|
|
611
|
+
* @returns The hierarchy XML, or null when every attempt failed.
|
|
612
|
+
*/
|
|
613
|
+
async function _AdbBackend_tryDumpViaUiautomator(deadline, maxAttempts, failures) {
|
|
614
|
+
try {
|
|
615
|
+
return await __classPrivateFieldGet(this, _AdbBackend_instances, "m", _AdbBackend_dumpViaUiautomator).call(this, deadline, maxAttempts, failures);
|
|
616
|
+
}
|
|
617
|
+
catch {
|
|
618
|
+
// Failures are already recorded in the accumulator.
|
|
619
|
+
return null;
|
|
620
|
+
}
|
|
621
|
+
}, _AdbBackend_dumpViaUiautomator =
|
|
622
|
+
/**
|
|
623
|
+
* The stock `uiautomator dump` capture with bounded retries.
|
|
624
|
+
*
|
|
625
|
+
* `uiautomator dump` exits 0 and prints its success banner even when it fails,
|
|
626
|
+
* so the XML payload is the only trustworthy signal.
|
|
627
|
+
*
|
|
628
|
+
* @param deadline - Absolute time (ms) the overall snapshot must finish by.
|
|
629
|
+
* @param maxAttempts - How many dump attempts to make.
|
|
630
|
+
* @param failures - Accumulator for diagnostic messages across dump paths.
|
|
631
|
+
* @returns The hierarchy XML.
|
|
632
|
+
* @throws If every attempt failed to produce complete hierarchy XML.
|
|
633
|
+
*/
|
|
634
|
+
async function _AdbBackend_dumpViaUiautomator(deadline, maxAttempts, failures) {
|
|
635
|
+
const startFailures = failures.length;
|
|
636
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
637
|
+
const remainingMs = deadline - Date.now();
|
|
638
|
+
if (remainingMs <= 0) {
|
|
639
|
+
failures.push(`dump attempt ${attempt}: skipped, ${DUMP_DEADLINE_MS}ms budget exhausted`);
|
|
640
|
+
break;
|
|
641
|
+
}
|
|
642
|
+
// Unique per attempt: on a shared path a failed dump leaves the previous
|
|
643
|
+
// file behind, so `cat` silently returns an earlier screen's hierarchy.
|
|
644
|
+
const remotePath = `${DUMP_REMOTE_DIR}/device-mcp-dump-${randomUUID()}.xml`;
|
|
645
|
+
try {
|
|
646
|
+
const dump = await exec('adb', ['-s', __classPrivateFieldGet(this, _AdbBackend_serial, "f"), 'shell', 'uiautomator', 'dump', remotePath],
|
|
647
|
+
// Never outlive the overall deadline.
|
|
648
|
+
{ timeoutMs: Math.min(DUMP_TIMEOUT_MS, remainingMs) });
|
|
649
|
+
const xml = await __classPrivateFieldGet(this, _AdbBackend_instances, "m", _AdbBackend_adb).call(this, ['shell', 'cat', remotePath]);
|
|
650
|
+
if (isValidHierarchyXml(xml)) {
|
|
651
|
+
return xml;
|
|
652
|
+
}
|
|
653
|
+
failures.push(`dump attempt ${attempt}: ${summarizeDumpOutput(dump.stderr, dump.stdout)}`);
|
|
654
|
+
}
|
|
655
|
+
catch (error) {
|
|
656
|
+
failures.push(`dump attempt ${attempt}: ${error instanceof Error ? error.message : String(error)}`);
|
|
657
|
+
}
|
|
658
|
+
finally {
|
|
659
|
+
// Never let cleanup mask the dump outcome.
|
|
660
|
+
await __classPrivateFieldGet(this, _AdbBackend_instances, "m", _AdbBackend_adb).call(this, ['shell', 'rm', '-f', remotePath]).catch(() => undefined);
|
|
661
|
+
}
|
|
662
|
+
const backoffMs = DUMP_RETRY_DELAY_MS * attempt;
|
|
663
|
+
if (attempt < maxAttempts && Date.now() + backoffMs < deadline) {
|
|
664
|
+
await new Promise((resolve) => {
|
|
665
|
+
setTimeout(resolve, backoffMs);
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
const attemptCount = failures.length - startFailures;
|
|
670
|
+
throw new Error(`uiautomator failed to capture the UI hierarchy after ${attemptCount} attempts.`);
|
|
671
|
+
}, _AdbBackend_composeDumpFailure = function _AdbBackend_composeDumpFailure(failures) {
|
|
672
|
+
const detail = failures.join('\n');
|
|
673
|
+
const remediation = describeDumpRemediation(detail);
|
|
674
|
+
return `Failed to capture the UI hierarchy.\n${detail}\n${remediation}`;
|
|
367
675
|
};
|
|
368
676
|
function collectAndroidAlertTexts(elements) {
|
|
369
677
|
const texts = [];
|