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.
- package/LICENSE +21 -0
- package/README.md +163 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +50 -0
- package/dist/index.js.map +1 -0
- package/dist/platforms/android.d.ts +14 -0
- package/dist/platforms/android.js +230 -0
- package/dist/platforms/android.js.map +1 -0
- package/dist/platforms/ios.d.ts +14 -0
- package/dist/platforms/ios.js +330 -0
- package/dist/platforms/ios.js.map +1 -0
- package/dist/tools/double-tap.d.ts +2 -0
- package/dist/tools/double-tap.js +47 -0
- package/dist/tools/double-tap.js.map +1 -0
- package/dist/tools/get-screen-info.d.ts +2 -0
- package/dist/tools/get-screen-info.js +25 -0
- package/dist/tools/get-screen-info.js.map +1 -0
- package/dist/tools/get-screen-state.d.ts +2 -0
- package/dist/tools/get-screen-state.js +61 -0
- package/dist/tools/get-screen-state.js.map +1 -0
- package/dist/tools/get-ui-tree.d.ts +2 -0
- package/dist/tools/get-ui-tree.js +25 -0
- package/dist/tools/get-ui-tree.js.map +1 -0
- package/dist/tools/launch-app.d.ts +2 -0
- package/dist/tools/launch-app.js +48 -0
- package/dist/tools/launch-app.js.map +1 -0
- package/dist/tools/list-devices.d.ts +2 -0
- package/dist/tools/list-devices.js +50 -0
- package/dist/tools/list-devices.js.map +1 -0
- package/dist/tools/long-press.d.ts +2 -0
- package/dist/tools/long-press.js +53 -0
- package/dist/tools/long-press.js.map +1 -0
- package/dist/tools/open-url.d.ts +2 -0
- package/dist/tools/open-url.js +46 -0
- package/dist/tools/open-url.js.map +1 -0
- package/dist/tools/press-key.d.ts +2 -0
- package/dist/tools/press-key.js +58 -0
- package/dist/tools/press-key.js.map +1 -0
- package/dist/tools/screenshot.d.ts +2 -0
- package/dist/tools/screenshot.js +47 -0
- package/dist/tools/screenshot.js.map +1 -0
- package/dist/tools/swipe.d.ts +2 -0
- package/dist/tools/swipe.js +132 -0
- package/dist/tools/swipe.js.map +1 -0
- package/dist/tools/tap-element.d.ts +2 -0
- package/dist/tools/tap-element.js +146 -0
- package/dist/tools/tap-element.js.map +1 -0
- package/dist/tools/tap.d.ts +2 -0
- package/dist/tools/tap.js +47 -0
- package/dist/tools/tap.js.map +1 -0
- package/dist/tools/type-text.d.ts +2 -0
- package/dist/tools/type-text.js +46 -0
- package/dist/tools/type-text.js.map +1 -0
- package/dist/tools/wait-for-element.d.ts +2 -0
- package/dist/tools/wait-for-element.js +95 -0
- package/dist/tools/wait-for-element.js.map +1 -0
- package/dist/tools/wait-for-stable.d.ts +2 -0
- package/dist/tools/wait-for-stable.js +59 -0
- package/dist/tools/wait-for-stable.js.map +1 -0
- package/dist/types.d.ts +30 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/exec.d.ts +8 -0
- package/dist/utils/exec.js +31 -0
- package/dist/utils/exec.js.map +1 -0
- package/dist/utils/format-response.d.ts +23 -0
- package/dist/utils/format-response.js +30 -0
- package/dist/utils/format-response.js.map +1 -0
- package/dist/utils/image.d.ts +9 -0
- package/dist/utils/image.js +21 -0
- package/dist/utils/image.js.map +1 -0
- package/dist/utils/observe.d.ts +23 -0
- package/dist/utils/observe.js +87 -0
- package/dist/utils/observe.js.map +1 -0
- package/dist/utils/ui-filter.d.ts +6 -0
- package/dist/utils/ui-filter.js +10 -0
- package/dist/utils/ui-filter.js.map +1 -0
- 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
|
package/dist/index.d.ts
ADDED
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>;
|