@metamask/device-mcp 0.3.2 → 0.4.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 +18 -1
- package/README.md +110 -21
- package/dist/android/NOTICE.md +65 -0
- package/dist/android/device-mcp-android-snapshot-helper-0.4.0.apk +0 -0
- package/dist/android/device-mcp-android-snapshot-helper-0.4.0.apk.idsig +0 -0
- package/dist/android/device-mcp-android-snapshot-helper-0.4.0.manifest.json +10 -0
- package/dist/backends/adb-backend.cjs +326 -17
- package/dist/backends/adb-backend.cjs.map +1 -1
- package/dist/backends/adb-backend.d.cts +1 -0
- package/dist/backends/adb-backend.d.cts.map +1 -1
- package/dist/backends/adb-backend.d.mts +1 -0
- package/dist/backends/adb-backend.d.mts.map +1 -1
- package/dist/backends/adb-backend.mjs +327 -18
- package/dist/backends/adb-backend.mjs.map +1 -1
- package/dist/backends/android-instrumentation/adb.cjs +28 -0
- package/dist/backends/android-instrumentation/adb.cjs.map +1 -0
- package/dist/backends/android-instrumentation/adb.d.cts +24 -0
- package/dist/backends/android-instrumentation/adb.d.cts.map +1 -0
- package/dist/backends/android-instrumentation/adb.d.mts +24 -0
- package/dist/backends/android-instrumentation/adb.d.mts.map +1 -0
- package/dist/backends/android-instrumentation/adb.mjs +25 -0
- package/dist/backends/android-instrumentation/adb.mjs.map +1 -0
- package/dist/backends/android-instrumentation/artifact.cjs +139 -0
- package/dist/backends/android-instrumentation/artifact.cjs.map +1 -0
- package/dist/backends/android-instrumentation/artifact.d.cts +28 -0
- package/dist/backends/android-instrumentation/artifact.d.cts.map +1 -0
- package/dist/backends/android-instrumentation/artifact.d.mts +28 -0
- package/dist/backends/android-instrumentation/artifact.d.mts.map +1 -0
- package/dist/backends/android-instrumentation/artifact.mjs +154 -0
- package/dist/backends/android-instrumentation/artifact.mjs.map +1 -0
- package/dist/backends/android-instrumentation/errors.cjs +79 -0
- package/dist/backends/android-instrumentation/errors.cjs.map +1 -0
- package/dist/backends/android-instrumentation/errors.d.cts +68 -0
- package/dist/backends/android-instrumentation/errors.d.cts.map +1 -0
- package/dist/backends/android-instrumentation/errors.d.mts +68 -0
- package/dist/backends/android-instrumentation/errors.d.mts.map +1 -0
- package/dist/backends/android-instrumentation/errors.mjs +73 -0
- package/dist/backends/android-instrumentation/errors.mjs.map +1 -0
- package/dist/backends/android-instrumentation/installer.cjs +235 -0
- package/dist/backends/android-instrumentation/installer.cjs.map +1 -0
- package/dist/backends/android-instrumentation/installer.d.cts +123 -0
- package/dist/backends/android-instrumentation/installer.d.cts.map +1 -0
- package/dist/backends/android-instrumentation/installer.d.mts +123 -0
- package/dist/backends/android-instrumentation/installer.d.mts.map +1 -0
- package/dist/backends/android-instrumentation/installer.mjs +225 -0
- package/dist/backends/android-instrumentation/installer.mjs.map +1 -0
- package/dist/backends/android-instrumentation/signer.cjs +435 -0
- package/dist/backends/android-instrumentation/signer.cjs.map +1 -0
- package/dist/backends/android-instrumentation/signer.d.cts +40 -0
- package/dist/backends/android-instrumentation/signer.d.cts.map +1 -0
- package/dist/backends/android-instrumentation/signer.d.mts +40 -0
- package/dist/backends/android-instrumentation/signer.d.mts.map +1 -0
- package/dist/backends/android-instrumentation/signer.mjs +431 -0
- package/dist/backends/android-instrumentation/signer.mjs.map +1 -0
- package/dist/backends/android-instrumentation/snapshot.cjs +159 -0
- package/dist/backends/android-instrumentation/snapshot.cjs.map +1 -0
- package/dist/backends/android-instrumentation/snapshot.d.cts +67 -0
- package/dist/backends/android-instrumentation/snapshot.d.cts.map +1 -0
- package/dist/backends/android-instrumentation/snapshot.d.mts +67 -0
- package/dist/backends/android-instrumentation/snapshot.d.mts.map +1 -0
- package/dist/backends/android-instrumentation/snapshot.mjs +154 -0
- package/dist/backends/android-instrumentation/snapshot.mjs.map +1 -0
- package/dist/backends/appium-backend.cjs +19 -1
- package/dist/backends/appium-backend.cjs.map +1 -1
- package/dist/backends/appium-backend.d.cts +1 -0
- package/dist/backends/appium-backend.d.cts.map +1 -1
- package/dist/backends/appium-backend.d.mts +1 -0
- package/dist/backends/appium-backend.d.mts.map +1 -1
- package/dist/backends/appium-backend.mjs +19 -1
- package/dist/backends/appium-backend.mjs.map +1 -1
- package/dist/backends/idb-backend.cjs +128 -31
- package/dist/backends/idb-backend.cjs.map +1 -1
- package/dist/backends/idb-backend.d.cts +1 -0
- package/dist/backends/idb-backend.d.cts.map +1 -1
- package/dist/backends/idb-backend.d.mts +1 -0
- package/dist/backends/idb-backend.d.mts.map +1 -1
- package/dist/backends/idb-backend.mjs +129 -32
- package/dist/backends/idb-backend.mjs.map +1 -1
- package/dist/backends/index.cjs +3 -0
- package/dist/backends/index.cjs.map +1 -1
- package/dist/backends/index.d.cts.map +1 -1
- package/dist/backends/index.d.mts.map +1 -1
- package/dist/backends/index.mjs +3 -0
- package/dist/backends/index.mjs.map +1 -1
- package/dist/backends/types.cjs.map +1 -1
- package/dist/backends/types.d.cts +6 -0
- package/dist/backends/types.d.cts.map +1 -1
- package/dist/backends/types.d.mts +6 -0
- package/dist/backends/types.d.mts.map +1 -1
- package/dist/backends/types.mjs.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/tools/swipe.cjs +3 -1
- package/dist/tools/swipe.cjs.map +1 -1
- package/dist/tools/swipe.d.cts.map +1 -1
- package/dist/tools/swipe.d.mts.map +1 -1
- package/dist/tools/swipe.mjs +3 -1
- package/dist/tools/swipe.mjs.map +1 -1
- package/dist/utils/output-path.cjs +13 -0
- package/dist/utils/output-path.cjs.map +1 -1
- package/dist/utils/output-path.d.cts +10 -0
- package/dist/utils/output-path.d.cts.map +1 -1
- package/dist/utils/output-path.d.mts +10 -0
- package/dist/utils/output-path.d.mts.map +1 -1
- package/dist/utils/output-path.mjs +12 -0
- package/dist/utils/output-path.mjs.map +1 -1
- package/dist/utils/platform.cjs +65 -7
- package/dist/utils/platform.cjs.map +1 -1
- package/dist/utils/platform.d.cts +33 -0
- package/dist/utils/platform.d.cts.map +1 -1
- package/dist/utils/platform.d.mts +33 -0
- package/dist/utils/platform.d.mts.map +1 -1
- package/dist/utils/platform.mjs +63 -7
- package/dist/utils/platform.mjs.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot.cjs","sourceRoot":"","sources":["../../../src/backends/android-instrumentation/snapshot.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Parser for the in-repo Android snapshot-helper instrumentation.\n *\n * The stock `uiautomator dump` calls `UiAutomation.waitForIdle(1000, 10000)`,\n * which never returns on a continuously-redrawing React Native screen (the\n * MetaMask homepage emits accessibility events at ~10Hz indefinitely). The\n * helper APK (`io.metamask.devicemcp.snapshothelper/.SnapshotInstrumentation`)\n * skips that idle wait and streams the hierarchy back as chunked base64 over\n * `am instrument` status records.\n *\n * This module contains only the pure parsing of that output so it can be tested\n * without a device. The orchestration (install, invoke, fall back) lives in the\n * ADB backend.\n */\n\n/** Emitted as `agentDeviceProtocol` in every status and result record. */\nexport const HELPER_PROTOCOL = 'device-mcp-snapshot-helper-v1';\n\n/** The instrumentation component `am instrument` targets. */\nexport const HELPER_PACKAGE = 'io.metamask.devicemcp.snapshothelper';\n\n/** Fully-qualified instrumentation runner name. */\nexport const HELPER_INSTRUMENTATION = `${HELPER_PACKAGE}/.SnapshotInstrumentation`;\n\n/**\n * A single decoded status record from an `am instrument -w` run.\n *\n * `am instrument` prints key/value lines under `INSTRUMENTATION_STATUS:` and\n * `INSTRUMENTATION_RESULT:` banners. The helper emits one status record per\n * hierarchy chunk and one final result record.\n */\ntype Record = Map<string, string>;\n\n/**\n * Parse a value out of a raw `am instrument` line.\n *\n * @param line - A single output line, already stripped of its trailing CR.\n * @param prefix - The banner prefix (`INSTRUMENTATION_STATUS: ` etc.).\n * @returns The `key`/`value` pair, or null when the line is not a `key=value`.\n */\nfunction parseKeyValue(\n line: string,\n prefix: string,\n): { key: string; value: string } | null {\n if (!line.startsWith(prefix)) {\n return null;\n }\n const body = line.slice(prefix.length);\n const eq = body.indexOf('=');\n if (eq === -1) {\n return null;\n }\n return { key: body.slice(0, eq), value: body.slice(eq + 1) };\n}\n\nconst STATUS_PREFIX = 'INSTRUMENTATION_STATUS: ';\n\nconst RESULT_PREFIX = 'INSTRUMENTATION_RESULT: ';\n\nconst STATUS_CODE_PREFIX = 'INSTRUMENTATION_STATUS_CODE:';\n\nconst RESULT_CODE_PREFIX = 'INSTRUMENTATION_CODE:';\n\n/**\n * `INSTRUMENTATION_CODE: -1` is `Activity.RESULT_FIRST_USER`, the helper's\n * success sentinel. Any other terminal code means the run did not finish\n * cleanly.\n */\nconst SUCCESS_INSTRUMENTATION_CODE = '-1';\n\n/**\n * Structured view of one `am instrument` run's output.\n */\ntype ParsedInstrumentationOutput = {\n /** Status records grouped by their `chunkIndex`. */\n chunks: Map<number, string>;\n /** The `chunkCount` the helper advertised, if any status record carried it. */\n chunkCount: number | null;\n /** Key/value pairs from the final `INSTRUMENTATION_RESULT:` record. */\n result: Record;\n /** The terminal `INSTRUMENTATION_CODE:` value, if present. */\n instrumentationCode: string | null;\n};\n\n/**\n * Split raw `am instrument -w` stdout into chunks, result, and terminal code.\n *\n * Handles the CRLF line endings adb injects and tolerates duplicate or\n * interleaved status records by keying chunks on their advertised index.\n *\n * @param stdout - Raw stdout from `adb shell am instrument -w ...`.\n * @returns The grouped records.\n */\nexport function parseInstrumentationOutput(\n stdout: string,\n): ParsedInstrumentationOutput {\n const chunks = new Map<number, string>();\n const result: Record = new Map();\n let chunkCount: number | null = null;\n let instrumentationCode: string | null = null;\n\n // adb terminates lines with CRLF; the trailing CR would corrupt base64.\n const lines = stdout.replace(/\\r/gu, '').split('\\n');\n\n let pendingChunkIndex: number | null = null;\n\n for (const line of lines) {\n const status = parseKeyValue(line, STATUS_PREFIX);\n if (status) {\n if (status.key === 'chunkIndex') {\n const index = Number.parseInt(status.value, 10);\n pendingChunkIndex = Number.isNaN(index) ? null : index;\n } else if (status.key === 'chunkCount') {\n const count = Number.parseInt(status.value, 10);\n if (!Number.isNaN(count)) {\n chunkCount = count;\n }\n } else if (status.key === 'payloadBase64' && pendingChunkIndex !== null) {\n chunks.set(pendingChunkIndex, status.value);\n }\n continue;\n }\n\n const resultPair = parseKeyValue(line, RESULT_PREFIX);\n if (resultPair) {\n result.set(resultPair.key, resultPair.value);\n continue;\n }\n\n if (line.startsWith(STATUS_CODE_PREFIX)) {\n // A status record has ended; the next chunk needs its own index line.\n pendingChunkIndex = null;\n continue;\n }\n\n if (line.startsWith(RESULT_CODE_PREFIX)) {\n instrumentationCode = line.slice(RESULT_CODE_PREFIX.length).trim();\n }\n }\n\n return { chunks, chunkCount, result, instrumentationCode };\n}\n\n/**\n * Reassemble the hierarchy XML from an `am instrument` run.\n *\n * Each chunk is an independently valid base64 encoding of a raw byte slice\n * (`Base64.NO_WRAP`), so the chunks must be decoded individually and their\n * bytes concatenated. Concatenating the base64 strings first and decoding once\n * corrupts the payload wherever a slice length is not a multiple of three.\n *\n * @param stdout - Raw stdout from `adb shell am instrument -w ...`.\n * @returns The reassembled hierarchy XML.\n * @throws If the helper reported failure, chunks are missing, or the payload is\n * not complete hierarchy XML.\n */\nexport function reassembleInstrumentationXml(stdout: string): string {\n const { chunks, chunkCount, result, instrumentationCode } =\n parseInstrumentationOutput(stdout);\n\n if (result.size === 0) {\n // No `INSTRUMENTATION_RESULT:` record at all: `am` was killed mid-stream\n // or the runner crashed before finishing. Distinct from a clean failure.\n throw new Error(\n 'Snapshot helper produced no result record (the instrumentation was ' +\n 'interrupted before it finished).',\n );\n }\n\n if (result.get('ok') !== 'true') {\n const message = result.get('message');\n const errorType = result.get('errorType');\n const detail = [errorType, message].filter(Boolean).join(': ');\n throw new Error(\n `Snapshot helper reported failure${detail ? ` (${detail})` : ''}.`,\n );\n }\n\n if (\n instrumentationCode !== null &&\n instrumentationCode !== SUCCESS_INSTRUMENTATION_CODE\n ) {\n throw new Error(\n `Snapshot helper ended with code ${instrumentationCode} (expected ${SUCCESS_INSTRUMENTATION_CODE}).`,\n );\n }\n\n const expectedCount = chunkCount ?? chunks.size;\n if (expectedCount === 0) {\n throw new Error('Snapshot helper produced no hierarchy chunks.');\n }\n\n const parts: Buffer[] = [];\n const missing: number[] = [];\n for (let index = 0; index < expectedCount; index += 1) {\n const payload = chunks.get(index);\n if (payload === undefined) {\n missing.push(index);\n continue;\n }\n parts.push(Buffer.from(payload, 'base64'));\n }\n\n if (missing.length > 0) {\n throw new Error(\n `Snapshot helper output was missing ${missing.length} of ${expectedCount} chunks (indices ${missing.slice(0, 10).join(', ')}${missing.length > 10 ? ', …' : ''}).`,\n );\n }\n\n const xml = Buffer.concat(parts).toString('utf8');\n\n if (!xml.includes('<hierarchy') || !xml.includes('</hierarchy>')) {\n throw new Error(\n 'Snapshot helper output did not contain complete hierarchy XML.',\n );\n }\n\n return xml;\n}\n"]}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser for the in-repo Android snapshot-helper instrumentation.
|
|
3
|
+
*
|
|
4
|
+
* The stock `uiautomator dump` calls `UiAutomation.waitForIdle(1000, 10000)`,
|
|
5
|
+
* which never returns on a continuously-redrawing React Native screen (the
|
|
6
|
+
* MetaMask homepage emits accessibility events at ~10Hz indefinitely). The
|
|
7
|
+
* helper APK (`io.metamask.devicemcp.snapshothelper/.SnapshotInstrumentation`)
|
|
8
|
+
* skips that idle wait and streams the hierarchy back as chunked base64 over
|
|
9
|
+
* `am instrument` status records.
|
|
10
|
+
*
|
|
11
|
+
* This module contains only the pure parsing of that output so it can be tested
|
|
12
|
+
* without a device. The orchestration (install, invoke, fall back) lives in the
|
|
13
|
+
* ADB backend.
|
|
14
|
+
*/
|
|
15
|
+
/** Emitted as `agentDeviceProtocol` in every status and result record. */
|
|
16
|
+
export declare const HELPER_PROTOCOL = "device-mcp-snapshot-helper-v1";
|
|
17
|
+
/** The instrumentation component `am instrument` targets. */
|
|
18
|
+
export declare const HELPER_PACKAGE = "io.metamask.devicemcp.snapshothelper";
|
|
19
|
+
/** Fully-qualified instrumentation runner name. */
|
|
20
|
+
export declare const HELPER_INSTRUMENTATION = "io.metamask.devicemcp.snapshothelper/.SnapshotInstrumentation";
|
|
21
|
+
/**
|
|
22
|
+
* A single decoded status record from an `am instrument -w` run.
|
|
23
|
+
*
|
|
24
|
+
* `am instrument` prints key/value lines under `INSTRUMENTATION_STATUS:` and
|
|
25
|
+
* `INSTRUMENTATION_RESULT:` banners. The helper emits one status record per
|
|
26
|
+
* hierarchy chunk and one final result record.
|
|
27
|
+
*/
|
|
28
|
+
type Record = Map<string, string>;
|
|
29
|
+
/**
|
|
30
|
+
* Structured view of one `am instrument` run's output.
|
|
31
|
+
*/
|
|
32
|
+
type ParsedInstrumentationOutput = {
|
|
33
|
+
/** Status records grouped by their `chunkIndex`. */
|
|
34
|
+
chunks: Map<number, string>;
|
|
35
|
+
/** The `chunkCount` the helper advertised, if any status record carried it. */
|
|
36
|
+
chunkCount: number | null;
|
|
37
|
+
/** Key/value pairs from the final `INSTRUMENTATION_RESULT:` record. */
|
|
38
|
+
result: Record;
|
|
39
|
+
/** The terminal `INSTRUMENTATION_CODE:` value, if present. */
|
|
40
|
+
instrumentationCode: string | null;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Split raw `am instrument -w` stdout into chunks, result, and terminal code.
|
|
44
|
+
*
|
|
45
|
+
* Handles the CRLF line endings adb injects and tolerates duplicate or
|
|
46
|
+
* interleaved status records by keying chunks on their advertised index.
|
|
47
|
+
*
|
|
48
|
+
* @param stdout - Raw stdout from `adb shell am instrument -w ...`.
|
|
49
|
+
* @returns The grouped records.
|
|
50
|
+
*/
|
|
51
|
+
export declare function parseInstrumentationOutput(stdout: string): ParsedInstrumentationOutput;
|
|
52
|
+
/**
|
|
53
|
+
* Reassemble the hierarchy XML from an `am instrument` run.
|
|
54
|
+
*
|
|
55
|
+
* Each chunk is an independently valid base64 encoding of a raw byte slice
|
|
56
|
+
* (`Base64.NO_WRAP`), so the chunks must be decoded individually and their
|
|
57
|
+
* bytes concatenated. Concatenating the base64 strings first and decoding once
|
|
58
|
+
* corrupts the payload wherever a slice length is not a multiple of three.
|
|
59
|
+
*
|
|
60
|
+
* @param stdout - Raw stdout from `adb shell am instrument -w ...`.
|
|
61
|
+
* @returns The reassembled hierarchy XML.
|
|
62
|
+
* @throws If the helper reported failure, chunks are missing, or the payload is
|
|
63
|
+
* not complete hierarchy XML.
|
|
64
|
+
*/
|
|
65
|
+
export declare function reassembleInstrumentationXml(stdout: string): string;
|
|
66
|
+
export {};
|
|
67
|
+
//# sourceMappingURL=snapshot.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot.d.cts","sourceRoot":"","sources":["../../../src/backends/android-instrumentation/snapshot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,0EAA0E;AAC1E,eAAO,MAAM,eAAe,kCAAkC,CAAC;AAE/D,6DAA6D;AAC7D,eAAO,MAAM,cAAc,yCAAyC,CAAC;AAErE,mDAAmD;AACnD,eAAO,MAAM,sBAAsB,kEAA+C,CAAC;AAEnF;;;;;;GAMG;AACH,KAAK,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAuClC;;GAEG;AACH,KAAK,2BAA2B,GAAG;IACjC,oDAAoD;IACpD,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,+EAA+E;IAC/E,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,uEAAuE;IACvE,MAAM,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,MAAM,GACb,2BAA2B,CA8C7B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CA8DnE"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser for the in-repo Android snapshot-helper instrumentation.
|
|
3
|
+
*
|
|
4
|
+
* The stock `uiautomator dump` calls `UiAutomation.waitForIdle(1000, 10000)`,
|
|
5
|
+
* which never returns on a continuously-redrawing React Native screen (the
|
|
6
|
+
* MetaMask homepage emits accessibility events at ~10Hz indefinitely). The
|
|
7
|
+
* helper APK (`io.metamask.devicemcp.snapshothelper/.SnapshotInstrumentation`)
|
|
8
|
+
* skips that idle wait and streams the hierarchy back as chunked base64 over
|
|
9
|
+
* `am instrument` status records.
|
|
10
|
+
*
|
|
11
|
+
* This module contains only the pure parsing of that output so it can be tested
|
|
12
|
+
* without a device. The orchestration (install, invoke, fall back) lives in the
|
|
13
|
+
* ADB backend.
|
|
14
|
+
*/
|
|
15
|
+
/** Emitted as `agentDeviceProtocol` in every status and result record. */
|
|
16
|
+
export declare const HELPER_PROTOCOL = "device-mcp-snapshot-helper-v1";
|
|
17
|
+
/** The instrumentation component `am instrument` targets. */
|
|
18
|
+
export declare const HELPER_PACKAGE = "io.metamask.devicemcp.snapshothelper";
|
|
19
|
+
/** Fully-qualified instrumentation runner name. */
|
|
20
|
+
export declare const HELPER_INSTRUMENTATION = "io.metamask.devicemcp.snapshothelper/.SnapshotInstrumentation";
|
|
21
|
+
/**
|
|
22
|
+
* A single decoded status record from an `am instrument -w` run.
|
|
23
|
+
*
|
|
24
|
+
* `am instrument` prints key/value lines under `INSTRUMENTATION_STATUS:` and
|
|
25
|
+
* `INSTRUMENTATION_RESULT:` banners. The helper emits one status record per
|
|
26
|
+
* hierarchy chunk and one final result record.
|
|
27
|
+
*/
|
|
28
|
+
type Record = Map<string, string>;
|
|
29
|
+
/**
|
|
30
|
+
* Structured view of one `am instrument` run's output.
|
|
31
|
+
*/
|
|
32
|
+
type ParsedInstrumentationOutput = {
|
|
33
|
+
/** Status records grouped by their `chunkIndex`. */
|
|
34
|
+
chunks: Map<number, string>;
|
|
35
|
+
/** The `chunkCount` the helper advertised, if any status record carried it. */
|
|
36
|
+
chunkCount: number | null;
|
|
37
|
+
/** Key/value pairs from the final `INSTRUMENTATION_RESULT:` record. */
|
|
38
|
+
result: Record;
|
|
39
|
+
/** The terminal `INSTRUMENTATION_CODE:` value, if present. */
|
|
40
|
+
instrumentationCode: string | null;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Split raw `am instrument -w` stdout into chunks, result, and terminal code.
|
|
44
|
+
*
|
|
45
|
+
* Handles the CRLF line endings adb injects and tolerates duplicate or
|
|
46
|
+
* interleaved status records by keying chunks on their advertised index.
|
|
47
|
+
*
|
|
48
|
+
* @param stdout - Raw stdout from `adb shell am instrument -w ...`.
|
|
49
|
+
* @returns The grouped records.
|
|
50
|
+
*/
|
|
51
|
+
export declare function parseInstrumentationOutput(stdout: string): ParsedInstrumentationOutput;
|
|
52
|
+
/**
|
|
53
|
+
* Reassemble the hierarchy XML from an `am instrument` run.
|
|
54
|
+
*
|
|
55
|
+
* Each chunk is an independently valid base64 encoding of a raw byte slice
|
|
56
|
+
* (`Base64.NO_WRAP`), so the chunks must be decoded individually and their
|
|
57
|
+
* bytes concatenated. Concatenating the base64 strings first and decoding once
|
|
58
|
+
* corrupts the payload wherever a slice length is not a multiple of three.
|
|
59
|
+
*
|
|
60
|
+
* @param stdout - Raw stdout from `adb shell am instrument -w ...`.
|
|
61
|
+
* @returns The reassembled hierarchy XML.
|
|
62
|
+
* @throws If the helper reported failure, chunks are missing, or the payload is
|
|
63
|
+
* not complete hierarchy XML.
|
|
64
|
+
*/
|
|
65
|
+
export declare function reassembleInstrumentationXml(stdout: string): string;
|
|
66
|
+
export {};
|
|
67
|
+
//# sourceMappingURL=snapshot.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot.d.mts","sourceRoot":"","sources":["../../../src/backends/android-instrumentation/snapshot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,0EAA0E;AAC1E,eAAO,MAAM,eAAe,kCAAkC,CAAC;AAE/D,6DAA6D;AAC7D,eAAO,MAAM,cAAc,yCAAyC,CAAC;AAErE,mDAAmD;AACnD,eAAO,MAAM,sBAAsB,kEAA+C,CAAC;AAEnF;;;;;;GAMG;AACH,KAAK,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAuClC;;GAEG;AACH,KAAK,2BAA2B,GAAG;IACjC,oDAAoD;IACpD,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,+EAA+E;IAC/E,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,uEAAuE;IACvE,MAAM,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,MAAM,GACb,2BAA2B,CA8C7B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CA8DnE"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser for the in-repo Android snapshot-helper instrumentation.
|
|
3
|
+
*
|
|
4
|
+
* The stock `uiautomator dump` calls `UiAutomation.waitForIdle(1000, 10000)`,
|
|
5
|
+
* which never returns on a continuously-redrawing React Native screen (the
|
|
6
|
+
* MetaMask homepage emits accessibility events at ~10Hz indefinitely). The
|
|
7
|
+
* helper APK (`io.metamask.devicemcp.snapshothelper/.SnapshotInstrumentation`)
|
|
8
|
+
* skips that idle wait and streams the hierarchy back as chunked base64 over
|
|
9
|
+
* `am instrument` status records.
|
|
10
|
+
*
|
|
11
|
+
* This module contains only the pure parsing of that output so it can be tested
|
|
12
|
+
* without a device. The orchestration (install, invoke, fall back) lives in the
|
|
13
|
+
* ADB backend.
|
|
14
|
+
*/
|
|
15
|
+
/** Emitted as `agentDeviceProtocol` in every status and result record. */
|
|
16
|
+
export const HELPER_PROTOCOL = 'device-mcp-snapshot-helper-v1';
|
|
17
|
+
/** The instrumentation component `am instrument` targets. */
|
|
18
|
+
export const HELPER_PACKAGE = 'io.metamask.devicemcp.snapshothelper';
|
|
19
|
+
/** Fully-qualified instrumentation runner name. */
|
|
20
|
+
export const HELPER_INSTRUMENTATION = `${HELPER_PACKAGE}/.SnapshotInstrumentation`;
|
|
21
|
+
/**
|
|
22
|
+
* Parse a value out of a raw `am instrument` line.
|
|
23
|
+
*
|
|
24
|
+
* @param line - A single output line, already stripped of its trailing CR.
|
|
25
|
+
* @param prefix - The banner prefix (`INSTRUMENTATION_STATUS: ` etc.).
|
|
26
|
+
* @returns The `key`/`value` pair, or null when the line is not a `key=value`.
|
|
27
|
+
*/
|
|
28
|
+
function parseKeyValue(line, prefix) {
|
|
29
|
+
if (!line.startsWith(prefix)) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
const body = line.slice(prefix.length);
|
|
33
|
+
const eq = body.indexOf('=');
|
|
34
|
+
if (eq === -1) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
return { key: body.slice(0, eq), value: body.slice(eq + 1) };
|
|
38
|
+
}
|
|
39
|
+
const STATUS_PREFIX = 'INSTRUMENTATION_STATUS: ';
|
|
40
|
+
const RESULT_PREFIX = 'INSTRUMENTATION_RESULT: ';
|
|
41
|
+
const STATUS_CODE_PREFIX = 'INSTRUMENTATION_STATUS_CODE:';
|
|
42
|
+
const RESULT_CODE_PREFIX = 'INSTRUMENTATION_CODE:';
|
|
43
|
+
/**
|
|
44
|
+
* `INSTRUMENTATION_CODE: -1` is `Activity.RESULT_FIRST_USER`, the helper's
|
|
45
|
+
* success sentinel. Any other terminal code means the run did not finish
|
|
46
|
+
* cleanly.
|
|
47
|
+
*/
|
|
48
|
+
const SUCCESS_INSTRUMENTATION_CODE = '-1';
|
|
49
|
+
/**
|
|
50
|
+
* Split raw `am instrument -w` stdout into chunks, result, and terminal code.
|
|
51
|
+
*
|
|
52
|
+
* Handles the CRLF line endings adb injects and tolerates duplicate or
|
|
53
|
+
* interleaved status records by keying chunks on their advertised index.
|
|
54
|
+
*
|
|
55
|
+
* @param stdout - Raw stdout from `adb shell am instrument -w ...`.
|
|
56
|
+
* @returns The grouped records.
|
|
57
|
+
*/
|
|
58
|
+
export function parseInstrumentationOutput(stdout) {
|
|
59
|
+
const chunks = new Map();
|
|
60
|
+
const result = new Map();
|
|
61
|
+
let chunkCount = null;
|
|
62
|
+
let instrumentationCode = null;
|
|
63
|
+
// adb terminates lines with CRLF; the trailing CR would corrupt base64.
|
|
64
|
+
const lines = stdout.replace(/\r/gu, '').split('\n');
|
|
65
|
+
let pendingChunkIndex = null;
|
|
66
|
+
for (const line of lines) {
|
|
67
|
+
const status = parseKeyValue(line, STATUS_PREFIX);
|
|
68
|
+
if (status) {
|
|
69
|
+
if (status.key === 'chunkIndex') {
|
|
70
|
+
const index = Number.parseInt(status.value, 10);
|
|
71
|
+
pendingChunkIndex = Number.isNaN(index) ? null : index;
|
|
72
|
+
}
|
|
73
|
+
else if (status.key === 'chunkCount') {
|
|
74
|
+
const count = Number.parseInt(status.value, 10);
|
|
75
|
+
if (!Number.isNaN(count)) {
|
|
76
|
+
chunkCount = count;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
else if (status.key === 'payloadBase64' && pendingChunkIndex !== null) {
|
|
80
|
+
chunks.set(pendingChunkIndex, status.value);
|
|
81
|
+
}
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const resultPair = parseKeyValue(line, RESULT_PREFIX);
|
|
85
|
+
if (resultPair) {
|
|
86
|
+
result.set(resultPair.key, resultPair.value);
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (line.startsWith(STATUS_CODE_PREFIX)) {
|
|
90
|
+
// A status record has ended; the next chunk needs its own index line.
|
|
91
|
+
pendingChunkIndex = null;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (line.startsWith(RESULT_CODE_PREFIX)) {
|
|
95
|
+
instrumentationCode = line.slice(RESULT_CODE_PREFIX.length).trim();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return { chunks, chunkCount, result, instrumentationCode };
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Reassemble the hierarchy XML from an `am instrument` run.
|
|
102
|
+
*
|
|
103
|
+
* Each chunk is an independently valid base64 encoding of a raw byte slice
|
|
104
|
+
* (`Base64.NO_WRAP`), so the chunks must be decoded individually and their
|
|
105
|
+
* bytes concatenated. Concatenating the base64 strings first and decoding once
|
|
106
|
+
* corrupts the payload wherever a slice length is not a multiple of three.
|
|
107
|
+
*
|
|
108
|
+
* @param stdout - Raw stdout from `adb shell am instrument -w ...`.
|
|
109
|
+
* @returns The reassembled hierarchy XML.
|
|
110
|
+
* @throws If the helper reported failure, chunks are missing, or the payload is
|
|
111
|
+
* not complete hierarchy XML.
|
|
112
|
+
*/
|
|
113
|
+
export function reassembleInstrumentationXml(stdout) {
|
|
114
|
+
const { chunks, chunkCount, result, instrumentationCode } = parseInstrumentationOutput(stdout);
|
|
115
|
+
if (result.size === 0) {
|
|
116
|
+
// No `INSTRUMENTATION_RESULT:` record at all: `am` was killed mid-stream
|
|
117
|
+
// or the runner crashed before finishing. Distinct from a clean failure.
|
|
118
|
+
throw new Error('Snapshot helper produced no result record (the instrumentation was ' +
|
|
119
|
+
'interrupted before it finished).');
|
|
120
|
+
}
|
|
121
|
+
if (result.get('ok') !== 'true') {
|
|
122
|
+
const message = result.get('message');
|
|
123
|
+
const errorType = result.get('errorType');
|
|
124
|
+
const detail = [errorType, message].filter(Boolean).join(': ');
|
|
125
|
+
throw new Error(`Snapshot helper reported failure${detail ? ` (${detail})` : ''}.`);
|
|
126
|
+
}
|
|
127
|
+
if (instrumentationCode !== null &&
|
|
128
|
+
instrumentationCode !== SUCCESS_INSTRUMENTATION_CODE) {
|
|
129
|
+
throw new Error(`Snapshot helper ended with code ${instrumentationCode} (expected ${SUCCESS_INSTRUMENTATION_CODE}).`);
|
|
130
|
+
}
|
|
131
|
+
const expectedCount = chunkCount ?? chunks.size;
|
|
132
|
+
if (expectedCount === 0) {
|
|
133
|
+
throw new Error('Snapshot helper produced no hierarchy chunks.');
|
|
134
|
+
}
|
|
135
|
+
const parts = [];
|
|
136
|
+
const missing = [];
|
|
137
|
+
for (let index = 0; index < expectedCount; index += 1) {
|
|
138
|
+
const payload = chunks.get(index);
|
|
139
|
+
if (payload === undefined) {
|
|
140
|
+
missing.push(index);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
parts.push(Buffer.from(payload, 'base64'));
|
|
144
|
+
}
|
|
145
|
+
if (missing.length > 0) {
|
|
146
|
+
throw new Error(`Snapshot helper output was missing ${missing.length} of ${expectedCount} chunks (indices ${missing.slice(0, 10).join(', ')}${missing.length > 10 ? ', …' : ''}).`);
|
|
147
|
+
}
|
|
148
|
+
const xml = Buffer.concat(parts).toString('utf8');
|
|
149
|
+
if (!xml.includes('<hierarchy') || !xml.includes('</hierarchy>')) {
|
|
150
|
+
throw new Error('Snapshot helper output did not contain complete hierarchy XML.');
|
|
151
|
+
}
|
|
152
|
+
return xml;
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=snapshot.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot.mjs","sourceRoot":"","sources":["../../../src/backends/android-instrumentation/snapshot.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Parser for the in-repo Android snapshot-helper instrumentation.\n *\n * The stock `uiautomator dump` calls `UiAutomation.waitForIdle(1000, 10000)`,\n * which never returns on a continuously-redrawing React Native screen (the\n * MetaMask homepage emits accessibility events at ~10Hz indefinitely). The\n * helper APK (`io.metamask.devicemcp.snapshothelper/.SnapshotInstrumentation`)\n * skips that idle wait and streams the hierarchy back as chunked base64 over\n * `am instrument` status records.\n *\n * This module contains only the pure parsing of that output so it can be tested\n * without a device. The orchestration (install, invoke, fall back) lives in the\n * ADB backend.\n */\n\n/** Emitted as `agentDeviceProtocol` in every status and result record. */\nexport const HELPER_PROTOCOL = 'device-mcp-snapshot-helper-v1';\n\n/** The instrumentation component `am instrument` targets. */\nexport const HELPER_PACKAGE = 'io.metamask.devicemcp.snapshothelper';\n\n/** Fully-qualified instrumentation runner name. */\nexport const HELPER_INSTRUMENTATION = `${HELPER_PACKAGE}/.SnapshotInstrumentation`;\n\n/**\n * A single decoded status record from an `am instrument -w` run.\n *\n * `am instrument` prints key/value lines under `INSTRUMENTATION_STATUS:` and\n * `INSTRUMENTATION_RESULT:` banners. The helper emits one status record per\n * hierarchy chunk and one final result record.\n */\ntype Record = Map<string, string>;\n\n/**\n * Parse a value out of a raw `am instrument` line.\n *\n * @param line - A single output line, already stripped of its trailing CR.\n * @param prefix - The banner prefix (`INSTRUMENTATION_STATUS: ` etc.).\n * @returns The `key`/`value` pair, or null when the line is not a `key=value`.\n */\nfunction parseKeyValue(\n line: string,\n prefix: string,\n): { key: string; value: string } | null {\n if (!line.startsWith(prefix)) {\n return null;\n }\n const body = line.slice(prefix.length);\n const eq = body.indexOf('=');\n if (eq === -1) {\n return null;\n }\n return { key: body.slice(0, eq), value: body.slice(eq + 1) };\n}\n\nconst STATUS_PREFIX = 'INSTRUMENTATION_STATUS: ';\n\nconst RESULT_PREFIX = 'INSTRUMENTATION_RESULT: ';\n\nconst STATUS_CODE_PREFIX = 'INSTRUMENTATION_STATUS_CODE:';\n\nconst RESULT_CODE_PREFIX = 'INSTRUMENTATION_CODE:';\n\n/**\n * `INSTRUMENTATION_CODE: -1` is `Activity.RESULT_FIRST_USER`, the helper's\n * success sentinel. Any other terminal code means the run did not finish\n * cleanly.\n */\nconst SUCCESS_INSTRUMENTATION_CODE = '-1';\n\n/**\n * Structured view of one `am instrument` run's output.\n */\ntype ParsedInstrumentationOutput = {\n /** Status records grouped by their `chunkIndex`. */\n chunks: Map<number, string>;\n /** The `chunkCount` the helper advertised, if any status record carried it. */\n chunkCount: number | null;\n /** Key/value pairs from the final `INSTRUMENTATION_RESULT:` record. */\n result: Record;\n /** The terminal `INSTRUMENTATION_CODE:` value, if present. */\n instrumentationCode: string | null;\n};\n\n/**\n * Split raw `am instrument -w` stdout into chunks, result, and terminal code.\n *\n * Handles the CRLF line endings adb injects and tolerates duplicate or\n * interleaved status records by keying chunks on their advertised index.\n *\n * @param stdout - Raw stdout from `adb shell am instrument -w ...`.\n * @returns The grouped records.\n */\nexport function parseInstrumentationOutput(\n stdout: string,\n): ParsedInstrumentationOutput {\n const chunks = new Map<number, string>();\n const result: Record = new Map();\n let chunkCount: number | null = null;\n let instrumentationCode: string | null = null;\n\n // adb terminates lines with CRLF; the trailing CR would corrupt base64.\n const lines = stdout.replace(/\\r/gu, '').split('\\n');\n\n let pendingChunkIndex: number | null = null;\n\n for (const line of lines) {\n const status = parseKeyValue(line, STATUS_PREFIX);\n if (status) {\n if (status.key === 'chunkIndex') {\n const index = Number.parseInt(status.value, 10);\n pendingChunkIndex = Number.isNaN(index) ? null : index;\n } else if (status.key === 'chunkCount') {\n const count = Number.parseInt(status.value, 10);\n if (!Number.isNaN(count)) {\n chunkCount = count;\n }\n } else if (status.key === 'payloadBase64' && pendingChunkIndex !== null) {\n chunks.set(pendingChunkIndex, status.value);\n }\n continue;\n }\n\n const resultPair = parseKeyValue(line, RESULT_PREFIX);\n if (resultPair) {\n result.set(resultPair.key, resultPair.value);\n continue;\n }\n\n if (line.startsWith(STATUS_CODE_PREFIX)) {\n // A status record has ended; the next chunk needs its own index line.\n pendingChunkIndex = null;\n continue;\n }\n\n if (line.startsWith(RESULT_CODE_PREFIX)) {\n instrumentationCode = line.slice(RESULT_CODE_PREFIX.length).trim();\n }\n }\n\n return { chunks, chunkCount, result, instrumentationCode };\n}\n\n/**\n * Reassemble the hierarchy XML from an `am instrument` run.\n *\n * Each chunk is an independently valid base64 encoding of a raw byte slice\n * (`Base64.NO_WRAP`), so the chunks must be decoded individually and their\n * bytes concatenated. Concatenating the base64 strings first and decoding once\n * corrupts the payload wherever a slice length is not a multiple of three.\n *\n * @param stdout - Raw stdout from `adb shell am instrument -w ...`.\n * @returns The reassembled hierarchy XML.\n * @throws If the helper reported failure, chunks are missing, or the payload is\n * not complete hierarchy XML.\n */\nexport function reassembleInstrumentationXml(stdout: string): string {\n const { chunks, chunkCount, result, instrumentationCode } =\n parseInstrumentationOutput(stdout);\n\n if (result.size === 0) {\n // No `INSTRUMENTATION_RESULT:` record at all: `am` was killed mid-stream\n // or the runner crashed before finishing. Distinct from a clean failure.\n throw new Error(\n 'Snapshot helper produced no result record (the instrumentation was ' +\n 'interrupted before it finished).',\n );\n }\n\n if (result.get('ok') !== 'true') {\n const message = result.get('message');\n const errorType = result.get('errorType');\n const detail = [errorType, message].filter(Boolean).join(': ');\n throw new Error(\n `Snapshot helper reported failure${detail ? ` (${detail})` : ''}.`,\n );\n }\n\n if (\n instrumentationCode !== null &&\n instrumentationCode !== SUCCESS_INSTRUMENTATION_CODE\n ) {\n throw new Error(\n `Snapshot helper ended with code ${instrumentationCode} (expected ${SUCCESS_INSTRUMENTATION_CODE}).`,\n );\n }\n\n const expectedCount = chunkCount ?? chunks.size;\n if (expectedCount === 0) {\n throw new Error('Snapshot helper produced no hierarchy chunks.');\n }\n\n const parts: Buffer[] = [];\n const missing: number[] = [];\n for (let index = 0; index < expectedCount; index += 1) {\n const payload = chunks.get(index);\n if (payload === undefined) {\n missing.push(index);\n continue;\n }\n parts.push(Buffer.from(payload, 'base64'));\n }\n\n if (missing.length > 0) {\n throw new Error(\n `Snapshot helper output was missing ${missing.length} of ${expectedCount} chunks (indices ${missing.slice(0, 10).join(', ')}${missing.length > 10 ? ', …' : ''}).`,\n );\n }\n\n const xml = Buffer.concat(parts).toString('utf8');\n\n if (!xml.includes('<hierarchy') || !xml.includes('</hierarchy>')) {\n throw new Error(\n 'Snapshot helper output did not contain complete hierarchy XML.',\n );\n }\n\n return xml;\n}\n"]}
|
|
@@ -10,7 +10,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
10
10
|
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");
|
|
11
11
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
12
|
};
|
|
13
|
-
var _AppiumBackend_instances, _AppiumBackend_client, _AppiumBackend_config, _AppiumBackend_recording, _AppiumBackend_recordingOutputPath, _AppiumBackend_getHierarchy, _AppiumBackend_getIosHierarchy, _AppiumBackend_requireClient;
|
|
13
|
+
var _AppiumBackend_instances, _AppiumBackend_client, _AppiumBackend_config, _AppiumBackend_recording, _AppiumBackend_recordingOutputPath, _AppiumBackend_getHierarchy, _AppiumBackend_getAndroidHierarchy, _AppiumBackend_getIosHierarchy, _AppiumBackend_requireClient;
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.AppiumBackend = void 0;
|
|
16
16
|
exports.parseAppiumAndroidHierarchy = parseAppiumAndroidHierarchy;
|
|
@@ -21,6 +21,7 @@ const output_path_js_1 = require("../utils/output-path.cjs");
|
|
|
21
21
|
class AppiumBackend {
|
|
22
22
|
constructor(config) {
|
|
23
23
|
_AppiumBackend_instances.add(this);
|
|
24
|
+
this.kind = 'appium';
|
|
24
25
|
_AppiumBackend_client.set(this, null);
|
|
25
26
|
_AppiumBackend_config.set(this, void 0);
|
|
26
27
|
_AppiumBackend_recording.set(this, false);
|
|
@@ -387,6 +388,23 @@ _AppiumBackend_client = new WeakMap(), _AppiumBackend_config = new WeakMap(), _A
|
|
|
387
388
|
if (this.platform === 'ios') {
|
|
388
389
|
return __classPrivateFieldGet(this, _AppiumBackend_instances, "m", _AppiumBackend_getIosHierarchy).call(this, client);
|
|
389
390
|
}
|
|
391
|
+
return __classPrivateFieldGet(this, _AppiumBackend_instances, "m", _AppiumBackend_getAndroidHierarchy).call(this, client);
|
|
392
|
+
}, _AppiumBackend_getAndroidHierarchy = async function _AppiumBackend_getAndroidHierarchy(client) {
|
|
393
|
+
// Stock `uiautomator dump` blocks on UiAutomation.waitForIdle before it
|
|
394
|
+
// will read the tree, so a screen with a continuous AccessibilityEvent
|
|
395
|
+
// stream (e.g. a React Native polling loop redrawing off-screen state)
|
|
396
|
+
// never goes idle and the dump fails with "could not get idle state".
|
|
397
|
+
// The UiAutomator2 server exposes `waitForIdleTimeout` as a setting;
|
|
398
|
+
// setting it to 0 disables the wait entirely so getPageSource returns the
|
|
399
|
+
// current hierarchy immediately regardless of event churn. This is the
|
|
400
|
+
// deterministic equivalent of the ADB backend's best-effort retry loop.
|
|
401
|
+
try {
|
|
402
|
+
await client.updateSettings({ waitForIdleTimeout: 0 });
|
|
403
|
+
}
|
|
404
|
+
catch {
|
|
405
|
+
// `waitForIdleTimeout` is unsupported on non-UiAutomator2 drivers (or
|
|
406
|
+
// very old ones); fall through and let getPageSource wait for idle.
|
|
407
|
+
}
|
|
390
408
|
return client.getPageSource();
|
|
391
409
|
}, _AppiumBackend_getIosHierarchy = async function _AppiumBackend_getIosHierarchy(client) {
|
|
392
410
|
// Limit hierarchy depth to avoid "JS stack is too deep" on complex iOS apps
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"appium-backend.cjs","sourceRoot":"","sources":["../../src/backends/appium-backend.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SessionConfig } from './session-file.js';\nimport type {\n DeviceBackend,\n DeviceButton,\n DeviceInfo,\n SnapshotResult,\n ScreenshotResult,\n ScreenshotFileResult,\n ScreenshotOptions,\n LogsResult,\n TapResult,\n AppStateResult,\n ElementQuery,\n UIElement,\n Platform,\n WindowSize,\n} from './types.js';\nimport { WebDriverClient, createSession } from './webdriver-client.js';\nimport {\n findElement,\n describeElement,\n computeSwipeEnd,\n} from '../utils/element.js';\nimport {\n resolveArtifactPath,\n writeArtifactFile,\n} from '../utils/output-path.js';\n\nexport class AppiumBackend implements DeviceBackend {\n readonly platform: Platform;\n\n #client: WebDriverClient | null = null;\n\n readonly #config: SessionConfig;\n\n #recording = false;\n\n #recordingOutputPath: string | null = null;\n\n constructor(config: SessionConfig) {\n this.#config = config;\n this.platform = config.platform;\n }\n\n async ensureConnected(): Promise<void> {\n if (this.#client) {\n return;\n }\n\n if (this.#config.mode === 'attach') {\n this.#client = new WebDriverClient(\n this.#config.appiumUrl,\n this.#config.sessionId,\n this.#config.auth,\n );\n await this.#client.getStatus();\n } else {\n const { client } = await createSession(\n this.#config.appiumUrl,\n this.#config.capabilities,\n this.#config.auth,\n );\n this.#client = client;\n }\n }\n\n async getDeviceInfo(): Promise<DeviceInfo> {\n const client = this.#requireClient();\n const details = await client.getSessionDetails();\n const caps = (details.capabilities ?? details) as Record<string, unknown>;\n\n return {\n platform: this.platform,\n deviceId: (caps.udid as string) ?? client.sessionId,\n name: (caps.deviceName as string) ?? 'Appium Device',\n osVersion: (caps.platformVersion as string) ?? 'Unknown',\n state: 'connected',\n };\n }\n\n async snapshot(): Promise<SnapshotResult> {\n const client = this.#requireClient();\n const raw = await this.#getHierarchy(client);\n const hierarchy =\n this.platform === 'ios'\n ? parseAppiumIosHierarchy(raw)\n : parseAppiumAndroidHierarchy(raw);\n\n return {\n platform: this.platform,\n hierarchy,\n raw,\n timestamp: Date.now(),\n };\n }\n\n async tapElement(query: ElementQuery): Promise<TapResult> {\n const snapshot = await this.snapshot();\n const element = findElement(snapshot.hierarchy, query);\n if (!element) {\n throw new Error(\n `Element not found: ${JSON.stringify(query)}\\n` +\n 'Use device_snapshot to inspect the current UI hierarchy.',\n );\n }\n\n const targetX = Math.round(element.frame.x + element.frame.width / 2);\n const targetY = Math.round(element.frame.y + element.frame.height / 2);\n await this.tapCoordinates(targetX, targetY);\n\n return {\n success: true,\n x: targetX,\n y: targetY,\n targetDescription: describeElement(element),\n };\n }\n\n async getElementText(query: ElementQuery): Promise<string> {\n const snapshot = await this.snapshot();\n const element = findElement(snapshot.hierarchy, query);\n if (!element) {\n throw new Error(\n `Element not found: ${JSON.stringify(query)}\\n` +\n 'Use device_snapshot to inspect the current UI hierarchy.',\n );\n }\n return element.label ?? element.value ?? '';\n }\n\n async tapCoordinates(targetX: number, targetY: number): Promise<void> {\n const client = this.#requireClient();\n await client.performActions([\n {\n type: 'pointer',\n id: 'finger1',\n parameters: { pointerType: 'touch' },\n actions: [\n { type: 'pointerMove', duration: 0, x: targetX, y: targetY },\n { type: 'pointerDown', button: 0 },\n { type: 'pause', duration: 50 },\n { type: 'pointerUp', button: 0 },\n ],\n },\n ]);\n await client.releaseActions();\n }\n\n async typeText(text: string): Promise<void> {\n const client = this.#requireClient();\n const activeElement = await client.findElement(\n 'xpath',\n '//*[@focused=\"true\"]',\n );\n const elementId = extractElementId(activeElement);\n await client.elementClear(elementId);\n await client.elementSendKeys(elementId, 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 const client = this.#requireClient();\n const dist = distance ?? 500;\n const sx = startX ?? 200;\n const sy = startY ?? 400;\n const [endX, endY] = computeSwipeEnd(sx, sy, direction, dist);\n\n await client.performActions([\n {\n type: 'pointer',\n id: 'finger1',\n parameters: { pointerType: 'touch' },\n actions: [\n { type: 'pointerMove', duration: 0, x: sx, y: sy },\n { type: 'pointerDown', button: 0 },\n { type: 'pointerMove', duration: 300, x: endX, y: endY },\n { type: 'pointerUp', button: 0 },\n ],\n },\n ]);\n await client.releaseActions();\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 snap = await this.snapshot();\n const element = findElement(snap.hierarchy, query);\n if (element) {\n return element;\n }\n await new Promise((resolve) => setTimeout(resolve, intervalMs));\n }\n throw new Error(\n `Timed out waiting for element: ${JSON.stringify(query)} (${timeoutMs}ms)`,\n );\n }\n\n async getAppState(bundleId: string): Promise<AppStateResult> {\n const client = this.#requireClient();\n\n if (this.platform === 'ios') {\n // iOS app state: 0=not installed, 1=not running, 2=background, 3=suspended, 4=foreground\n const state = await client.queryAppState(bundleId);\n const stateMap: Record<number, string> = {\n 0: 'Not Installed',\n 1: 'Not Running',\n 2: 'Running (background)',\n 3: 'Suspended',\n 4: 'Running (foreground)',\n };\n return { bundleId, state: stateMap[state] ?? `Unknown (${state})` };\n }\n\n try {\n const activity = await client.getCurrentActivity();\n return { bundleId, state: `Running (${activity})` };\n } catch {\n return { bundleId, state: 'Not Running' };\n }\n }\n\n async #getHierarchy(client: WebDriverClient): Promise<string> {\n if (this.platform === 'ios') {\n return this.#getIosHierarchy(client);\n }\n return client.getPageSource();\n }\n\n async #getIosHierarchy(client: WebDriverClient): Promise<string> {\n // Limit hierarchy depth to avoid \"JS stack is too deep\" on complex iOS apps\n try {\n await client.updateSettings({ snapshotMaxDepth: 30 });\n } catch {\n // snapshotMaxDepth not supported on older XCUITest drivers\n }\n\n try {\n return await client.execute<string>('mobile: source', [\n { format: 'xml' },\n ]);\n } catch {\n return client.getPageSource();\n }\n }\n\n screenshot(\n outputPath?: string,\n options?: { encode?: true },\n ): Promise<ScreenshotResult>;\n\n screenshot(\n outputPath: string | undefined,\n options: { encode: false },\n ): Promise<ScreenshotFileResult>;\n\n screenshot(\n outputPath?: string,\n options?: ScreenshotOptions,\n ): Promise<ScreenshotResult | ScreenshotFileResult>;\n\n async screenshot(\n outputPath?: string,\n options?: ScreenshotOptions,\n ): Promise<ScreenshotResult | ScreenshotFileResult> {\n const client = this.#requireClient();\n const data = await client.execute<string>('mobile: getScreenshot', []);\n if (options?.encode === false) {\n const path = resolveArtifactPath(outputPath, 'screenshot');\n await writeArtifactFile(path, Buffer.from(data, 'base64'));\n return { data: undefined, format: 'png', path };\n }\n let path: string | undefined;\n if (outputPath) {\n path = resolveArtifactPath(outputPath, 'screenshot');\n await writeArtifactFile(path, Buffer.from(data, 'base64'));\n }\n return { data, format: 'png', path };\n }\n\n async openApp(bundleId: string): Promise<void> {\n const client = this.#requireClient();\n await client.execute('mobile: activateApp', [{ bundleId }]);\n }\n\n async closeApp(bundleId: string): Promise<void> {\n const client = this.#requireClient();\n await client.execute('mobile: terminateApp', [{ bundleId }]);\n }\n\n async pressButton(button: DeviceButton): Promise<void> {\n const client = this.#requireClient();\n if (this.platform === 'ios') {\n const iosMap: Record<DeviceButton, string> = {\n home: 'home',\n lock: 'lock',\n back: 'home',\n enter: '\\n',\n };\n await client.execute('mobile: pressButton', [{ name: iosMap[button] }]);\n } else {\n // Android keyevent codes\n const androidMap: Record<DeviceButton, number> = {\n home: 3,\n back: 4,\n enter: 66,\n lock: 26,\n };\n await client.execute('mobile: pressKey', [\n { keycode: androidMap[button] },\n ]);\n }\n }\n\n async dismissKeyboard(): Promise<void> {\n const client = this.#requireClient();\n if (this.platform === 'ios') {\n await client.execute('mobile: hideKeyboard', []);\n } else {\n await client.execute('mobile: hideKeyboard', []);\n }\n }\n\n async dismissAlert(accept: boolean): Promise<void> {\n const client = this.#requireClient();\n const action = accept ? 'accept' : 'dismiss';\n await client.execute(`mobile: ${action}Alert`, []);\n }\n\n async getLogs(durationSeconds = 30, filter?: string): Promise<LogsResult> {\n const client = this.#requireClient();\n const logType = this.platform === 'ios' ? 'syslog' : 'logcat';\n const raw = await client.execute<Record<string, unknown>[]>(\n 'mobile: getLog',\n [{ type: logType }],\n );\n\n let entries = raw.map((entry) => ({\n timestamp: typeof entry.timestamp === 'string' ? entry.timestamp : '',\n level: typeof entry.level === 'string' ? entry.level : 'info',\n message: typeof entry.message === 'string' ? entry.message : '',\n }));\n\n if (filter) {\n const pattern = new RegExp(filter, 'iu');\n entries = entries.filter((entry) => pattern.test(entry.message));\n }\n\n const cutoff = Date.now() - durationSeconds * 1000;\n entries = entries.filter((entry) => {\n const ts = Number(entry.timestamp);\n return isNaN(ts) || ts >= cutoff;\n });\n\n return { entries, source: `appium ${logType}` };\n }\n\n async longPress(query: ElementQuery, durationMs = 1000): Promise<TapResult> {\n const snap = await this.snapshot();\n const element = findElement(snap.hierarchy, query);\n if (!element) {\n throw new Error(\n `Element not found: ${JSON.stringify(query)}\\n` +\n 'Use device_snapshot to inspect the current UI hierarchy.',\n );\n }\n\n const client = this.#requireClient();\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 client.performActions([\n {\n type: 'pointer',\n id: 'finger1',\n parameters: { pointerType: 'touch' },\n actions: [\n { type: 'pointerMove', duration: 0, x: cx, y: cy },\n { type: 'pointerDown', button: 0 },\n { type: 'pause', duration: durationMs },\n { type: 'pointerUp', button: 0 },\n ],\n },\n ]);\n await client.releaseActions();\n\n return {\n success: true,\n x: cx,\n y: cy,\n targetDescription: describeElement(element),\n };\n }\n\n async scrollToElement(\n query: ElementQuery,\n direction: 'up' | 'down' = 'down',\n maxAttempts = 10,\n ): Promise<UIElement> {\n let previousRaw = '';\n\n for (let i = 0; i < maxAttempts; i++) {\n const snap = await this.snapshot();\n const element = findElement(snap.hierarchy, query);\n if (element) {\n return element;\n }\n if (snap.raw === previousRaw) {\n break;\n }\n previousRaw = snap.raw;\n await this.swipe(direction);\n }\n throw new Error(\n `Element not found after scrolling: ${JSON.stringify(query)}\\n` +\n 'Use device_snapshot to inspect the current UI hierarchy.',\n );\n }\n\n async getAlertText(): Promise<string> {\n const client = this.#requireClient();\n try {\n return await client.execute<string>('mobile: getAlertText', []);\n } catch {\n const snap = await this.snapshot();\n const texts = collectAppiumAlertTexts(snap.hierarchy, this.platform);\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\n async getWindowSize(): Promise<WindowSize> {\n const client = this.#requireClient();\n try {\n const rect = await client.getWindowRect();\n return { width: rect.width, height: rect.height };\n } catch {\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');\n }\n }\n\n async getContexts(): Promise<string[]> {\n const client = this.#requireClient();\n return client.getContexts();\n }\n\n async setContext(context: string): Promise<void> {\n const client = this.#requireClient();\n await client.setCurrentContext(context);\n }\n\n async getClipboard(): Promise<string> {\n const client = this.#requireClient();\n const b64 = await client.execute<string>('mobile: getClipboard', [\n { contentType: 'plaintext' },\n ]);\n return Buffer.from(b64, 'base64').toString('utf-8');\n }\n\n async setClipboard(text: string): Promise<void> {\n const client = this.#requireClient();\n await client.execute('mobile: setClipboard', [\n {\n content: Buffer.from(text).toString('base64'),\n contentType: 'plaintext',\n },\n ]);\n }\n\n async startScreenRecording(outputPath?: string): Promise<void> {\n if (this.#recording) {\n throw new Error('Screen recording is already in progress');\n }\n const client = this.#requireClient();\n await client.execute('mobile: startRecordingScreen', [{}]);\n this.#recording = true;\n this.#recordingOutputPath = resolveArtifactPath(outputPath, 'recording');\n }\n\n async stopScreenRecording(): Promise<string> {\n if (!this.#recording || !this.#recordingOutputPath) {\n throw new Error('No screen recording in progress');\n }\n const client = this.#requireClient();\n const b64 = await client.execute<string>('mobile: stopRecordingScreen', []);\n const data = Buffer.from(b64, 'base64');\n if (data.length === 0) {\n throw new Error('Screen recording returned empty data');\n }\n const outputPath = this.#recordingOutputPath;\n await writeArtifactFile(outputPath, data);\n this.#recording = false;\n this.#recordingOutputPath = null;\n return outputPath;\n }\n\n #requireClient(): WebDriverClient {\n if (!this.#client) {\n throw new Error(\n 'AppiumBackend not connected. Call ensureConnected() first.',\n );\n }\n return this.#client;\n }\n}\n\nfunction collectAppiumAlertTexts(\n elements: UIElement[],\n platform: Platform,\n): string[] {\n const texts: string[] = [];\n for (const el of elements) {\n const isAlert =\n platform === 'ios'\n ? el.type === 'Alert' || el.type === 'Sheet'\n : el.type.includes('Dialog');\n if (isAlert) {\n collectAppiumTextValues(el.children ?? [], texts);\n return texts;\n }\n if (el.children) {\n const found = collectAppiumAlertTexts(el.children, platform);\n if (found.length > 0) {\n return found;\n }\n }\n }\n return texts;\n}\n\nfunction collectAppiumTextValues(elements: UIElement[], texts: string[]): void {\n for (const el of elements) {\n const text = el.value ?? el.label;\n if (\n text &&\n (el.type === 'StaticText' ||\n el.type.includes('TextView') ||\n el.type === 'Text')\n ) {\n texts.push(text);\n }\n if (el.children) {\n collectAppiumTextValues(el.children, texts);\n }\n }\n}\n\nfunction extractElementId(element: Record<string, string>): string {\n // W3C uses 'element-6066-11e4-a52e-4f735466cecf', JSONWP uses 'ELEMENT'\n const id =\n element['element-6066-11e4-a52e-4f735466cecf'] ||\n element.ELEMENT ||\n Object.values(element)[0];\n\n if (!id) {\n throw new Error(\n `Failed to extract element ID from Appium response: ${JSON.stringify(element)}`,\n );\n }\n return id;\n}\n\nexport function parseAppiumAndroidHierarchy(xml: string): UIElement[] {\n const elements: UIElement[] = [];\n const nodeRegex = /<node\\s+([^>]+)\\/?>/gu;\n let match;\n\n while ((match = nodeRegex.exec(xml)) !== null) {\n const element = parseAndroidNodeAttrs(match[1]);\n if (element) {\n elements.push(element);\n }\n }\n\n return elements;\n}\n\nfunction parseAndroidNodeAttrs(attrs: string): UIElement | null {\n const get = (name: string): string | undefined => {\n const attrMatch = attrs.match(new RegExp(`${name}=\"([^\"]*)\"`, 'u'));\n return attrMatch?.[1] || undefined;\n };\n\n const boundsStr = get('bounds');\n if (!boundsStr) {\n return null;\n }\n\n // bounds format: [x1,y1][x2,y2]\n const boundsMatch = boundsStr.match(/\\[(\\d+),(\\d+)\\]\\[(\\d+),(\\d+)\\]/u);\n if (!boundsMatch) {\n return null;\n }\n\n const x1 = parseInt(boundsMatch[1], 10);\n const y1 = parseInt(boundsMatch[2], 10);\n const x2 = parseInt(boundsMatch[3], 10);\n const y2 = parseInt(boundsMatch[4], 10);\n\n return {\n type: get('class') ?? 'Unknown',\n label: get('content-desc') || undefined,\n value: get('text') || undefined,\n identifier: get('resource-id') || undefined,\n frame: { x: x1, y: y1, width: x2 - x1, height: y2 - y1 },\n enabled: get('enabled') !== 'false',\n };\n}\n\nexport function parseAppiumIosHierarchy(xml: string): UIElement[] {\n const elements: UIElement[] = [];\n // XCUITest XML uses <XCUIElementType* ...> elements\n const nodeRegex = /<(XCUIElementType\\w+)\\s+([^>]+)\\/?>/gu;\n let match;\n\n while ((match = nodeRegex.exec(xml)) !== null) {\n const typeName = match[1];\n const element = parseIosNodeAttrs(typeName, match[2]);\n if (element) {\n elements.push(element);\n }\n }\n\n return elements;\n}\n\nfunction parseIosNodeAttrs(typeName: string, attrs: string): UIElement | null {\n const get = (name: string): string | undefined => {\n const attrMatch = attrs.match(new RegExp(`${name}=\"([^\"]*)\"`, 'u'));\n return attrMatch?.[1] || undefined;\n };\n\n const xStr = get('x');\n const yStr = get('y');\n const widthStr = get('width');\n const heightStr = get('height');\n\n if (!xStr || !yStr || !widthStr || !heightStr) {\n return null;\n }\n\n const shortType = typeName.replace(/^XCUIElementType/u, '');\n\n return {\n type: shortType,\n label: get('label') || undefined,\n value: get('value') || undefined,\n identifier: get('name') || undefined,\n frame: {\n x: parseInt(xStr, 10),\n y: parseInt(yStr, 10),\n width: parseInt(widthStr, 10),\n height: parseInt(heightStr, 10),\n },\n enabled: get('enabled') !== 'false',\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"appium-backend.cjs","sourceRoot":"","sources":["../../src/backends/appium-backend.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SessionConfig } from './session-file.js';\nimport type {\n DeviceBackend,\n DeviceButton,\n DeviceInfo,\n SnapshotResult,\n ScreenshotResult,\n ScreenshotFileResult,\n ScreenshotOptions,\n LogsResult,\n TapResult,\n AppStateResult,\n ElementQuery,\n UIElement,\n Platform,\n WindowSize,\n} from './types.js';\nimport { WebDriverClient, createSession } from './webdriver-client.js';\nimport {\n findElement,\n describeElement,\n computeSwipeEnd,\n} from '../utils/element.js';\nimport {\n resolveArtifactPath,\n writeArtifactFile,\n} from '../utils/output-path.js';\n\nexport class AppiumBackend implements DeviceBackend {\n readonly kind = 'appium' as const;\n\n readonly platform: Platform;\n\n #client: WebDriverClient | null = null;\n\n readonly #config: SessionConfig;\n\n #recording = false;\n\n #recordingOutputPath: string | null = null;\n\n constructor(config: SessionConfig) {\n this.#config = config;\n this.platform = config.platform;\n }\n\n async ensureConnected(): Promise<void> {\n if (this.#client) {\n return;\n }\n\n if (this.#config.mode === 'attach') {\n this.#client = new WebDriverClient(\n this.#config.appiumUrl,\n this.#config.sessionId,\n this.#config.auth,\n );\n await this.#client.getStatus();\n } else {\n const { client } = await createSession(\n this.#config.appiumUrl,\n this.#config.capabilities,\n this.#config.auth,\n );\n this.#client = client;\n }\n }\n\n async getDeviceInfo(): Promise<DeviceInfo> {\n const client = this.#requireClient();\n const details = await client.getSessionDetails();\n const caps = (details.capabilities ?? details) as Record<string, unknown>;\n\n return {\n platform: this.platform,\n deviceId: (caps.udid as string) ?? client.sessionId,\n name: (caps.deviceName as string) ?? 'Appium Device',\n osVersion: (caps.platformVersion as string) ?? 'Unknown',\n state: 'connected',\n };\n }\n\n async snapshot(): Promise<SnapshotResult> {\n const client = this.#requireClient();\n const raw = await this.#getHierarchy(client);\n const hierarchy =\n this.platform === 'ios'\n ? parseAppiumIosHierarchy(raw)\n : parseAppiumAndroidHierarchy(raw);\n\n return {\n platform: this.platform,\n hierarchy,\n raw,\n timestamp: Date.now(),\n };\n }\n\n async tapElement(query: ElementQuery): Promise<TapResult> {\n const snapshot = await this.snapshot();\n const element = findElement(snapshot.hierarchy, query);\n if (!element) {\n throw new Error(\n `Element not found: ${JSON.stringify(query)}\\n` +\n 'Use device_snapshot to inspect the current UI hierarchy.',\n );\n }\n\n const targetX = Math.round(element.frame.x + element.frame.width / 2);\n const targetY = Math.round(element.frame.y + element.frame.height / 2);\n await this.tapCoordinates(targetX, targetY);\n\n return {\n success: true,\n x: targetX,\n y: targetY,\n targetDescription: describeElement(element),\n };\n }\n\n async getElementText(query: ElementQuery): Promise<string> {\n const snapshot = await this.snapshot();\n const element = findElement(snapshot.hierarchy, query);\n if (!element) {\n throw new Error(\n `Element not found: ${JSON.stringify(query)}\\n` +\n 'Use device_snapshot to inspect the current UI hierarchy.',\n );\n }\n return element.label ?? element.value ?? '';\n }\n\n async tapCoordinates(targetX: number, targetY: number): Promise<void> {\n const client = this.#requireClient();\n await client.performActions([\n {\n type: 'pointer',\n id: 'finger1',\n parameters: { pointerType: 'touch' },\n actions: [\n { type: 'pointerMove', duration: 0, x: targetX, y: targetY },\n { type: 'pointerDown', button: 0 },\n { type: 'pause', duration: 50 },\n { type: 'pointerUp', button: 0 },\n ],\n },\n ]);\n await client.releaseActions();\n }\n\n async typeText(text: string): Promise<void> {\n const client = this.#requireClient();\n const activeElement = await client.findElement(\n 'xpath',\n '//*[@focused=\"true\"]',\n );\n const elementId = extractElementId(activeElement);\n await client.elementClear(elementId);\n await client.elementSendKeys(elementId, 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 const client = this.#requireClient();\n const dist = distance ?? 500;\n const sx = startX ?? 200;\n const sy = startY ?? 400;\n const [endX, endY] = computeSwipeEnd(sx, sy, direction, dist);\n\n await client.performActions([\n {\n type: 'pointer',\n id: 'finger1',\n parameters: { pointerType: 'touch' },\n actions: [\n { type: 'pointerMove', duration: 0, x: sx, y: sy },\n { type: 'pointerDown', button: 0 },\n { type: 'pointerMove', duration: 300, x: endX, y: endY },\n { type: 'pointerUp', button: 0 },\n ],\n },\n ]);\n await client.releaseActions();\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 snap = await this.snapshot();\n const element = findElement(snap.hierarchy, query);\n if (element) {\n return element;\n }\n await new Promise((resolve) => setTimeout(resolve, intervalMs));\n }\n throw new Error(\n `Timed out waiting for element: ${JSON.stringify(query)} (${timeoutMs}ms)`,\n );\n }\n\n async getAppState(bundleId: string): Promise<AppStateResult> {\n const client = this.#requireClient();\n\n if (this.platform === 'ios') {\n // iOS app state: 0=not installed, 1=not running, 2=background, 3=suspended, 4=foreground\n const state = await client.queryAppState(bundleId);\n const stateMap: Record<number, string> = {\n 0: 'Not Installed',\n 1: 'Not Running',\n 2: 'Running (background)',\n 3: 'Suspended',\n 4: 'Running (foreground)',\n };\n return { bundleId, state: stateMap[state] ?? `Unknown (${state})` };\n }\n\n try {\n const activity = await client.getCurrentActivity();\n return { bundleId, state: `Running (${activity})` };\n } catch {\n return { bundleId, state: 'Not Running' };\n }\n }\n\n async #getHierarchy(client: WebDriverClient): Promise<string> {\n if (this.platform === 'ios') {\n return this.#getIosHierarchy(client);\n }\n return this.#getAndroidHierarchy(client);\n }\n\n async #getAndroidHierarchy(client: WebDriverClient): Promise<string> {\n // Stock `uiautomator dump` blocks on UiAutomation.waitForIdle before it\n // will read the tree, so a screen with a continuous AccessibilityEvent\n // stream (e.g. a React Native polling loop redrawing off-screen state)\n // never goes idle and the dump fails with \"could not get idle state\".\n // The UiAutomator2 server exposes `waitForIdleTimeout` as a setting;\n // setting it to 0 disables the wait entirely so getPageSource returns the\n // current hierarchy immediately regardless of event churn. This is the\n // deterministic equivalent of the ADB backend's best-effort retry loop.\n try {\n await client.updateSettings({ waitForIdleTimeout: 0 });\n } catch {\n // `waitForIdleTimeout` is unsupported on non-UiAutomator2 drivers (or\n // very old ones); fall through and let getPageSource wait for idle.\n }\n return client.getPageSource();\n }\n\n async #getIosHierarchy(client: WebDriverClient): Promise<string> {\n // Limit hierarchy depth to avoid \"JS stack is too deep\" on complex iOS apps\n try {\n await client.updateSettings({ snapshotMaxDepth: 30 });\n } catch {\n // snapshotMaxDepth not supported on older XCUITest drivers\n }\n\n try {\n return await client.execute<string>('mobile: source', [\n { format: 'xml' },\n ]);\n } catch {\n return client.getPageSource();\n }\n }\n\n screenshot(\n outputPath?: string,\n options?: { encode?: true },\n ): Promise<ScreenshotResult>;\n\n screenshot(\n outputPath: string | undefined,\n options: { encode: false },\n ): Promise<ScreenshotFileResult>;\n\n screenshot(\n outputPath?: string,\n options?: ScreenshotOptions,\n ): Promise<ScreenshotResult | ScreenshotFileResult>;\n\n async screenshot(\n outputPath?: string,\n options?: ScreenshotOptions,\n ): Promise<ScreenshotResult | ScreenshotFileResult> {\n const client = this.#requireClient();\n const data = await client.execute<string>('mobile: getScreenshot', []);\n if (options?.encode === false) {\n const path = resolveArtifactPath(outputPath, 'screenshot');\n await writeArtifactFile(path, Buffer.from(data, 'base64'));\n return { data: undefined, format: 'png', path };\n }\n let path: string | undefined;\n if (outputPath) {\n path = resolveArtifactPath(outputPath, 'screenshot');\n await writeArtifactFile(path, Buffer.from(data, 'base64'));\n }\n return { data, format: 'png', path };\n }\n\n async openApp(bundleId: string): Promise<void> {\n const client = this.#requireClient();\n await client.execute('mobile: activateApp', [{ bundleId }]);\n }\n\n async closeApp(bundleId: string): Promise<void> {\n const client = this.#requireClient();\n await client.execute('mobile: terminateApp', [{ bundleId }]);\n }\n\n async pressButton(button: DeviceButton): Promise<void> {\n const client = this.#requireClient();\n if (this.platform === 'ios') {\n const iosMap: Record<DeviceButton, string> = {\n home: 'home',\n lock: 'lock',\n back: 'home',\n enter: '\\n',\n };\n await client.execute('mobile: pressButton', [{ name: iosMap[button] }]);\n } else {\n // Android keyevent codes\n const androidMap: Record<DeviceButton, number> = {\n home: 3,\n back: 4,\n enter: 66,\n lock: 26,\n };\n await client.execute('mobile: pressKey', [\n { keycode: androidMap[button] },\n ]);\n }\n }\n\n async dismissKeyboard(): Promise<void> {\n const client = this.#requireClient();\n if (this.platform === 'ios') {\n await client.execute('mobile: hideKeyboard', []);\n } else {\n await client.execute('mobile: hideKeyboard', []);\n }\n }\n\n async dismissAlert(accept: boolean): Promise<void> {\n const client = this.#requireClient();\n const action = accept ? 'accept' : 'dismiss';\n await client.execute(`mobile: ${action}Alert`, []);\n }\n\n async getLogs(durationSeconds = 30, filter?: string): Promise<LogsResult> {\n const client = this.#requireClient();\n const logType = this.platform === 'ios' ? 'syslog' : 'logcat';\n const raw = await client.execute<Record<string, unknown>[]>(\n 'mobile: getLog',\n [{ type: logType }],\n );\n\n let entries = raw.map((entry) => ({\n timestamp: typeof entry.timestamp === 'string' ? entry.timestamp : '',\n level: typeof entry.level === 'string' ? entry.level : 'info',\n message: typeof entry.message === 'string' ? entry.message : '',\n }));\n\n if (filter) {\n const pattern = new RegExp(filter, 'iu');\n entries = entries.filter((entry) => pattern.test(entry.message));\n }\n\n const cutoff = Date.now() - durationSeconds * 1000;\n entries = entries.filter((entry) => {\n const ts = Number(entry.timestamp);\n return isNaN(ts) || ts >= cutoff;\n });\n\n return { entries, source: `appium ${logType}` };\n }\n\n async longPress(query: ElementQuery, durationMs = 1000): Promise<TapResult> {\n const snap = await this.snapshot();\n const element = findElement(snap.hierarchy, query);\n if (!element) {\n throw new Error(\n `Element not found: ${JSON.stringify(query)}\\n` +\n 'Use device_snapshot to inspect the current UI hierarchy.',\n );\n }\n\n const client = this.#requireClient();\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 client.performActions([\n {\n type: 'pointer',\n id: 'finger1',\n parameters: { pointerType: 'touch' },\n actions: [\n { type: 'pointerMove', duration: 0, x: cx, y: cy },\n { type: 'pointerDown', button: 0 },\n { type: 'pause', duration: durationMs },\n { type: 'pointerUp', button: 0 },\n ],\n },\n ]);\n await client.releaseActions();\n\n return {\n success: true,\n x: cx,\n y: cy,\n targetDescription: describeElement(element),\n };\n }\n\n async scrollToElement(\n query: ElementQuery,\n direction: 'up' | 'down' = 'down',\n maxAttempts = 10,\n ): Promise<UIElement> {\n let previousRaw = '';\n\n for (let i = 0; i < maxAttempts; i++) {\n const snap = await this.snapshot();\n const element = findElement(snap.hierarchy, query);\n if (element) {\n return element;\n }\n if (snap.raw === previousRaw) {\n break;\n }\n previousRaw = snap.raw;\n await this.swipe(direction);\n }\n throw new Error(\n `Element not found after scrolling: ${JSON.stringify(query)}\\n` +\n 'Use device_snapshot to inspect the current UI hierarchy.',\n );\n }\n\n async getAlertText(): Promise<string> {\n const client = this.#requireClient();\n try {\n return await client.execute<string>('mobile: getAlertText', []);\n } catch {\n const snap = await this.snapshot();\n const texts = collectAppiumAlertTexts(snap.hierarchy, this.platform);\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\n async getWindowSize(): Promise<WindowSize> {\n const client = this.#requireClient();\n try {\n const rect = await client.getWindowRect();\n return { width: rect.width, height: rect.height };\n } catch {\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');\n }\n }\n\n async getContexts(): Promise<string[]> {\n const client = this.#requireClient();\n return client.getContexts();\n }\n\n async setContext(context: string): Promise<void> {\n const client = this.#requireClient();\n await client.setCurrentContext(context);\n }\n\n async getClipboard(): Promise<string> {\n const client = this.#requireClient();\n const b64 = await client.execute<string>('mobile: getClipboard', [\n { contentType: 'plaintext' },\n ]);\n return Buffer.from(b64, 'base64').toString('utf-8');\n }\n\n async setClipboard(text: string): Promise<void> {\n const client = this.#requireClient();\n await client.execute('mobile: setClipboard', [\n {\n content: Buffer.from(text).toString('base64'),\n contentType: 'plaintext',\n },\n ]);\n }\n\n async startScreenRecording(outputPath?: string): Promise<void> {\n if (this.#recording) {\n throw new Error('Screen recording is already in progress');\n }\n const client = this.#requireClient();\n await client.execute('mobile: startRecordingScreen', [{}]);\n this.#recording = true;\n this.#recordingOutputPath = resolveArtifactPath(outputPath, 'recording');\n }\n\n async stopScreenRecording(): Promise<string> {\n if (!this.#recording || !this.#recordingOutputPath) {\n throw new Error('No screen recording in progress');\n }\n const client = this.#requireClient();\n const b64 = await client.execute<string>('mobile: stopRecordingScreen', []);\n const data = Buffer.from(b64, 'base64');\n if (data.length === 0) {\n throw new Error('Screen recording returned empty data');\n }\n const outputPath = this.#recordingOutputPath;\n await writeArtifactFile(outputPath, data);\n this.#recording = false;\n this.#recordingOutputPath = null;\n return outputPath;\n }\n\n #requireClient(): WebDriverClient {\n if (!this.#client) {\n throw new Error(\n 'AppiumBackend not connected. Call ensureConnected() first.',\n );\n }\n return this.#client;\n }\n}\n\nfunction collectAppiumAlertTexts(\n elements: UIElement[],\n platform: Platform,\n): string[] {\n const texts: string[] = [];\n for (const el of elements) {\n const isAlert =\n platform === 'ios'\n ? el.type === 'Alert' || el.type === 'Sheet'\n : el.type.includes('Dialog');\n if (isAlert) {\n collectAppiumTextValues(el.children ?? [], texts);\n return texts;\n }\n if (el.children) {\n const found = collectAppiumAlertTexts(el.children, platform);\n if (found.length > 0) {\n return found;\n }\n }\n }\n return texts;\n}\n\nfunction collectAppiumTextValues(elements: UIElement[], texts: string[]): void {\n for (const el of elements) {\n const text = el.value ?? el.label;\n if (\n text &&\n (el.type === 'StaticText' ||\n el.type.includes('TextView') ||\n el.type === 'Text')\n ) {\n texts.push(text);\n }\n if (el.children) {\n collectAppiumTextValues(el.children, texts);\n }\n }\n}\n\nfunction extractElementId(element: Record<string, string>): string {\n // W3C uses 'element-6066-11e4-a52e-4f735466cecf', JSONWP uses 'ELEMENT'\n const id =\n element['element-6066-11e4-a52e-4f735466cecf'] ||\n element.ELEMENT ||\n Object.values(element)[0];\n\n if (!id) {\n throw new Error(\n `Failed to extract element ID from Appium response: ${JSON.stringify(element)}`,\n );\n }\n return id;\n}\n\nexport function parseAppiumAndroidHierarchy(xml: string): UIElement[] {\n const elements: UIElement[] = [];\n const nodeRegex = /<node\\s+([^>]+)\\/?>/gu;\n let match;\n\n while ((match = nodeRegex.exec(xml)) !== null) {\n const element = parseAndroidNodeAttrs(match[1]);\n if (element) {\n elements.push(element);\n }\n }\n\n return elements;\n}\n\nfunction parseAndroidNodeAttrs(attrs: string): UIElement | null {\n const get = (name: string): string | undefined => {\n const attrMatch = attrs.match(new RegExp(`${name}=\"([^\"]*)\"`, 'u'));\n return attrMatch?.[1] || undefined;\n };\n\n const boundsStr = get('bounds');\n if (!boundsStr) {\n return null;\n }\n\n // bounds format: [x1,y1][x2,y2]\n const boundsMatch = boundsStr.match(/\\[(\\d+),(\\d+)\\]\\[(\\d+),(\\d+)\\]/u);\n if (!boundsMatch) {\n return null;\n }\n\n const x1 = parseInt(boundsMatch[1], 10);\n const y1 = parseInt(boundsMatch[2], 10);\n const x2 = parseInt(boundsMatch[3], 10);\n const y2 = parseInt(boundsMatch[4], 10);\n\n return {\n type: get('class') ?? 'Unknown',\n label: get('content-desc') || undefined,\n value: get('text') || undefined,\n identifier: get('resource-id') || undefined,\n frame: { x: x1, y: y1, width: x2 - x1, height: y2 - y1 },\n enabled: get('enabled') !== 'false',\n };\n}\n\nexport function parseAppiumIosHierarchy(xml: string): UIElement[] {\n const elements: UIElement[] = [];\n // XCUITest XML uses <XCUIElementType* ...> elements\n const nodeRegex = /<(XCUIElementType\\w+)\\s+([^>]+)\\/?>/gu;\n let match;\n\n while ((match = nodeRegex.exec(xml)) !== null) {\n const typeName = match[1];\n const element = parseIosNodeAttrs(typeName, match[2]);\n if (element) {\n elements.push(element);\n }\n }\n\n return elements;\n}\n\nfunction parseIosNodeAttrs(typeName: string, attrs: string): UIElement | null {\n const get = (name: string): string | undefined => {\n const attrMatch = attrs.match(new RegExp(`${name}=\"([^\"]*)\"`, 'u'));\n return attrMatch?.[1] || undefined;\n };\n\n const xStr = get('x');\n const yStr = get('y');\n const widthStr = get('width');\n const heightStr = get('height');\n\n if (!xStr || !yStr || !widthStr || !heightStr) {\n return null;\n }\n\n const shortType = typeName.replace(/^XCUIElementType/u, '');\n\n return {\n type: shortType,\n label: get('label') || undefined,\n value: get('value') || undefined,\n identifier: get('name') || undefined,\n frame: {\n x: parseInt(xStr, 10),\n y: parseInt(yStr, 10),\n width: parseInt(widthStr, 10),\n height: parseInt(heightStr, 10),\n },\n enabled: get('enabled') !== 'false',\n };\n}\n"]}
|
|
@@ -2,6 +2,7 @@ import type { SessionConfig } from "./session-file.cjs";
|
|
|
2
2
|
import type { DeviceBackend, DeviceButton, DeviceInfo, SnapshotResult, ScreenshotResult, ScreenshotFileResult, ScreenshotOptions, LogsResult, TapResult, AppStateResult, ElementQuery, UIElement, Platform, WindowSize } from "./types.cjs";
|
|
3
3
|
export declare class AppiumBackend implements DeviceBackend {
|
|
4
4
|
#private;
|
|
5
|
+
readonly kind: "appium";
|
|
5
6
|
readonly platform: Platform;
|
|
6
7
|
constructor(config: SessionConfig);
|
|
7
8
|
ensureConnected(): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"appium-backend.d.cts","sourceRoot":"","sources":["../../src/backends/appium-backend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,2BAA0B;AACvD,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,oBAAoB,EACpB,iBAAiB,EACjB,UAAU,EACV,SAAS,EACT,cAAc,EACd,YAAY,EACZ,SAAS,EACT,QAAQ,EACR,UAAU,EACX,oBAAmB;AAYpB,qBAAa,aAAc,YAAW,aAAa;;IACjD,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;gBAUhB,MAAM,EAAE,aAAa;IAK3B,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAsBhC,aAAa,IAAI,OAAO,CAAC,UAAU,CAAC;IAcpC,QAAQ,IAAI,OAAO,CAAC,cAAc,CAAC;IAgBnC,UAAU,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC;IAsBnD,cAAc,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAYpD,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB/D,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWrC,KAAK,CACT,SAAS,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,EAC3C,MAAM,CAAC,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC;IAuBV,cAAc,CAClB,KAAK,EAAE,YAAY,EACnB,SAAS,SAAS,EAClB,UAAU,SAAM,GACf,OAAO,CAAC,SAAS,CAAC;IAef,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"appium-backend.d.cts","sourceRoot":"","sources":["../../src/backends/appium-backend.ts"],"names":[],"mappings":"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"}
|