devlens-mcp 0.3.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/.claude/settings.json +12 -0
- package/.claude/settings.local.json +17 -0
- package/INSTALLATION_GUIDE.md +354 -0
- package/QUICK_START.md +153 -0
- package/README.md +354 -0
- package/bin/cli.ts +22 -0
- package/bin/register.ts +96 -0
- package/dist/bin/cli.d.ts +3 -0
- package/dist/bin/cli.d.ts.map +1 -0
- package/dist/bin/cli.js +20 -0
- package/dist/bin/cli.js.map +1 -0
- package/dist/bin/register.d.ts +10 -0
- package/dist/bin/register.d.ts.map +1 -0
- package/dist/bin/register.js +92 -0
- package/dist/bin/register.js.map +1 -0
- package/dist/src/config/devlens-config.d.ts +92 -0
- package/dist/src/config/devlens-config.d.ts.map +1 -0
- package/dist/src/config/devlens-config.js +70 -0
- package/dist/src/config/devlens-config.js.map +1 -0
- package/dist/src/index.d.ts +35 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +8 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/metro/cdp-client.d.ts +48 -0
- package/dist/src/metro/cdp-client.d.ts.map +1 -0
- package/dist/src/metro/cdp-client.js +127 -0
- package/dist/src/metro/cdp-client.js.map +1 -0
- package/dist/src/metro/log-collector.d.ts +30 -0
- package/dist/src/metro/log-collector.d.ts.map +1 -0
- package/dist/src/metro/log-collector.js +114 -0
- package/dist/src/metro/log-collector.js.map +1 -0
- package/dist/src/metro/metro-bridge.d.ts +56 -0
- package/dist/src/metro/metro-bridge.d.ts.map +1 -0
- package/dist/src/metro/metro-bridge.js +255 -0
- package/dist/src/metro/metro-bridge.js.map +1 -0
- package/dist/src/metro/network-inspector.d.ts +34 -0
- package/dist/src/metro/network-inspector.d.ts.map +1 -0
- package/dist/src/metro/network-inspector.js +100 -0
- package/dist/src/metro/network-inspector.js.map +1 -0
- package/dist/src/platform/android/adb.d.ts +50 -0
- package/dist/src/platform/android/adb.d.ts.map +1 -0
- package/dist/src/platform/android/adb.js +137 -0
- package/dist/src/platform/android/adb.js.map +1 -0
- package/dist/src/platform/android/android-device.d.ts +21 -0
- package/dist/src/platform/android/android-device.d.ts.map +1 -0
- package/dist/src/platform/android/android-device.js +94 -0
- package/dist/src/platform/android/android-device.js.map +1 -0
- package/dist/src/platform/android/ui-automator.d.ts +17 -0
- package/dist/src/platform/android/ui-automator.d.ts.map +1 -0
- package/dist/src/platform/android/ui-automator.js +126 -0
- package/dist/src/platform/android/ui-automator.js.map +1 -0
- package/dist/src/platform/device-manager.d.ts +28 -0
- package/dist/src/platform/device-manager.d.ts.map +1 -0
- package/dist/src/platform/device-manager.js +185 -0
- package/dist/src/platform/device-manager.js.map +1 -0
- package/dist/src/platform/device.d.ts +86 -0
- package/dist/src/platform/device.d.ts.map +1 -0
- package/dist/src/platform/device.js +7 -0
- package/dist/src/platform/device.js.map +1 -0
- package/dist/src/platform/ios/accessibility.d.ts +17 -0
- package/dist/src/platform/ios/accessibility.d.ts.map +1 -0
- package/dist/src/platform/ios/accessibility.js +159 -0
- package/dist/src/platform/ios/accessibility.js.map +1 -0
- package/dist/src/platform/ios/ios-device.d.ts +22 -0
- package/dist/src/platform/ios/ios-device.d.ts.map +1 -0
- package/dist/src/platform/ios/ios-device.js +97 -0
- package/dist/src/platform/ios/ios-device.js.map +1 -0
- package/dist/src/platform/ios/simctl.d.ts +54 -0
- package/dist/src/platform/ios/simctl.d.ts.map +1 -0
- package/dist/src/platform/ios/simctl.js +192 -0
- package/dist/src/platform/ios/simctl.js.map +1 -0
- package/dist/src/server.d.ts +3 -0
- package/dist/src/server.d.ts.map +1 -0
- package/dist/src/server.js +176 -0
- package/dist/src/server.js.map +1 -0
- package/dist/src/snapshot/formatter.d.ts +18 -0
- package/dist/src/snapshot/formatter.d.ts.map +1 -0
- package/dist/src/snapshot/formatter.js +86 -0
- package/dist/src/snapshot/formatter.js.map +1 -0
- package/dist/src/snapshot/ref-registry.d.ts +67 -0
- package/dist/src/snapshot/ref-registry.d.ts.map +1 -0
- package/dist/src/snapshot/ref-registry.js +169 -0
- package/dist/src/snapshot/ref-registry.js.map +1 -0
- package/dist/src/snapshot/snapshot-differ.d.ts +57 -0
- package/dist/src/snapshot/snapshot-differ.d.ts.map +1 -0
- package/dist/src/snapshot/snapshot-differ.js +153 -0
- package/dist/src/snapshot/snapshot-differ.js.map +1 -0
- package/dist/src/tools/app-tools.d.ts +71 -0
- package/dist/src/tools/app-tools.d.ts.map +1 -0
- package/dist/src/tools/app-tools.js +97 -0
- package/dist/src/tools/app-tools.js.map +1 -0
- package/dist/src/tools/device-tools.d.ts +53 -0
- package/dist/src/tools/device-tools.d.ts.map +1 -0
- package/dist/src/tools/device-tools.js +86 -0
- package/dist/src/tools/device-tools.js.map +1 -0
- package/dist/src/tools/ds-tools.d.ts +65 -0
- package/dist/src/tools/ds-tools.d.ts.map +1 -0
- package/dist/src/tools/ds-tools.js +314 -0
- package/dist/src/tools/ds-tools.js.map +1 -0
- package/dist/src/tools/interaction-tools.d.ts +248 -0
- package/dist/src/tools/interaction-tools.d.ts.map +1 -0
- package/dist/src/tools/interaction-tools.js +391 -0
- package/dist/src/tools/interaction-tools.js.map +1 -0
- package/dist/src/tools/metro-tools.d.ts +115 -0
- package/dist/src/tools/metro-tools.d.ts.map +1 -0
- package/dist/src/tools/metro-tools.js +270 -0
- package/dist/src/tools/metro-tools.js.map +1 -0
- package/dist/src/tools/navigation-tools.d.ts +36 -0
- package/dist/src/tools/navigation-tools.d.ts.map +1 -0
- package/dist/src/tools/navigation-tools.js +60 -0
- package/dist/src/tools/navigation-tools.js.map +1 -0
- package/dist/src/tools/screenshot-tools.d.ts +298 -0
- package/dist/src/tools/screenshot-tools.d.ts.map +1 -0
- package/dist/src/tools/screenshot-tools.js +565 -0
- package/dist/src/tools/screenshot-tools.js.map +1 -0
- package/dist/src/tools/snapshot-tools.d.ts +161 -0
- package/dist/src/tools/snapshot-tools.d.ts.map +1 -0
- package/dist/src/tools/snapshot-tools.js +479 -0
- package/dist/src/tools/snapshot-tools.js.map +1 -0
- package/dist/src/utils/image-preprocess.d.ts +49 -0
- package/dist/src/utils/image-preprocess.d.ts.map +1 -0
- package/dist/src/utils/image-preprocess.js +322 -0
- package/dist/src/utils/image-preprocess.js.map +1 -0
- package/dist/src/utils/retry.d.ts +21 -0
- package/dist/src/utils/retry.d.ts.map +1 -0
- package/dist/src/utils/retry.js +33 -0
- package/dist/src/utils/retry.js.map +1 -0
- package/dist/src/visual/comparator.d.ts +51 -0
- package/dist/src/visual/comparator.d.ts.map +1 -0
- package/dist/src/visual/comparator.js +119 -0
- package/dist/src/visual/comparator.js.map +1 -0
- package/dist/src/visual/layout-analyzer.d.ts +64 -0
- package/dist/src/visual/layout-analyzer.d.ts.map +1 -0
- package/dist/src/visual/layout-analyzer.js +198 -0
- package/dist/src/visual/layout-analyzer.js.map +1 -0
- package/dist/src/visual/screenshot.d.ts +17 -0
- package/dist/src/visual/screenshot.d.ts.map +1 -0
- package/dist/src/visual/screenshot.js +39 -0
- package/dist/src/visual/screenshot.js.map +1 -0
- package/docs/figma-workflow.md +289 -0
- package/docs/setup-guide.md +360 -0
- package/docs/tool-reference.md +622 -0
- package/package.json +57 -0
- package/src/config/devlens-config.ts +76 -0
- package/src/index.ts +5 -0
- package/src/metro/cdp-client.ts +160 -0
- package/src/metro/log-collector.ts +137 -0
- package/src/metro/metro-bridge.ts +307 -0
- package/src/metro/network-inspector.ts +134 -0
- package/src/platform/android/adb.ts +200 -0
- package/src/platform/android/android-device.ts +116 -0
- package/src/platform/android/ui-automator.ts +141 -0
- package/src/platform/device-manager.ts +229 -0
- package/src/platform/device.ts +110 -0
- package/src/platform/ios/accessibility.ts +189 -0
- package/src/platform/ios/ios-device.ts +116 -0
- package/src/platform/ios/simctl.ts +244 -0
- package/src/server.ts +228 -0
- package/src/snapshot/formatter.ts +102 -0
- package/src/snapshot/ref-registry.ts +230 -0
- package/src/snapshot/snapshot-differ.ts +220 -0
- package/src/tools/app-tools.ts +111 -0
- package/src/tools/device-tools.ts +96 -0
- package/src/tools/ds-tools.ts +395 -0
- package/src/tools/interaction-tools.ts +467 -0
- package/src/tools/metro-tools.ts +320 -0
- package/src/tools/navigation-tools.ts +71 -0
- package/src/tools/screenshot-tools.ts +698 -0
- package/src/tools/snapshot-tools.ts +585 -0
- package/src/utils/image-preprocess.ts +430 -0
- package/src/utils/retry.ts +51 -0
- package/src/visual/comparator.ts +191 -0
- package/src/visual/layout-analyzer.ts +283 -0
- package/src/visual/screenshot.ts +49 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AndroidDevice = void 0;
|
|
4
|
+
const adb_js_1 = require("./adb.js");
|
|
5
|
+
const ui_automator_js_1 = require("./ui-automator.js");
|
|
6
|
+
// Android key codes
|
|
7
|
+
const KEY_HOME = 3;
|
|
8
|
+
const KEY_BACK = 4;
|
|
9
|
+
const KEY_ENTER = 66;
|
|
10
|
+
class AndroidDevice {
|
|
11
|
+
adb;
|
|
12
|
+
constructor(deviceId) {
|
|
13
|
+
this.adb = new adb_js_1.Adb(deviceId);
|
|
14
|
+
}
|
|
15
|
+
async getInfo() {
|
|
16
|
+
const [screenSize, orientation, version, name] = await Promise.all([
|
|
17
|
+
this.adb.getScreenSize(),
|
|
18
|
+
this.adb.getOrientation(),
|
|
19
|
+
this.adb.getAndroidVersion(),
|
|
20
|
+
this.adb.getDeviceName(),
|
|
21
|
+
]);
|
|
22
|
+
return {
|
|
23
|
+
id: this.adb.deviceId,
|
|
24
|
+
name,
|
|
25
|
+
platform: "android",
|
|
26
|
+
osVersion: version,
|
|
27
|
+
screenWidth: screenSize.width,
|
|
28
|
+
screenHeight: screenSize.height,
|
|
29
|
+
orientation: orientation === 0 || orientation === 2 ? "portrait" : "landscape",
|
|
30
|
+
isBooted: true,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
async getSnapshot() {
|
|
34
|
+
const xml = await this.adb.dumpUiHierarchy();
|
|
35
|
+
return (0, ui_automator_js_1.parseUiAutomatorXml)(xml);
|
|
36
|
+
}
|
|
37
|
+
async takeScreenshot() {
|
|
38
|
+
return this.adb.screenshot();
|
|
39
|
+
}
|
|
40
|
+
async tap(x, y) {
|
|
41
|
+
await this.adb.tap(x, y);
|
|
42
|
+
}
|
|
43
|
+
async doubleTap(x, y) {
|
|
44
|
+
await this.adb.tap(x, y);
|
|
45
|
+
await sleep(100);
|
|
46
|
+
await this.adb.tap(x, y);
|
|
47
|
+
}
|
|
48
|
+
async longPress(x, y, durationMs = 1000) {
|
|
49
|
+
await this.adb.longPress(x, y, durationMs);
|
|
50
|
+
}
|
|
51
|
+
async typeText(text) {
|
|
52
|
+
await this.adb.typeText(text);
|
|
53
|
+
}
|
|
54
|
+
async swipe(startX, startY, endX, endY, durationMs = 300) {
|
|
55
|
+
await this.adb.swipe(startX, startY, endX, endY, durationMs);
|
|
56
|
+
}
|
|
57
|
+
async pressButton(button) {
|
|
58
|
+
const keyMap = {
|
|
59
|
+
home: KEY_HOME,
|
|
60
|
+
back: KEY_BACK,
|
|
61
|
+
enter: KEY_ENTER,
|
|
62
|
+
};
|
|
63
|
+
await this.adb.keyEvent(keyMap[button]);
|
|
64
|
+
}
|
|
65
|
+
async setOrientation(orientation) {
|
|
66
|
+
const rotation = orientation === "portrait" ? 0 : 1;
|
|
67
|
+
await this.adb.setRotation(rotation);
|
|
68
|
+
}
|
|
69
|
+
async launchApp(appId) {
|
|
70
|
+
await this.adb.launchApp(appId);
|
|
71
|
+
}
|
|
72
|
+
async terminateApp(appId) {
|
|
73
|
+
await this.adb.terminateApp(appId);
|
|
74
|
+
}
|
|
75
|
+
async installApp(path) {
|
|
76
|
+
await this.adb.installApp(path);
|
|
77
|
+
}
|
|
78
|
+
async listApps() {
|
|
79
|
+
const packages = await this.adb.listPackages();
|
|
80
|
+
return packages.map((pkg) => ({
|
|
81
|
+
appId: pkg,
|
|
82
|
+
name: pkg,
|
|
83
|
+
isRunning: false, // Would need `ps` to determine
|
|
84
|
+
}));
|
|
85
|
+
}
|
|
86
|
+
async openUrl(url) {
|
|
87
|
+
await this.adb.openUrl(url);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
exports.AndroidDevice = AndroidDevice;
|
|
91
|
+
function sleep(ms) {
|
|
92
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=android-device.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"android-device.js","sourceRoot":"","sources":["../../../../src/platform/android/android-device.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { SnapshotNode } from "../device.js";
|
|
2
|
+
/**
|
|
3
|
+
* Parses Android UI Automator XML dump into SnapshotNode tree.
|
|
4
|
+
*
|
|
5
|
+
* The XML format from `uiautomator dump` looks like:
|
|
6
|
+
* <hierarchy rotation="0">
|
|
7
|
+
* <node index="0" text="" resource-id="..." class="android.widget.FrameLayout"
|
|
8
|
+
* package="com.example" content-desc="" checkable="false" checked="false"
|
|
9
|
+
* clickable="false" enabled="true" focusable="false" focused="false"
|
|
10
|
+
* scrollable="false" long-clickable="false" password="false" selected="false"
|
|
11
|
+
* bounds="[0,0][1080,2340]">
|
|
12
|
+
* <node ... />
|
|
13
|
+
* </node>
|
|
14
|
+
* </hierarchy>
|
|
15
|
+
*/
|
|
16
|
+
export declare function parseUiAutomatorXml(xml: string): SnapshotNode;
|
|
17
|
+
//# sourceMappingURL=ui-automator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-automator.d.ts","sourceRoot":"","sources":["../../../../src/platform/android/ui-automator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAU,MAAM,cAAc,CAAC;AAEzD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,CA+B7D"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseUiAutomatorXml = parseUiAutomatorXml;
|
|
4
|
+
const fast_xml_parser_1 = require("fast-xml-parser");
|
|
5
|
+
/**
|
|
6
|
+
* Parses Android UI Automator XML dump into SnapshotNode tree.
|
|
7
|
+
*
|
|
8
|
+
* The XML format from `uiautomator dump` looks like:
|
|
9
|
+
* <hierarchy rotation="0">
|
|
10
|
+
* <node index="0" text="" resource-id="..." class="android.widget.FrameLayout"
|
|
11
|
+
* package="com.example" content-desc="" checkable="false" checked="false"
|
|
12
|
+
* clickable="false" enabled="true" focusable="false" focused="false"
|
|
13
|
+
* scrollable="false" long-clickable="false" password="false" selected="false"
|
|
14
|
+
* bounds="[0,0][1080,2340]">
|
|
15
|
+
* <node ... />
|
|
16
|
+
* </node>
|
|
17
|
+
* </hierarchy>
|
|
18
|
+
*/
|
|
19
|
+
function parseUiAutomatorXml(xml) {
|
|
20
|
+
const parser = new fast_xml_parser_1.XMLParser({
|
|
21
|
+
ignoreAttributes: false,
|
|
22
|
+
attributeNamePrefix: "@_",
|
|
23
|
+
allowBooleanAttributes: true,
|
|
24
|
+
isArray: (name) => name === "node",
|
|
25
|
+
});
|
|
26
|
+
const parsed = parser.parse(xml);
|
|
27
|
+
const hierarchy = parsed.hierarchy;
|
|
28
|
+
if (!hierarchy || !hierarchy.node) {
|
|
29
|
+
throw new Error("Invalid UI Automator dump: no hierarchy or root node found");
|
|
30
|
+
}
|
|
31
|
+
const rootNodes = Array.isArray(hierarchy.node)
|
|
32
|
+
? hierarchy.node
|
|
33
|
+
: [hierarchy.node];
|
|
34
|
+
// Create a virtual root that contains all top-level nodes
|
|
35
|
+
return {
|
|
36
|
+
type: "hierarchy",
|
|
37
|
+
text: undefined,
|
|
38
|
+
label: undefined,
|
|
39
|
+
description: undefined,
|
|
40
|
+
bounds: { x: 0, y: 0, width: 0, height: 0 },
|
|
41
|
+
interactive: false,
|
|
42
|
+
children: rootNodes.map(convertNode),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function convertNode(node) {
|
|
46
|
+
const bounds = parseBounds(node["@_bounds"] || "[0,0][0,0]");
|
|
47
|
+
const text = node["@_text"] || undefined;
|
|
48
|
+
const description = node["@_content-desc"] || undefined;
|
|
49
|
+
const resourceId = node["@_resource-id"] || undefined;
|
|
50
|
+
const className = node["@_class"] || "unknown";
|
|
51
|
+
const clickable = node["@_clickable"] === "true";
|
|
52
|
+
const longClickable = node["@_long-clickable"] === "true";
|
|
53
|
+
const checkable = node["@_checkable"] === "true";
|
|
54
|
+
const scrollable = node["@_scrollable"] === "true";
|
|
55
|
+
const focusable = node["@_focusable"] === "true";
|
|
56
|
+
const interactive = clickable || longClickable || checkable || scrollable || focusable;
|
|
57
|
+
const children = [];
|
|
58
|
+
if (node.node) {
|
|
59
|
+
const childNodes = Array.isArray(node.node) ? node.node : [node.node];
|
|
60
|
+
for (const child of childNodes) {
|
|
61
|
+
children.push(convertNode(child));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
type: simplifyClassName(className),
|
|
66
|
+
text: text && text.length > 0 ? text : undefined,
|
|
67
|
+
label: description && description.length > 0 ? description : undefined,
|
|
68
|
+
description: description && description.length > 0 ? description : undefined,
|
|
69
|
+
bounds,
|
|
70
|
+
interactive,
|
|
71
|
+
focused: node["@_focused"] === "true",
|
|
72
|
+
enabled: node["@_enabled"] === "true",
|
|
73
|
+
visible: bounds.width > 0 && bounds.height > 0,
|
|
74
|
+
value: getElementValue(node),
|
|
75
|
+
resourceId: resourceId && resourceId.length > 0 ? resourceId : undefined,
|
|
76
|
+
children,
|
|
77
|
+
rawAttributes: extractRawAttributes(node),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/** Parse Android bounds format "[left,top][right,bottom]" into Bounds */
|
|
81
|
+
function parseBounds(boundsStr) {
|
|
82
|
+
const match = boundsStr.match(/\[(\d+),(\d+)\]\[(\d+),(\d+)\]/);
|
|
83
|
+
if (!match) {
|
|
84
|
+
return { x: 0, y: 0, width: 0, height: 0 };
|
|
85
|
+
}
|
|
86
|
+
const left = parseInt(match[1]);
|
|
87
|
+
const top = parseInt(match[2]);
|
|
88
|
+
const right = parseInt(match[3]);
|
|
89
|
+
const bottom = parseInt(match[4]);
|
|
90
|
+
return {
|
|
91
|
+
x: left,
|
|
92
|
+
y: top,
|
|
93
|
+
width: right - left,
|
|
94
|
+
height: bottom - top,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/** Simplify Android class names for readability */
|
|
98
|
+
function simplifyClassName(className) {
|
|
99
|
+
// "android.widget.TextView" → "TextView"
|
|
100
|
+
// "android.view.View" → "View"
|
|
101
|
+
const parts = className.split(".");
|
|
102
|
+
return parts[parts.length - 1];
|
|
103
|
+
}
|
|
104
|
+
/** Extract element value (for checkboxes, switches, inputs, etc.) */
|
|
105
|
+
function getElementValue(node) {
|
|
106
|
+
if (node["@_checked"] === "true")
|
|
107
|
+
return "checked";
|
|
108
|
+
if (node["@_checked"] === "false" && node["@_checkable"] === "true")
|
|
109
|
+
return "unchecked";
|
|
110
|
+
if (node["@_selected"] === "true")
|
|
111
|
+
return "selected";
|
|
112
|
+
if (node["@_password"] === "true")
|
|
113
|
+
return "•••";
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
/** Extract all raw attributes for debugging */
|
|
117
|
+
function extractRawAttributes(node) {
|
|
118
|
+
const attrs = {};
|
|
119
|
+
for (const key of Object.keys(node)) {
|
|
120
|
+
if (key.startsWith("@_")) {
|
|
121
|
+
attrs[key.replace("@_", "")] = String(node[key]);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return attrs;
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=ui-automator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-automator.js","sourceRoot":"","sources":["../../../../src/platform/android/ui-automator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Device } from "./device.js";
|
|
2
|
+
export interface DiscoveredDevice {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
platform: "ios" | "android";
|
|
6
|
+
osVersion: string;
|
|
7
|
+
isBooted: boolean;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* DeviceManager discovers running iOS Simulators and Android Emulators,
|
|
11
|
+
* and creates Device instances for them.
|
|
12
|
+
*/
|
|
13
|
+
export declare class DeviceManager {
|
|
14
|
+
private activeDevice;
|
|
15
|
+
private activeDeviceId;
|
|
16
|
+
private activePlatform;
|
|
17
|
+
discoverDevices(): Promise<DiscoveredDevice[]>;
|
|
18
|
+
getDevice(deviceId?: string): Promise<Device>;
|
|
19
|
+
/**
|
|
20
|
+
* Quick health check to verify a cached device is still alive.
|
|
21
|
+
* Returns false if the device is disconnected or no longer booted.
|
|
22
|
+
*/
|
|
23
|
+
private checkDeviceHealth;
|
|
24
|
+
private discoverAndroidDevices;
|
|
25
|
+
private discoverIosDevices;
|
|
26
|
+
private getAdbPath;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=device-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-manager.d.ts","sourceRoot":"","sources":["../../../src/platform/device-manager.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAc,MAAM,aAAa,CAAC;AAMtD,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,KAAK,GAAG,SAAS,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;GAGG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,cAAc,CAAkC;IAElD,eAAe,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAkB9C,SAAS,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAqDnD;;;OAGG;YACW,iBAAiB;YAgCjB,sBAAsB;YAkDtB,kBAAkB;IAsChC,OAAO,CAAC,UAAU;CAQnB"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DeviceManager = void 0;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
const util_1 = require("util");
|
|
6
|
+
const android_device_js_1 = require("./android/android-device.js");
|
|
7
|
+
const ios_device_js_1 = require("./ios/ios-device.js");
|
|
8
|
+
const execFileAsync = (0, util_1.promisify)(child_process_1.execFile);
|
|
9
|
+
/**
|
|
10
|
+
* DeviceManager discovers running iOS Simulators and Android Emulators,
|
|
11
|
+
* and creates Device instances for them.
|
|
12
|
+
*/
|
|
13
|
+
class DeviceManager {
|
|
14
|
+
activeDevice = null;
|
|
15
|
+
activeDeviceId = null;
|
|
16
|
+
activePlatform = null;
|
|
17
|
+
async discoverDevices() {
|
|
18
|
+
const devices = [];
|
|
19
|
+
const [androidDevices, iosDevices] = await Promise.allSettled([
|
|
20
|
+
this.discoverAndroidDevices(),
|
|
21
|
+
this.discoverIosDevices(),
|
|
22
|
+
]);
|
|
23
|
+
if (androidDevices.status === "fulfilled") {
|
|
24
|
+
devices.push(...androidDevices.value);
|
|
25
|
+
}
|
|
26
|
+
if (iosDevices.status === "fulfilled") {
|
|
27
|
+
devices.push(...iosDevices.value);
|
|
28
|
+
}
|
|
29
|
+
return devices;
|
|
30
|
+
}
|
|
31
|
+
async getDevice(deviceId) {
|
|
32
|
+
// If we have a cached device and it matches, verify it's still healthy
|
|
33
|
+
if (this.activeDevice &&
|
|
34
|
+
this.activeDeviceId &&
|
|
35
|
+
(!deviceId || deviceId === this.activeDeviceId)) {
|
|
36
|
+
const isHealthy = await this.checkDeviceHealth(this.activeDeviceId, this.activePlatform);
|
|
37
|
+
if (isHealthy) {
|
|
38
|
+
return this.activeDevice;
|
|
39
|
+
}
|
|
40
|
+
// Device is stale — clear cache and rediscover
|
|
41
|
+
this.activeDevice = null;
|
|
42
|
+
this.activeDeviceId = null;
|
|
43
|
+
this.activePlatform = null;
|
|
44
|
+
}
|
|
45
|
+
const devices = await this.discoverDevices();
|
|
46
|
+
const bootedDevices = devices.filter((d) => d.isBooted);
|
|
47
|
+
if (bootedDevices.length === 0) {
|
|
48
|
+
throw new Error("No running simulators or emulators found. Please start a device first.");
|
|
49
|
+
}
|
|
50
|
+
let target;
|
|
51
|
+
if (deviceId) {
|
|
52
|
+
const found = bootedDevices.find((d) => d.id === deviceId);
|
|
53
|
+
if (!found) {
|
|
54
|
+
throw new Error(`Device "${deviceId}" not found or not running. Available: ${bootedDevices.map((d) => d.id).join(", ")}`);
|
|
55
|
+
}
|
|
56
|
+
target = found;
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
target = bootedDevices[0];
|
|
60
|
+
}
|
|
61
|
+
if (target.platform === "android") {
|
|
62
|
+
this.activeDevice = new android_device_js_1.AndroidDevice(target.id);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
this.activeDevice = new ios_device_js_1.IosDevice(target.id);
|
|
66
|
+
}
|
|
67
|
+
this.activeDeviceId = target.id;
|
|
68
|
+
this.activePlatform = target.platform;
|
|
69
|
+
return this.activeDevice;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Quick health check to verify a cached device is still alive.
|
|
73
|
+
* Returns false if the device is disconnected or no longer booted.
|
|
74
|
+
*/
|
|
75
|
+
async checkDeviceHealth(deviceId, platform) {
|
|
76
|
+
try {
|
|
77
|
+
if (platform === "android") {
|
|
78
|
+
const adbPath = this.getAdbPath();
|
|
79
|
+
const { stdout } = await execFileAsync(adbPath, ["-s", deviceId, "get-state"], {
|
|
80
|
+
timeout: 5000,
|
|
81
|
+
});
|
|
82
|
+
return stdout.trim() === "device";
|
|
83
|
+
}
|
|
84
|
+
else if (platform === "ios") {
|
|
85
|
+
const { stdout } = await execFileAsync("xcrun", ["simctl", "list", "devices", "--json"], { timeout: 5000 });
|
|
86
|
+
const data = JSON.parse(stdout);
|
|
87
|
+
for (const deviceList of Object.values(data.devices)) {
|
|
88
|
+
const found = deviceList.find((d) => d.udid === deviceId);
|
|
89
|
+
if (found)
|
|
90
|
+
return found.state === "Booted";
|
|
91
|
+
}
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async discoverAndroidDevices() {
|
|
101
|
+
try {
|
|
102
|
+
const adbPath = this.getAdbPath();
|
|
103
|
+
const { stdout } = await execFileAsync(adbPath, ["devices", "-l"]);
|
|
104
|
+
const devices = [];
|
|
105
|
+
for (const line of stdout.split("\n").slice(1)) {
|
|
106
|
+
const trimmed = line.trim();
|
|
107
|
+
if (!trimmed || trimmed === "")
|
|
108
|
+
continue;
|
|
109
|
+
const parts = trimmed.split(/\s+/);
|
|
110
|
+
const id = parts[0];
|
|
111
|
+
const status = parts[1];
|
|
112
|
+
if (status !== "device")
|
|
113
|
+
continue;
|
|
114
|
+
// Extract model name from properties
|
|
115
|
+
const modelMatch = trimmed.match(/model:(\S+)/);
|
|
116
|
+
const name = modelMatch ? modelMatch[1] : id;
|
|
117
|
+
// Get Android version
|
|
118
|
+
let osVersion = "unknown";
|
|
119
|
+
try {
|
|
120
|
+
const { stdout: versionOut } = await execFileAsync(adbPath, [
|
|
121
|
+
"-s",
|
|
122
|
+
id,
|
|
123
|
+
"shell",
|
|
124
|
+
"getprop",
|
|
125
|
+
"ro.build.version.release",
|
|
126
|
+
]);
|
|
127
|
+
osVersion = versionOut.trim();
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
// Ignore version fetch errors
|
|
131
|
+
}
|
|
132
|
+
devices.push({
|
|
133
|
+
id,
|
|
134
|
+
name,
|
|
135
|
+
platform: "android",
|
|
136
|
+
osVersion,
|
|
137
|
+
isBooted: true,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
return devices;
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return [];
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
async discoverIosDevices() {
|
|
147
|
+
try {
|
|
148
|
+
const { stdout } = await execFileAsync("xcrun", [
|
|
149
|
+
"simctl",
|
|
150
|
+
"list",
|
|
151
|
+
"devices",
|
|
152
|
+
"--json",
|
|
153
|
+
]);
|
|
154
|
+
const data = JSON.parse(stdout);
|
|
155
|
+
const devices = [];
|
|
156
|
+
for (const [runtime, deviceList] of Object.entries(data.devices)) {
|
|
157
|
+
const osVersion = runtime.replace(/^com\.apple\.CoreSimulator\.SimRuntime\./, "").replace(/-/g, ".").replace(/^iOS\./, "");
|
|
158
|
+
for (const device of deviceList) {
|
|
159
|
+
if (device.state === "Booted") {
|
|
160
|
+
devices.push({
|
|
161
|
+
id: device.udid,
|
|
162
|
+
name: device.name,
|
|
163
|
+
platform: "ios",
|
|
164
|
+
osVersion,
|
|
165
|
+
isBooted: true,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return devices;
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
return [];
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
getAdbPath() {
|
|
177
|
+
const androidHome = process.env.ANDROID_HOME || process.env.ANDROID_SDK_ROOT;
|
|
178
|
+
if (androidHome) {
|
|
179
|
+
return `${androidHome}/platform-tools/adb`;
|
|
180
|
+
}
|
|
181
|
+
return "adb";
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
exports.DeviceManager = DeviceManager;
|
|
185
|
+
//# sourceMappingURL=device-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-manager.js","sourceRoot":"","sources":["../../../src/platform/device-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Abstract Device interface — the core abstraction that iOS and Android drivers implement.
|
|
3
|
+
* Every MCP tool delegates to this interface. Tools never know which platform they're on.
|
|
4
|
+
*/
|
|
5
|
+
export interface DeviceInfo {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
platform: "ios" | "android";
|
|
9
|
+
osVersion: string;
|
|
10
|
+
screenWidth: number;
|
|
11
|
+
screenHeight: number;
|
|
12
|
+
orientation: "portrait" | "landscape";
|
|
13
|
+
isBooted: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface AppInfo {
|
|
16
|
+
appId: string;
|
|
17
|
+
name: string;
|
|
18
|
+
isRunning: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface Bounds {
|
|
21
|
+
x: number;
|
|
22
|
+
y: number;
|
|
23
|
+
width: number;
|
|
24
|
+
height: number;
|
|
25
|
+
}
|
|
26
|
+
export interface SnapshotNode {
|
|
27
|
+
/** Element class/type (e.g., "android.widget.TextView", "UILabel") */
|
|
28
|
+
type: string;
|
|
29
|
+
/** Text content of the element */
|
|
30
|
+
text?: string;
|
|
31
|
+
/** Accessibility label */
|
|
32
|
+
label?: string;
|
|
33
|
+
/** Content description / accessibility hint */
|
|
34
|
+
description?: string;
|
|
35
|
+
/** Bounding box in screen coordinates */
|
|
36
|
+
bounds: Bounds;
|
|
37
|
+
/** Whether the element is interactive (clickable, tappable) */
|
|
38
|
+
interactive: boolean;
|
|
39
|
+
/** Whether the element is focused */
|
|
40
|
+
focused?: boolean;
|
|
41
|
+
/** Whether the element is enabled */
|
|
42
|
+
enabled?: boolean;
|
|
43
|
+
/** Whether the element is visible */
|
|
44
|
+
visible?: boolean;
|
|
45
|
+
/** Element value (for inputs, toggles, etc.) */
|
|
46
|
+
value?: string;
|
|
47
|
+
/** Resource ID (Android) or accessibility identifier (iOS) */
|
|
48
|
+
resourceId?: string;
|
|
49
|
+
/** Child elements */
|
|
50
|
+
children: SnapshotNode[];
|
|
51
|
+
/** Platform-specific raw attributes (for debugging) */
|
|
52
|
+
rawAttributes?: Record<string, string>;
|
|
53
|
+
}
|
|
54
|
+
export interface Device {
|
|
55
|
+
/** Get device information */
|
|
56
|
+
getInfo(): Promise<DeviceInfo>;
|
|
57
|
+
/** Get the accessibility tree of the current screen */
|
|
58
|
+
getSnapshot(): Promise<SnapshotNode>;
|
|
59
|
+
/** Take a screenshot, returns PNG buffer */
|
|
60
|
+
takeScreenshot(): Promise<Buffer>;
|
|
61
|
+
/** Tap at screen coordinates */
|
|
62
|
+
tap(x: number, y: number): Promise<void>;
|
|
63
|
+
/** Double-tap at screen coordinates */
|
|
64
|
+
doubleTap(x: number, y: number): Promise<void>;
|
|
65
|
+
/** Long press at screen coordinates */
|
|
66
|
+
longPress(x: number, y: number, durationMs?: number): Promise<void>;
|
|
67
|
+
/** Type text (assumes an input is already focused) */
|
|
68
|
+
typeText(text: string): Promise<void>;
|
|
69
|
+
/** Swipe from one point to another */
|
|
70
|
+
swipe(startX: number, startY: number, endX: number, endY: number, durationMs?: number): Promise<void>;
|
|
71
|
+
/** Press a hardware/system button */
|
|
72
|
+
pressButton(button: "home" | "back" | "enter"): Promise<void>;
|
|
73
|
+
/** Set device orientation */
|
|
74
|
+
setOrientation(orientation: "portrait" | "landscape"): Promise<void>;
|
|
75
|
+
/** Launch an app by its bundle/package ID */
|
|
76
|
+
launchApp(appId: string): Promise<void>;
|
|
77
|
+
/** Terminate a running app */
|
|
78
|
+
terminateApp(appId: string): Promise<void>;
|
|
79
|
+
/** Install an app from a file path */
|
|
80
|
+
installApp(path: string): Promise<void>;
|
|
81
|
+
/** List installed apps */
|
|
82
|
+
listApps(): Promise<AppInfo[]>;
|
|
83
|
+
/** Open a URL (deep link or web URL) */
|
|
84
|
+
openUrl(url: string): Promise<void>;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=device.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device.d.ts","sourceRoot":"","sources":["../../../src/platform/device.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Abstract Device interface — the core abstraction that iOS and Android drivers implement.
|
|
4
|
+
* Every MCP tool delegates to this interface. Tools never know which platform they're on.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
//# sourceMappingURL=device.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device.js","sourceRoot":"","sources":["../../../src/platform/device.ts"],"names":[],"mappings":";AAAA;;;GAGG"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { SnapshotNode } from "../device.js";
|
|
2
|
+
/**
|
|
3
|
+
* Parses iOS accessibility output from `simctl ui` into SnapshotNode tree.
|
|
4
|
+
*
|
|
5
|
+
* The simctl accessibility output varies by iOS/Xcode version. This parser
|
|
6
|
+
* handles the common formats:
|
|
7
|
+
*
|
|
8
|
+
* Format 1 (structured):
|
|
9
|
+
* Element: UIButton, Label: "Submit", Traits: Button, Frame: {{20, 100}, {340, 44}}
|
|
10
|
+
*
|
|
11
|
+
* Format 2 (XML-like from accessibility inspector):
|
|
12
|
+
* <AXElement type="UIButton" label="Submit" frame="20 100 340 44" ...>
|
|
13
|
+
*
|
|
14
|
+
* When neither format is available, we fall back to a basic tree.
|
|
15
|
+
*/
|
|
16
|
+
export declare function parseIosAccessibilityOutput(output: string): SnapshotNode;
|
|
17
|
+
//# sourceMappingURL=accessibility.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accessibility.d.ts","sourceRoot":"","sources":["../../../../src/platform/ios/accessibility.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAU,MAAM,cAAc,CAAC;AAEzD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,MAAM,GACb,YAAY,CAad"}
|