@metamask/device-mcp 0.2.0 → 0.3.1

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.
Files changed (107) hide show
  1. package/CHANGELOG.md +28 -1
  2. package/README.md +55 -2
  3. package/dist/backends/adb-backend.cjs +12 -5
  4. package/dist/backends/adb-backend.cjs.map +1 -1
  5. package/dist/backends/adb-backend.d.cts +8 -2
  6. package/dist/backends/adb-backend.d.cts.map +1 -1
  7. package/dist/backends/adb-backend.d.mts +8 -2
  8. package/dist/backends/adb-backend.d.mts.map +1 -1
  9. package/dist/backends/adb-backend.mjs +12 -5
  10. package/dist/backends/adb-backend.mjs.map +1 -1
  11. package/dist/backends/appium-backend.cjs +16 -7
  12. package/dist/backends/appium-backend.cjs.map +1 -1
  13. package/dist/backends/appium-backend.d.cts +8 -2
  14. package/dist/backends/appium-backend.d.cts.map +1 -1
  15. package/dist/backends/appium-backend.d.mts +8 -2
  16. package/dist/backends/appium-backend.d.mts.map +1 -1
  17. package/dist/backends/appium-backend.mjs +16 -7
  18. package/dist/backends/appium-backend.mjs.map +1 -1
  19. package/dist/backends/idb-backend.cjs +12 -5
  20. package/dist/backends/idb-backend.cjs.map +1 -1
  21. package/dist/backends/idb-backend.d.cts +8 -2
  22. package/dist/backends/idb-backend.d.cts.map +1 -1
  23. package/dist/backends/idb-backend.d.mts +8 -2
  24. package/dist/backends/idb-backend.d.mts.map +1 -1
  25. package/dist/backends/idb-backend.mjs +12 -5
  26. package/dist/backends/idb-backend.mjs.map +1 -1
  27. package/dist/backends/index.cjs +4 -3
  28. package/dist/backends/index.cjs.map +1 -1
  29. package/dist/backends/index.d.cts.map +1 -1
  30. package/dist/backends/index.d.mts.map +1 -1
  31. package/dist/backends/index.mjs +4 -3
  32. package/dist/backends/index.mjs.map +1 -1
  33. package/dist/backends/types.cjs.map +1 -1
  34. package/dist/backends/types.d.cts +19 -1
  35. package/dist/backends/types.d.cts.map +1 -1
  36. package/dist/backends/types.d.mts +19 -1
  37. package/dist/backends/types.d.mts.map +1 -1
  38. package/dist/backends/types.mjs.map +1 -1
  39. package/dist/hermes/hermes-cdp.cjs +674 -0
  40. package/dist/hermes/hermes-cdp.cjs.map +1 -0
  41. package/dist/hermes/hermes-cdp.d.cts +248 -0
  42. package/dist/hermes/hermes-cdp.d.cts.map +1 -0
  43. package/dist/hermes/hermes-cdp.d.mts +248 -0
  44. package/dist/hermes/hermes-cdp.d.mts.map +1 -0
  45. package/dist/hermes/hermes-cdp.mjs +665 -0
  46. package/dist/hermes/hermes-cdp.mjs.map +1 -0
  47. package/dist/hermes/session.cjs +196 -0
  48. package/dist/hermes/session.cjs.map +1 -0
  49. package/dist/hermes/session.d.cts +163 -0
  50. package/dist/hermes/session.d.cts.map +1 -0
  51. package/dist/hermes/session.d.mts +163 -0
  52. package/dist/hermes/session.d.mts.map +1 -0
  53. package/dist/hermes/session.mjs +189 -0
  54. package/dist/hermes/session.mjs.map +1 -0
  55. package/dist/index.cjs +21 -1
  56. package/dist/index.cjs.map +1 -1
  57. package/dist/index.d.cts +7 -1
  58. package/dist/index.d.cts.map +1 -1
  59. package/dist/index.d.mts +7 -1
  60. package/dist/index.d.mts.map +1 -1
  61. package/dist/index.mjs +3 -0
  62. package/dist/index.mjs.map +1 -1
  63. package/dist/server.cjs +3 -0
  64. package/dist/server.cjs.map +1 -1
  65. package/dist/server.d.cts.map +1 -1
  66. package/dist/server.d.mts.map +1 -1
  67. package/dist/server.mjs +4 -1
  68. package/dist/server.mjs.map +1 -1
  69. package/dist/tools/hermes-cdp.cjs +102 -0
  70. package/dist/tools/hermes-cdp.cjs.map +1 -0
  71. package/dist/tools/hermes-cdp.d.cts +18 -0
  72. package/dist/tools/hermes-cdp.d.cts.map +1 -0
  73. package/dist/tools/hermes-cdp.d.mts +18 -0
  74. package/dist/tools/hermes-cdp.d.mts.map +1 -0
  75. package/dist/tools/hermes-cdp.mjs +99 -0
  76. package/dist/tools/hermes-cdp.mjs.map +1 -0
  77. package/dist/tools/hermes-targets.cjs +127 -0
  78. package/dist/tools/hermes-targets.cjs.map +1 -0
  79. package/dist/tools/hermes-targets.d.cts +20 -0
  80. package/dist/tools/hermes-targets.d.cts.map +1 -0
  81. package/dist/tools/hermes-targets.d.mts +20 -0
  82. package/dist/tools/hermes-targets.d.mts.map +1 -0
  83. package/dist/tools/hermes-targets.mjs +124 -0
  84. package/dist/tools/hermes-targets.mjs.map +1 -0
  85. package/dist/tools/index.cjs +5 -1
  86. package/dist/tools/index.cjs.map +1 -1
  87. package/dist/tools/index.d.cts +2 -0
  88. package/dist/tools/index.d.cts.map +1 -1
  89. package/dist/tools/index.d.mts +2 -0
  90. package/dist/tools/index.d.mts.map +1 -1
  91. package/dist/tools/index.mjs +2 -0
  92. package/dist/tools/index.mjs.map +1 -1
  93. package/dist/tools/screenshot.cjs +8 -3
  94. package/dist/tools/screenshot.cjs.map +1 -1
  95. package/dist/tools/screenshot.d.cts.map +1 -1
  96. package/dist/tools/screenshot.d.mts.map +1 -1
  97. package/dist/tools/screenshot.mjs +8 -3
  98. package/dist/tools/screenshot.mjs.map +1 -1
  99. package/dist/utils/output-path.cjs +128 -0
  100. package/dist/utils/output-path.cjs.map +1 -0
  101. package/dist/utils/output-path.d.cts +41 -0
  102. package/dist/utils/output-path.d.cts.map +1 -0
  103. package/dist/utils/output-path.d.mts +41 -0
  104. package/dist/utils/output-path.d.mts.map +1 -0
  105. package/dist/utils/output-path.mjs +123 -0
  106. package/dist/utils/output-path.mjs.map +1 -0
  107. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -7,6 +7,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.3.1]
