mobile-debug-mcp 0.10.0 → 0.11.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/server.js CHANGED
@@ -2,12 +2,11 @@
2
2
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
4
  import { ListToolsRequestSchema, CallToolRequestSchema } from "@modelcontextprotocol/sdk/types.js";
5
+ import { ToolsManage } from './tools/manage.js';
5
6
  import { ToolsInteract } from './tools/interact.js';
6
7
  import { ToolsObserve } from './tools/observe.js';
7
- import { AndroidInteract } from './android/interact.js';
8
- import { iOSInteract } from './ios/interact.js';
9
- import { AndroidObserve } from './android/observe.js';
10
- import { iOSObserve } from './ios/observe.js';
8
+ import { AndroidManage } from './android/manage.js';
9
+ import { iOSManage } from './ios/manage.js';
11
10
  const server = new Server({
12
11
  name: "mobile-debug-mcp",
13
12
  version: "0.7.0"
@@ -375,7 +374,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
375
374
  try {
376
375
  if (name === "start_app") {
377
376
  const { platform, appId, deviceId } = args;
378
- const res = await (platform === 'android' ? new AndroidInteract().startApp(appId, deviceId) : new iOSInteract().startApp(appId, deviceId));
377
+ const res = await (platform === 'android' ? new AndroidManage().startApp(appId, deviceId) : new iOSManage().startApp(appId, deviceId));
379
378
  const response = {
380
379
  device: res.device,
381
380
  appStarted: res.appStarted,
@@ -385,25 +384,25 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
385
384
  }
386
385
  if (name === "terminate_app") {
387
386
  const { platform, appId, deviceId } = args;
388
- const res = await (platform === 'android' ? new AndroidInteract().terminateApp(appId, deviceId) : new iOSInteract().terminateApp(appId, deviceId));
387
+ const res = await (platform === 'android' ? new AndroidManage().terminateApp(appId, deviceId) : new iOSManage().terminateApp(appId, deviceId));
389
388
  const response = { device: res.device, appTerminated: res.appTerminated };
390
389
  return wrapResponse(response);
391
390
  }
392
391
  if (name === "restart_app") {
393
392
  const { platform, appId, deviceId } = args;
394
- const res = await (platform === 'android' ? new AndroidInteract().restartApp(appId, deviceId) : new iOSInteract().restartApp(appId, deviceId));
393
+ const res = await (platform === 'android' ? new AndroidManage().restartApp(appId, deviceId) : new iOSManage().restartApp(appId, deviceId));
395
394
  const response = { device: res.device, appRestarted: res.appRestarted, launchTimeMs: res.launchTimeMs };
396
395
  return wrapResponse(response);
397
396
  }
398
397
  if (name === "reset_app_data") {
399
398
  const { platform, appId, deviceId } = args;
400
- const res = await (platform === 'android' ? new AndroidInteract().resetAppData(appId, deviceId) : new iOSInteract().resetAppData(appId, deviceId));
399
+ const res = await (platform === 'android' ? new AndroidManage().resetAppData(appId, deviceId) : new iOSManage().resetAppData(appId, deviceId));
401
400
  const response = { device: res.device, dataCleared: res.dataCleared };
402
401
  return wrapResponse(response);
403
402
  }
404
403
  if (name === "install_app") {
405
404
  const { platform, appPath, deviceId } = args;
406
- const res = await ToolsInteract.installAppHandler({ platform, appPath, deviceId });
405
+ const res = await ToolsManage.installAppHandler({ platform, appPath, deviceId });
407
406
  const response = {
408
407
  device: res.device,
409
408
  installed: res.installed,
@@ -412,6 +411,17 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
412
411
  };
413
412
  return wrapResponse(response);
414
413
  }
414
+ if (name === 'build_and_install') {
415
+ const { platform, projectPath, deviceId, timeout } = args;
416
+ const res = await ToolsManage.buildAndInstallHandler({ platform, projectPath, deviceId, timeout });
417
+ // res: { ndjson, result }
418
+ return {
419
+ content: [
420
+ { type: 'text', text: res.ndjson },
421
+ { type: 'text', text: JSON.stringify(res.result, null, 2) }
422
+ ]
423
+ };
424
+ }
415
425
  if (name === "get_logs") {
416
426
  const { platform, appId, deviceId, lines } = args;
417
427
  const res = await ToolsObserve.getLogsHandler({ platform, appId, deviceId, lines });
@@ -424,7 +434,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
424
434
  }
425
435
  if (name === "list_devices") {
426
436
  const { platform, appId } = (args || {});
427
- const res = await ToolsObserve.listDevicesHandler({ platform, appId });
437
+ const res = await ToolsManage.listDevicesHandler({ platform, appId });
428
438
  return wrapResponse(res);
429
439
  }
430
440
  if (name === "capture_screenshot") {
@@ -439,37 +449,37 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
439
449
  }
440
450
  if (name === "get_ui_tree") {
441
451
  const { platform, deviceId } = args;
442
- const res = await (platform === 'android' ? new AndroidObserve().getUITree(deviceId) : new iOSObserve().getUITree(deviceId));
452
+ const res = await ToolsObserve.getUITreeHandler({ platform, deviceId });
443
453
  return wrapResponse(res);
444
454
  }
445
455
  if (name === "get_current_screen") {
446
456
  const { deviceId } = (args || {});
447
- const res = await new AndroidObserve().getCurrentScreen(deviceId);
457
+ const res = await ToolsObserve.getCurrentScreenHandler({ deviceId });
448
458
  return wrapResponse(res);
449
459
  }
450
460
  if (name === "wait_for_element") {
451
461
  const { platform, text, timeout, deviceId } = (args || {});
452
- const res = await (platform === 'android' ? new AndroidInteract().waitForElement(text, timeout, deviceId) : new iOSInteract().waitForElement(text, timeout, deviceId));
462
+ const res = await ToolsInteract.waitForElementHandler({ platform, text, timeout, deviceId });
453
463
  return wrapResponse(res);
454
464
  }
455
465
  if (name === "tap") {
456
466
  const { platform, x, y, deviceId } = (args || {});
457
- const res = await (platform === 'android' ? new AndroidInteract().tap(x, y, deviceId) : new iOSInteract().tap(x, y, deviceId));
467
+ const res = await ToolsInteract.tapHandler({ platform, x, y, deviceId });
458
468
  return wrapResponse(res);
459
469
  }
460
470
  if (name === "swipe") {
461
471
  const { x1, y1, x2, y2, duration, deviceId } = (args || {});
462
- const res = await new AndroidInteract().swipe(x1, y1, x2, y2, duration, deviceId);
472
+ const res = await ToolsInteract.swipeHandler({ x1, y1, x2, y2, duration, deviceId });
463
473
  return wrapResponse(res);
464
474
  }
465
475
  if (name === "type_text") {
466
476
  const { text, deviceId } = (args || {});
467
- const res = await new AndroidInteract().typeText(text, deviceId);
477
+ const res = await ToolsInteract.typeTextHandler({ text, deviceId });
468
478
  return wrapResponse(res);
469
479
  }
470
480
  if (name === "press_back") {
471
481
  const { deviceId } = (args || {});
472
- const res = await new AndroidInteract().pressBack(deviceId);
482
+ const res = await ToolsInteract.pressBackHandler({ deviceId });
473
483
  return wrapResponse(res);
474
484
  }
475
485
  if (name === 'start_log_stream') {
@@ -1,89 +1,39 @@
1
- import { promises as fs } from 'fs';
2
- import path from 'path';
3
1
  import { resolveTargetDevice } from '../resolve-device.js';
4
2
  import { AndroidInteract } from '../android/interact.js';
5
3
  import { iOSInteract } from '../ios/interact.js';
6
4
  export class ToolsInteract {
7
- static async installAppHandler({ platform, appPath, deviceId }) {
8
- let chosenPlatform = platform;
9
- try {
10
- const stat = await fs.stat(appPath).catch(() => null);
11
- if (stat && stat.isDirectory()) {
12
- const files = (await fs.readdir(appPath).catch(() => []));
13
- if (files.some(f => f.endsWith('.xcodeproj') || f.endsWith('.xcworkspace'))) {
14
- chosenPlatform = 'ios';
15
- }
16
- else if (files.includes('gradlew') || files.includes('build.gradle') || files.includes('settings.gradle') || (files.includes('app') && (await fs.stat(path.join(appPath, 'app')).catch(() => null)))) {
17
- chosenPlatform = 'android';
18
- }
19
- else {
20
- chosenPlatform = 'android';
21
- }
22
- }
23
- else if (typeof appPath === 'string') {
24
- const ext = path.extname(appPath).toLowerCase();
25
- if (ext === '.apk')
26
- chosenPlatform = 'android';
27
- else if (ext === '.ipa' || ext === '.app')
28
- chosenPlatform = 'ios';
29
- else
30
- chosenPlatform = 'android';
31
- }
32
- }
33
- catch {
34
- chosenPlatform = 'android';
35
- }
36
- if (chosenPlatform === 'android') {
5
+ static async waitForElementHandler({ platform, text, timeout, deviceId }) {
6
+ const effectiveTimeout = timeout ?? 10000;
7
+ if (platform === 'android') {
37
8
  const resolved = await resolveTargetDevice({ platform: 'android', deviceId });
38
- const androidInteract = new AndroidInteract();
39
- const result = await androidInteract.installApp(appPath, resolved.id);
40
- return result;
9
+ return await new AndroidInteract().waitForElement(text, effectiveTimeout, resolved.id);
41
10
  }
42
11
  else {
43
12
  const resolved = await resolveTargetDevice({ platform: 'ios', deviceId });
44
- const iosInteract = new iOSInteract();
45
- const result = await iosInteract.installApp(appPath, resolved.id);
46
- return result;
13
+ return await new iOSInteract().waitForElement(text, effectiveTimeout, resolved.id);
47
14
  }
48
15
  }
49
- static async startAppHandler({ platform, appId, deviceId }) {
50
- if (platform === 'android') {
51
- const resolved = await resolveTargetDevice({ platform: 'android', appId, deviceId });
52
- return await new AndroidInteract().startApp(appId, resolved.id);
16
+ static async tapHandler({ platform, x, y, deviceId }) {
17
+ const effectivePlatform = platform || 'android';
18
+ if (effectivePlatform === 'android') {
19
+ const resolved = await resolveTargetDevice({ platform: 'android', deviceId });
20
+ return await new AndroidInteract().tap(x, y, resolved.id);
53
21
  }
54
22
  else {
55
- const resolved = await resolveTargetDevice({ platform: 'ios', appId, deviceId });
56
- return await new iOSInteract().startApp(appId, resolved.id);
23
+ const resolved = await resolveTargetDevice({ platform: 'ios', deviceId });
24
+ return await new iOSInteract().tap(x, y, resolved.id);
57
25
  }
58
26
  }
59
- static async terminateAppHandler({ platform, appId, deviceId }) {
60
- if (platform === 'android') {
61
- const resolved = await resolveTargetDevice({ platform: 'android', appId, deviceId });
62
- return await new AndroidInteract().terminateApp(appId, resolved.id);
63
- }
64
- else {
65
- const resolved = await resolveTargetDevice({ platform: 'ios', appId, deviceId });
66
- return await new iOSInteract().terminateApp(appId, resolved.id);
67
- }
27
+ static async swipeHandler({ x1, y1, x2, y2, duration, deviceId }) {
28
+ const resolved = await resolveTargetDevice({ platform: 'android', deviceId });
29
+ return await new AndroidInteract().swipe(x1, y1, x2, y2, duration, resolved.id);
68
30
  }
69
- static async restartAppHandler({ platform, appId, deviceId }) {
70
- if (platform === 'android') {
71
- const resolved = await resolveTargetDevice({ platform: 'android', appId, deviceId });
72
- return await new AndroidInteract().restartApp(appId, resolved.id);
73
- }
74
- else {
75
- const resolved = await resolveTargetDevice({ platform: 'ios', appId, deviceId });
76
- return await new iOSInteract().restartApp(appId, resolved.id);
77
- }
31
+ static async typeTextHandler({ text, deviceId }) {
32
+ const resolved = await resolveTargetDevice({ platform: 'android', deviceId });
33
+ return await new AndroidInteract().typeText(text, resolved.id);
78
34
  }
79
- static async resetAppDataHandler({ platform, appId, deviceId }) {
80
- if (platform === 'android') {
81
- const resolved = await resolveTargetDevice({ platform: 'android', appId, deviceId });
82
- return await new AndroidInteract().resetAppData(appId, resolved.id);
83
- }
84
- else {
85
- const resolved = await resolveTargetDevice({ platform: 'ios', appId, deviceId });
86
- return await new iOSInteract().resetAppData(appId, resolved.id);
87
- }
35
+ static async pressBackHandler({ deviceId }) {
36
+ const resolved = await resolveTargetDevice({ platform: 'android', deviceId });
37
+ return await new AndroidInteract().pressBack(resolved.id);
88
38
  }
89
39
  }
@@ -0,0 +1,180 @@
1
+ import { promises as fs } from 'fs';
2
+ import path from 'path';
3
+ import { resolveTargetDevice, listDevices } from '../resolve-device.js';
4
+ import { AndroidManage } from '../android/manage.js';
5
+ import { iOSManage } from '../ios/manage.js';
6
+ export class ToolsManage {
7
+ static async buildAppHandler({ platform, projectPath, variant }) {
8
+ // delegate to platform-specific build implementations
9
+ const chosen = platform || 'android';
10
+ if (chosen === 'android') {
11
+ const android = new AndroidManage();
12
+ const artifact = await android.build(projectPath, variant);
13
+ return artifact;
14
+ }
15
+ else {
16
+ const ios = new iOSManage();
17
+ const artifact = await ios.build(projectPath, variant);
18
+ return artifact;
19
+ }
20
+ }
21
+ static async installAppHandler({ platform, appPath, deviceId }) {
22
+ let chosenPlatform = platform;
23
+ try {
24
+ const stat = await fs.stat(appPath).catch(() => null);
25
+ if (stat && stat.isDirectory()) {
26
+ // If the directory itself looks like an .app bundle, treat as iOS
27
+ if (appPath.endsWith('.app')) {
28
+ chosenPlatform = 'ios';
29
+ }
30
+ else {
31
+ const files = (await fs.readdir(appPath).catch(() => []));
32
+ if (files.some(f => f.endsWith('.xcodeproj') || f.endsWith('.xcworkspace'))) {
33
+ chosenPlatform = 'ios';
34
+ }
35
+ else if (files.includes('gradlew') || files.includes('build.gradle') || files.includes('settings.gradle') || (files.includes('app') && (await fs.stat(path.join(appPath, 'app')).catch(() => null)))) {
36
+ chosenPlatform = 'android';
37
+ }
38
+ else {
39
+ chosenPlatform = 'android';
40
+ }
41
+ }
42
+ }
43
+ else if (typeof appPath === 'string') {
44
+ const ext = path.extname(appPath).toLowerCase();
45
+ if (ext === '.apk')
46
+ chosenPlatform = 'android';
47
+ else if (ext === '.ipa' || ext === '.app')
48
+ chosenPlatform = 'ios';
49
+ else
50
+ chosenPlatform = 'android';
51
+ }
52
+ }
53
+ catch {
54
+ chosenPlatform = 'android';
55
+ }
56
+ if (chosenPlatform === 'android') {
57
+ const resolved = await resolveTargetDevice({ platform: 'android', deviceId });
58
+ const androidRun = new AndroidManage();
59
+ const result = await androidRun.installApp(appPath, resolved.id);
60
+ return result;
61
+ }
62
+ else {
63
+ const resolved = await resolveTargetDevice({ platform: 'ios', deviceId });
64
+ const iosRun = new iOSManage();
65
+ const result = await iosRun.installApp(appPath, resolved.id);
66
+ return result;
67
+ }
68
+ }
69
+ static async startAppHandler({ platform, appId, deviceId }) {
70
+ if (platform === 'android') {
71
+ const resolved = await resolveTargetDevice({ platform: 'android', appId, deviceId });
72
+ return await new AndroidManage().startApp(appId, resolved.id);
73
+ }
74
+ else {
75
+ const resolved = await resolveTargetDevice({ platform: 'ios', appId, deviceId });
76
+ return await new iOSManage().startApp(appId, resolved.id);
77
+ }
78
+ }
79
+ static async terminateAppHandler({ platform, appId, deviceId }) {
80
+ if (platform === 'android') {
81
+ const resolved = await resolveTargetDevice({ platform: 'android', appId, deviceId });
82
+ return await new AndroidManage().terminateApp(appId, resolved.id);
83
+ }
84
+ else {
85
+ const resolved = await resolveTargetDevice({ platform: 'ios', appId, deviceId });
86
+ return await new iOSManage().terminateApp(appId, resolved.id);
87
+ }
88
+ }
89
+ static async restartAppHandler({ platform, appId, deviceId }) {
90
+ if (platform === 'android') {
91
+ const resolved = await resolveTargetDevice({ platform: 'android', appId, deviceId });
92
+ return await new AndroidManage().restartApp(appId, resolved.id);
93
+ }
94
+ else {
95
+ const resolved = await resolveTargetDevice({ platform: 'ios', appId, deviceId });
96
+ return await new iOSManage().restartApp(appId, resolved.id);
97
+ }
98
+ }
99
+ static async resetAppDataHandler({ platform, appId, deviceId }) {
100
+ if (platform === 'android') {
101
+ const resolved = await resolveTargetDevice({ platform: 'android', appId, deviceId });
102
+ return await new AndroidManage().resetAppData(appId, resolved.id);
103
+ }
104
+ else {
105
+ const resolved = await resolveTargetDevice({ platform: 'ios', appId, deviceId });
106
+ return await new iOSManage().resetAppData(appId, resolved.id);
107
+ }
108
+ }
109
+ static async buildAndInstallHandler({ platform, projectPath, deviceId, timeout }) {
110
+ const events = [];
111
+ const pushEvent = (obj) => events.push(JSON.stringify(obj));
112
+ const effectiveTimeout = timeout ?? 180000; // reserved for future streaming/timeouts
113
+ void effectiveTimeout;
114
+ // determine platform if not provided by inspecting path
115
+ let chosenPlatform = platform;
116
+ try {
117
+ const stat = await fs.stat(projectPath).catch(() => null);
118
+ if (!chosenPlatform) {
119
+ if (stat && stat.isDirectory()) {
120
+ const files = (await fs.readdir(projectPath).catch(() => []));
121
+ if (files.some(f => f.endsWith('.xcodeproj') || f.endsWith('.xcworkspace')))
122
+ chosenPlatform = 'ios';
123
+ else
124
+ chosenPlatform = 'android';
125
+ }
126
+ else {
127
+ const ext = path.extname(projectPath).toLowerCase();
128
+ if (ext === '.apk')
129
+ chosenPlatform = 'android';
130
+ else if (ext === '.ipa' || ext === '.app')
131
+ chosenPlatform = 'ios';
132
+ else
133
+ chosenPlatform = 'android';
134
+ }
135
+ }
136
+ }
137
+ catch {
138
+ chosenPlatform = chosenPlatform || 'android';
139
+ }
140
+ pushEvent({ type: 'build', status: 'started', platform: chosenPlatform });
141
+ let buildRes;
142
+ try {
143
+ buildRes = await ToolsManage.buildAppHandler({ platform: chosenPlatform, projectPath });
144
+ if (buildRes && buildRes.error) {
145
+ pushEvent({ type: 'build', status: 'failed', error: buildRes.error });
146
+ return { ndjson: events.join('\n') + '\n', result: { success: false, error: buildRes.error } };
147
+ }
148
+ pushEvent({ type: 'build', status: 'finished', artifactPath: buildRes.artifactPath });
149
+ }
150
+ catch (e) {
151
+ const msg = e instanceof Error ? e.message : String(e);
152
+ pushEvent({ type: 'build', status: 'failed', error: msg });
153
+ return { ndjson: events.join('\n') + '\n', result: { success: false, error: msg } };
154
+ }
155
+ // Install phase
156
+ const artifact = buildRes.artifactPath || projectPath;
157
+ pushEvent({ type: 'install', status: 'started', artifactPath: artifact, deviceId });
158
+ let installRes;
159
+ try {
160
+ installRes = await ToolsManage.installAppHandler({ platform: chosenPlatform, appPath: artifact, deviceId });
161
+ if (installRes && installRes.installed === true) {
162
+ pushEvent({ type: 'install', status: 'finished', artifactPath: artifact, device: installRes.device });
163
+ return { ndjson: events.join('\n') + '\n', result: { success: true, artifactPath: artifact, device: installRes.device, output: installRes.output } };
164
+ }
165
+ else {
166
+ pushEvent({ type: 'install', status: 'failed', error: installRes.error || 'unknown' });
167
+ return { ndjson: events.join('\n') + '\n', result: { success: false, error: installRes.error || 'install failed' } };
168
+ }
169
+ }
170
+ catch (e) {
171
+ const msg = e instanceof Error ? e.message : String(e);
172
+ pushEvent({ type: 'install', status: 'failed', error: msg });
173
+ return { ndjson: events.join('\n') + '\n', result: { success: false, error: msg } };
174
+ }
175
+ }
176
+ static async listDevicesHandler({ platform, appId }) {
177
+ const devices = await listDevices(platform, appId);
178
+ return { devices };
179
+ }
180
+ }
@@ -1,9 +1,21 @@
1
- import { resolveTargetDevice, listDevices } from '../resolve-device.js';
1
+ import { resolveTargetDevice } from '../resolve-device.js';
2
2
  import { AndroidObserve } from '../android/observe.js';
3
3
  import { iOSObserve } from '../ios/observe.js';
4
- import { AndroidInteract } from '../android/interact.js';
5
- import { iOSInteract } from '../ios/interact.js';
6
4
  export class ToolsObserve {
5
+ static async getUITreeHandler({ platform, deviceId }) {
6
+ if (platform === 'android') {
7
+ const resolved = await resolveTargetDevice({ platform: 'android', deviceId });
8
+ return await new AndroidObserve().getUITree(resolved.id);
9
+ }
10
+ else {
11
+ const resolved = await resolveTargetDevice({ platform: 'ios', deviceId });
12
+ return await new iOSObserve().getUITree(resolved.id);
13
+ }
14
+ }
15
+ static async getCurrentScreenHandler({ deviceId }) {
16
+ const resolved = await resolveTargetDevice({ platform: 'android', deviceId });
17
+ return await new AndroidObserve().getCurrentScreen(resolved.id);
18
+ }
7
19
  static async getLogsHandler({ platform, appId, deviceId, lines }) {
8
20
  if (platform === 'android') {
9
21
  const resolved = await resolveTargetDevice({ platform: 'android', appId, deviceId });
@@ -25,93 +37,35 @@ export class ToolsObserve {
25
37
  const sid = sessionId || 'default';
26
38
  if (effectivePlatform === 'android') {
27
39
  const resolved = await resolveTargetDevice({ platform: 'android', appId: packageName, deviceId });
28
- // AndroidObserve uses utils for log stream control; delegate to android/utils functions where appropriate
29
- const { startAndroidLogStream } = await import('../android/utils.js');
30
- return await startAndroidLogStream(packageName, level || 'error', resolved.id, sid);
40
+ // Delegate to AndroidObserve's log stream methods
41
+ return await new AndroidObserve().startLogStream(packageName, level || 'error', resolved.id, sid);
31
42
  }
32
43
  else {
33
44
  const resolved = await resolveTargetDevice({ platform: 'ios', appId: packageName, deviceId });
34
- // iOSObserve implements startIOSLogStream via ios/utils; use its helper
35
- const { startIOSLogStream } = await import('../ios/utils.js');
36
- return await startIOSLogStream(packageName, resolved.id, sid);
45
+ // Delegate to iOSObserve for starting log streams
46
+ return await new iOSObserve().startLogStream(packageName, resolved.id, sid);
37
47
  }
38
48
  }
39
49
  static async readLogStreamHandler({ platform, sessionId, limit, since }) {
40
50
  const effectivePlatform = platform || 'android';
41
51
  const sid = sessionId || 'default';
42
52
  if (effectivePlatform === 'android') {
43
- const { readLogStreamLines } = await import('../android/utils.js');
44
- return await readLogStreamLines(sid, limit ?? 100, since);
53
+ return await new AndroidObserve().readLogStream(sid, limit ?? 100, since);
45
54
  }
46
55
  else {
47
- const { readIOSLogStreamLines } = await import('../ios/utils.js');
48
- return await readIOSLogStreamLines(sid, limit ?? 100, since);
56
+ return await new iOSObserve().readLogStream(sid, limit ?? 100, since);
49
57
  }
50
58
  }
51
59
  static async stopLogStreamHandler({ platform, sessionId }) {
52
60
  const effectivePlatform = platform || 'android';
53
61
  const sid = sessionId || 'default';
54
62
  if (effectivePlatform === 'android') {
55
- const { stopAndroidLogStream } = await import('../android/utils.js');
56
- return await stopAndroidLogStream(sid);
57
- }
58
- else {
59
- const { stopIOSLogStream } = await import('../ios/utils.js');
60
- return await stopIOSLogStream(sid);
61
- }
62
- }
63
- static async getUITreeHandler({ platform, deviceId }) {
64
- if (platform === 'android') {
65
- const resolved = await resolveTargetDevice({ platform: 'android', deviceId });
66
- return await new AndroidObserve().getUITree(resolved.id);
67
- }
68
- else {
69
- const resolved = await resolveTargetDevice({ platform: 'ios', deviceId });
70
- return await new iOSObserve().getUITree(resolved.id);
71
- }
72
- }
73
- static async getCurrentScreenHandler({ deviceId }) {
74
- const resolved = await resolveTargetDevice({ platform: 'android', deviceId });
75
- return await new AndroidObserve().getCurrentScreen(resolved.id);
76
- }
77
- static async waitForElementHandler({ platform, text, timeout, deviceId }) {
78
- const effectiveTimeout = timeout ?? 10000;
79
- if (platform === 'android') {
80
- const resolved = await resolveTargetDevice({ platform: 'android', deviceId });
81
- return await new AndroidInteract().waitForElement(text, effectiveTimeout, resolved.id);
82
- }
83
- else {
84
- const resolved = await resolveTargetDevice({ platform: 'ios', deviceId });
85
- return await new iOSInteract().waitForElement(text, effectiveTimeout, resolved.id);
86
- }
87
- }
88
- static async tapHandler({ platform, x, y, deviceId }) {
89
- const effectivePlatform = platform || 'android';
90
- if (effectivePlatform === 'android') {
91
- const resolved = await resolveTargetDevice({ platform: 'android', deviceId });
92
- return await new AndroidInteract().tap(x, y, resolved.id);
63
+ return await new AndroidObserve().stopLogStream(sid);
93
64
  }
94
65
  else {
95
- const resolved = await resolveTargetDevice({ platform: 'ios', deviceId });
96
- return await new iOSInteract().tap(x, y, resolved.id);
66
+ return await new iOSObserve().stopLogStream(sid);
97
67
  }
98
68
  }
99
- static async swipeHandler({ x1, y1, x2, y2, duration, deviceId }) {
100
- const resolved = await resolveTargetDevice({ platform: 'android', deviceId });
101
- return await new AndroidInteract().swipe(x1, y1, x2, y2, duration, resolved.id);
102
- }
103
- static async typeTextHandler({ text, deviceId }) {
104
- const resolved = await resolveTargetDevice({ platform: 'android', deviceId });
105
- return await new AndroidInteract().typeText(text, resolved.id);
106
- }
107
- static async pressBackHandler({ deviceId }) {
108
- const resolved = await resolveTargetDevice({ platform: 'android', deviceId });
109
- return await new AndroidInteract().pressBack(resolved.id);
110
- }
111
- static async listDevicesHandler({ platform, appId }) {
112
- const devices = await listDevices(platform, appId);
113
- return { devices };
114
- }
115
69
  static async captureScreenshotHandler({ platform, deviceId }) {
116
70
  const effectivePlatform = platform || 'android';
117
71
  if (effectivePlatform === 'android') {