@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.mjs","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.mjs","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"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Device-scoped ADB executor used by the snapshot-helper trust + install
|
|
4
|
+
* modules. Wrapping `exec('adb', ['-s', serial, ...])` behind a small function
|
|
5
|
+
* keeps the serial in one place and lets those modules be unit-tested with an
|
|
6
|
+
* injected fake executor instead of spawning real `adb`.
|
|
7
|
+
*
|
|
8
|
+
* The executor abstraction is adapted from agent-device (MIT, Copyright (c)
|
|
9
|
+
* Callstack); our `ExecResult` already matches its result shape.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.createAndroidAdbExecutor = createAndroidAdbExecutor;
|
|
13
|
+
const exec_js_1 = require("../../utils/exec.cjs");
|
|
14
|
+
const DEFAULT_MAX_BUFFER = 16 * 1024 * 1024;
|
|
15
|
+
/**
|
|
16
|
+
* Create an ADB executor bound to a single device serial. Every call is
|
|
17
|
+
* prefixed with `-s <serial>` so callers pass only the sub-command.
|
|
18
|
+
*
|
|
19
|
+
* @param serial - The target device serial.
|
|
20
|
+
* @returns An executor that runs `adb -s <serial> <args...>`.
|
|
21
|
+
*/
|
|
22
|
+
function createAndroidAdbExecutor(serial) {
|
|
23
|
+
return async (args, options) => (0, exec_js_1.exec)('adb', ['-s', serial, ...args], {
|
|
24
|
+
timeoutMs: options?.timeoutMs,
|
|
25
|
+
maxBuffer: options?.maxBuffer ?? DEFAULT_MAX_BUFFER,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=adb.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adb.cjs","sourceRoot":"","sources":["../../../src/backends/android-instrumentation/adb.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;AAwBH,4DAMC;AA5BD,kDAA2C;AAa3C,MAAM,kBAAkB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAE5C;;;;;;GAMG;AACH,SAAgB,wBAAwB,CAAC,MAAc;IACrD,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,CAC7B,IAAA,cAAI,EAAC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE;QACnC,SAAS,EAAE,OAAO,EAAE,SAAS;QAC7B,SAAS,EAAE,OAAO,EAAE,SAAS,IAAI,kBAAkB;KACpD,CAAC,CAAC;AACP,CAAC","sourcesContent":["/**\n * Device-scoped ADB executor used by the snapshot-helper trust + install\n * modules. Wrapping `exec('adb', ['-s', serial, ...])` behind a small function\n * keeps the serial in one place and lets those modules be unit-tested with an\n * injected fake executor instead of spawning real `adb`.\n *\n * The executor abstraction is adapted from agent-device (MIT, Copyright (c)\n * Callstack); our `ExecResult` already matches its result shape.\n */\n\nimport { exec } from '../../utils/exec.js';\nimport type { ExecResult } from '../../utils/exec.js';\n\nexport type AndroidAdbExecOptions = {\n timeoutMs?: number;\n maxBuffer?: number;\n};\n\nexport type AndroidAdbExecutor = (\n args: string[],\n options?: AndroidAdbExecOptions,\n) => Promise<ExecResult>;\n\nconst DEFAULT_MAX_BUFFER = 16 * 1024 * 1024;\n\n/**\n * Create an ADB executor bound to a single device serial. Every call is\n * prefixed with `-s <serial>` so callers pass only the sub-command.\n *\n * @param serial - The target device serial.\n * @returns An executor that runs `adb -s <serial> <args...>`.\n */\nexport function createAndroidAdbExecutor(serial: string): AndroidAdbExecutor {\n return async (args, options) =>\n exec('adb', ['-s', serial, ...args], {\n timeoutMs: options?.timeoutMs,\n maxBuffer: options?.maxBuffer ?? DEFAULT_MAX_BUFFER,\n });\n}\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device-scoped ADB executor used by the snapshot-helper trust + install
|
|
3
|
+
* modules. Wrapping `exec('adb', ['-s', serial, ...])` behind a small function
|
|
4
|
+
* keeps the serial in one place and lets those modules be unit-tested with an
|
|
5
|
+
* injected fake executor instead of spawning real `adb`.
|
|
6
|
+
*
|
|
7
|
+
* The executor abstraction is adapted from agent-device (MIT, Copyright (c)
|
|
8
|
+
* Callstack); our `ExecResult` already matches its result shape.
|
|
9
|
+
*/
|
|
10
|
+
import type { ExecResult } from "../../utils/exec.cjs";
|
|
11
|
+
export type AndroidAdbExecOptions = {
|
|
12
|
+
timeoutMs?: number;
|
|
13
|
+
maxBuffer?: number;
|
|
14
|
+
};
|
|
15
|
+
export type AndroidAdbExecutor = (args: string[], options?: AndroidAdbExecOptions) => Promise<ExecResult>;
|
|
16
|
+
/**
|
|
17
|
+
* Create an ADB executor bound to a single device serial. Every call is
|
|
18
|
+
* prefixed with `-s <serial>` so callers pass only the sub-command.
|
|
19
|
+
*
|
|
20
|
+
* @param serial - The target device serial.
|
|
21
|
+
* @returns An executor that runs `adb -s <serial> <args...>`.
|
|
22
|
+
*/
|
|
23
|
+
export declare function createAndroidAdbExecutor(serial: string): AndroidAdbExecutor;
|
|
24
|
+
//# sourceMappingURL=adb.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adb.d.cts","sourceRoot":"","sources":["../../../src/backends/android-instrumentation/adb.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,6BAA4B;AAEtD,MAAM,MAAM,qBAAqB,GAAG;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,CAC/B,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE,qBAAqB,KAC5B,OAAO,CAAC,UAAU,CAAC,CAAC;AAIzB;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,kBAAkB,CAM3E"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device-scoped ADB executor used by the snapshot-helper trust + install
|
|
3
|
+
* modules. Wrapping `exec('adb', ['-s', serial, ...])` behind a small function
|
|
4
|
+
* keeps the serial in one place and lets those modules be unit-tested with an
|
|
5
|
+
* injected fake executor instead of spawning real `adb`.
|
|
6
|
+
*
|
|
7
|
+
* The executor abstraction is adapted from agent-device (MIT, Copyright (c)
|
|
8
|
+
* Callstack); our `ExecResult` already matches its result shape.
|
|
9
|
+
*/
|
|
10
|
+
import type { ExecResult } from "../../utils/exec.mjs";
|
|
11
|
+
export type AndroidAdbExecOptions = {
|
|
12
|
+
timeoutMs?: number;
|
|
13
|
+
maxBuffer?: number;
|
|
14
|
+
};
|
|
15
|
+
export type AndroidAdbExecutor = (args: string[], options?: AndroidAdbExecOptions) => Promise<ExecResult>;
|
|
16
|
+
/**
|
|
17
|
+
* Create an ADB executor bound to a single device serial. Every call is
|
|
18
|
+
* prefixed with `-s <serial>` so callers pass only the sub-command.
|
|
19
|
+
*
|
|
20
|
+
* @param serial - The target device serial.
|
|
21
|
+
* @returns An executor that runs `adb -s <serial> <args...>`.
|
|
22
|
+
*/
|
|
23
|
+
export declare function createAndroidAdbExecutor(serial: string): AndroidAdbExecutor;
|
|
24
|
+
//# sourceMappingURL=adb.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adb.d.mts","sourceRoot":"","sources":["../../../src/backends/android-instrumentation/adb.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,6BAA4B;AAEtD,MAAM,MAAM,qBAAqB,GAAG;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,CAC/B,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE,qBAAqB,KAC5B,OAAO,CAAC,UAAU,CAAC,CAAC;AAIzB;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,kBAAkB,CAM3E"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device-scoped ADB executor used by the snapshot-helper trust + install
|
|
3
|
+
* modules. Wrapping `exec('adb', ['-s', serial, ...])` behind a small function
|
|
4
|
+
* keeps the serial in one place and lets those modules be unit-tested with an
|
|
5
|
+
* injected fake executor instead of spawning real `adb`.
|
|
6
|
+
*
|
|
7
|
+
* The executor abstraction is adapted from agent-device (MIT, Copyright (c)
|
|
8
|
+
* Callstack); our `ExecResult` already matches its result shape.
|
|
9
|
+
*/
|
|
10
|
+
import { exec } from "../../utils/exec.mjs";
|
|
11
|
+
const DEFAULT_MAX_BUFFER = 16 * 1024 * 1024;
|
|
12
|
+
/**
|
|
13
|
+
* Create an ADB executor bound to a single device serial. Every call is
|
|
14
|
+
* prefixed with `-s <serial>` so callers pass only the sub-command.
|
|
15
|
+
*
|
|
16
|
+
* @param serial - The target device serial.
|
|
17
|
+
* @returns An executor that runs `adb -s <serial> <args...>`.
|
|
18
|
+
*/
|
|
19
|
+
export function createAndroidAdbExecutor(serial) {
|
|
20
|
+
return async (args, options) => exec('adb', ['-s', serial, ...args], {
|
|
21
|
+
timeoutMs: options?.timeoutMs,
|
|
22
|
+
maxBuffer: options?.maxBuffer ?? DEFAULT_MAX_BUFFER,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=adb.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adb.mjs","sourceRoot":"","sources":["../../../src/backends/android-instrumentation/adb.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,IAAI,EAAE,6BAA4B;AAa3C,MAAM,kBAAkB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAE5C;;;;;;GAMG;AACH,MAAM,UAAU,wBAAwB,CAAC,MAAc;IACrD,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,CAC7B,IAAI,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE;QACnC,SAAS,EAAE,OAAO,EAAE,SAAS;QAC7B,SAAS,EAAE,OAAO,EAAE,SAAS,IAAI,kBAAkB;KACpD,CAAC,CAAC;AACP,CAAC","sourcesContent":["/**\n * Device-scoped ADB executor used by the snapshot-helper trust + install\n * modules. Wrapping `exec('adb', ['-s', serial, ...])` behind a small function\n * keeps the serial in one place and lets those modules be unit-tested with an\n * injected fake executor instead of spawning real `adb`.\n *\n * The executor abstraction is adapted from agent-device (MIT, Copyright (c)\n * Callstack); our `ExecResult` already matches its result shape.\n */\n\nimport { exec } from '../../utils/exec.js';\nimport type { ExecResult } from '../../utils/exec.js';\n\nexport type AndroidAdbExecOptions = {\n timeoutMs?: number;\n maxBuffer?: number;\n};\n\nexport type AndroidAdbExecutor = (\n args: string[],\n options?: AndroidAdbExecOptions,\n) => Promise<ExecResult>;\n\nconst DEFAULT_MAX_BUFFER = 16 * 1024 * 1024;\n\n/**\n * Create an ADB executor bound to a single device serial. Every call is\n * prefixed with `-s <serial>` so callers pass only the sub-command.\n *\n * @param serial - The target device serial.\n * @returns An executor that runs `adb -s <serial> <args...>`.\n */\nexport function createAndroidAdbExecutor(serial: string): AndroidAdbExecutor {\n return async (args, options) =>\n exec('adb', ['-s', serial, ...args], {\n timeoutMs: options?.timeoutMs,\n maxBuffer: options?.maxBuffer ?? DEFAULT_MAX_BUFFER,\n });\n}\n"]}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Derived from agent-device (MIT, Copyright (c) Callstack); adapted for our
|
|
3
|
+
// source-built helper APK and package constants.
|
|
4
|
+
/**
|
|
5
|
+
* Reads and validates the provenance manifest that `build-android-helper.sh`
|
|
6
|
+
* emits next to the bundled helper APK. The manifest is the single source of
|
|
7
|
+
* truth for the package name, instrumentation runner, versionCode, the APK
|
|
8
|
+
* file digest (a tamper check on `dist/`), and the pinned signer certificate
|
|
9
|
+
* SHA-256 that the installed helper must match before we run `am instrument`.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.loadHelperArtifact = loadHelperArtifact;
|
|
13
|
+
const node_crypto_1 = require("node:crypto");
|
|
14
|
+
const promises_1 = require("node:fs/promises");
|
|
15
|
+
const node_path_1 = require("node:path");
|
|
16
|
+
const errors_js_1 = require("./errors.cjs");
|
|
17
|
+
const snapshot_js_1 = require("./snapshot.cjs");
|
|
18
|
+
// The compiled module lives at dist/backends/android-instrumentation/, so the
|
|
19
|
+
// bundled helper APK directory (dist/android) is two levels up.
|
|
20
|
+
const HELPER_APK_DIR = (0, node_path_1.join)(__dirname, '..', '..', 'android');
|
|
21
|
+
const HELPER_APK_PREFIX = 'device-mcp-android-snapshot-helper-';
|
|
22
|
+
const MANIFEST_SUFFIX = '.manifest.json';
|
|
23
|
+
/**
|
|
24
|
+
* Raise an {@link SnapshotHelperError} for a missing or malformed artifact.
|
|
25
|
+
*
|
|
26
|
+
* @param message - What went wrong.
|
|
27
|
+
* @throws Always.
|
|
28
|
+
*/
|
|
29
|
+
function invalid(message) {
|
|
30
|
+
throw new errors_js_1.SnapshotHelperError('ARTIFACT_INVALID', message);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Assert a manifest field is a non-empty string.
|
|
34
|
+
*
|
|
35
|
+
* @param value - The parsed field value.
|
|
36
|
+
* @param field - The field name, for error messages.
|
|
37
|
+
* @returns The value, narrowed to string.
|
|
38
|
+
*/
|
|
39
|
+
function requireString(value, field) {
|
|
40
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
41
|
+
invalid(`Helper manifest field "${field}" is missing or not a string`);
|
|
42
|
+
}
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Assert a manifest field is a finite non-negative integer.
|
|
47
|
+
*
|
|
48
|
+
* @param value - The parsed field value.
|
|
49
|
+
* @param field - The field name, for error messages.
|
|
50
|
+
* @returns The value, narrowed to number.
|
|
51
|
+
*/
|
|
52
|
+
function requireInt(value, field) {
|
|
53
|
+
if (typeof value !== 'number' || !Number.isInteger(value) || value < 0) {
|
|
54
|
+
invalid(`Helper manifest field "${field}" is missing or not an integer`);
|
|
55
|
+
}
|
|
56
|
+
return value;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Locate the helper manifest in the bundled `dist/android` directory.
|
|
60
|
+
*
|
|
61
|
+
* @returns The absolute path to the newest matching manifest.
|
|
62
|
+
* @throws {@link SnapshotHelperError} when the directory or manifest is absent.
|
|
63
|
+
*/
|
|
64
|
+
async function resolveManifestPath() {
|
|
65
|
+
let entries;
|
|
66
|
+
try {
|
|
67
|
+
entries = await (0, promises_1.readdir)(HELPER_APK_DIR);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return invalid(`Snapshot helper directory is missing (${HELPER_APK_DIR}). ` +
|
|
71
|
+
'The package may have been built without the Android helper.');
|
|
72
|
+
}
|
|
73
|
+
const manifest = entries
|
|
74
|
+
.filter((name) => name.startsWith(HELPER_APK_PREFIX) && name.endsWith(MANIFEST_SUFFIX))
|
|
75
|
+
.sort()
|
|
76
|
+
.at(-1);
|
|
77
|
+
if (!manifest) {
|
|
78
|
+
return invalid(`No snapshot helper manifest found in ${HELPER_APK_DIR}. ` +
|
|
79
|
+
'The package may have been built without the Android helper.');
|
|
80
|
+
}
|
|
81
|
+
return (0, node_path_1.join)(HELPER_APK_DIR, manifest);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Compute the SHA-256 of a file as lowercase hex.
|
|
85
|
+
*
|
|
86
|
+
* @param path - The file to digest.
|
|
87
|
+
* @returns The lowercase-hex SHA-256.
|
|
88
|
+
*/
|
|
89
|
+
async function sha256File(path) {
|
|
90
|
+
const bytes = await (0, promises_1.readFile)(path);
|
|
91
|
+
return (0, node_crypto_1.createHash)('sha256').update(bytes).digest('hex');
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Load and validate the bundled helper artifact: parse the manifest, confirm
|
|
95
|
+
* it targets our package, resolve the APK path, and verify the APK's SHA-256
|
|
96
|
+
* matches the manifest (a tamper check on the shipped `dist/`).
|
|
97
|
+
*
|
|
98
|
+
* @returns The validated artifact descriptor.
|
|
99
|
+
* @throws {@link SnapshotHelperError} when anything is missing, malformed, or
|
|
100
|
+
* the APK digest does not match the manifest.
|
|
101
|
+
*/
|
|
102
|
+
async function loadHelperArtifact() {
|
|
103
|
+
const manifestPath = await resolveManifestPath();
|
|
104
|
+
let parsed;
|
|
105
|
+
try {
|
|
106
|
+
parsed = JSON.parse(await (0, promises_1.readFile)(manifestPath, 'utf8'));
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
return invalid(`Helper manifest is not valid JSON (${manifestPath}): ${String(error)}`);
|
|
110
|
+
}
|
|
111
|
+
if (typeof parsed !== 'object' || parsed === null) {
|
|
112
|
+
return invalid(`Helper manifest is not an object (${manifestPath})`);
|
|
113
|
+
}
|
|
114
|
+
const manifest = parsed;
|
|
115
|
+
const packageName = requireString(manifest.packageName, 'packageName');
|
|
116
|
+
if (packageName !== snapshot_js_1.HELPER_PACKAGE) {
|
|
117
|
+
invalid(`Helper manifest packageName "${packageName}" does not match the ` +
|
|
118
|
+
`expected "${snapshot_js_1.HELPER_PACKAGE}"`);
|
|
119
|
+
}
|
|
120
|
+
const assetName = requireString(manifest.assetName, 'assetName');
|
|
121
|
+
const sha256 = requireString(manifest.sha256, 'sha256');
|
|
122
|
+
const apkPath = (0, node_path_1.join)(HELPER_APK_DIR, assetName);
|
|
123
|
+
const actualSha256 = await sha256File(apkPath).catch(() => invalid(`Helper APK is missing or unreadable (${apkPath})`));
|
|
124
|
+
if (actualSha256 !== sha256.toLowerCase()) {
|
|
125
|
+
invalid(`Helper APK digest does not match its manifest (${apkPath}). ` +
|
|
126
|
+
'The bundled artifact may be corrupt or tampered with.');
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
version: requireString(manifest.version, 'version'),
|
|
130
|
+
versionCode: requireInt(manifest.versionCode, 'versionCode'),
|
|
131
|
+
packageName,
|
|
132
|
+
instrumentationRunner: requireString(manifest.instrumentationRunner, 'instrumentationRunner'),
|
|
133
|
+
apkPath,
|
|
134
|
+
sha256: sha256.toLowerCase(),
|
|
135
|
+
signerSha256: requireString(manifest.signerSha256, 'signerSha256').toLowerCase(),
|
|
136
|
+
minSdk: requireInt(manifest.minSdk, 'minSdk'),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=artifact.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifact.cjs","sourceRoot":"","sources":["../../../src/backends/android-instrumentation/artifact.ts"],"names":[],"mappings":"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","sourcesContent":["// Derived from agent-device (MIT, Copyright (c) Callstack); adapted for our\n// source-built helper APK and package constants.\n/**\n * Reads and validates the provenance manifest that `build-android-helper.sh`\n * emits next to the bundled helper APK. The manifest is the single source of\n * truth for the package name, instrumentation runner, versionCode, the APK\n * file digest (a tamper check on `dist/`), and the pinned signer certificate\n * SHA-256 that the installed helper must match before we run `am instrument`.\n */\n\nimport { createHash } from 'node:crypto';\nimport { readdir, readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nimport { SnapshotHelperError } from './errors.js';\nimport { HELPER_PACKAGE } from './snapshot.js';\n\n// The compiled module lives at dist/backends/android-instrumentation/, so the\n// bundled helper APK directory (dist/android) is two levels up.\nconst HELPER_APK_DIR = join(__dirname, '..', '..', 'android');\n\nconst HELPER_APK_PREFIX = 'device-mcp-android-snapshot-helper-';\n\nconst MANIFEST_SUFFIX = '.manifest.json';\n\nexport type HelperArtifact = {\n version: string;\n versionCode: number;\n packageName: string;\n instrumentationRunner: string;\n apkPath: string;\n sha256: string;\n signerSha256: string;\n minSdk: number;\n};\n\n/**\n * Raise an {@link SnapshotHelperError} for a missing or malformed artifact.\n *\n * @param message - What went wrong.\n * @throws Always.\n */\nfunction invalid(message: string): never {\n throw new SnapshotHelperError('ARTIFACT_INVALID', message);\n}\n\n/**\n * Assert a manifest field is a non-empty string.\n *\n * @param value - The parsed field value.\n * @param field - The field name, for error messages.\n * @returns The value, narrowed to string.\n */\nfunction requireString(value: unknown, field: string): string {\n if (typeof value !== 'string' || value.length === 0) {\n invalid(`Helper manifest field \"${field}\" is missing or not a string`);\n }\n return value;\n}\n\n/**\n * Assert a manifest field is a finite non-negative integer.\n *\n * @param value - The parsed field value.\n * @param field - The field name, for error messages.\n * @returns The value, narrowed to number.\n */\nfunction requireInt(value: unknown, field: string): number {\n if (typeof value !== 'number' || !Number.isInteger(value) || value < 0) {\n invalid(`Helper manifest field \"${field}\" is missing or not an integer`);\n }\n return value;\n}\n\n/**\n * Locate the helper manifest in the bundled `dist/android` directory.\n *\n * @returns The absolute path to the newest matching manifest.\n * @throws {@link SnapshotHelperError} when the directory or manifest is absent.\n */\nasync function resolveManifestPath(): Promise<string> {\n let entries: string[];\n try {\n entries = await readdir(HELPER_APK_DIR);\n } catch {\n return invalid(\n `Snapshot helper directory is missing (${HELPER_APK_DIR}). ` +\n 'The package may have been built without the Android helper.',\n );\n }\n const manifest = entries\n .filter(\n (name) =>\n name.startsWith(HELPER_APK_PREFIX) && name.endsWith(MANIFEST_SUFFIX),\n )\n .sort()\n .at(-1);\n if (!manifest) {\n return invalid(\n `No snapshot helper manifest found in ${HELPER_APK_DIR}. ` +\n 'The package may have been built without the Android helper.',\n );\n }\n return join(HELPER_APK_DIR, manifest);\n}\n\n/**\n * Compute the SHA-256 of a file as lowercase hex.\n *\n * @param path - The file to digest.\n * @returns The lowercase-hex SHA-256.\n */\nasync function sha256File(path: string): Promise<string> {\n const bytes = await readFile(path);\n return createHash('sha256').update(bytes).digest('hex');\n}\n\n/**\n * Load and validate the bundled helper artifact: parse the manifest, confirm\n * it targets our package, resolve the APK path, and verify the APK's SHA-256\n * matches the manifest (a tamper check on the shipped `dist/`).\n *\n * @returns The validated artifact descriptor.\n * @throws {@link SnapshotHelperError} when anything is missing, malformed, or\n * the APK digest does not match the manifest.\n */\nexport async function loadHelperArtifact(): Promise<HelperArtifact> {\n const manifestPath = await resolveManifestPath();\n let parsed: unknown;\n try {\n parsed = JSON.parse(await readFile(manifestPath, 'utf8'));\n } catch (error) {\n return invalid(\n `Helper manifest is not valid JSON (${manifestPath}): ${String(error)}`,\n );\n }\n if (typeof parsed !== 'object' || parsed === null) {\n return invalid(`Helper manifest is not an object (${manifestPath})`);\n }\n const manifest = parsed as Record<string, unknown>;\n\n const packageName = requireString(manifest.packageName, 'packageName');\n if (packageName !== HELPER_PACKAGE) {\n invalid(\n `Helper manifest packageName \"${packageName}\" does not match the ` +\n `expected \"${HELPER_PACKAGE}\"`,\n );\n }\n\n const assetName = requireString(manifest.assetName, 'assetName');\n const sha256 = requireString(manifest.sha256, 'sha256');\n const apkPath = join(HELPER_APK_DIR, assetName);\n\n const actualSha256 = await sha256File(apkPath).catch(() =>\n invalid(`Helper APK is missing or unreadable (${apkPath})`),\n );\n if (actualSha256 !== sha256.toLowerCase()) {\n invalid(\n `Helper APK digest does not match its manifest (${apkPath}). ` +\n 'The bundled artifact may be corrupt or tampered with.',\n );\n }\n\n return {\n version: requireString(manifest.version, 'version'),\n versionCode: requireInt(manifest.versionCode, 'versionCode'),\n packageName,\n instrumentationRunner: requireString(\n manifest.instrumentationRunner,\n 'instrumentationRunner',\n ),\n apkPath,\n sha256: sha256.toLowerCase(),\n signerSha256: requireString(\n manifest.signerSha256,\n 'signerSha256',\n ).toLowerCase(),\n minSdk: requireInt(manifest.minSdk, 'minSdk'),\n };\n}\n"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads and validates the provenance manifest that `build-android-helper.sh`
|
|
3
|
+
* emits next to the bundled helper APK. The manifest is the single source of
|
|
4
|
+
* truth for the package name, instrumentation runner, versionCode, the APK
|
|
5
|
+
* file digest (a tamper check on `dist/`), and the pinned signer certificate
|
|
6
|
+
* SHA-256 that the installed helper must match before we run `am instrument`.
|
|
7
|
+
*/
|
|
8
|
+
export type HelperArtifact = {
|
|
9
|
+
version: string;
|
|
10
|
+
versionCode: number;
|
|
11
|
+
packageName: string;
|
|
12
|
+
instrumentationRunner: string;
|
|
13
|
+
apkPath: string;
|
|
14
|
+
sha256: string;
|
|
15
|
+
signerSha256: string;
|
|
16
|
+
minSdk: number;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Load and validate the bundled helper artifact: parse the manifest, confirm
|
|
20
|
+
* it targets our package, resolve the APK path, and verify the APK's SHA-256
|
|
21
|
+
* matches the manifest (a tamper check on the shipped `dist/`).
|
|
22
|
+
*
|
|
23
|
+
* @returns The validated artifact descriptor.
|
|
24
|
+
* @throws {@link SnapshotHelperError} when anything is missing, malformed, or
|
|
25
|
+
* the APK digest does not match the manifest.
|
|
26
|
+
*/
|
|
27
|
+
export declare function loadHelperArtifact(): Promise<HelperArtifact>;
|
|
28
|
+
//# sourceMappingURL=artifact.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifact.d.cts","sourceRoot":"","sources":["../../../src/backends/android-instrumentation/artifact.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AAiBH,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAmFF;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,cAAc,CAAC,CAqDlE"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads and validates the provenance manifest that `build-android-helper.sh`
|
|
3
|
+
* emits next to the bundled helper APK. The manifest is the single source of
|
|
4
|
+
* truth for the package name, instrumentation runner, versionCode, the APK
|
|
5
|
+
* file digest (a tamper check on `dist/`), and the pinned signer certificate
|
|
6
|
+
* SHA-256 that the installed helper must match before we run `am instrument`.
|
|
7
|
+
*/
|
|
8
|
+
export type HelperArtifact = {
|
|
9
|
+
version: string;
|
|
10
|
+
versionCode: number;
|
|
11
|
+
packageName: string;
|
|
12
|
+
instrumentationRunner: string;
|
|
13
|
+
apkPath: string;
|
|
14
|
+
sha256: string;
|
|
15
|
+
signerSha256: string;
|
|
16
|
+
minSdk: number;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Load and validate the bundled helper artifact: parse the manifest, confirm
|
|
20
|
+
* it targets our package, resolve the APK path, and verify the APK's SHA-256
|
|
21
|
+
* matches the manifest (a tamper check on the shipped `dist/`).
|
|
22
|
+
*
|
|
23
|
+
* @returns The validated artifact descriptor.
|
|
24
|
+
* @throws {@link SnapshotHelperError} when anything is missing, malformed, or
|
|
25
|
+
* the APK digest does not match the manifest.
|
|
26
|
+
*/
|
|
27
|
+
export declare function loadHelperArtifact(): Promise<HelperArtifact>;
|
|
28
|
+
//# sourceMappingURL=artifact.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifact.d.mts","sourceRoot":"","sources":["../../../src/backends/android-instrumentation/artifact.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AAiBH,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAmFF;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,cAAc,CAAC,CAqDlE"}
|