mobai-mcp 1.2.1 → 1.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/dist/index.js +373 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -238,6 +238,136 @@ const TOOLS = [
|
|
|
238
238
|
required: ["device_id"],
|
|
239
239
|
},
|
|
240
240
|
},
|
|
241
|
+
{
|
|
242
|
+
name: "double_tap",
|
|
243
|
+
description: "Double tap an element by index (from UI tree) or coordinates",
|
|
244
|
+
inputSchema: {
|
|
245
|
+
type: "object",
|
|
246
|
+
properties: {
|
|
247
|
+
device_id: {
|
|
248
|
+
type: "string",
|
|
249
|
+
description: "Device ID",
|
|
250
|
+
},
|
|
251
|
+
index: {
|
|
252
|
+
type: "number",
|
|
253
|
+
description: "Element index from UI tree (preferred)",
|
|
254
|
+
},
|
|
255
|
+
x: {
|
|
256
|
+
type: "number",
|
|
257
|
+
description: "X coordinate (use with y instead of index)",
|
|
258
|
+
},
|
|
259
|
+
y: {
|
|
260
|
+
type: "number",
|
|
261
|
+
description: "Y coordinate (use with x instead of index)",
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
required: ["device_id"],
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
name: "long_press",
|
|
269
|
+
description: "Long press an element by index (from UI tree) or coordinates. Uses a fixed 0.5s hold duration.",
|
|
270
|
+
inputSchema: {
|
|
271
|
+
type: "object",
|
|
272
|
+
properties: {
|
|
273
|
+
device_id: {
|
|
274
|
+
type: "string",
|
|
275
|
+
description: "Device ID",
|
|
276
|
+
},
|
|
277
|
+
index: {
|
|
278
|
+
type: "number",
|
|
279
|
+
description: "Element index from UI tree (preferred)",
|
|
280
|
+
},
|
|
281
|
+
x: {
|
|
282
|
+
type: "number",
|
|
283
|
+
description: "X coordinate (use with y instead of index)",
|
|
284
|
+
},
|
|
285
|
+
y: {
|
|
286
|
+
type: "number",
|
|
287
|
+
description: "Y coordinate (use with x instead of index)",
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
required: ["device_id"],
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
name: "two_finger_tap",
|
|
295
|
+
description: "Perform a two-finger tap at coordinates (iOS only)",
|
|
296
|
+
inputSchema: {
|
|
297
|
+
type: "object",
|
|
298
|
+
properties: {
|
|
299
|
+
device_id: {
|
|
300
|
+
type: "string",
|
|
301
|
+
description: "Device ID",
|
|
302
|
+
},
|
|
303
|
+
index: {
|
|
304
|
+
type: "number",
|
|
305
|
+
description: "Element index from UI tree (preferred)",
|
|
306
|
+
},
|
|
307
|
+
x: {
|
|
308
|
+
type: "number",
|
|
309
|
+
description: "X coordinate (use with y instead of index)",
|
|
310
|
+
},
|
|
311
|
+
y: {
|
|
312
|
+
type: "number",
|
|
313
|
+
description: "Y coordinate (use with x instead of index)",
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
required: ["device_id"],
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
name: "drag",
|
|
321
|
+
description: "Drag from one point to another (press, hold, move, release)",
|
|
322
|
+
inputSchema: {
|
|
323
|
+
type: "object",
|
|
324
|
+
properties: {
|
|
325
|
+
device_id: {
|
|
326
|
+
type: "string",
|
|
327
|
+
description: "Device ID",
|
|
328
|
+
},
|
|
329
|
+
from_x: {
|
|
330
|
+
type: "number",
|
|
331
|
+
description: "Starting X coordinate",
|
|
332
|
+
},
|
|
333
|
+
from_y: {
|
|
334
|
+
type: "number",
|
|
335
|
+
description: "Starting Y coordinate",
|
|
336
|
+
},
|
|
337
|
+
to_x: {
|
|
338
|
+
type: "number",
|
|
339
|
+
description: "Ending X coordinate",
|
|
340
|
+
},
|
|
341
|
+
to_y: {
|
|
342
|
+
type: "number",
|
|
343
|
+
description: "Ending Y coordinate",
|
|
344
|
+
},
|
|
345
|
+
duration_ms: {
|
|
346
|
+
type: "number",
|
|
347
|
+
description: "Drag duration in milliseconds (default: 500)",
|
|
348
|
+
},
|
|
349
|
+
press_duration_ms: {
|
|
350
|
+
type: "number",
|
|
351
|
+
description: "Hold duration before dragging in milliseconds (0 = no hold). Use for press-and-drag gestures like moving app icons.",
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
required: ["device_id", "from_x", "from_y", "to_x", "to_y"],
|
|
355
|
+
},
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
name: "dismiss_keyboard",
|
|
359
|
+
description: "Dismiss the on-screen keyboard if visible",
|
|
360
|
+
inputSchema: {
|
|
361
|
+
type: "object",
|
|
362
|
+
properties: {
|
|
363
|
+
device_id: {
|
|
364
|
+
type: "string",
|
|
365
|
+
description: "Device ID",
|
|
366
|
+
},
|
|
367
|
+
},
|
|
368
|
+
required: ["device_id"],
|
|
369
|
+
},
|
|
370
|
+
},
|
|
241
371
|
{
|
|
242
372
|
name: "type_text",
|
|
243
373
|
description: "Type text on the device (tap input field first to focus)",
|
|
@@ -355,7 +485,7 @@ const TOOLS = [
|
|
|
355
485
|
description: `Execute a batch of automation steps using the DSL (Domain Specific Language).
|
|
356
486
|
This is the PREFERRED method for complex automation as it's more reliable than sequential API calls.
|
|
357
487
|
|
|
358
|
-
DSL supports: observe, tap, type, toggle, swipe, scroll, open_app, navigate, wait_for, assert_*, if_exists, delay, execute_js (web)
|
|
488
|
+
DSL supports: observe, tap, type, toggle, swipe, scroll, open_app, kill_app, navigate, wait_for, screenshot, set_location, reset_location, assert_*, if_exists, delay, execute_js (web)
|
|
359
489
|
|
|
360
490
|
Example DSL script:
|
|
361
491
|
{
|
|
@@ -530,6 +660,78 @@ Example DSL script:
|
|
|
530
660
|
required: ["device_id", "script"],
|
|
531
661
|
},
|
|
532
662
|
},
|
|
663
|
+
{
|
|
664
|
+
name: "uninstall_app",
|
|
665
|
+
description: "Uninstall an application from the device by bundle ID / package name.",
|
|
666
|
+
inputSchema: {
|
|
667
|
+
type: "object",
|
|
668
|
+
properties: {
|
|
669
|
+
device_id: {
|
|
670
|
+
type: "string",
|
|
671
|
+
description: "Device ID",
|
|
672
|
+
},
|
|
673
|
+
bundle_id: {
|
|
674
|
+
type: "string",
|
|
675
|
+
description: "App bundle ID (iOS) or package name (Android) to uninstall",
|
|
676
|
+
},
|
|
677
|
+
},
|
|
678
|
+
required: ["device_id", "bundle_id"],
|
|
679
|
+
},
|
|
680
|
+
},
|
|
681
|
+
{
|
|
682
|
+
name: "kill_app",
|
|
683
|
+
description: "Force-kill a running application. On iOS (17+), uses CoreDevice appservice SIGKILL. On Android, uses 'am force-stop'.",
|
|
684
|
+
inputSchema: {
|
|
685
|
+
type: "object",
|
|
686
|
+
properties: {
|
|
687
|
+
device_id: {
|
|
688
|
+
type: "string",
|
|
689
|
+
description: "Device ID",
|
|
690
|
+
},
|
|
691
|
+
bundle_id: {
|
|
692
|
+
type: "string",
|
|
693
|
+
description: "Bundle ID / package name of the app to kill",
|
|
694
|
+
},
|
|
695
|
+
},
|
|
696
|
+
required: ["device_id", "bundle_id"],
|
|
697
|
+
},
|
|
698
|
+
},
|
|
699
|
+
{
|
|
700
|
+
name: "set_location",
|
|
701
|
+
description: "Set a simulated GPS location on the device. Supports: iOS (all versions), Android emulators (all versions), Android real devices (12+ only).",
|
|
702
|
+
inputSchema: {
|
|
703
|
+
type: "object",
|
|
704
|
+
properties: {
|
|
705
|
+
device_id: {
|
|
706
|
+
type: "string",
|
|
707
|
+
description: "Device ID",
|
|
708
|
+
},
|
|
709
|
+
lat: {
|
|
710
|
+
type: "number",
|
|
711
|
+
description: "Latitude (-90 to 90)",
|
|
712
|
+
},
|
|
713
|
+
lon: {
|
|
714
|
+
type: "number",
|
|
715
|
+
description: "Longitude (-180 to 180)",
|
|
716
|
+
},
|
|
717
|
+
},
|
|
718
|
+
required: ["device_id", "lat", "lon"],
|
|
719
|
+
},
|
|
720
|
+
},
|
|
721
|
+
{
|
|
722
|
+
name: "reset_location",
|
|
723
|
+
description: "Reset the device location to its real GPS position, removing any simulated location. Supports: iOS (all versions), Android emulators (all versions), Android real devices (12+ only).",
|
|
724
|
+
inputSchema: {
|
|
725
|
+
type: "object",
|
|
726
|
+
properties: {
|
|
727
|
+
device_id: {
|
|
728
|
+
type: "string",
|
|
729
|
+
description: "Device ID",
|
|
730
|
+
},
|
|
731
|
+
},
|
|
732
|
+
required: ["device_id"],
|
|
733
|
+
},
|
|
734
|
+
},
|
|
533
735
|
{
|
|
534
736
|
name: "http_request",
|
|
535
737
|
description: `Make a raw HTTP request to the MobAI API. Use this for advanced operations not covered by other tools.
|
|
@@ -616,6 +818,56 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
616
818
|
result = await makeRequest("POST", `/devices/${args?.device_id}/tap`, body);
|
|
617
819
|
break;
|
|
618
820
|
}
|
|
821
|
+
case "double_tap": {
|
|
822
|
+
const body = {};
|
|
823
|
+
if (args?.index !== undefined)
|
|
824
|
+
body.index = args.index;
|
|
825
|
+
if (args?.x !== undefined && args?.y !== undefined) {
|
|
826
|
+
body.x = args.x;
|
|
827
|
+
body.y = args.y;
|
|
828
|
+
}
|
|
829
|
+
result = await makeRequest("POST", `/devices/${args?.device_id}/double-tap`, body);
|
|
830
|
+
break;
|
|
831
|
+
}
|
|
832
|
+
case "long_press": {
|
|
833
|
+
const body = {};
|
|
834
|
+
if (args?.index !== undefined)
|
|
835
|
+
body.index = args.index;
|
|
836
|
+
if (args?.x !== undefined && args?.y !== undefined) {
|
|
837
|
+
body.x = args.x;
|
|
838
|
+
body.y = args.y;
|
|
839
|
+
}
|
|
840
|
+
result = await makeRequest("POST", `/devices/${args?.device_id}/long-press`, body);
|
|
841
|
+
break;
|
|
842
|
+
}
|
|
843
|
+
case "two_finger_tap": {
|
|
844
|
+
const body = {};
|
|
845
|
+
if (args?.index !== undefined)
|
|
846
|
+
body.index = args.index;
|
|
847
|
+
if (args?.x !== undefined && args?.y !== undefined) {
|
|
848
|
+
body.x = args.x;
|
|
849
|
+
body.y = args.y;
|
|
850
|
+
}
|
|
851
|
+
result = await makeRequest("POST", `/devices/${args?.device_id}/two-finger-tap`, body);
|
|
852
|
+
break;
|
|
853
|
+
}
|
|
854
|
+
case "drag": {
|
|
855
|
+
const dragBody = {
|
|
856
|
+
fromX: args?.from_x,
|
|
857
|
+
fromY: args?.from_y,
|
|
858
|
+
toX: args?.to_x,
|
|
859
|
+
toY: args?.to_y,
|
|
860
|
+
duration: args?.duration_ms ?? 500,
|
|
861
|
+
};
|
|
862
|
+
if (args?.press_duration_ms) {
|
|
863
|
+
dragBody.pressDuration = args.press_duration_ms;
|
|
864
|
+
}
|
|
865
|
+
result = await makeRequest("POST", `/devices/${args?.device_id}/drag`, dragBody);
|
|
866
|
+
break;
|
|
867
|
+
}
|
|
868
|
+
case "dismiss_keyboard":
|
|
869
|
+
result = await makeRequest("POST", `/devices/${args?.device_id}/dismiss-keyboard`);
|
|
870
|
+
break;
|
|
619
871
|
case "type_text":
|
|
620
872
|
result = await makeRequest("POST", `/devices/${args?.device_id}/type`, { text: args?.text });
|
|
621
873
|
break;
|
|
@@ -642,6 +894,23 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
642
894
|
case "get_ocr":
|
|
643
895
|
result = await makeRequest("GET", `/devices/${args?.device_id}/ocr`);
|
|
644
896
|
break;
|
|
897
|
+
case "uninstall_app":
|
|
898
|
+
result = await makeRequest("DELETE", `/devices/${args?.device_id}/apps/${encodeURIComponent(args?.bundle_id)}`);
|
|
899
|
+
break;
|
|
900
|
+
case "kill_app":
|
|
901
|
+
result = await makeRequest("POST", `/devices/${args?.device_id}/kill-app`, {
|
|
902
|
+
bundleId: args?.bundle_id,
|
|
903
|
+
});
|
|
904
|
+
break;
|
|
905
|
+
case "set_location":
|
|
906
|
+
result = await makeRequest("POST", `/devices/${args?.device_id}/location`, {
|
|
907
|
+
lat: args?.lat,
|
|
908
|
+
lon: args?.lon,
|
|
909
|
+
});
|
|
910
|
+
break;
|
|
911
|
+
case "reset_location":
|
|
912
|
+
result = await makeRequest("DELETE", `/devices/${args?.device_id}/location`);
|
|
913
|
+
break;
|
|
645
914
|
case "execute_dsl":
|
|
646
915
|
result = await makeRequest("POST", `/devices/${args?.device_id}/dsl/execute`, args?.script, 300000 // 5 minutes
|
|
647
916
|
);
|
|
@@ -822,10 +1091,17 @@ const API_REFERENCE = `# MobAI API Reference
|
|
|
822
1091
|
| Endpoint | Method | Description |
|
|
823
1092
|
|----------|--------|-------------|
|
|
824
1093
|
| /devices/{id}/tap | POST | Tap element: {"index": N} or {"x": X, "y": Y} |
|
|
1094
|
+
| /devices/{id}/double-tap | POST | Double tap: {"index": N} or {"x": X, "y": Y} |
|
|
1095
|
+
| /devices/{id}/long-press | POST | Long press (0.5s): {"index": N} or {"x": X, "y": Y} |
|
|
1096
|
+
| /devices/{id}/two-finger-tap | POST | Two-finger tap (iOS): {"index": N} or {"x": X, "y": Y} |
|
|
825
1097
|
| /devices/{id}/swipe | POST | Swipe: {"fromX", "fromY", "toX", "toY", "duration"} |
|
|
1098
|
+
| /devices/{id}/drag | POST | Drag: {"fromX", "fromY", "toX", "toY", "duration", "pressDuration"} |
|
|
826
1099
|
| /devices/{id}/type | POST | Type text: {"text": "..."} |
|
|
1100
|
+
| /devices/{id}/dismiss-keyboard | POST | Dismiss on-screen keyboard |
|
|
827
1101
|
| /devices/{id}/go-home | POST | Go to home screen |
|
|
828
1102
|
| /devices/{id}/launch-app | POST | Launch app: {"bundleId": "..."} |
|
|
1103
|
+
| /devices/{id}/apps/{bundleId} | DELETE | Uninstall app by bundle ID |
|
|
1104
|
+
| /devices/{id}/kill-app | POST | Kill app: {"bundleId": "..."} |
|
|
829
1105
|
|
|
830
1106
|
## DSL Execution
|
|
831
1107
|
|
|
@@ -839,6 +1115,15 @@ const API_REFERENCE = `# MobAI API Reference
|
|
|
839
1115
|
|----------|--------|-------------|
|
|
840
1116
|
| /devices/{id}/agent/run | POST | Run AI agent: {"task": "..."} |
|
|
841
1117
|
|
|
1118
|
+
## Performance Metrics
|
|
1119
|
+
|
|
1120
|
+
| Endpoint | Method | Description |
|
|
1121
|
+
|----------|--------|-------------|
|
|
1122
|
+
| /devices/{id}/metrics/start | POST | Start metrics collection |
|
|
1123
|
+
| /devices/{id}/metrics/stop | POST | Stop collection, return summary |
|
|
1124
|
+
| /devices/{id}/metrics | GET | Get raw metrics buffer |
|
|
1125
|
+
| /devices/{id}/metrics/summary | GET | Get current summary without stopping |
|
|
1126
|
+
|
|
842
1127
|
## Web Automation
|
|
843
1128
|
|
|
844
1129
|
| Endpoint | Method | Description |
|
|
@@ -922,12 +1207,18 @@ The DSL (Domain Specific Language) enables batch execution of multiple automatio
|
|
|
922
1207
|
| scroll | Scroll in container | direction, predicate (container), to_element |
|
|
923
1208
|
| open_app | Launch app | bundle_id |
|
|
924
1209
|
| navigate | Go home/back | target ("home", "back") |
|
|
925
|
-
| wait_for | Wait for element | predicate, timeout_ms |
|
|
1210
|
+
| wait_for | Wait for element or UI stability | predicate, timeout_ms, poll_interval_ms, stable (wait for UI to stop changing) |
|
|
1211
|
+
| screenshot | Save screenshot to file | file_path (directory), name (optional filename) |
|
|
926
1212
|
| assert_exists | Verify element exists | predicate, timeout_ms |
|
|
927
1213
|
| assert_not_exists | Verify element gone | predicate |
|
|
928
1214
|
| delay | Wait fixed time | duration_ms |
|
|
929
1215
|
| if_exists | Conditional | predicate, then, else |
|
|
930
1216
|
| select_web_context | Select browser/WebView | url_contains, title_contains (optional filters) |
|
|
1217
|
+
| kill_app | Force-kill running app | bundle_id |
|
|
1218
|
+
| set_location | Simulate GPS location (Android 12+ for real devices) | lat, lon |
|
|
1219
|
+
| reset_location | Reset to real GPS (Android 12+ for real devices) | (no fields) |
|
|
1220
|
+
| metrics_start | Start performance monitoring | types, bundle_id, label, thresholds, capture_logs |
|
|
1221
|
+
| metrics_stop | Stop monitoring, get summary | format ("summary" or "detailed") |
|
|
931
1222
|
|
|
932
1223
|
## Predicates
|
|
933
1224
|
|
|
@@ -990,6 +1281,86 @@ Use \`include: ["ocr"]\` in observe to get text recognition when UI tree is empt
|
|
|
990
1281
|
\`\`\`
|
|
991
1282
|
|
|
992
1283
|
Returns text with coordinates for tapping (already adjusted for tapping).
|
|
1284
|
+
|
|
1285
|
+
## Performance Metrics
|
|
1286
|
+
|
|
1287
|
+
Collect CPU, memory, FPS, network, and battery metrics during test flows with optional logging capture.
|
|
1288
|
+
|
|
1289
|
+
### Start Metrics Collection
|
|
1290
|
+
\`\`\`json
|
|
1291
|
+
{
|
|
1292
|
+
"action": "metrics_start",
|
|
1293
|
+
"types": ["system_cpu", "system_memory", "fps"],
|
|
1294
|
+
"bundle_id": "com.example.app",
|
|
1295
|
+
"label": "login_flow",
|
|
1296
|
+
"capture_logs": true,
|
|
1297
|
+
"thresholds": {
|
|
1298
|
+
"cpu_high": 80,
|
|
1299
|
+
"fps_low": 45,
|
|
1300
|
+
"memory_growth_mb_min": 50
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
\`\`\`
|
|
1304
|
+
|
|
1305
|
+
**Fields:**
|
|
1306
|
+
- \`types\`: Metrics to collect - system_cpu, system_memory, fps, network, battery, process
|
|
1307
|
+
- \`bundle_id\`: Filter to specific app (optional)
|
|
1308
|
+
- \`label\`: Human-readable session label (optional)
|
|
1309
|
+
- \`thresholds\`: Custom thresholds for anomaly detection (optional)
|
|
1310
|
+
- \`capture_logs\`: Capture device logs during session (default: false)
|
|
1311
|
+
|
|
1312
|
+
### Stop and Get Summary
|
|
1313
|
+
\`\`\`json
|
|
1314
|
+
{"action": "metrics_stop", "format": "summary"}
|
|
1315
|
+
\`\`\`
|
|
1316
|
+
|
|
1317
|
+
**Response:**
|
|
1318
|
+
\`\`\`json
|
|
1319
|
+
{
|
|
1320
|
+
"metrics_summary": {
|
|
1321
|
+
"session": {
|
|
1322
|
+
"label": "login_flow",
|
|
1323
|
+
"duration_seconds": 45.2,
|
|
1324
|
+
"sample_count": 45,
|
|
1325
|
+
"session_id": "abc123",
|
|
1326
|
+
"data_file": "/tmp/mobai/metrics/abc123.jsonl",
|
|
1327
|
+
"logs_file": "/tmp/mobai/logs/abc123.jsonl",
|
|
1328
|
+
"logs_available": true
|
|
1329
|
+
},
|
|
1330
|
+
"overall_health": "warning",
|
|
1331
|
+
"health_score": 72,
|
|
1332
|
+
"system_cpu": {"avg": 34.5, "max": 89.2, "p95": 78.1, "status": "ok"},
|
|
1333
|
+
"system_memory": {"avg_percent": 45.2, "growth_mb": 28.5, "trend": "increasing", "status": "warning"},
|
|
1334
|
+
"fps": {"avg": 58.2, "min": 24.0, "jank_percent": 8.5, "status": "warning"},
|
|
1335
|
+
"anomalies": {
|
|
1336
|
+
"cpu_spikes": [
|
|
1337
|
+
{"at_s": 0.5, "peak": 288, "duration_ms": 18147, "source": "system"}
|
|
1338
|
+
],
|
|
1339
|
+
"fps_drops": [
|
|
1340
|
+
{"start_s": 1.2, "end_s": 16.8, "min_fps": 39.5, "avg_fps": 42.3, "samples": 1}
|
|
1341
|
+
],
|
|
1342
|
+
},
|
|
1343
|
+
"recommendations": [
|
|
1344
|
+
"FPS dropped to 24 at +15s - investigate screen transition"
|
|
1345
|
+
]
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
\`\`\`
|
|
1349
|
+
|
|
1350
|
+
### Example: Performance Test Flow
|
|
1351
|
+
\`\`\`json
|
|
1352
|
+
{
|
|
1353
|
+
"version": "0.2",
|
|
1354
|
+
"steps": [
|
|
1355
|
+
{"action": "metrics_start", "types": ["system_cpu", "system_memory", "fps"], "label": "app_launch"},
|
|
1356
|
+
{"action": "open_app", "bundle_id": "com.example.app"},
|
|
1357
|
+
{"action": "wait_for", "predicate": {"text": "Welcome"}, "timeout_ms": 10000},
|
|
1358
|
+
{"action": "tap", "predicate": {"text": "Login"}},
|
|
1359
|
+
{"action": "delay", "duration_ms": 5000},
|
|
1360
|
+
{"action": "metrics_stop", "format": "summary"}
|
|
1361
|
+
]
|
|
1362
|
+
}
|
|
1363
|
+
\`\`\`
|
|
993
1364
|
`;
|
|
994
1365
|
const NATIVE_RUNNER_GUIDE = `# Native App Automation Guide
|
|
995
1366
|
|