11
+
12
+ ### Changed
13
+
14
+ - Export generateLocators/formatLocators ([#17](https://github.com/MetaMask/device-mcp/pull/17))
15
+
16
+ ## [0.3.0]
17
+
18
+ ### Added
19
+
20
+ - Add Hermes CDP tools (`hermes_cdp`, `hermes_targets`) for inspecting the React Native Hermes JS runtime via Metro ([#14](https://github.com/MetaMask/device-mcp/pull/14))
21
+
22
+ ### Changed
23
+
24
+ - Make screenshot base64 encoding optional — `DeviceBackend.screenshot(outputPath, { encode })` accepts `{ encode: false }` to return only the file `path` and skip base64 encoding. `ScreenshotResult.data` is now optional; the default (`encode: true`) preserves existing behavior ([#15](https://github.com/MetaMask/device-mcp/pull/15))
25
+ - The Appium backend now saves the screenshot to `outputPath` when one is provided (previously the argument was ignored) ([#15](https://github.com/MetaMask/device-mcp/pull/15))
26
+
27
+ ### Fixed
28
+
29
+ - Fix `device_screenshot` failing on macOS — the captured PNG is now read in-process via `fs.readFile(path, 'base64')` instead of shelling out to the `base64` CLI, which is not portable (BSD `base64` on macOS rejects the GNU positional-file syntax that was used previously) ([#15](https://github.com/MetaMask/device-mcp/pull/15))
30
+
31
+ ### Security
32
+
33
+ - Harden screenshot and screen-recording file writes across the ADB, IDB, and Appium backends — default captures now use a private per-process temporary directory (`0700`) with unpredictable names instead of the predictable `/tmp/device-mcp-*-<timestamp>` paths, files are written owner-only (`0600`), and a symlink at the destination is never followed. Caller-supplied output paths can be confined to a single directory via the new `DEVICE_MCP_OUTPUT_DIR` environment variable
34
+
10
35
  ## [0.2.0]
11
36
 
12
37
  ### Added
@@ -24,6 +49,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
24
49
  - `.device-session` file for attaching to existing Appium sessions or creating new ones
25
50
  - 16 MCP tools: `device_snapshot`, `device_screenshot`, `device_info`, `device_tap_element`, `device_tap_coordinates`, `device_type`, `device_swipe`, `device_long_press`, `device_wait_for`, `device_app_state`, `device_open_app`, `device_close_app`, `device_press_button`, `device_dismiss_keyboard`, `device_dismiss_alert`, `device_logs`
26
51
 
27
- [Unreleased]: https://github.com/MetaMask/device-mcp/compare/v0.2.0...HEAD
52
+ [Unreleased]: https://github.com/MetaMask/device-mcp/compare/v0.3.1...HEAD
53
+ [0.3.1]: https://github.com/MetaMask/device-mcp/compare/v0.3.0...v0.3.1
54
+ [0.3.0]: https://github.com/MetaMask/device-mcp/compare/v0.2.0...v0.3.0
28
55
  [0.2.0]: https://github.com/MetaMask/device-mcp/compare/v0.1.0...v0.2.0
29
56
  [0.1.0]: https://github.com/MetaMask/device-mcp/releases/tag/v0.1.0
package/README.md CHANGED
@@ -18,6 +18,7 @@ Provides device interaction tools for LLM agents to inspect UI state, interact w
18
18
  - **iOS local**: Xcode Command Line Tools (for `xcrun simctl`) + [IDB](https://fbidb.io/) for UI interaction (`brew tap facebook/fb && brew install idb-companion && pip3 install fb-idb`)
19
19
  - **Android local**: ADB (Android SDK platform-tools) — auto-discovered from `$ANDROID_HOME`, `$ANDROID_SDK_ROOT`, or `~/Library/Android/sdk`
20
20
  - **Remote/BrowserStack**: No local tools needed — connects via Appium W3C WebDriver HTTP
21
+ - **Hermes CDP** (debugging the React Native JS runtime): uses the global `WebSocket` API. Node 22+ works out of the box; **Node 20 requires launching with `NODE_OPTIONS="--experimental-websocket"`**.
21
22
 
22
23
  ## Installation
23
24
 
@@ -157,6 +158,13 @@ The `.device-session` file is typically written by the test runner when it creat
157
158
  | `device_dismiss_keyboard` | Hide the on-screen keyboard after typing. |
158
159
  | `device_dismiss_alert` | Accept or dismiss a system alert or permission dialog. |
159
160
 
161
+ ### Hermes CDP
162
+
163
+ | Tool | Description |
164
+ | ---------------- | ------------------------------------------------------------------------- |
165
+ | `hermes_cdp` | Speak raw Chrome DevTools Protocol to the React Native Hermes JS runtime. |
166
+ | `hermes_targets` | List and diagnose the debuggable Hermes targets exposed by Metro. |
167
+
160
168
  ### Element Identification
161
169
 
162
170
  Elements are identified by accessibility attributes — not internal refs. Matching is **fuzzy**: partial text and case-insensitive matches work. For example, querying `{ label: "Confirm" }` matches an element with label `"Confirm Transaction"`.
@@ -187,6 +195,51 @@ Elements are identified by accessibility attributes — not internal refs. Match
187
195
  | `device_dismiss_alert` | find button + tap | find button + tap | `mobile: accept/dismissAlert` |
188
196
  | `device_logs` | `idb log` | `logcat` | `mobile: getLog` |
189
197
 
198
+ ## Hermes CDP
199
+
200
+ Beyond native UI automation (idb/adb), the server can speak [Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/) (CDP) directly to the **React Native Hermes** JS runtime of a running app via Metro's inspector proxy. This lets an agent evaluate JavaScript, inspect runtime state, and diagnose the app's JS layer — complementing the native tools. Works on **both iOS and Android** (the transport is identical HTTP/WebSocket to Metro; only the default `appId` differs).
201
+
202
+ This is _React Native Hermes CDP via Metro's inspector proxy_ — not WebView/browser CDP, and not the iOS WebKit Inspector Protocol. The app's Hermes runtime connects out to Metro; Metro exposes debuggable targets over `http://localhost:<metroPort>/json` and a per-target `webSocketDebuggerUrl`, and the server speaks real CDP over that WebSocket.
203
+
204
+ ### Prerequisites
205
+
206
+ - A **DEBUG build with Metro running** on the inspector port. Release builds expose no inspector (`/json` returns `[]`).
207
+ - The global `WebSocket` API: **Node 22+ works out of the box**; **Node 20 requires launching with `NODE_OPTIONS="--experimental-websocket"`** (see [Requirements](#requirements)).
208
+
209
+ ### Tools
210
+
211
+ | Tool | Description |
212
+ | ---------------- | ------------------------------------------------------------------------- |
213
+ | `hermes_cdp` | Speak raw Chrome DevTools Protocol to the React Native Hermes JS runtime. |
214
+ | `hermes_targets` | List and diagnose the debuggable Hermes targets exposed by Metro. |
215
+
216
+ Example `hermes_cdp` call: method `Runtime.evaluate` with params `{"expression":"1+1","returnByValue":true}` — the raw response nests the value at `result.result.value`.
217
+
218
+ ### Safety model
219
+
220
+ A single Metro can have multiple apps/simulators registered, so executing CDP against the wrong target could run code in the wrong workspace. The server applies five fail-closed checks before executing:
221
+
222
+ 1. **Strict `appId` match** — only targets whose `appId` equals the expected id (no substring, no `targets[0]` fallback).
223
+ 2. **`HermesInternal` identity probe** — evaluates `HermesInternal.getRuntimeProperties()` before the user's method; non-Hermes targets fail closed.
224
+ 3. **Device pin** — pins `reactNative.logicalDeviceId` on first success; later calls are filtered to the pin.
225
+ 4. **Multi-device fail-closed** — multiple distinct logical devices (or any missing the field) fail closed rather than guess.
226
+ 5. **WebSocket URL validation** — protocol must be `ws:`, hostname must be loopback, and port must equal the resolved Metro port.
227
+
228
+ The synthetic legacy page (`React Native Experimental (Improved Chrome Reloads)`) is filtered out, and destructive methods `Runtime.terminateExecution` and `Inspector.detached` are blocked.
229
+
230
+ ### Configuration
231
+
232
+ - **appId** defaults to `io.metamask.MetaMask` (iOS) / `io.metamask` (Android). Override globally via the `HERMES_APP_ID` env var or per-call via the tool's `appId` param. Android users not on the default must pass `appId` or set `HERMES_APP_ID`; `hermes_targets` with `all: true` aids discovery of the real appId.
233
+ - **Metro port** defaults to `8081`. Override via the `HERMES_METRO_PORT` env var or per-call via the tool's `metroPort` param.
234
+
235
+ ## File Output & Safety
236
+
237
+ `device_screenshot` and `device_screen_recording` write image/video files to disk. These artifacts can contain sensitive on-screen content (seed phrases, private keys, balances), so writes are hardened:
238
+
239
+ - **No `outputPath`** — the file is written into a private, per-process temporary directory (`0700`) with an unpredictable name, rather than a predictable `/tmp/device-mcp-*-<timestamp>` path (which is exposed to symlink and information-disclosure attacks on shared hosts).
240
+ - **With `outputPath`** — the path is resolved to an absolute path and written owner-only (`0600`); a symlink already present at the destination is never followed.
241
+ - **`DEVICE_MCP_OUTPUT_DIR`** — set this to confine every caller-supplied `outputPath` to a single directory. Any path resolving outside it is rejected. Leave it unset to allow writing to any path the server process can access.
242
+
190
243
  ## MCP Client Configuration
191
244
 
192
245
  ### opencode
@@ -260,7 +313,7 @@ Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
260
313
  @metamask/device-mcp
261
314
  ├── src/
262
315
  │ ├── index.ts # Entry point — lazy backend, stdio MCP server
263
- │ ├── server.ts # MCP server — registers 25 tools
316
+ │ ├── server.ts # MCP server — registers 28 tools
264
317
  │ ├── backends/
265
318
  │ │ ├── types.ts # DeviceBackend interface
266
319
  │ │ ├── idb-backend.ts # iOS local — IDB commands + simctl fallback
@@ -269,7 +322,7 @@ Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
269
322
  │ │ ├── webdriver-client.ts # Minimal W3C WebDriver HTTP client (fetch)
270
323
  │ │ ├── session-file.ts # .device-session file reader
271
324
  │ │ └── index.ts # createBackend() + createLazyBackend() factory
272
- │ ├── tools/ # One file per MCP tool (25 tools)
325
+ │ ├── tools/ # One file per MCP tool (28 tools)
273
326
  │ │ ├── list-devices.ts # device_list_devices — enumerate connected devices
274
327
  │ │ ├── select-device.ts # device_select_device — choose device for session
275
328
  │ │ └── ... # snapshot, tap, type, swipe, etc.
@@ -16,9 +16,11 @@ exports.AdbBackend = void 0;
16
16
  exports.parseAndroidHierarchy = parseAndroidHierarchy;
17
17
  exports.parseNodeAttributes = parseNodeAttributes;
18
18
  const node_child_process_1 = require("node:child_process");
19
+ const promises_1 = require("node:fs/promises");
19
20
  const alert_labels_js_1 = require("../utils/alert-labels.cjs");
20
21
  const element_js_1 = require("../utils/element.cjs");
21
22
  const exec_js_1 = require("../utils/exec.cjs");
23
+ const output_path_js_1 = require("../utils/output-path.cjs");
22
24
  class AdbBackend {
23
25
  constructor(serial) {
24
26
  _AdbBackend_instances.add(this);
@@ -158,8 +160,8 @@ class AdbBackend {
158
160
  }
159
161
  return { bundleId, state: 'Not Installed' };
160
162
  }
161
- async screenshot(outputPath) {
162
- const localPath = outputPath ?? `/tmp/device-mcp-screenshot-${Date.now()}.png`;
163
+ async screenshot(outputPath, options) {
164
+ const localPath = (0, output_path_js_1.resolveArtifactPath)(outputPath, 'screenshot');
163
165
  await __classPrivateFieldGet(this, _AdbBackend_instances, "m", _AdbBackend_adb).call(this, ['shell', 'screencap', '-p', '/sdcard/screenshot.png']);
164
166
  await (0, exec_js_1.execStrict)('adb', [
165
167
  '-s',
@@ -169,8 +171,12 @@ class AdbBackend {
169
171
  localPath,
170
172
  ]);
171
173
  await __classPrivateFieldGet(this, _AdbBackend_instances, "m", _AdbBackend_adb).call(this, ['shell', 'rm', '-f', '/sdcard/screenshot.png']);
172
- const data = await (0, exec_js_1.execStrict)('base64', [localPath]);
173
- return { data: data.trim(), format: 'png', path: localPath };
174
+ (0, output_path_js_1.hardenArtifactFile)(localPath);
175
+ if (options?.encode === false) {
176
+ return { data: undefined, format: 'png', path: localPath };
177
+ }
178
+ const data = await (0, promises_1.readFile)(localPath, 'base64');
179
+ return { data, format: 'png', path: localPath };
174
180
  }
175
181
  async openApp(bundleId) {
176
182
  await __classPrivateFieldGet(this, _AdbBackend_instances, "m", _AdbBackend_adb).call(this, [
@@ -320,7 +326,7 @@ class AdbBackend {
320
326
  if (__classPrivateFieldGet(this, _AdbBackend_recordingProcess, "f")) {
321
327
  throw new Error('Screen recording is already in progress');
322
328
  }
323
- __classPrivateFieldSet(this, _AdbBackend_recordingPath, outputPath ?? `/tmp/device-mcp-recording-${Date.now()}.mp4`, "f");
329
+ __classPrivateFieldSet(this, _AdbBackend_recordingPath, (0, output_path_js_1.resolveArtifactPath)(outputPath, 'recording'), "f");
324
330
  __classPrivateFieldSet(this, _AdbBackend_recordingProcess, (0, node_child_process_1.spawn)('adb', [
325
331
  '-s',
326
332
  __classPrivateFieldGet(this, _AdbBackend_serial, "f"),
@@ -350,6 +356,7 @@ class AdbBackend {
350
356
  localPath,
351
357
  ]);
352
358
  await __classPrivateFieldGet(this, _AdbBackend_instances, "m", _AdbBackend_adb).call(this, ['shell', 'rm', '-f', __classPrivateFieldGet(this, _AdbBackend_recordingRemotePath, "f")]);
359
+ (0, output_path_js_1.hardenArtifactFile)(localPath);
353
360
  return localPath;
354
361
  }
355
362
  }
@@ -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';\n\nimport type {\n DeviceBackend,\n DeviceButton,\n DeviceInfo,\n SnapshotResult,\n ScreenshotResult,\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';\n\nexport class AdbBackend implements DeviceBackend {\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 async screenshot(outputPath?: string): Promise<ScreenshotResult> {\n const localPath =\n outputPath ?? `/tmp/device-mcp-screenshot-${Date.now()}.png`;\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 const data = await execStrict('base64', [localPath]);\n return { data: data.trim(), 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 =\n outputPath ?? `/tmp/device-mcp-recording-${Date.now()}.mp4`;\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 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 { 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 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,4 +1,4 @@
1
- import type { DeviceBackend, DeviceButton, DeviceInfo, SnapshotResult, ScreenshotResult, LogsResult, TapResult, AppStateResult, ElementQuery, UIElement, WindowSize } from "./types.cjs";
1
+ import type { DeviceBackend, DeviceButton, DeviceInfo, SnapshotResult, ScreenshotResult, ScreenshotFileResult, ScreenshotOptions, LogsResult, TapResult, AppStateResult, ElementQuery, UIElement, WindowSize } from "./types.cjs";
2
2
  export declare class AdbBackend implements DeviceBackend {
3
3
  #private;
4
4
  readonly platform: "android";
@@ -13,7 +13,13 @@ export declare class AdbBackend implements DeviceBackend {
13
13
  swipe(direction: 'up' | 'down' | 'left' | 'right', startX?: number, startY?: number, distance?: number): Promise<void>;
14
14
  waitForElement(query: ElementQuery, timeoutMs?: number, intervalMs?: number): Promise<UIElement>;
15
15
  getAppState(bundleId: string): Promise<AppStateResult>;
16
- screenshot(outputPath?: string): Promise<ScreenshotResult>;
16
+ screenshot(outputPath?: string, options?: {
17
+ encode?: true;
18
+ }): Promise<ScreenshotResult>;
19
+ screenshot(outputPath: string | undefined, options: {
20
+ encode: false;
21
+ }): Promise<ScreenshotFileResult>;
22
+ screenshot(outputPath?: string, options?: ScreenshotOptions): Promise<ScreenshotResult | ScreenshotFileResult>;
17
23
  openApp(bundleId: string): Promise<void>;
18
24
  closeApp(bundleId: string): Promise<void>;
19
25
  pressButton(button: DeviceButton): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"adb-backend.d.cts","sourceRoot":"","sources":["../../src/backends/adb-backend.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,SAAS,EACT,cAAc,EACd,YAAY,EACZ,SAAS,EACT,UAAU,EACX,oBAAmB;AASpB,qBAAa,UAAW,YAAW,aAAa;;IAC9C,QAAQ,CAAC,QAAQ,EAAG,SAAS,CAAU;gBAU3B,MAAM,EAAE,MAAM;IAQpB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAehC,aAAa,IAAI,OAAO,CAAC,UAAU,CAAC;IAcpC,QAAQ,IAAI,OAAO,CAAC,cAAc,CAAC;IAWnC,UAAU,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC;IAsBnD,cAAc,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAYpD,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAInD,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBrC,KAAK,CACT,SAAS,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,EAC3C,MAAM,CAAC,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC;IAmBV,cAAc,CAClB,KAAK,EAAE,YAAY,EACnB,SAAS,SAAS,EAClB,UAAU,SAAM,GACf,OAAO,CAAC,SAAS,CAAC;IAef,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAqCtD,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAgB1D,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYxC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzC,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAWhD,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAIhC,YAAY,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAiB5C,OAAO,CAAC,eAAe,SAAK,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAwBnE,SAAS,CAAC,KAAK,EAAE,YAAY,EAAE,UAAU,SAAO,GAAG,OAAO,CAAC,SAAS,CAAC;IA+BrE,eAAe,CACnB,KAAK,EAAE,YAAY,EACnB,SAAS,GAAE,IAAI,GAAG,MAAe,EACjC,WAAW,SAAK,GACf,OAAO,CAAC,SAAS,CAAC;IAqBf,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAY/B,aAAa,IAAI,OAAO,CAAC,UAAU,CAAC;IAYpC,WAAW,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAIhC,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS1C,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAO/B,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO1C,oBAAoB,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBxD,mBAAmB,IAAI,OAAO,CAAC,MAAM,CAAC;CAoB7C;AAgCD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,CAgC9D;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CA8BnE"}
1
+ {"version":3,"file":"adb-backend.d.cts","sourceRoot":"","sources":["../../src/backends/adb-backend.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { DeviceBackend, DeviceButton, DeviceInfo, SnapshotResult, ScreenshotResult, LogsResult, TapResult, AppStateResult, ElementQuery, UIElement, WindowSize } from "./types.mjs";
1
+ import type { DeviceBackend, DeviceButton, DeviceInfo, SnapshotResult, ScreenshotResult, ScreenshotFileResult, ScreenshotOptions, LogsResult, TapResult, AppStateResult, ElementQuery, UIElement, WindowSize } from "./types.mjs";
2
2
  export declare class AdbBackend implements DeviceBackend {
3
3
  #private;
4
4
  readonly platform: "android";
@@ -13,7 +13,13 @@ export declare class AdbBackend implements DeviceBackend {
13
13
  swipe(direction: 'up' | 'down' | 'left' | 'right', startX?: number, startY?: number, distance?: number): Promise<void>;
14
14
  waitForElement(query: ElementQuery, timeoutMs?: number, intervalMs?: number): Promise<UIElement>;
15
15
  getAppState(bundleId: string): Promise<AppStateResult>;
16
- screenshot(outputPath?: string): Promise<ScreenshotResult>;
16
+ screenshot(outputPath?: string, options?: {
17
+ encode?: true;
18
+ }): Promise<ScreenshotResult>;
19
+ screenshot(outputPath: string | undefined, options: {
20
+ encode: false;
21
+ }): Promise<ScreenshotFileResult>;
22
+ screenshot(outputPath?: string, options?: ScreenshotOptions): Promise<ScreenshotResult | ScreenshotFileResult>;
17
23
  openApp(bundleId: string): Promise<void>;
18
24
  closeApp(bundleId: string): Promise<void>;
19
25
  pressButton(button: DeviceButton): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"adb-backend.d.mts","sourceRoot":"","sources":["../../src/backends/adb-backend.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,SAAS,EACT,cAAc,EACd,YAAY,EACZ,SAAS,EACT,UAAU,EACX,oBAAmB;AASpB,qBAAa,UAAW,YAAW,aAAa;;IAC9C,QAAQ,CAAC,QAAQ,EAAG,SAAS,CAAU;gBAU3B,MAAM,EAAE,MAAM;IAQpB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAehC,aAAa,IAAI,OAAO,CAAC,UAAU,CAAC;IAcpC,QAAQ,IAAI,OAAO,CAAC,cAAc,CAAC;IAWnC,UAAU,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC;IAsBnD,cAAc,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAYpD,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAInD,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBrC,KAAK,CACT,SAAS,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,EAC3C,MAAM,CAAC,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC;IAmBV,cAAc,CAClB,KAAK,EAAE,YAAY,EACnB,SAAS,SAAS,EAClB,UAAU,SAAM,GACf,OAAO,CAAC,SAAS,CAAC;IAef,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAqCtD,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAgB1D,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYxC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzC,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAWhD,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAIhC,YAAY,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAiB5C,OAAO,CAAC,eAAe,SAAK,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAwBnE,SAAS,CAAC,KAAK,EAAE,YAAY,EAAE,UAAU,SAAO,GAAG,OAAO,CAAC,SAAS,CAAC;IA+BrE,eAAe,CACnB,KAAK,EAAE,YAAY,EACnB,SAAS,GAAE,IAAI,GAAG,MAAe,EACjC,WAAW,SAAK,GACf,OAAO,CAAC,SAAS,CAAC;IAqBf,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAY/B,aAAa,IAAI,OAAO,CAAC,UAAU,CAAC;IAYpC,WAAW,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAIhC,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS1C,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAO/B,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO1C,oBAAoB,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBxD,mBAAmB,IAAI,OAAO,CAAC,MAAM,CAAC;CAoB7C;AAgCD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,CAgC9D;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CA8BnE"}
1
+ {"version":3,"file":"adb-backend.d.mts","sourceRoot":"","sources":["../../src/backends/adb-backend.ts"],"names":[],"mappings":"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"}
@@ -11,9 +11,11 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
11
11
  };
12
12
  var _AdbBackend_instances, _AdbBackend_serial, _AdbBackend_recordingProcess, _AdbBackend_recordingPath, _AdbBackend_recordingRemotePath, _AdbBackend_adb, _AdbBackend_dumpUiHierarchy;
13
13
  import { spawn } from "node:child_process";
14
+ import { readFile } from "node:fs/promises";
14
15
  import { ACCEPT_LABELS, DENY_LABELS } from "../utils/alert-labels.mjs";
15
16
  import { findElement, describeElement, computeSwipeEnd } from "../utils/element.mjs";
16
17
  import { execStrict, isCommandAvailable } from "../utils/exec.mjs";
18
+ import { hardenArtifactFile, resolveArtifactPath } from "../utils/output-path.mjs";
17
19
  export class AdbBackend {
18
20
  constructor(serial) {
19
21
  _AdbBackend_instances.add(this);
@@ -153,8 +155,8 @@ export class AdbBackend {
153
155
  }
154
156
  return { bundleId, state: 'Not Installed' };
155
157
  }
156
- async screenshot(outputPath) {
157
- const localPath = outputPath ?? `/tmp/device-mcp-screenshot-${Date.now()}.png`;
158
+ async screenshot(outputPath, options) {
159
+ const localPath = resolveArtifactPath(outputPath, 'screenshot');
158
160
  await __classPrivateFieldGet(this, _AdbBackend_instances, "m", _AdbBackend_adb).call(this, ['shell', 'screencap', '-p', '/sdcard/screenshot.png']);
159
161
  await execStrict('adb', [
160
162
  '-s',
@@ -164,8 +166,12 @@ export class AdbBackend {
164
166
  localPath,
165
167
  ]);
166
168
  await __classPrivateFieldGet(this, _AdbBackend_instances, "m", _AdbBackend_adb).call(this, ['shell', 'rm', '-f', '/sdcard/screenshot.png']);
167
- const data = await execStrict('base64', [localPath]);
168
- return { data: data.trim(), format: 'png', path: localPath };
169
+ hardenArtifactFile(localPath);
170
+ if (options?.encode === false) {
171
+ return { data: undefined, format: 'png', path: localPath };
172
+ }
173
+ const data = await readFile(localPath, 'base64');
174
+ return { data, format: 'png', path: localPath };
169
175
  }
170
176
  async openApp(bundleId) {
171
177
  await __classPrivateFieldGet(this, _AdbBackend_instances, "m", _AdbBackend_adb).call(this, [
@@ -315,7 +321,7 @@ export class AdbBackend {
315
321
  if (__classPrivateFieldGet(this, _AdbBackend_recordingProcess, "f")) {
316
322
  throw new Error('Screen recording is already in progress');
317
323
  }
318
- __classPrivateFieldSet(this, _AdbBackend_recordingPath, outputPath ?? `/tmp/device-mcp-recording-${Date.now()}.mp4`, "f");
324
+ __classPrivateFieldSet(this, _AdbBackend_recordingPath, resolveArtifactPath(outputPath, 'recording'), "f");
319
325
  __classPrivateFieldSet(this, _AdbBackend_recordingProcess, spawn('adb', [
320
326
  '-s',
321
327
  __classPrivateFieldGet(this, _AdbBackend_serial, "f"),
@@ -345,6 +351,7 @@ export class AdbBackend {
345
351
  localPath,
346
352
  ]);
347
353
  await __classPrivateFieldGet(this, _AdbBackend_instances, "m", _AdbBackend_adb).call(this, ['shell', 'rm', '-f', __classPrivateFieldGet(this, _AdbBackend_recordingRemotePath, "f")]);
354
+ hardenArtifactFile(localPath);
348
355
  return localPath;
349
356
  }
350
357
  }