@metamask/device-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/README.md +266 -0
- package/dist/backends/adb-backend.cjs +442 -0
- package/dist/backends/adb-backend.cjs.map +1 -0
- package/dist/backends/adb-backend.d.cts +35 -0
- package/dist/backends/adb-backend.d.cts.map +1 -0
- package/dist/backends/adb-backend.d.mts +35 -0
- package/dist/backends/adb-backend.d.mts.map +1 -0
- package/dist/backends/adb-backend.mjs +436 -0
- package/dist/backends/adb-backend.mjs.map +1 -0
- package/dist/backends/appium-backend.cjs +518 -0
- package/dist/backends/appium-backend.cjs.map +1 -0
- package/dist/backends/appium-backend.d.cts +36 -0
- package/dist/backends/appium-backend.d.cts.map +1 -0
- package/dist/backends/appium-backend.d.mts +36 -0
- package/dist/backends/appium-backend.d.mts.map +1 -0
- package/dist/backends/appium-backend.mjs +512 -0
- package/dist/backends/appium-backend.mjs.map +1 -0
- package/dist/backends/idb-backend.cjs +442 -0
- package/dist/backends/idb-backend.cjs.map +1 -0
- package/dist/backends/idb-backend.d.cts +35 -0
- package/dist/backends/idb-backend.d.cts.map +1 -0
- package/dist/backends/idb-backend.d.mts +35 -0
- package/dist/backends/idb-backend.d.mts.map +1 -0
- package/dist/backends/idb-backend.mjs +436 -0
- package/dist/backends/idb-backend.mjs.map +1 -0
- package/dist/backends/index.cjs +134 -0
- package/dist/backends/index.cjs.map +1 -0
- package/dist/backends/index.d.cts +16 -0
- package/dist/backends/index.d.cts.map +1 -0
- package/dist/backends/index.d.mts +16 -0
- package/dist/backends/index.d.mts.map +1 -0
- package/dist/backends/index.mjs +126 -0
- package/dist/backends/index.mjs.map +1 -0
- package/dist/backends/session-file.cjs +62 -0
- package/dist/backends/session-file.cjs.map +1 -0
- package/dist/backends/session-file.d.cts +24 -0
- package/dist/backends/session-file.d.cts.map +1 -0
- package/dist/backends/session-file.d.mts +24 -0
- package/dist/backends/session-file.d.mts.map +1 -0
- package/dist/backends/session-file.mjs +59 -0
- package/dist/backends/session-file.mjs.map +1 -0
- package/dist/backends/types.cjs +3 -0
- package/dist/backends/types.cjs.map +1 -0
- package/dist/backends/types.d.cts +93 -0
- package/dist/backends/types.d.cts.map +1 -0
- package/dist/backends/types.d.mts +93 -0
- package/dist/backends/types.d.mts.map +1 -0
- package/dist/backends/types.mjs +2 -0
- package/dist/backends/types.mjs.map +1 -0
- package/dist/backends/webdriver-client.cjs +167 -0
- package/dist/backends/webdriver-client.cjs.map +1 -0
- package/dist/backends/webdriver-client.d.cts +55 -0
- package/dist/backends/webdriver-client.d.cts.map +1 -0
- package/dist/backends/webdriver-client.d.mts +55 -0
- package/dist/backends/webdriver-client.d.mts.map +1 -0
- package/dist/backends/webdriver-client.mjs +162 -0
- package/dist/backends/webdriver-client.mjs.map +1 -0
- package/dist/cli/device-mcp.cjs +20 -0
- package/dist/cli/device-mcp.cjs.map +1 -0
- package/dist/cli/device-mcp.d.cts +3 -0
- package/dist/cli/device-mcp.d.cts.map +1 -0
- package/dist/cli/device-mcp.d.mts +3 -0
- package/dist/cli/device-mcp.d.mts.map +1 -0
- package/dist/cli/device-mcp.mjs +18 -0
- package/dist/cli/device-mcp.mjs.map +1 -0
- package/dist/index.cjs +9 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +5 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +5 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +3 -0
- package/dist/index.mjs.map +1 -0
- package/dist/server.cjs +43 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +4 -0
- package/dist/server.d.cts.map +1 -0
- package/dist/server.d.mts +4 -0
- package/dist/server.d.mts.map +1 -0
- package/dist/server.mjs +40 -0
- package/dist/server.mjs.map +1 -0
- package/dist/tools/alert-text.cjs +25 -0
- package/dist/tools/alert-text.cjs.map +1 -0
- package/dist/tools/alert-text.d.cts +4 -0
- package/dist/tools/alert-text.d.cts.map +1 -0
- package/dist/tools/alert-text.d.mts +4 -0
- package/dist/tools/alert-text.d.mts.map +1 -0
- package/dist/tools/alert-text.mjs +22 -0
- package/dist/tools/alert-text.mjs.map +1 -0
- package/dist/tools/app-state.cjs +40 -0
- package/dist/tools/app-state.cjs.map +1 -0
- package/dist/tools/app-state.d.cts +4 -0
- package/dist/tools/app-state.d.cts.map +1 -0
- package/dist/tools/app-state.d.mts +4 -0
- package/dist/tools/app-state.d.mts.map +1 -0
- package/dist/tools/app-state.mjs +37 -0
- package/dist/tools/app-state.mjs.map +1 -0
- package/dist/tools/clipboard.cjs +42 -0
- package/dist/tools/clipboard.cjs.map +1 -0
- package/dist/tools/clipboard.d.cts +4 -0
- package/dist/tools/clipboard.d.cts.map +1 -0
- package/dist/tools/clipboard.d.mts +4 -0
- package/dist/tools/clipboard.d.mts.map +1 -0
- package/dist/tools/clipboard.mjs +39 -0
- package/dist/tools/clipboard.mjs.map +1 -0
- package/dist/tools/close-app.cjs +25 -0
- package/dist/tools/close-app.cjs.map +1 -0
- package/dist/tools/close-app.d.cts +4 -0
- package/dist/tools/close-app.d.cts.map +1 -0
- package/dist/tools/close-app.d.mts +4 -0
- package/dist/tools/close-app.d.mts.map +1 -0
- package/dist/tools/close-app.mjs +22 -0
- package/dist/tools/close-app.mjs.map +1 -0
- package/dist/tools/context.cjs +52 -0
- package/dist/tools/context.cjs.map +1 -0
- package/dist/tools/context.d.cts +4 -0
- package/dist/tools/context.d.cts.map +1 -0
- package/dist/tools/context.d.mts +4 -0
- package/dist/tools/context.d.mts.map +1 -0
- package/dist/tools/context.mjs +49 -0
- package/dist/tools/context.mjs.map +1 -0
- package/dist/tools/device-info.cjs +34 -0
- package/dist/tools/device-info.cjs.map +1 -0
- package/dist/tools/device-info.d.cts +4 -0
- package/dist/tools/device-info.d.cts.map +1 -0
- package/dist/tools/device-info.d.mts +4 -0
- package/dist/tools/device-info.d.mts.map +1 -0
- package/dist/tools/device-info.mjs +31 -0
- package/dist/tools/device-info.mjs.map +1 -0
- package/dist/tools/dismiss-alert.cjs +34 -0
- package/dist/tools/dismiss-alert.cjs.map +1 -0
- package/dist/tools/dismiss-alert.d.cts +4 -0
- package/dist/tools/dismiss-alert.d.cts.map +1 -0
- package/dist/tools/dismiss-alert.d.mts +4 -0
- package/dist/tools/dismiss-alert.d.mts.map +1 -0
- package/dist/tools/dismiss-alert.mjs +31 -0
- package/dist/tools/dismiss-alert.mjs.map +1 -0
- package/dist/tools/dismiss-keyboard.cjs +23 -0
- package/dist/tools/dismiss-keyboard.cjs.map +1 -0
- package/dist/tools/dismiss-keyboard.d.cts +4 -0
- package/dist/tools/dismiss-keyboard.d.cts.map +1 -0
- package/dist/tools/dismiss-keyboard.d.mts +4 -0
- package/dist/tools/dismiss-keyboard.d.mts.map +1 -0
- package/dist/tools/dismiss-keyboard.mjs +20 -0
- package/dist/tools/dismiss-keyboard.mjs.map +1 -0
- package/dist/tools/generate-locators.cjs +33 -0
- package/dist/tools/generate-locators.cjs.map +1 -0
- package/dist/tools/generate-locators.d.cts +4 -0
- package/dist/tools/generate-locators.d.cts.map +1 -0
- package/dist/tools/generate-locators.d.mts +4 -0
- package/dist/tools/generate-locators.d.mts.map +1 -0
- package/dist/tools/generate-locators.mjs +30 -0
- package/dist/tools/generate-locators.mjs.map +1 -0
- package/dist/tools/index.cjs +50 -0
- package/dist/tools/index.cjs.map +1 -0
- package/dist/tools/index.d.cts +24 -0
- package/dist/tools/index.d.cts.map +1 -0
- package/dist/tools/index.d.mts +24 -0
- package/dist/tools/index.d.mts.map +1 -0
- package/dist/tools/index.mjs +24 -0
- package/dist/tools/index.mjs.map +1 -0
- package/dist/tools/logs.cjs +48 -0
- package/dist/tools/logs.cjs.map +1 -0
- package/dist/tools/logs.d.cts +4 -0
- package/dist/tools/logs.d.cts.map +1 -0
- package/dist/tools/logs.d.mts +4 -0
- package/dist/tools/logs.d.mts.map +1 -0
- package/dist/tools/logs.mjs +45 -0
- package/dist/tools/logs.mjs.map +1 -0
- package/dist/tools/long-press.cjs +38 -0
- package/dist/tools/long-press.cjs.map +1 -0
- package/dist/tools/long-press.d.cts +4 -0
- package/dist/tools/long-press.d.cts.map +1 -0
- package/dist/tools/long-press.d.mts +4 -0
- package/dist/tools/long-press.d.mts.map +1 -0
- package/dist/tools/long-press.mjs +35 -0
- package/dist/tools/long-press.mjs.map +1 -0
- package/dist/tools/open-app.cjs +26 -0
- package/dist/tools/open-app.cjs.map +1 -0
- package/dist/tools/open-app.d.cts +4 -0
- package/dist/tools/open-app.d.cts.map +1 -0
- package/dist/tools/open-app.d.mts +4 -0
- package/dist/tools/open-app.d.mts.map +1 -0
- package/dist/tools/open-app.mjs +23 -0
- package/dist/tools/open-app.mjs.map +1 -0
- package/dist/tools/press-button.cjs +30 -0
- package/dist/tools/press-button.cjs.map +1 -0
- package/dist/tools/press-button.d.cts +4 -0
- package/dist/tools/press-button.d.cts.map +1 -0
- package/dist/tools/press-button.d.mts +4 -0
- package/dist/tools/press-button.d.mts.map +1 -0
- package/dist/tools/press-button.mjs +27 -0
- package/dist/tools/press-button.mjs.map +1 -0
- package/dist/tools/screen-recording.cjs +46 -0
- package/dist/tools/screen-recording.cjs.map +1 -0
- package/dist/tools/screen-recording.d.cts +4 -0
- package/dist/tools/screen-recording.d.cts.map +1 -0
- package/dist/tools/screen-recording.d.mts +4 -0
- package/dist/tools/screen-recording.d.mts.map +1 -0
- package/dist/tools/screen-recording.mjs +43 -0
- package/dist/tools/screen-recording.mjs.map +1 -0
- package/dist/tools/screenshot.cjs +42 -0
- package/dist/tools/screenshot.cjs.map +1 -0
- package/dist/tools/screenshot.d.cts +4 -0
- package/dist/tools/screenshot.d.cts.map +1 -0
- package/dist/tools/screenshot.d.mts +4 -0
- package/dist/tools/screenshot.d.mts.map +1 -0
- package/dist/tools/screenshot.mjs +39 -0
- package/dist/tools/screenshot.mjs.map +1 -0
- package/dist/tools/scroll-to-element.cjs +56 -0
- package/dist/tools/scroll-to-element.cjs.map +1 -0
- package/dist/tools/scroll-to-element.d.cts +4 -0
- package/dist/tools/scroll-to-element.d.cts.map +1 -0
- package/dist/tools/scroll-to-element.d.mts +4 -0
- package/dist/tools/scroll-to-element.d.mts.map +1 -0
- package/dist/tools/scroll-to-element.mjs +53 -0
- package/dist/tools/scroll-to-element.mjs.map +1 -0
- package/dist/tools/shared.cjs +11 -0
- package/dist/tools/shared.cjs.map +1 -0
- package/dist/tools/shared.d.cts +8 -0
- package/dist/tools/shared.d.cts.map +1 -0
- package/dist/tools/shared.d.mts +8 -0
- package/dist/tools/shared.d.mts.map +1 -0
- package/dist/tools/shared.mjs +8 -0
- package/dist/tools/shared.mjs.map +1 -0
- package/dist/tools/snapshot.cjs +37 -0
- package/dist/tools/snapshot.cjs.map +1 -0
- package/dist/tools/snapshot.d.cts +4 -0
- package/dist/tools/snapshot.d.cts.map +1 -0
- package/dist/tools/snapshot.d.mts +4 -0
- package/dist/tools/snapshot.d.mts.map +1 -0
- package/dist/tools/snapshot.mjs +34 -0
- package/dist/tools/snapshot.mjs.map +1 -0
- package/dist/tools/swipe.cjs +39 -0
- package/dist/tools/swipe.cjs.map +1 -0
- package/dist/tools/swipe.d.cts +4 -0
- package/dist/tools/swipe.d.cts.map +1 -0
- package/dist/tools/swipe.d.mts +4 -0
- package/dist/tools/swipe.d.mts.map +1 -0
- package/dist/tools/swipe.mjs +36 -0
- package/dist/tools/swipe.mjs.map +1 -0
- package/dist/tools/tap-coordinates.cjs +27 -0
- package/dist/tools/tap-coordinates.cjs.map +1 -0
- package/dist/tools/tap-coordinates.d.cts +4 -0
- package/dist/tools/tap-coordinates.d.cts.map +1 -0
- package/dist/tools/tap-coordinates.d.mts +4 -0
- package/dist/tools/tap-coordinates.d.mts.map +1 -0
- package/dist/tools/tap-coordinates.mjs +24 -0
- package/dist/tools/tap-coordinates.mjs.map +1 -0
- package/dist/tools/tap-element.cjs +46 -0
- package/dist/tools/tap-element.cjs.map +1 -0
- package/dist/tools/tap-element.d.cts +4 -0
- package/dist/tools/tap-element.d.cts.map +1 -0
- package/dist/tools/tap-element.d.mts +4 -0
- package/dist/tools/tap-element.d.mts.map +1 -0
- package/dist/tools/tap-element.mjs +43 -0
- package/dist/tools/tap-element.mjs.map +1 -0
- package/dist/tools/type-text.cjs +32 -0
- package/dist/tools/type-text.cjs.map +1 -0
- package/dist/tools/type-text.d.cts +4 -0
- package/dist/tools/type-text.d.cts.map +1 -0
- package/dist/tools/type-text.d.mts +4 -0
- package/dist/tools/type-text.d.mts.map +1 -0
- package/dist/tools/type-text.mjs +29 -0
- package/dist/tools/type-text.mjs.map +1 -0
- package/dist/tools/wait-for.cjs +51 -0
- package/dist/tools/wait-for.cjs.map +1 -0
- package/dist/tools/wait-for.d.cts +4 -0
- package/dist/tools/wait-for.d.cts.map +1 -0
- package/dist/tools/wait-for.d.mts +4 -0
- package/dist/tools/wait-for.d.mts.map +1 -0
- package/dist/tools/wait-for.mjs +48 -0
- package/dist/tools/wait-for.mjs.map +1 -0
- package/dist/tools/window-size.cjs +29 -0
- package/dist/tools/window-size.cjs.map +1 -0
- package/dist/tools/window-size.d.cts +4 -0
- package/dist/tools/window-size.d.cts.map +1 -0
- package/dist/tools/window-size.d.mts +4 -0
- package/dist/tools/window-size.d.mts.map +1 -0
- package/dist/tools/window-size.mjs +26 -0
- package/dist/tools/window-size.mjs.map +1 -0
- package/dist/utils/alert-labels.cjs +26 -0
- package/dist/utils/alert-labels.cjs.map +1 -0
- package/dist/utils/alert-labels.d.cts +3 -0
- package/dist/utils/alert-labels.d.cts.map +1 -0
- package/dist/utils/alert-labels.d.mts +3 -0
- package/dist/utils/alert-labels.d.mts.map +1 -0
- package/dist/utils/alert-labels.mjs +23 -0
- package/dist/utils/alert-labels.mjs.map +1 -0
- package/dist/utils/element.cjs +212 -0
- package/dist/utils/element.cjs.map +1 -0
- package/dist/utils/element.d.cts +24 -0
- package/dist/utils/element.d.cts.map +1 -0
- package/dist/utils/element.d.mts +24 -0
- package/dist/utils/element.d.mts.map +1 -0
- package/dist/utils/element.mjs +203 -0
- package/dist/utils/element.mjs.map +1 -0
- package/dist/utils/exec.cjs +57 -0
- package/dist/utils/exec.cjs.map +1 -0
- package/dist/utils/exec.d.cts +23 -0
- package/dist/utils/exec.d.cts.map +1 -0
- package/dist/utils/exec.d.mts +23 -0
- package/dist/utils/exec.d.mts.map +1 -0
- package/dist/utils/exec.mjs +52 -0
- package/dist/utils/exec.mjs.map +1 -0
- package/dist/utils/platform.cjs +93 -0
- package/dist/utils/platform.cjs.map +1 -0
- package/dist/utils/platform.d.cts +8 -0
- package/dist/utils/platform.d.cts.map +1 -0
- package/dist/utils/platform.d.mts +8 -0
- package/dist/utils/platform.d.mts.map +1 -0
- package/dist/utils/platform.mjs +90 -0
- package/dist/utils/platform.mjs.map +1 -0
- package/package.json +106 -0
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
2
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
4
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
5
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
+
};
|
|
7
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
+
};
|
|
12
|
+
var _IdbBackend_connected, _IdbBackend_udid, _IdbBackend_recordingProcess, _IdbBackend_recordingPath;
|
|
13
|
+
import { execFile as execFileCb, spawn } from "node:child_process";
|
|
14
|
+
import { ACCEPT_LABELS, DENY_LABELS } from "../utils/alert-labels.mjs";
|
|
15
|
+
import { findElement, describeElement, computeSwipeEnd } from "../utils/element.mjs";
|
|
16
|
+
import { execStrict, exec, isCommandAvailable } from "../utils/exec.mjs";
|
|
17
|
+
export class IdbBackend {
|
|
18
|
+
constructor(udid) {
|
|
19
|
+
this.platform = 'ios';
|
|
20
|
+
_IdbBackend_connected.set(this, false);
|
|
21
|
+
_IdbBackend_udid.set(this, void 0);
|
|
22
|
+
_IdbBackend_recordingProcess.set(this, null);
|
|
23
|
+
_IdbBackend_recordingPath.set(this, null);
|
|
24
|
+
__classPrivateFieldSet(this, _IdbBackend_udid, udid, "f");
|
|
25
|
+
}
|
|
26
|
+
async ensureConnected() {
|
|
27
|
+
if (__classPrivateFieldGet(this, _IdbBackend_connected, "f")) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (!(await isCommandAvailable('idb'))) {
|
|
31
|
+
throw new Error('idb is not installed.\n' +
|
|
32
|
+
'Install: brew tap facebook/fb && brew install idb-companion && pip3 install fb-idb');
|
|
33
|
+
}
|
|
34
|
+
await exec('idb', ['connect', __classPrivateFieldGet(this, _IdbBackend_udid, "f")]);
|
|
35
|
+
__classPrivateFieldSet(this, _IdbBackend_connected, true, "f");
|
|
36
|
+
}
|
|
37
|
+
async getDeviceInfo() {
|
|
38
|
+
await this.ensureConnected();
|
|
39
|
+
const raw = await execStrict('idb', [
|
|
40
|
+
'describe',
|
|
41
|
+
'--udid',
|
|
42
|
+
__classPrivateFieldGet(this, _IdbBackend_udid, "f"),
|
|
43
|
+
'--json',
|
|
44
|
+
]);
|
|
45
|
+
let info;
|
|
46
|
+
try {
|
|
47
|
+
info = JSON.parse(raw);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
throw new Error(`idb describe returned invalid JSON for device ${__classPrivateFieldGet(this, _IdbBackend_udid, "f")}: ${raw.slice(0, 200)}`);
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
platform: 'ios',
|
|
54
|
+
deviceId: __classPrivateFieldGet(this, _IdbBackend_udid, "f"),
|
|
55
|
+
name: info.name ?? 'Unknown',
|
|
56
|
+
osVersion: info.os_version ?? 'Unknown',
|
|
57
|
+
state: info.state ?? 'Unknown',
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
async snapshot() {
|
|
61
|
+
await this.ensureConnected();
|
|
62
|
+
const raw = await execStrict('idb', [
|
|
63
|
+
'ui',
|
|
64
|
+
'describe-all',
|
|
65
|
+
'--udid',
|
|
66
|
+
__classPrivateFieldGet(this, _IdbBackend_udid, "f"),
|
|
67
|
+
]);
|
|
68
|
+
const hierarchy = parseIdbHierarchy(raw);
|
|
69
|
+
return {
|
|
70
|
+
platform: 'ios',
|
|
71
|
+
hierarchy,
|
|
72
|
+
raw,
|
|
73
|
+
timestamp: Date.now(),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
async tapElement(query) {
|
|
77
|
+
await this.ensureConnected();
|
|
78
|
+
const snapshot = await this.snapshot();
|
|
79
|
+
const element = findElement(snapshot.hierarchy, query);
|
|
80
|
+
if (!element) {
|
|
81
|
+
throw new Error(`Element not found: ${JSON.stringify(query)}\n` +
|
|
82
|
+
'Use device_snapshot to inspect the current UI hierarchy.');
|
|
83
|
+
}
|
|
84
|
+
const x = Math.round(element.frame.x + element.frame.width / 2);
|
|
85
|
+
const y = Math.round(element.frame.y + element.frame.height / 2);
|
|
86
|
+
await this.tapCoordinates(x, y);
|
|
87
|
+
return {
|
|
88
|
+
success: true,
|
|
89
|
+
x,
|
|
90
|
+
y,
|
|
91
|
+
targetDescription: describeElement(element),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
async tapCoordinates(x, y) {
|
|
95
|
+
await this.ensureConnected();
|
|
96
|
+
await execStrict('idb', [
|
|
97
|
+
'ui',
|
|
98
|
+
'tap',
|
|
99
|
+
String(x),
|
|
100
|
+
String(y),
|
|
101
|
+
'--udid',
|
|
102
|
+
__classPrivateFieldGet(this, _IdbBackend_udid, "f"),
|
|
103
|
+
]);
|
|
104
|
+
}
|
|
105
|
+
async typeText(text) {
|
|
106
|
+
await this.ensureConnected();
|
|
107
|
+
await exec('idb', [
|
|
108
|
+
'ui',
|
|
109
|
+
'key-sequence',
|
|
110
|
+
'--',
|
|
111
|
+
'cmd+a',
|
|
112
|
+
'--udid',
|
|
113
|
+
__classPrivateFieldGet(this, _IdbBackend_udid, "f"),
|
|
114
|
+
]);
|
|
115
|
+
await exec('idb', ['ui', 'key', 'DELETE', '--udid', __classPrivateFieldGet(this, _IdbBackend_udid, "f")]);
|
|
116
|
+
await execStrict('idb', ['ui', 'text', text, '--udid', __classPrivateFieldGet(this, _IdbBackend_udid, "f")]);
|
|
117
|
+
}
|
|
118
|
+
async swipe(direction, startX, startY, distance) {
|
|
119
|
+
await this.ensureConnected();
|
|
120
|
+
const d = distance ?? 500;
|
|
121
|
+
const sx = startX ?? 200;
|
|
122
|
+
const sy = startY ?? 400;
|
|
123
|
+
const [endX, endY] = computeSwipeEnd(sx, sy, direction, d);
|
|
124
|
+
await execStrict('idb', [
|
|
125
|
+
'ui',
|
|
126
|
+
'swipe',
|
|
127
|
+
String(sx),
|
|
128
|
+
String(sy),
|
|
129
|
+
String(endX),
|
|
130
|
+
String(endY),
|
|
131
|
+
'--udid',
|
|
132
|
+
__classPrivateFieldGet(this, _IdbBackend_udid, "f"),
|
|
133
|
+
]);
|
|
134
|
+
}
|
|
135
|
+
async waitForElement(query, timeoutMs = 10000, intervalMs = 500) {
|
|
136
|
+
const start = Date.now();
|
|
137
|
+
while (Date.now() - start < timeoutMs) {
|
|
138
|
+
const snapshot = await this.snapshot();
|
|
139
|
+
const element = findElement(snapshot.hierarchy, query);
|
|
140
|
+
if (element) {
|
|
141
|
+
return element;
|
|
142
|
+
}
|
|
143
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
144
|
+
}
|
|
145
|
+
throw new Error(`Timed out waiting for element: ${JSON.stringify(query)} (${timeoutMs}ms)`);
|
|
146
|
+
}
|
|
147
|
+
async getAppState(bundleId) {
|
|
148
|
+
await this.ensureConnected();
|
|
149
|
+
const raw = await execStrict('idb', ['list-apps', '--udid', __classPrivateFieldGet(this, _IdbBackend_udid, "f")]);
|
|
150
|
+
for (const line of raw.trim().split('\n')) {
|
|
151
|
+
if (line.includes(bundleId)) {
|
|
152
|
+
const parts = line.split('|').map((s) => s.trim());
|
|
153
|
+
return {
|
|
154
|
+
bundleId,
|
|
155
|
+
state: parts[2] ?? 'Unknown',
|
|
156
|
+
pid: parts[3] ? parseInt(parts[3], 10) : undefined,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return { bundleId, state: 'Not Installed' };
|
|
161
|
+
}
|
|
162
|
+
async screenshot(outputPath) {
|
|
163
|
+
await this.ensureConnected();
|
|
164
|
+
const path = outputPath ?? `/tmp/device-mcp-screenshot-${Date.now()}.png`;
|
|
165
|
+
await execStrict('idb', ['screenshot', path, '--udid', __classPrivateFieldGet(this, _IdbBackend_udid, "f")]);
|
|
166
|
+
const data = await execStrict('base64', [path]);
|
|
167
|
+
return { data: data.trim(), format: 'png', path };
|
|
168
|
+
}
|
|
169
|
+
async openApp(bundleId) {
|
|
170
|
+
await this.ensureConnected();
|
|
171
|
+
await execStrict('idb', ['launch', bundleId, '--udid', __classPrivateFieldGet(this, _IdbBackend_udid, "f")]);
|
|
172
|
+
}
|
|
173
|
+
async closeApp(bundleId) {
|
|
174
|
+
await this.ensureConnected();
|
|
175
|
+
await execStrict('idb', ['terminate', bundleId, '--udid', __classPrivateFieldGet(this, _IdbBackend_udid, "f")]);
|
|
176
|
+
}
|
|
177
|
+
async pressButton(button) {
|
|
178
|
+
await this.ensureConnected();
|
|
179
|
+
const keyMap = {
|
|
180
|
+
home: 'HOME',
|
|
181
|
+
lock: 'LOCK',
|
|
182
|
+
back: 'HOME',
|
|
183
|
+
enter: 'RETURN',
|
|
184
|
+
};
|
|
185
|
+
await execStrict('idb', [
|
|
186
|
+
'ui',
|
|
187
|
+
'key',
|
|
188
|
+
keyMap[button],
|
|
189
|
+
'--udid',
|
|
190
|
+
__classPrivateFieldGet(this, _IdbBackend_udid, "f"),
|
|
191
|
+
]);
|
|
192
|
+
}
|
|
193
|
+
async dismissKeyboard() {
|
|
194
|
+
await this.ensureConnected();
|
|
195
|
+
await execStrict('idb', ['ui', 'key', 'RETURN', '--udid', __classPrivateFieldGet(this, _IdbBackend_udid, "f")]);
|
|
196
|
+
}
|
|
197
|
+
async dismissAlert(accept) {
|
|
198
|
+
await this.ensureConnected();
|
|
199
|
+
const snap = await this.snapshot();
|
|
200
|
+
const candidates = accept ? ACCEPT_LABELS : DENY_LABELS;
|
|
201
|
+
for (const label of candidates) {
|
|
202
|
+
const element = findElement(snap.hierarchy, { label });
|
|
203
|
+
if (element) {
|
|
204
|
+
const cx = Math.round(element.frame.x + element.frame.width / 2);
|
|
205
|
+
const cy = Math.round(element.frame.y + element.frame.height / 2);
|
|
206
|
+
await this.tapCoordinates(cx, cy);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
throw new Error(`No alert button found. Tried: ${candidates.join(', ')}`);
|
|
211
|
+
}
|
|
212
|
+
async getLogs(durationSeconds = 30, filter) {
|
|
213
|
+
await this.ensureConnected();
|
|
214
|
+
const args = [
|
|
215
|
+
'log',
|
|
216
|
+
'--udid',
|
|
217
|
+
__classPrivateFieldGet(this, _IdbBackend_udid, "f"),
|
|
218
|
+
'--',
|
|
219
|
+
'--last',
|
|
220
|
+
`${durationSeconds}s`,
|
|
221
|
+
];
|
|
222
|
+
if (filter) {
|
|
223
|
+
args.push('--predicate', `eventMessage CONTAINS "${filter}"`);
|
|
224
|
+
}
|
|
225
|
+
const raw = await execStrict('idb', args);
|
|
226
|
+
const entries = raw
|
|
227
|
+
.trim()
|
|
228
|
+
.split('\n')
|
|
229
|
+
.filter(Boolean)
|
|
230
|
+
.map((line) => ({
|
|
231
|
+
timestamp: line.substring(0, 31).trim(),
|
|
232
|
+
level: 'info',
|
|
233
|
+
message: line.substring(31).trim(),
|
|
234
|
+
}));
|
|
235
|
+
return { entries, source: 'idb log' };
|
|
236
|
+
}
|
|
237
|
+
async longPress(query, durationMs = 1000) {
|
|
238
|
+
await this.ensureConnected();
|
|
239
|
+
const snap = await this.snapshot();
|
|
240
|
+
const element = findElement(snap.hierarchy, query);
|
|
241
|
+
if (!element) {
|
|
242
|
+
throw new Error(`Element not found: ${JSON.stringify(query)}\n` +
|
|
243
|
+
'Use device_snapshot to inspect the current UI hierarchy.');
|
|
244
|
+
}
|
|
245
|
+
const cx = Math.round(element.frame.x + element.frame.width / 2);
|
|
246
|
+
const cy = Math.round(element.frame.y + element.frame.height / 2);
|
|
247
|
+
await execStrict('idb', [
|
|
248
|
+
'ui',
|
|
249
|
+
'tap',
|
|
250
|
+
String(cx),
|
|
251
|
+
String(cy),
|
|
252
|
+
'--duration',
|
|
253
|
+
String(durationMs / 1000),
|
|
254
|
+
'--udid',
|
|
255
|
+
__classPrivateFieldGet(this, _IdbBackend_udid, "f"),
|
|
256
|
+
]);
|
|
257
|
+
return {
|
|
258
|
+
success: true,
|
|
259
|
+
x: cx,
|
|
260
|
+
y: cy,
|
|
261
|
+
targetDescription: describeElement(element),
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
async scrollToElement(query, direction = 'down', maxAttempts = 10) {
|
|
265
|
+
await this.ensureConnected();
|
|
266
|
+
let previousRaw = '';
|
|
267
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
268
|
+
const snap = await this.snapshot();
|
|
269
|
+
const element = findElement(snap.hierarchy, query);
|
|
270
|
+
if (element) {
|
|
271
|
+
return element;
|
|
272
|
+
}
|
|
273
|
+
if (snap.raw === previousRaw) {
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
previousRaw = snap.raw;
|
|
277
|
+
await this.swipe(direction);
|
|
278
|
+
}
|
|
279
|
+
throw new Error(`Element not found after scrolling: ${JSON.stringify(query)}\n` +
|
|
280
|
+
'Use device_snapshot to inspect the current UI hierarchy.');
|
|
281
|
+
}
|
|
282
|
+
async getAlertText() {
|
|
283
|
+
await this.ensureConnected();
|
|
284
|
+
const snap = await this.snapshot();
|
|
285
|
+
const texts = collectAlertTexts(snap.hierarchy);
|
|
286
|
+
if (texts.length === 0) {
|
|
287
|
+
throw new Error('No alert is currently displayed.\n' +
|
|
288
|
+
'Use device_snapshot to inspect the current UI hierarchy.');
|
|
289
|
+
}
|
|
290
|
+
return texts.join('\n');
|
|
291
|
+
}
|
|
292
|
+
async getWindowSize() {
|
|
293
|
+
await this.ensureConnected();
|
|
294
|
+
const raw = await execStrict('idb', [
|
|
295
|
+
'describe',
|
|
296
|
+
'--udid',
|
|
297
|
+
__classPrivateFieldGet(this, _IdbBackend_udid, "f"),
|
|
298
|
+
'--json',
|
|
299
|
+
]);
|
|
300
|
+
try {
|
|
301
|
+
const info = JSON.parse(raw);
|
|
302
|
+
const dims = info.screen_dimensions;
|
|
303
|
+
if (dims?.width && dims?.height) {
|
|
304
|
+
return { width: dims.width, height: dims.height };
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
catch {
|
|
308
|
+
// fall through to snapshot-based approach
|
|
309
|
+
}
|
|
310
|
+
const snap = await this.snapshot();
|
|
311
|
+
if (snap.hierarchy.length > 0) {
|
|
312
|
+
const root = snap.hierarchy[0];
|
|
313
|
+
if (root.frame.width > 0 && root.frame.height > 0) {
|
|
314
|
+
return { width: root.frame.width, height: root.frame.height };
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
throw new Error('Unable to determine window size from device');
|
|
318
|
+
}
|
|
319
|
+
async getContexts() {
|
|
320
|
+
return ['NATIVE_APP'];
|
|
321
|
+
}
|
|
322
|
+
async setContext(context) {
|
|
323
|
+
if (context === 'NATIVE_APP') {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
throw new Error('Context switching requires Appium backend. IDB only supports NATIVE_APP.');
|
|
327
|
+
}
|
|
328
|
+
async getClipboard() {
|
|
329
|
+
await this.ensureConnected();
|
|
330
|
+
return execStrict('xcrun', ['simctl', 'pbpaste', __classPrivateFieldGet(this, _IdbBackend_udid, "f")]);
|
|
331
|
+
}
|
|
332
|
+
async setClipboard(text) {
|
|
333
|
+
await this.ensureConnected();
|
|
334
|
+
await new Promise((resolve, reject) => {
|
|
335
|
+
const proc = execFileCb('xcrun', ['simctl', 'pbcopy', __classPrivateFieldGet(this, _IdbBackend_udid, "f")], { timeout: 30000 }, (error) => (error ? reject(new Error(error.message)) : resolve()));
|
|
336
|
+
proc.stdin?.end(text);
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
async startScreenRecording(outputPath) {
|
|
340
|
+
await this.ensureConnected();
|
|
341
|
+
if (__classPrivateFieldGet(this, _IdbBackend_recordingProcess, "f")) {
|
|
342
|
+
throw new Error('Screen recording is already in progress');
|
|
343
|
+
}
|
|
344
|
+
const path = outputPath ?? `/tmp/device-mcp-recording-${Date.now()}.mp4`;
|
|
345
|
+
__classPrivateFieldSet(this, _IdbBackend_recordingPath, path, "f");
|
|
346
|
+
__classPrivateFieldSet(this, _IdbBackend_recordingProcess, spawn('idb', [
|
|
347
|
+
'record-video',
|
|
348
|
+
path,
|
|
349
|
+
'--udid',
|
|
350
|
+
__classPrivateFieldGet(this, _IdbBackend_udid, "f"),
|
|
351
|
+
]), "f");
|
|
352
|
+
__classPrivateFieldGet(this, _IdbBackend_recordingProcess, "f").on('error', () => {
|
|
353
|
+
__classPrivateFieldSet(this, _IdbBackend_recordingProcess, null, "f");
|
|
354
|
+
__classPrivateFieldSet(this, _IdbBackend_recordingPath, null, "f");
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
async stopScreenRecording() {
|
|
358
|
+
if (!__classPrivateFieldGet(this, _IdbBackend_recordingProcess, "f") || !__classPrivateFieldGet(this, _IdbBackend_recordingPath, "f")) {
|
|
359
|
+
throw new Error('No screen recording in progress');
|
|
360
|
+
}
|
|
361
|
+
const path = __classPrivateFieldGet(this, _IdbBackend_recordingPath, "f");
|
|
362
|
+
__classPrivateFieldGet(this, _IdbBackend_recordingProcess, "f").kill('SIGINT');
|
|
363
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
364
|
+
__classPrivateFieldSet(this, _IdbBackend_recordingProcess, null, "f");
|
|
365
|
+
__classPrivateFieldSet(this, _IdbBackend_recordingPath, null, "f");
|
|
366
|
+
return path;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
_IdbBackend_connected = new WeakMap(), _IdbBackend_udid = new WeakMap(), _IdbBackend_recordingProcess = new WeakMap(), _IdbBackend_recordingPath = new WeakMap();
|
|
370
|
+
function collectAlertTexts(elements) {
|
|
371
|
+
const texts = [];
|
|
372
|
+
for (const el of elements) {
|
|
373
|
+
const isAlertContainer = el.type === 'Alert' || el.type === 'Sheet' || el.type.includes('Alert');
|
|
374
|
+
if (isAlertContainer) {
|
|
375
|
+
collectTextsFromChildren(el.children ?? [], texts);
|
|
376
|
+
return texts;
|
|
377
|
+
}
|
|
378
|
+
if (el.children) {
|
|
379
|
+
const found = collectAlertTexts(el.children);
|
|
380
|
+
if (found.length > 0) {
|
|
381
|
+
return found;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return texts;
|
|
386
|
+
}
|
|
387
|
+
function collectTextsFromChildren(elements, texts) {
|
|
388
|
+
for (const el of elements) {
|
|
389
|
+
if (el.type === 'StaticText' || el.type === 'TextView') {
|
|
390
|
+
const text = el.value ?? el.label;
|
|
391
|
+
if (text) {
|
|
392
|
+
texts.push(text);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
if (el.children) {
|
|
396
|
+
collectTextsFromChildren(el.children, texts);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
export function parseIdbHierarchy(raw) {
|
|
401
|
+
try {
|
|
402
|
+
const parsed = JSON.parse(raw);
|
|
403
|
+
if (Array.isArray(parsed)) {
|
|
404
|
+
return parsed.map(mapIdbElement);
|
|
405
|
+
}
|
|
406
|
+
if (parsed.children) {
|
|
407
|
+
return parsed.children.map(mapIdbElement);
|
|
408
|
+
}
|
|
409
|
+
return [mapIdbElement(parsed)];
|
|
410
|
+
}
|
|
411
|
+
catch {
|
|
412
|
+
return [
|
|
413
|
+
{
|
|
414
|
+
type: 'raw',
|
|
415
|
+
label: raw.slice(0, 200),
|
|
416
|
+
frame: { x: 0, y: 0, width: 0, height: 0 },
|
|
417
|
+
enabled: true,
|
|
418
|
+
},
|
|
419
|
+
];
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
export function mapIdbElement(node) {
|
|
423
|
+
const frame = node.frame;
|
|
424
|
+
return {
|
|
425
|
+
type: node.type ?? node.AXType ?? 'Unknown',
|
|
426
|
+
label: node.AXLabel ?? node.label ?? undefined,
|
|
427
|
+
value: node.AXValue ?? node.value ?? undefined,
|
|
428
|
+
identifier: node.AXUniqueId ?? node.identifier ?? undefined,
|
|
429
|
+
frame: frame ?? { x: 0, y: 0, width: 0, height: 0 },
|
|
430
|
+
enabled: node.enabled ?? true,
|
|
431
|
+
children: Array.isArray(node.children)
|
|
432
|
+
? node.children.map(mapIdbElement)
|
|
433
|
+
: undefined,
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
//# sourceMappingURL=idb-backend.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"idb-backend.mjs","sourceRoot":"","sources":["../../src/backends/idb-backend.ts"],"names":[],"mappings":"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","sourcesContent":["import { execFile as execFileCb, 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, exec, isCommandAvailable } from '../utils/exec.js';\n\nexport class IdbBackend implements DeviceBackend {\n readonly platform = 'ios' as const;\n\n #connected = false;\n\n readonly #udid: string;\n\n #recordingProcess: ChildProcess | null = null;\n\n #recordingPath: string | null = null;\n\n constructor(udid: string) {\n this.#udid = udid;\n }\n\n async ensureConnected(): Promise<void> {\n if (this.#connected) {\n return;\n }\n\n if (!(await isCommandAvailable('idb'))) {\n throw new Error(\n 'idb is not installed.\\n' +\n 'Install: brew tap facebook/fb && brew install idb-companion && pip3 install fb-idb',\n );\n }\n\n await exec('idb', ['connect', this.#udid]);\n this.#connected = true;\n }\n\n async getDeviceInfo(): Promise<DeviceInfo> {\n await this.ensureConnected();\n const raw = await execStrict('idb', [\n 'describe',\n '--udid',\n this.#udid,\n '--json',\n ]);\n let info: Record<string, unknown>;\n try {\n info = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n throw new Error(\n `idb describe returned invalid JSON for device ${this.#udid}: ${raw.slice(0, 200)}`,\n );\n }\n return {\n platform: 'ios',\n deviceId: this.#udid,\n name: (info.name as string) ?? 'Unknown',\n osVersion: (info.os_version as string) ?? 'Unknown',\n state: (info.state as string) ?? 'Unknown',\n };\n }\n\n async snapshot(): Promise<SnapshotResult> {\n await this.ensureConnected();\n const raw = await execStrict('idb', [\n 'ui',\n 'describe-all',\n '--udid',\n this.#udid,\n ]);\n const hierarchy = parseIdbHierarchy(raw);\n return {\n platform: 'ios',\n hierarchy,\n raw,\n timestamp: Date.now(),\n };\n }\n\n async tapElement(query: ElementQuery): Promise<TapResult> {\n await this.ensureConnected();\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 tapCoordinates(x: number, y: number): Promise<void> {\n await this.ensureConnected();\n await execStrict('idb', [\n 'ui',\n 'tap',\n String(x),\n String(y),\n '--udid',\n this.#udid,\n ]);\n }\n\n async typeText(text: string): Promise<void> {\n await this.ensureConnected();\n await exec('idb', [\n 'ui',\n 'key-sequence',\n '--',\n 'cmd+a',\n '--udid',\n this.#udid,\n ]);\n await exec('idb', ['ui', 'key', 'DELETE', '--udid', this.#udid]);\n await execStrict('idb', ['ui', 'text', text, '--udid', this.#udid]);\n }\n\n async swipe(\n direction: 'up' | 'down' | 'left' | 'right',\n startX?: number,\n startY?: number,\n distance?: number,\n ): Promise<void> {\n await this.ensureConnected();\n const d = distance ?? 500;\n const sx = startX ?? 200;\n const sy = startY ?? 400;\n const [endX, endY] = computeSwipeEnd(sx, sy, direction, d);\n\n await execStrict('idb', [\n 'ui',\n 'swipe',\n String(sx),\n String(sy),\n String(endX),\n String(endY),\n '--udid',\n this.#udid,\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 await this.ensureConnected();\n const raw = await execStrict('idb', ['list-apps', '--udid', this.#udid]);\n\n for (const line of raw.trim().split('\\n')) {\n if (line.includes(bundleId)) {\n const parts = line.split('|').map((s) => s.trim());\n return {\n bundleId,\n state: parts[2] ?? 'Unknown',\n pid: parts[3] ? parseInt(parts[3], 10) : undefined,\n };\n }\n }\n\n return { bundleId, state: 'Not Installed' };\n }\n\n async screenshot(outputPath?: string): Promise<ScreenshotResult> {\n await this.ensureConnected();\n const path = outputPath ?? `/tmp/device-mcp-screenshot-${Date.now()}.png`;\n await execStrict('idb', ['screenshot', path, '--udid', this.#udid]);\n const data = await execStrict('base64', [path]);\n return { data: data.trim(), format: 'png', path };\n }\n\n async openApp(bundleId: string): Promise<void> {\n await this.ensureConnected();\n await execStrict('idb', ['launch', bundleId, '--udid', this.#udid]);\n }\n\n async closeApp(bundleId: string): Promise<void> {\n await this.ensureConnected();\n await execStrict('idb', ['terminate', bundleId, '--udid', this.#udid]);\n }\n\n async pressButton(button: DeviceButton): Promise<void> {\n await this.ensureConnected();\n const keyMap: Record<DeviceButton, string> = {\n home: 'HOME',\n lock: 'LOCK',\n back: 'HOME',\n enter: 'RETURN',\n };\n await execStrict('idb', [\n 'ui',\n 'key',\n keyMap[button],\n '--udid',\n this.#udid,\n ]);\n }\n\n async dismissKeyboard(): Promise<void> {\n await this.ensureConnected();\n await execStrict('idb', ['ui', 'key', 'RETURN', '--udid', this.#udid]);\n }\n\n async dismissAlert(accept: boolean): Promise<void> {\n await this.ensureConnected();\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, { 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 await this.ensureConnected();\n const args = [\n 'log',\n '--udid',\n this.#udid,\n '--',\n '--last',\n `${durationSeconds}s`,\n ];\n if (filter) {\n args.push('--predicate', `eventMessage CONTAINS \"${filter}\"`);\n }\n const raw = await execStrict('idb', args);\n const entries = raw\n .trim()\n .split('\\n')\n .filter(Boolean)\n .map((line) => ({\n timestamp: line.substring(0, 31).trim(),\n level: 'info',\n message: line.substring(31).trim(),\n }));\n return { entries, source: 'idb log' };\n }\n\n async longPress(query: ElementQuery, durationMs = 1000): Promise<TapResult> {\n await this.ensureConnected();\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 execStrict('idb', [\n 'ui',\n 'tap',\n String(cx),\n String(cy),\n '--duration',\n String(durationMs / 1000),\n '--udid',\n this.#udid,\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 await this.ensureConnected();\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 await this.ensureConnected();\n const snap = await this.snapshot();\n const texts = collectAlertTexts(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 await this.ensureConnected();\n const raw = await execStrict('idb', [\n 'describe',\n '--udid',\n this.#udid,\n '--json',\n ]);\n try {\n const info = JSON.parse(raw) as Record<string, unknown>;\n const dims = info.screen_dimensions as\n | { width: number; height: number }\n | undefined;\n if (dims?.width && dims?.height) {\n return { width: dims.width, height: dims.height };\n }\n } catch {\n // fall through to snapshot-based approach\n }\n const snap = await this.snapshot();\n if (snap.hierarchy.length > 0) {\n const root = snap.hierarchy[0];\n if (root.frame.width > 0 && root.frame.height > 0) {\n return { width: root.frame.width, height: root.frame.height };\n }\n }\n throw new Error('Unable to determine window size from device');\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. IDB only supports NATIVE_APP.',\n );\n }\n\n async getClipboard(): Promise<string> {\n await this.ensureConnected();\n return execStrict('xcrun', ['simctl', 'pbpaste', this.#udid]);\n }\n\n async setClipboard(text: string): Promise<void> {\n await this.ensureConnected();\n await new Promise<void>((resolve, reject) => {\n const proc = execFileCb(\n 'xcrun',\n ['simctl', 'pbcopy', this.#udid],\n { timeout: 30_000 },\n (error) => (error ? reject(new Error(error.message)) : resolve()),\n );\n proc.stdin?.end(text);\n });\n }\n\n async startScreenRecording(outputPath?: string): Promise<void> {\n await this.ensureConnected();\n if (this.#recordingProcess) {\n throw new Error('Screen recording is already in progress');\n }\n const path = outputPath ?? `/tmp/device-mcp-recording-${Date.now()}.mp4`;\n this.#recordingPath = path;\n this.#recordingProcess = spawn('idb', [\n 'record-video',\n path,\n '--udid',\n this.#udid,\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 path = this.#recordingPath;\n this.#recordingProcess.kill('SIGINT');\n await new Promise((resolve) => setTimeout(resolve, 1000));\n this.#recordingProcess = null;\n this.#recordingPath = null;\n return path;\n }\n}\n\nfunction collectAlertTexts(elements: UIElement[]): string[] {\n const texts: string[] = [];\n for (const el of elements) {\n const isAlertContainer =\n el.type === 'Alert' || el.type === 'Sheet' || el.type.includes('Alert');\n if (isAlertContainer) {\n collectTextsFromChildren(el.children ?? [], texts);\n return texts;\n }\n if (el.children) {\n const found = collectAlertTexts(el.children);\n if (found.length > 0) {\n return found;\n }\n }\n }\n return texts;\n}\n\nfunction collectTextsFromChildren(\n elements: UIElement[],\n texts: string[],\n): void {\n for (const el of elements) {\n if (el.type === 'StaticText' || el.type === 'TextView') {\n const text = el.value ?? el.label;\n if (text) {\n texts.push(text);\n }\n }\n if (el.children) {\n collectTextsFromChildren(el.children, texts);\n }\n }\n}\n\nexport function parseIdbHierarchy(raw: string): UIElement[] {\n try {\n const parsed = JSON.parse(raw);\n if (Array.isArray(parsed)) {\n return parsed.map(mapIdbElement);\n }\n if (parsed.children) {\n return parsed.children.map(mapIdbElement);\n }\n return [mapIdbElement(parsed)];\n } catch {\n return [\n {\n type: 'raw',\n label: raw.slice(0, 200),\n frame: { x: 0, y: 0, width: 0, height: 0 },\n enabled: true,\n },\n ];\n }\n}\n\nexport function mapIdbElement(node: Record<string, unknown>): UIElement {\n const frame = node.frame as\n | { x: number; y: number; width: number; height: number }\n | undefined;\n\n return {\n type: (node.type as string) ?? (node.AXType as string) ?? 'Unknown',\n label: (node.AXLabel as string) ?? (node.label as string) ?? undefined,\n value: (node.AXValue as string) ?? (node.value as string) ?? undefined,\n identifier:\n (node.AXUniqueId as string) ?? (node.identifier as string) ?? undefined,\n frame: frame ?? { x: 0, y: 0, width: 0, height: 0 },\n enabled: (node.enabled as boolean) ?? true,\n children: Array.isArray(node.children)\n ? node.children.map(mapIdbElement)\n : undefined,\n };\n}\n"]}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.IdbBackend = exports.AppiumBackend = exports.AdbBackend = void 0;
|
|
4
|
+
exports.createBackend = createBackend;
|
|
5
|
+
exports.createLazyBackend = createLazyBackend;
|
|
6
|
+
const adb_backend_js_1 = require("./adb-backend.cjs");
|
|
7
|
+
const appium_backend_js_1 = require("./appium-backend.cjs");
|
|
8
|
+
const idb_backend_js_1 = require("./idb-backend.cjs");
|
|
9
|
+
const session_file_js_1 = require("./session-file.cjs");
|
|
10
|
+
const platform_js_1 = require("../utils/platform.cjs");
|
|
11
|
+
var adb_backend_js_2 = require("./adb-backend.cjs");
|
|
12
|
+
Object.defineProperty(exports, "AdbBackend", { enumerable: true, get: function () { return adb_backend_js_2.AdbBackend; } });
|
|
13
|
+
var appium_backend_js_2 = require("./appium-backend.cjs");
|
|
14
|
+
Object.defineProperty(exports, "AppiumBackend", { enumerable: true, get: function () { return appium_backend_js_2.AppiumBackend; } });
|
|
15
|
+
var idb_backend_js_2 = require("./idb-backend.cjs");
|
|
16
|
+
Object.defineProperty(exports, "IdbBackend", { enumerable: true, get: function () { return idb_backend_js_2.IdbBackend; } });
|
|
17
|
+
async function createBackend(explicitDeviceId) {
|
|
18
|
+
const sessionConfig = await (0, session_file_js_1.readSessionFile)();
|
|
19
|
+
if (sessionConfig) {
|
|
20
|
+
console.error(`Found .device-session (${sessionConfig.mode}, ${sessionConfig.platform})`);
|
|
21
|
+
const backend = new appium_backend_js_1.AppiumBackend(sessionConfig);
|
|
22
|
+
await backend.ensureConnected();
|
|
23
|
+
return backend;
|
|
24
|
+
}
|
|
25
|
+
const { platform, deviceId } = await (0, platform_js_1.detectPlatform)(explicitDeviceId);
|
|
26
|
+
const backend = platform === 'ios' ? new idb_backend_js_1.IdbBackend(deviceId) : new adb_backend_js_1.AdbBackend(deviceId);
|
|
27
|
+
await backend.ensureConnected();
|
|
28
|
+
return backend;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Lazy backend that defers device detection to first tool call.
|
|
32
|
+
* Allows the MCP server to start and complete the handshake
|
|
33
|
+
* even when no device is connected yet.
|
|
34
|
+
*
|
|
35
|
+
* @param explicitDeviceId - Optional device ID override.
|
|
36
|
+
* @returns A DeviceBackend that resolves on first use.
|
|
37
|
+
*/
|
|
38
|
+
function createLazyBackend(explicitDeviceId) {
|
|
39
|
+
let inner = null;
|
|
40
|
+
let connecting = null;
|
|
41
|
+
async function resolve() {
|
|
42
|
+
if (inner) {
|
|
43
|
+
return inner;
|
|
44
|
+
}
|
|
45
|
+
if (!connecting) {
|
|
46
|
+
connecting = createBackend(explicitDeviceId).then((backend) => {
|
|
47
|
+
inner = backend;
|
|
48
|
+
return backend;
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return connecting;
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
get platform() {
|
|
55
|
+
return inner?.platform ?? 'ios';
|
|
56
|
+
},
|
|
57
|
+
async getDeviceInfo() {
|
|
58
|
+
return (await resolve()).getDeviceInfo();
|
|
59
|
+
},
|
|
60
|
+
async snapshot() {
|
|
61
|
+
return (await resolve()).snapshot();
|
|
62
|
+
},
|
|
63
|
+
async tapElement(query) {
|
|
64
|
+
return (await resolve()).tapElement(query);
|
|
65
|
+
},
|
|
66
|
+
async tapCoordinates(x, y) {
|
|
67
|
+
return (await resolve()).tapCoordinates(x, y);
|
|
68
|
+
},
|
|
69
|
+
async typeText(text) {
|
|
70
|
+
return (await resolve()).typeText(text);
|
|
71
|
+
},
|
|
72
|
+
async swipe(direction, startX, startY, distance) {
|
|
73
|
+
return (await resolve()).swipe(direction, startX, startY, distance);
|
|
74
|
+
},
|
|
75
|
+
async waitForElement(query, timeoutMs, intervalMs) {
|
|
76
|
+
return (await resolve()).waitForElement(query, timeoutMs, intervalMs);
|
|
77
|
+
},
|
|
78
|
+
async getAppState(bundleId) {
|
|
79
|
+
return (await resolve()).getAppState(bundleId);
|
|
80
|
+
},
|
|
81
|
+
async screenshot(outputPath) {
|
|
82
|
+
return (await resolve()).screenshot(outputPath);
|
|
83
|
+
},
|
|
84
|
+
async openApp(bundleId) {
|
|
85
|
+
return (await resolve()).openApp(bundleId);
|
|
86
|
+
},
|
|
87
|
+
async closeApp(bundleId) {
|
|
88
|
+
return (await resolve()).closeApp(bundleId);
|
|
89
|
+
},
|
|
90
|
+
async pressButton(button) {
|
|
91
|
+
return (await resolve()).pressButton(button);
|
|
92
|
+
},
|
|
93
|
+
async dismissKeyboard() {
|
|
94
|
+
return (await resolve()).dismissKeyboard();
|
|
95
|
+
},
|
|
96
|
+
async dismissAlert(accept) {
|
|
97
|
+
return (await resolve()).dismissAlert(accept);
|
|
98
|
+
},
|
|
99
|
+
async getLogs(durationSeconds, filter) {
|
|
100
|
+
return (await resolve()).getLogs(durationSeconds, filter);
|
|
101
|
+
},
|
|
102
|
+
async longPress(query, durationMs) {
|
|
103
|
+
return (await resolve()).longPress(query, durationMs);
|
|
104
|
+
},
|
|
105
|
+
async scrollToElement(query, direction, maxAttempts) {
|
|
106
|
+
return (await resolve()).scrollToElement(query, direction, maxAttempts);
|
|
107
|
+
},
|
|
108
|
+
async getAlertText() {
|
|
109
|
+
return (await resolve()).getAlertText();
|
|
110
|
+
},
|
|
111
|
+
async getWindowSize() {
|
|
112
|
+
return (await resolve()).getWindowSize();
|
|
113
|
+
},
|
|
114
|
+
async getContexts() {
|
|
115
|
+
return (await resolve()).getContexts();
|
|
116
|
+
},
|
|
117
|
+
async setContext(context) {
|
|
118
|
+
return (await resolve()).setContext(context);
|
|
119
|
+
},
|
|
120
|
+
async getClipboard() {
|
|
121
|
+
return (await resolve()).getClipboard();
|
|
122
|
+
},
|
|
123
|
+
async setClipboard(text) {
|
|
124
|
+
return (await resolve()).setClipboard(text);
|
|
125
|
+
},
|
|
126
|
+
async startScreenRecording(outputPath) {
|
|
127
|
+
return (await resolve()).startScreenRecording(outputPath);
|
|
128
|
+
},
|
|
129
|
+
async stopScreenRecording() {
|
|
130
|
+
return (await resolve()).stopScreenRecording();
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sourceRoot":"","sources":["../../src/backends/index.ts"],"names":[],"mappings":";;;AAyBA,sCAqBC;AAUD,8CA6IC;AArMD,sDAA8C;AAC9C,4DAAoD;AACpD,sDAA8C;AAC9C,wDAAoD;AAepD,uDAAsD;AAGtD,oDAA8C;AAArC,4GAAA,UAAU,OAAA;AACnB,0DAAoD;AAA3C,kHAAA,aAAa,OAAA;AACtB,oDAA8C;AAArC,4GAAA,UAAU,OAAA;AAEZ,KAAK,UAAU,aAAa,CACjC,gBAAyB;IAEzB,MAAM,aAAa,GAAG,MAAM,IAAA,iCAAe,GAAE,CAAC;IAE9C,IAAI,aAAa,EAAE,CAAC;QAClB,OAAO,CAAC,KAAK,CACX,0BAA0B,aAAa,CAAC,IAAI,KAAK,aAAa,CAAC,QAAQ,GAAG,CAC3E,CAAC;QACF,MAAM,OAAO,GAAG,IAAI,iCAAa,CAAC,aAAa,CAAC,CAAC;QACjD,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC;QAChC,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,MAAM,IAAA,4BAAc,EAAC,gBAAgB,CAAC,CAAC;IAEtE,MAAM,OAAO,GACX,QAAQ,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,2BAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,2BAAU,CAAC,QAAQ,CAAC,CAAC;IAE3E,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC;IAChC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,iBAAiB,CAAC,gBAAyB;IACzD,IAAI,KAAK,GAAyB,IAAI,CAAC;IACvC,IAAI,UAAU,GAAkC,IAAI,CAAC;IAErD,KAAK,UAAU,OAAO;QACpB,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,UAAU,GAAG,aAAa,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE;gBAC5D,KAAK,GAAG,OAAO,CAAC;gBAChB,OAAO,OAAO,CAAC;YACjB,CAAC,CAAC,CAAC;QACL,CAAC;QACD,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,OAAO;QACL,IAAI,QAAQ;YACV,OAAO,KAAK,EAAE,QAAQ,IAAI,KAAK,CAAC;QAClC,CAAC;QAED,KAAK,CAAC,aAAa;YACjB,OAAO,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC,aAAa,EAAE,CAAC;QAC3C,CAAC;QAED,KAAK,CAAC,QAAQ;YACZ,OAAO,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC;QACtC,CAAC;QAED,KAAK,CAAC,UAAU,CAAC,KAAmB;YAClC,OAAO,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QAC7C,CAAC;QAED,KAAK,CAAC,cAAc,CAAC,CAAS,EAAE,CAAS;YACvC,OAAO,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAChD,CAAC;QAED,KAAK,CAAC,QAAQ,CAAC,IAAY;YACzB,OAAO,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC1C,CAAC;QAED,KAAK,CAAC,KAAK,CACT,SAA2C,EAC3C,MAAe,EACf,MAAe,EACf,QAAiB;YAEjB,OAAO,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;QACtE,CAAC;QAED,KAAK,CAAC,cAAc,CAClB,KAAmB,EACnB,SAAkB,EAClB,UAAmB;YAEnB,OAAO,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC,cAAc,CAAC,KAAK,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;QACxE,CAAC;QAED,KAAK,CAAC,WAAW,CAAC,QAAgB;YAChC,OAAO,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;QACjD,CAAC;QAED,KAAK,CAAC,UAAU,CAAC,UAAmB;YAClC,OAAO,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;QAClD,CAAC;QAED,KAAK,CAAC,OAAO,CAAC,QAAgB;YAC5B,OAAO,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC7C,CAAC;QAED,KAAK,CAAC,QAAQ,CAAC,QAAgB;YAC7B,OAAO,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC9C,CAAC;QAED,KAAK,CAAC,WAAW,CAAC,MAAoB;YACpC,OAAO,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAC/C,CAAC;QAED,KAAK,CAAC,eAAe;YACnB,OAAO,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC,eAAe,EAAE,CAAC;QAC7C,CAAC;QAED,KAAK,CAAC,YAAY,CAAC,MAAe;YAChC,OAAO,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QAChD,CAAC;QAED,KAAK,CAAC,OAAO,CACX,eAAwB,EACxB,MAAe;YAEf,OAAO,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;QAC5D,CAAC;QAED,KAAK,CAAC,SAAS,CACb,KAAmB,EACnB,UAAmB;YAEnB,OAAO,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC,SAAS,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;QACxD,CAAC;QAED,KAAK,CAAC,eAAe,CACnB,KAAmB,EACnB,SAAyB,EACzB,WAAoB;YAEpB,OAAO,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC,eAAe,CAAC,KAAK,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC;QAC1E,CAAC;QAED,KAAK,CAAC,YAAY;YAChB,OAAO,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC,YAAY,EAAE,CAAC;QAC1C,CAAC;QAED,KAAK,CAAC,aAAa;YACjB,OAAO,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC,aAAa,EAAE,CAAC;QAC3C,CAAC;QAED,KAAK,CAAC,WAAW;YACf,OAAO,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QACzC,CAAC;QAED,KAAK,CAAC,UAAU,CAAC,OAAe;YAC9B,OAAO,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QAC/C,CAAC;QAED,KAAK,CAAC,YAAY;YAChB,OAAO,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC,YAAY,EAAE,CAAC;QAC1C,CAAC;QAED,KAAK,CAAC,YAAY,CAAC,IAAY;YAC7B,OAAO,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;QAC9C,CAAC;QAED,KAAK,CAAC,oBAAoB,CAAC,UAAmB;YAC5C,OAAO,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC,oBAAoB,CAAC,UAAU,CAAC,CAAC;QAC5D,CAAC;QAED,KAAK,CAAC,mBAAmB;YACvB,OAAO,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC,mBAAmB,EAAE,CAAC;QACjD,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import { AdbBackend } from './adb-backend.js';\nimport { AppiumBackend } from './appium-backend.js';\nimport { IdbBackend } from './idb-backend.js';\nimport { readSessionFile } from './session-file.js';\nimport type {\n DeviceBackend,\n DeviceButton,\n DeviceInfo,\n LogsResult,\n ScreenshotResult,\n SnapshotResult,\n TapResult,\n AppStateResult,\n ElementQuery,\n UIElement,\n Platform,\n WindowSize,\n} from './types.js';\nimport { detectPlatform } from '../utils/platform.js';\n\nexport type { DeviceBackend } from './types.js';\nexport { AdbBackend } from './adb-backend.js';\nexport { AppiumBackend } from './appium-backend.js';\nexport { IdbBackend } from './idb-backend.js';\n\nexport async function createBackend(\n explicitDeviceId?: string,\n): Promise<DeviceBackend> {\n const sessionConfig = await readSessionFile();\n\n if (sessionConfig) {\n console.error(\n `Found .device-session (${sessionConfig.mode}, ${sessionConfig.platform})`,\n );\n const backend = new AppiumBackend(sessionConfig);\n await backend.ensureConnected();\n return backend;\n }\n\n const { platform, deviceId } = await detectPlatform(explicitDeviceId);\n\n const backend =\n platform === 'ios' ? new IdbBackend(deviceId) : new AdbBackend(deviceId);\n\n await backend.ensureConnected();\n return backend;\n}\n\n/**\n * Lazy backend that defers device detection to first tool call.\n * Allows the MCP server to start and complete the handshake\n * even when no device is connected yet.\n *\n * @param explicitDeviceId - Optional device ID override.\n * @returns A DeviceBackend that resolves on first use.\n */\nexport function createLazyBackend(explicitDeviceId?: string): DeviceBackend {\n let inner: DeviceBackend | null = null;\n let connecting: Promise<DeviceBackend> | null = null;\n\n async function resolve(): Promise<DeviceBackend> {\n if (inner) {\n return inner;\n }\n if (!connecting) {\n connecting = createBackend(explicitDeviceId).then((backend) => {\n inner = backend;\n return backend;\n });\n }\n return connecting;\n }\n\n return {\n get platform(): Platform {\n return inner?.platform ?? 'ios';\n },\n\n async getDeviceInfo(): Promise<DeviceInfo> {\n return (await resolve()).getDeviceInfo();\n },\n\n async snapshot(): Promise<SnapshotResult> {\n return (await resolve()).snapshot();\n },\n\n async tapElement(query: ElementQuery): Promise<TapResult> {\n return (await resolve()).tapElement(query);\n },\n\n async tapCoordinates(x: number, y: number): Promise<void> {\n return (await resolve()).tapCoordinates(x, y);\n },\n\n async typeText(text: string): Promise<void> {\n return (await resolve()).typeText(text);\n },\n\n async swipe(\n direction: 'up' | 'down' | 'left' | 'right',\n startX?: number,\n startY?: number,\n distance?: number,\n ): Promise<void> {\n return (await resolve()).swipe(direction, startX, startY, distance);\n },\n\n async waitForElement(\n query: ElementQuery,\n timeoutMs?: number,\n intervalMs?: number,\n ): Promise<UIElement> {\n return (await resolve()).waitForElement(query, timeoutMs, intervalMs);\n },\n\n async getAppState(bundleId: string): Promise<AppStateResult> {\n return (await resolve()).getAppState(bundleId);\n },\n\n async screenshot(outputPath?: string): Promise<ScreenshotResult> {\n return (await resolve()).screenshot(outputPath);\n },\n\n async openApp(bundleId: string): Promise<void> {\n return (await resolve()).openApp(bundleId);\n },\n\n async closeApp(bundleId: string): Promise<void> {\n return (await resolve()).closeApp(bundleId);\n },\n\n async pressButton(button: DeviceButton): Promise<void> {\n return (await resolve()).pressButton(button);\n },\n\n async dismissKeyboard(): Promise<void> {\n return (await resolve()).dismissKeyboard();\n },\n\n async dismissAlert(accept: boolean): Promise<void> {\n return (await resolve()).dismissAlert(accept);\n },\n\n async getLogs(\n durationSeconds?: number,\n filter?: string,\n ): Promise<LogsResult> {\n return (await resolve()).getLogs(durationSeconds, filter);\n },\n\n async longPress(\n query: ElementQuery,\n durationMs?: number,\n ): Promise<TapResult> {\n return (await resolve()).longPress(query, durationMs);\n },\n\n async scrollToElement(\n query: ElementQuery,\n direction?: 'up' | 'down',\n maxAttempts?: number,\n ): Promise<UIElement> {\n return (await resolve()).scrollToElement(query, direction, maxAttempts);\n },\n\n async getAlertText(): Promise<string> {\n return (await resolve()).getAlertText();\n },\n\n async getWindowSize(): Promise<WindowSize> {\n return (await resolve()).getWindowSize();\n },\n\n async getContexts(): Promise<string[]> {\n return (await resolve()).getContexts();\n },\n\n async setContext(context: string): Promise<void> {\n return (await resolve()).setContext(context);\n },\n\n async getClipboard(): Promise<string> {\n return (await resolve()).getClipboard();\n },\n\n async setClipboard(text: string): Promise<void> {\n return (await resolve()).setClipboard(text);\n },\n\n async startScreenRecording(outputPath?: string): Promise<void> {\n return (await resolve()).startScreenRecording(outputPath);\n },\n\n async stopScreenRecording(): Promise<string> {\n return (await resolve()).stopScreenRecording();\n },\n };\n}\n"]}
|