mcp-mobile-interaction 1.0.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.
Files changed (78) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +163 -0
  3. package/dist/index.d.ts +2 -0
  4. package/dist/index.js +50 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/platforms/android.d.ts +14 -0
  7. package/dist/platforms/android.js +230 -0
  8. package/dist/platforms/android.js.map +1 -0
  9. package/dist/platforms/ios.d.ts +14 -0
  10. package/dist/platforms/ios.js +330 -0
  11. package/dist/platforms/ios.js.map +1 -0
  12. package/dist/tools/double-tap.d.ts +2 -0
  13. package/dist/tools/double-tap.js +47 -0
  14. package/dist/tools/double-tap.js.map +1 -0
  15. package/dist/tools/get-screen-info.d.ts +2 -0
  16. package/dist/tools/get-screen-info.js +25 -0
  17. package/dist/tools/get-screen-info.js.map +1 -0
  18. package/dist/tools/get-screen-state.d.ts +2 -0
  19. package/dist/tools/get-screen-state.js +61 -0
  20. package/dist/tools/get-screen-state.js.map +1 -0
  21. package/dist/tools/get-ui-tree.d.ts +2 -0
  22. package/dist/tools/get-ui-tree.js +25 -0
  23. package/dist/tools/get-ui-tree.js.map +1 -0
  24. package/dist/tools/launch-app.d.ts +2 -0
  25. package/dist/tools/launch-app.js +48 -0
  26. package/dist/tools/launch-app.js.map +1 -0
  27. package/dist/tools/list-devices.d.ts +2 -0
  28. package/dist/tools/list-devices.js +50 -0
  29. package/dist/tools/list-devices.js.map +1 -0
  30. package/dist/tools/long-press.d.ts +2 -0
  31. package/dist/tools/long-press.js +53 -0
  32. package/dist/tools/long-press.js.map +1 -0
  33. package/dist/tools/open-url.d.ts +2 -0
  34. package/dist/tools/open-url.js +46 -0
  35. package/dist/tools/open-url.js.map +1 -0
  36. package/dist/tools/press-key.d.ts +2 -0
  37. package/dist/tools/press-key.js +58 -0
  38. package/dist/tools/press-key.js.map +1 -0
  39. package/dist/tools/screenshot.d.ts +2 -0
  40. package/dist/tools/screenshot.js +47 -0
  41. package/dist/tools/screenshot.js.map +1 -0
  42. package/dist/tools/swipe.d.ts +2 -0
  43. package/dist/tools/swipe.js +132 -0
  44. package/dist/tools/swipe.js.map +1 -0
  45. package/dist/tools/tap-element.d.ts +2 -0
  46. package/dist/tools/tap-element.js +146 -0
  47. package/dist/tools/tap-element.js.map +1 -0
  48. package/dist/tools/tap.d.ts +2 -0
  49. package/dist/tools/tap.js +47 -0
  50. package/dist/tools/tap.js.map +1 -0
  51. package/dist/tools/type-text.d.ts +2 -0
  52. package/dist/tools/type-text.js +46 -0
  53. package/dist/tools/type-text.js.map +1 -0
  54. package/dist/tools/wait-for-element.d.ts +2 -0
  55. package/dist/tools/wait-for-element.js +95 -0
  56. package/dist/tools/wait-for-element.js.map +1 -0
  57. package/dist/tools/wait-for-stable.d.ts +2 -0
  58. package/dist/tools/wait-for-stable.js +59 -0
  59. package/dist/tools/wait-for-stable.js.map +1 -0
  60. package/dist/types.d.ts +30 -0
  61. package/dist/types.js +2 -0
  62. package/dist/types.js.map +1 -0
  63. package/dist/utils/exec.d.ts +8 -0
  64. package/dist/utils/exec.js +31 -0
  65. package/dist/utils/exec.js.map +1 -0
  66. package/dist/utils/format-response.d.ts +23 -0
  67. package/dist/utils/format-response.js +30 -0
  68. package/dist/utils/format-response.js.map +1 -0
  69. package/dist/utils/image.d.ts +9 -0
  70. package/dist/utils/image.js +21 -0
  71. package/dist/utils/image.js.map +1 -0
  72. package/dist/utils/observe.d.ts +23 -0
  73. package/dist/utils/observe.js +87 -0
  74. package/dist/utils/observe.js.map +1 -0
  75. package/dist/utils/ui-filter.d.ts +6 -0
  76. package/dist/utils/ui-filter.js +10 -0
  77. package/dist/utils/ui-filter.js.map +1 -0
  78. package/package.json +45 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Pablo Ortiz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,163 @@
1
+ # mcp-mobile-interaction
2
+
3
+ An MCP (Model Context Protocol) server that lets Claude interact with Android and iOS devices/emulators. Take screenshots, tap, swipe, type, inspect UI elements, and more — no Appium required.
4
+
5
+ ## Prerequisites
6
+
7
+ ### Android
8
+ - [Android SDK](https://developer.android.com/studio) with `adb` in your PATH
9
+ - An Android emulator running or a physical device connected via USB with ADB debugging enabled
10
+
11
+ ### iOS
12
+ - macOS with [Xcode](https://developer.apple.com/xcode/) installed (provides `xcrun simctl`)
13
+ - For physical devices: [idb](https://fbidb.io/) (`brew install idb-companion && pip install fb-idb`)
14
+ - A booted iOS simulator or connected physical device
15
+
16
+ ## Installation
17
+
18
+ ### With Claude Code
19
+
20
+ ```bash
21
+ claude mcp add mobile -- npx -y mcp-mobile-interaction
22
+ ```
23
+
24
+ Or add `.mcp.json` to your project root (shared with your team):
25
+
26
+ ```json
27
+ {
28
+ "mcpServers": {
29
+ "mobile": {
30
+ "command": "npx",
31
+ "args": ["-y", "mcp-mobile-interaction"]
32
+ }
33
+ }
34
+ }
35
+ ```
36
+
37
+ ### With Claude Desktop
38
+
39
+ Add to your `claude_desktop_config.json`:
40
+
41
+ ```json
42
+ {
43
+ "mcpServers": {
44
+ "mobile": {
45
+ "command": "npx",
46
+ "args": ["-y", "mcp-mobile-interaction"]
47
+ }
48
+ }
49
+ }
50
+ ```
51
+
52
+ ### Manual
53
+
54
+ ```bash
55
+ npm install -g mcp-mobile-interaction
56
+ ```
57
+
58
+ ## Tools
59
+
60
+ All tools accept a `platform` parameter (`"android"` or `"ios"`) and an optional `device_id` (defaults to the first connected device).
61
+
62
+ ### Core Tools
63
+
64
+ | Tool | Description |
65
+ |------|-------------|
66
+ | `list_devices` | List connected devices and emulators/simulators |
67
+ | `screenshot` | Capture a screenshot (returns base64 JPEG) |
68
+ | `get_ui_tree` | Get a flat list of UI elements with bounds and center coordinates |
69
+ | `get_screen_info` | Get screen dimensions, density, and orientation |
70
+ | `get_screen_state` | Get UI tree + screenshot in a single call (saves a round-trip) |
71
+
72
+ ### Action Tools
73
+
74
+ | Tool | Description |
75
+ |------|-------------|
76
+ | `tap` | Tap at (x, y) coordinates |
77
+ | `double_tap` | Double-tap at (x, y) coordinates |
78
+ | `long_press` | Long-press at (x, y) with configurable duration |
79
+ | `swipe` | Swipe between coordinates or by direction (up/down/left/right) |
80
+ | `type_text` | Type text into the focused input field |
81
+ | `press_key` | Press a key (home, back, enter, delete, volume_up, volume_down, power, tab, recent_apps) |
82
+ | `launch_app` | Launch an app by package name / bundle ID |
83
+ | `open_url` | Open a URL or deep link |
84
+ | `tap_element` | Find an element by text and tap it (combines get_ui_tree + tap) |
85
+
86
+ ### Waiting Tools
87
+
88
+ | Tool | Description |
89
+ |------|-------------|
90
+ | `wait_for_element` | Poll until an element matching text/type criteria appears on screen |
91
+ | `wait_for_stable` | Poll until the screen stops changing (two consecutive UI snapshots match) |
92
+
93
+ ## Observe Mode
94
+
95
+ All 8 action tools (`tap`, `double_tap`, `long_press`, `swipe`, `type_text`, `press_key`, `launch_app`, `open_url`) support optional **observe** parameters that capture the screen state after the action completes — returning the result in a single round-trip instead of two:
96
+
97
+ | Parameter | Description |
98
+ |-----------|-------------|
99
+ | `observe` | `"none"` (default), `"ui_tree"`, `"screenshot"`, or `"both"` |
100
+ | `observe_delay_ms` | Milliseconds to wait before capturing (default: 500) |
101
+ | `observe_stabilize` | If `true`, wait for UI to stop changing instead of a fixed delay |
102
+
103
+ ### Example: before vs after
104
+
105
+ **Before** (2 calls):
106
+ ```
107
+ tap(x=540, y=960) → get_ui_tree()
108
+ ```
109
+
110
+ **After** (1 call):
111
+ ```
112
+ tap(x=540, y=960, observe="ui_tree")
113
+ ```
114
+
115
+ For a 5-step test flow, this cuts round-trips roughly in half.
116
+
117
+ ## Examples
118
+
119
+ ### Take a screenshot
120
+
121
+ ```
122
+ "Take a screenshot of my Android emulator"
123
+ ```
124
+
125
+ Claude will call `screenshot` with `platform: "android"` and display the image.
126
+
127
+ ### Navigate an app
128
+
129
+ ```
130
+ "Open Settings on my iOS simulator, then scroll down and tap General"
131
+ ```
132
+
133
+ Claude will use `launch_app`, `tap_element`, and `swipe` to navigate.
134
+
135
+ ### Run a test flow efficiently
136
+
137
+ ```
138
+ "Tap 'Picking Flow', wait for the sessions to load, tap the first session, fill in the value, and submit"
139
+ ```
140
+
141
+ Claude will use `tap_element` with `observe_stabilize: true` and `wait_for_element` to handle loading states server-side.
142
+
143
+ ### Inspect the UI
144
+
145
+ ```
146
+ "What buttons are visible on the screen?"
147
+ ```
148
+
149
+ Claude will use `get_ui_tree` to list all visible elements with their types, text, and coordinates.
150
+
151
+ ## How It Works
152
+
153
+ - **Android**: Uses `adb` commands directly (screencap, input, uiautomator, am, wm)
154
+ - **iOS Simulators**: Uses `xcrun simctl` (screenshot, io, launch, openurl)
155
+ - **iOS Physical Devices**: Uses `idb` (Facebook's iOS Development Bridge)
156
+
157
+ Screenshots are compressed with [sharp](https://sharp.pixelplumbing.com/) (resized + JPEG quality) to stay under Claude's 1MB image limit.
158
+
159
+ UI elements include `type`, `text`, `bounds`, `center_x`/`center_y` (for tapping), `clickable`, `resource_id`, `enabled`, and `focused`.
160
+
161
+ ## License
162
+
163
+ MIT
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { registerListDevicesTool } from "./tools/list-devices.js";
5
+ import { registerScreenshotTool } from "./tools/screenshot.js";
6
+ import { registerTapTool } from "./tools/tap.js";
7
+ import { registerDoubleTapTool } from "./tools/double-tap.js";
8
+ import { registerLongPressTool } from "./tools/long-press.js";
9
+ import { registerSwipeTool } from "./tools/swipe.js";
10
+ import { registerTypeTextTool } from "./tools/type-text.js";
11
+ import { registerPressKeyTool } from "./tools/press-key.js";
12
+ import { registerGetUiTreeTool } from "./tools/get-ui-tree.js";
13
+ import { registerGetScreenInfoTool } from "./tools/get-screen-info.js";
14
+ import { registerLaunchAppTool } from "./tools/launch-app.js";
15
+ import { registerOpenUrlTool } from "./tools/open-url.js";
16
+ import { registerWaitForElementTool } from "./tools/wait-for-element.js";
17
+ import { registerWaitForStableTool } from "./tools/wait-for-stable.js";
18
+ import { registerTapElementTool } from "./tools/tap-element.js";
19
+ import { registerGetScreenStateTool } from "./tools/get-screen-state.js";
20
+ const server = new McpServer({
21
+ name: "mcp-mobile-interaction",
22
+ version: "1.0.0",
23
+ });
24
+ // Register all 16 tools
25
+ registerListDevicesTool(server);
26
+ registerScreenshotTool(server);
27
+ registerTapTool(server);
28
+ registerDoubleTapTool(server);
29
+ registerLongPressTool(server);
30
+ registerSwipeTool(server);
31
+ registerTypeTextTool(server);
32
+ registerPressKeyTool(server);
33
+ registerGetUiTreeTool(server);
34
+ registerGetScreenInfoTool(server);
35
+ registerLaunchAppTool(server);
36
+ registerOpenUrlTool(server);
37
+ registerWaitForElementTool(server);
38
+ registerWaitForStableTool(server);
39
+ registerTapElementTool(server);
40
+ registerGetScreenStateTool(server);
41
+ async function main() {
42
+ const transport = new StdioServerTransport();
43
+ await server.connect(transport);
44
+ console.error("mcp-mobile-interaction server running on stdio");
45
+ }
46
+ main().catch((error) => {
47
+ console.error("Fatal error:", error);
48
+ process.exit(1);
49
+ });
50
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAC;AACzE,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAC;AAEzE,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,wBAAwB;IAC9B,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,wBAAwB;AACxB,uBAAuB,CAAC,MAAM,CAAC,CAAC;AAChC,sBAAsB,CAAC,MAAM,CAAC,CAAC;AAC/B,eAAe,CAAC,MAAM,CAAC,CAAC;AACxB,qBAAqB,CAAC,MAAM,CAAC,CAAC;AAC9B,qBAAqB,CAAC,MAAM,CAAC,CAAC;AAC9B,iBAAiB,CAAC,MAAM,CAAC,CAAC;AAC1B,oBAAoB,CAAC,MAAM,CAAC,CAAC;AAC7B,oBAAoB,CAAC,MAAM,CAAC,CAAC;AAC7B,qBAAqB,CAAC,MAAM,CAAC,CAAC;AAC9B,yBAAyB,CAAC,MAAM,CAAC,CAAC;AAClC,qBAAqB,CAAC,MAAM,CAAC,CAAC;AAC9B,mBAAmB,CAAC,MAAM,CAAC,CAAC;AAC5B,0BAA0B,CAAC,MAAM,CAAC,CAAC;AACnC,yBAAyB,CAAC,MAAM,CAAC,CAAC;AAClC,sBAAsB,CAAC,MAAM,CAAC,CAAC;AAC/B,0BAA0B,CAAC,MAAM,CAAC,CAAC;AAEnC,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,CAAC,KAAK,CAAC,gDAAgD,CAAC,CAAC;AAClE,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { Device, UiElement, ScreenInfo } from "../types.js";
2
+ export declare function listDevices(): Promise<Device[]>;
3
+ export declare function getFirstDeviceId(): Promise<string>;
4
+ export declare function screenshot(deviceId?: string): Promise<Buffer>;
5
+ export declare function tap(x: number, y: number, deviceId?: string): Promise<void>;
6
+ export declare function doubleTap(x: number, y: number, deviceId?: string): Promise<void>;
7
+ export declare function longPress(x: number, y: number, durationMs?: number, deviceId?: string): Promise<void>;
8
+ export declare function swipe(startX: number, startY: number, endX: number, endY: number, durationMs?: number, deviceId?: string): Promise<void>;
9
+ export declare function typeText(text: string, deviceId?: string): Promise<void>;
10
+ export declare function pressKey(key: string, deviceId?: string): Promise<void>;
11
+ export declare function getUiTree(deviceId?: string): Promise<UiElement[]>;
12
+ export declare function getScreenInfo(deviceId?: string): Promise<ScreenInfo>;
13
+ export declare function launchApp(packageName: string, deviceId?: string): Promise<void>;
14
+ export declare function openUrl(url: string, deviceId?: string): Promise<void>;
@@ -0,0 +1,230 @@
1
+ import { exec, execBuffer } from "../utils/exec.js";
2
+ function adb(deviceId, cmd) {
3
+ return `adb -s ${deviceId} ${cmd}`;
4
+ }
5
+ export async function listDevices() {
6
+ const output = await exec("adb devices -l");
7
+ const lines = output.trim().split("\n").slice(1); // skip header
8
+ const devices = [];
9
+ for (const line of lines) {
10
+ const trimmed = line.trim();
11
+ if (!trimmed || trimmed.startsWith("*"))
12
+ continue;
13
+ const parts = trimmed.split(/\s+/);
14
+ const id = parts[0];
15
+ const status = parts[1];
16
+ // Extract model name from "model:XXX" token
17
+ const modelToken = parts.find((p) => p.startsWith("model:"));
18
+ const name = modelToken ? modelToken.split(":")[1] : id;
19
+ devices.push({ id, name, platform: "android", status });
20
+ }
21
+ return devices;
22
+ }
23
+ export async function getFirstDeviceId() {
24
+ const devices = await listDevices();
25
+ const connected = devices.filter((d) => d.status === "device");
26
+ if (connected.length === 0) {
27
+ throw new Error("No connected Android devices found. Make sure an emulator is running or a device is connected via USB with ADB debugging enabled.");
28
+ }
29
+ return connected[0].id;
30
+ }
31
+ async function resolveDevice(deviceId) {
32
+ return deviceId ?? (await getFirstDeviceId());
33
+ }
34
+ export async function screenshot(deviceId) {
35
+ const id = await resolveDevice(deviceId);
36
+ return execBuffer(adb(id, "exec-out screencap -p"), { timeout: 30_000 });
37
+ }
38
+ export async function tap(x, y, deviceId) {
39
+ const id = await resolveDevice(deviceId);
40
+ await exec(adb(id, `shell input tap ${x} ${y}`));
41
+ }
42
+ export async function doubleTap(x, y, deviceId) {
43
+ const id = await resolveDevice(deviceId);
44
+ // Two rapid taps with minimal delay
45
+ await exec(adb(id, `shell "input tap ${x} ${y} && sleep 0.05 && input tap ${x} ${y}"`));
46
+ }
47
+ export async function longPress(x, y, durationMs = 1000, deviceId) {
48
+ const id = await resolveDevice(deviceId);
49
+ // Swipe from point to same point = long press
50
+ await exec(adb(id, `shell input swipe ${x} ${y} ${x} ${y} ${durationMs}`));
51
+ }
52
+ export async function swipe(startX, startY, endX, endY, durationMs = 300, deviceId) {
53
+ const id = await resolveDevice(deviceId);
54
+ await exec(adb(id, `shell input swipe ${startX} ${startY} ${endX} ${endY} ${durationMs}`));
55
+ }
56
+ export async function typeText(text, deviceId) {
57
+ const id = await resolveDevice(deviceId);
58
+ // Escape special characters for ADB shell
59
+ const escaped = text
60
+ .replace(/\\/g, "\\\\")
61
+ .replace(/"/g, '\\"')
62
+ .replace(/'/g, "\\'")
63
+ .replace(/ /g, "%s")
64
+ .replace(/&/g, "\\&")
65
+ .replace(/</g, "\\<")
66
+ .replace(/>/g, "\\>")
67
+ .replace(/\|/g, "\\|")
68
+ .replace(/;/g, "\\;")
69
+ .replace(/\(/g, "\\(")
70
+ .replace(/\)/g, "\\)")
71
+ .replace(/\$/g, "\\$")
72
+ .replace(/`/g, "\\`");
73
+ await exec(adb(id, `shell input text "${escaped}"`));
74
+ }
75
+ const KEYCODE_MAP = {
76
+ home: 3,
77
+ back: 4,
78
+ enter: 66,
79
+ delete: 67,
80
+ volume_up: 24,
81
+ volume_down: 25,
82
+ power: 26,
83
+ tab: 61,
84
+ recent_apps: 187,
85
+ };
86
+ export async function pressKey(key, deviceId) {
87
+ const id = await resolveDevice(deviceId);
88
+ const keycode = KEYCODE_MAP[key];
89
+ if (keycode === undefined) {
90
+ throw new Error(`Unknown key: ${key}. Supported keys: ${Object.keys(KEYCODE_MAP).join(", ")}`);
91
+ }
92
+ await exec(adb(id, `shell input keyevent ${keycode}`));
93
+ }
94
+ export async function getUiTree(deviceId) {
95
+ const id = await resolveDevice(deviceId);
96
+ // Strategy: tty → file → wait+tty → wait+file
97
+ const strategies = [
98
+ () => dumpUiViaTty(id),
99
+ () => dumpUiViaFile(id),
100
+ async () => { await delay(800); return dumpUiViaTty(id); },
101
+ async () => { await delay(800); return dumpUiViaFile(id); },
102
+ ];
103
+ for (const strategy of strategies) {
104
+ try {
105
+ const xml = await strategy();
106
+ if (xml) {
107
+ const elements = parseUiXml(xml);
108
+ if (elements.length > 0)
109
+ return elements;
110
+ }
111
+ }
112
+ catch {
113
+ // Try next strategy
114
+ }
115
+ }
116
+ throw new Error("Failed to parse UI tree XML from uiautomator dump after 4 attempts. The screen may be in transition — try again after a short delay.");
117
+ }
118
+ function delay(ms) {
119
+ return new Promise((resolve) => setTimeout(resolve, ms));
120
+ }
121
+ async function dumpUiViaTty(deviceId) {
122
+ const output = await exec(adb(deviceId, "exec-out uiautomator dump /dev/tty"), {
123
+ timeout: 30_000,
124
+ });
125
+ // Strip null bytes and other binary artifacts
126
+ const cleaned = output.replace(/\0/g, "").trim();
127
+ return extractXml(cleaned);
128
+ }
129
+ async function dumpUiViaFile(deviceId) {
130
+ const remotePath = "/sdcard/window_dump.xml";
131
+ await exec(adb(deviceId, `shell uiautomator dump ${remotePath}`), {
132
+ timeout: 30_000,
133
+ });
134
+ const output = await exec(adb(deviceId, `shell cat ${remotePath}`), {
135
+ timeout: 10_000,
136
+ });
137
+ // Clean up remote file
138
+ exec(adb(deviceId, `shell rm -f ${remotePath}`)).catch(() => { });
139
+ const cleaned = output.replace(/\0/g, "").trim();
140
+ return extractXml(cleaned);
141
+ }
142
+ function extractXml(output) {
143
+ const xmlMatch = output.match(/<\?xml[\s\S]*<\/hierarchy>/);
144
+ if (xmlMatch)
145
+ return xmlMatch[0];
146
+ const altMatch = output.match(/<hierarchy[\s\S]*<\/hierarchy>/);
147
+ if (altMatch)
148
+ return altMatch[0];
149
+ return null;
150
+ }
151
+ function parseUiXml(xml) {
152
+ const elements = [];
153
+ // Match both self-closing <node ... /> and opening <node ...> tags
154
+ const nodeRegex = /<node\s+([^>]+?)\/?>/g;
155
+ let match;
156
+ let index = 0;
157
+ while ((match = nodeRegex.exec(xml)) !== null) {
158
+ const attrs = match[1];
159
+ const type = extractAttr(attrs, "class")?.split(".").pop() ?? "Unknown";
160
+ const text = extractAttr(attrs, "text") ?? "";
161
+ const contentDesc = extractAttr(attrs, "content-desc") ?? "";
162
+ const clickable = extractAttr(attrs, "clickable") === "true";
163
+ const boundsStr = extractAttr(attrs, "bounds") ?? "";
164
+ const rawResourceId = extractAttr(attrs, "resource-id") ?? "";
165
+ const enabled = extractAttr(attrs, "enabled") === "true";
166
+ const focused = extractAttr(attrs, "focused") === "true";
167
+ // Parse bounds "[x1,y1][x2,y2]"
168
+ const boundsMatch = boundsStr.match(/\[(\d+),(\d+)\]\[(\d+),(\d+)\]/);
169
+ if (!boundsMatch)
170
+ continue;
171
+ const x1 = parseInt(boundsMatch[1], 10);
172
+ const y1 = parseInt(boundsMatch[2], 10);
173
+ const x2 = parseInt(boundsMatch[3], 10);
174
+ const y2 = parseInt(boundsMatch[4], 10);
175
+ const displayText = text || contentDesc;
176
+ // Strip package prefix from resource-id (e.g. "com.example:id/btn" → "btn")
177
+ const resourceId = rawResourceId
178
+ ? rawResourceId.replace(/^[^:]+:id\//, "")
179
+ : undefined;
180
+ elements.push({
181
+ index,
182
+ type,
183
+ text: displayText,
184
+ bounds: {
185
+ x: x1,
186
+ y: y1,
187
+ width: x2 - x1,
188
+ height: y2 - y1,
189
+ },
190
+ center_x: Math.round((x1 + x2) / 2),
191
+ center_y: Math.round((y1 + y2) / 2),
192
+ clickable,
193
+ resource_id: resourceId,
194
+ enabled,
195
+ focused,
196
+ });
197
+ index++;
198
+ }
199
+ return elements;
200
+ }
201
+ function extractAttr(attrs, name) {
202
+ const regex = new RegExp(`${name}="([^"]*)"`);
203
+ const match = attrs.match(regex);
204
+ return match ? match[1] : undefined;
205
+ }
206
+ export async function getScreenInfo(deviceId) {
207
+ const id = await resolveDevice(deviceId);
208
+ const [sizeOutput, densityOutput] = await Promise.all([
209
+ exec(adb(id, "shell wm size")),
210
+ exec(adb(id, "shell wm density")),
211
+ ]);
212
+ // Parse "Physical size: 1080x1920"
213
+ const sizeMatch = sizeOutput.match(/(\d+)x(\d+)/);
214
+ const width = sizeMatch ? parseInt(sizeMatch[1], 10) : 0;
215
+ const height = sizeMatch ? parseInt(sizeMatch[2], 10) : 0;
216
+ // Parse "Physical density: 420"
217
+ const densityMatch = densityOutput.match(/(\d+)/);
218
+ const density = densityMatch ? parseInt(densityMatch[1], 10) : 0;
219
+ const orientation = width > height ? "landscape" : "portrait";
220
+ return { width, height, density, orientation };
221
+ }
222
+ export async function launchApp(packageName, deviceId) {
223
+ const id = await resolveDevice(deviceId);
224
+ await exec(adb(id, `shell monkey -p ${packageName} -c android.intent.category.LAUNCHER 1`));
225
+ }
226
+ export async function openUrl(url, deviceId) {
227
+ const id = await resolveDevice(deviceId);
228
+ await exec(adb(id, `shell am start -a android.intent.action.VIEW -d "${url}"`));
229
+ }
230
+ //# sourceMappingURL=android.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"android.js","sourceRoot":"","sources":["../../src/platforms/android.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { Device, UiElement, ScreenInfo } from "../types.js";
2
+ export declare function listDevices(): Promise<Device[]>;
3
+ export declare function getFirstDeviceId(): Promise<string>;
4
+ export declare function screenshot(deviceId?: string): Promise<Buffer>;
5
+ export declare function tap(x: number, y: number, deviceId?: string): Promise<void>;
6
+ export declare function doubleTap(x: number, y: number, deviceId?: string): Promise<void>;
7
+ export declare function longPress(x: number, y: number, durationMs?: number, deviceId?: string): Promise<void>;
8
+ export declare function swipe(startX: number, startY: number, endX: number, endY: number, durationMs?: number, deviceId?: string): Promise<void>;
9
+ export declare function typeText(text: string, deviceId?: string): Promise<void>;
10
+ export declare function pressKey(key: string, deviceId?: string): Promise<void>;
11
+ export declare function getUiTree(deviceId?: string): Promise<UiElement[]>;
12
+ export declare function getScreenInfo(deviceId?: string): Promise<ScreenInfo>;
13
+ export declare function launchApp(bundleId: string, deviceId?: string): Promise<void>;
14
+ export declare function openUrl(url: string, deviceId?: string): Promise<void>;