mobile-mcp-ai 2.1.2__py3-none-any.whl → 2.5.8__py3-none-any.whl

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.
Files changed (65) hide show
  1. mobile_mcp/__init__.py +34 -0
  2. mobile_mcp/config.py +142 -0
  3. mobile_mcp/core/basic_tools_lite.py +3266 -0
  4. {core → mobile_mcp/core}/device_manager.py +2 -2
  5. mobile_mcp/core/dynamic_config.py +272 -0
  6. mobile_mcp/core/ios_client_wda.py +569 -0
  7. mobile_mcp/core/ios_device_manager_wda.py +306 -0
  8. {core → mobile_mcp/core}/mobile_client.py +279 -39
  9. mobile_mcp/core/template_matcher.py +429 -0
  10. mobile_mcp/core/templates/close_buttons/auto_x_0112_151217.png +0 -0
  11. mobile_mcp/core/templates/close_buttons/auto_x_0112_152037.png +0 -0
  12. mobile_mcp/core/templates/close_buttons/auto_x_0112_152840.png +0 -0
  13. mobile_mcp/core/templates/close_buttons/auto_x_0112_153256.png +0 -0
  14. mobile_mcp/core/templates/close_buttons/auto_x_0112_154847.png +0 -0
  15. mobile_mcp/core/templates/close_buttons/gray_x_stock_ad.png +0 -0
  16. {core → mobile_mcp/core}/utils/smart_wait.py +3 -3
  17. mobile_mcp/mcp_tools/__init__.py +10 -0
  18. mobile_mcp/mcp_tools/mcp_server.py +1071 -0
  19. mobile_mcp_ai-2.5.8.dist-info/METADATA +469 -0
  20. mobile_mcp_ai-2.5.8.dist-info/RECORD +32 -0
  21. mobile_mcp_ai-2.5.8.dist-info/entry_points.txt +2 -0
  22. mobile_mcp_ai-2.5.8.dist-info/licenses/LICENSE +201 -0
  23. mobile_mcp_ai-2.5.8.dist-info/top_level.txt +1 -0
  24. core/ai/__init__.py +0 -11
  25. core/ai/ai_analyzer.py +0 -197
  26. core/ai/ai_config.py +0 -116
  27. core/ai/ai_platform_adapter.py +0 -399
  28. core/ai/smart_test_executor.py +0 -520
  29. core/ai/test_generator.py +0 -365
  30. core/ai/test_generator_from_history.py +0 -391
  31. core/ai/test_generator_standalone.py +0 -293
  32. core/assertion/__init__.py +0 -9
  33. core/assertion/smart_assertion.py +0 -341
  34. core/basic_tools.py +0 -377
  35. core/h5/__init__.py +0 -10
  36. core/h5/h5_handler.py +0 -548
  37. core/ios_client.py +0 -219
  38. core/ios_device_manager.py +0 -252
  39. core/locator/__init__.py +0 -10
  40. core/locator/cursor_ai_auto_analyzer.py +0 -119
  41. core/locator/cursor_vision_helper.py +0 -414
  42. core/locator/mobile_smart_locator.py +0 -1640
  43. core/locator/position_analyzer.py +0 -813
  44. core/locator/script_updater.py +0 -157
  45. core/nl_test_runner.py +0 -585
  46. core/smart_app_launcher.py +0 -334
  47. core/smart_tools.py +0 -311
  48. mcp/__init__.py +0 -8
  49. mcp/mcp_server.py +0 -1919
  50. mcp/mcp_server_simple.py +0 -476
  51. mobile_mcp_ai-2.1.2.dist-info/METADATA +0 -567
  52. mobile_mcp_ai-2.1.2.dist-info/RECORD +0 -45
  53. mobile_mcp_ai-2.1.2.dist-info/entry_points.txt +0 -2
  54. mobile_mcp_ai-2.1.2.dist-info/top_level.txt +0 -4
  55. vision/__init__.py +0 -10
  56. vision/vision_locator.py +0 -404
  57. {core → mobile_mcp/core}/__init__.py +0 -0
  58. {core → mobile_mcp/core}/utils/__init__.py +0 -0
  59. {core → mobile_mcp/core}/utils/logger.py +0 -0
  60. {core → mobile_mcp/core}/utils/operation_history_manager.py +0 -0
  61. {utils → mobile_mcp/utils}/__init__.py +0 -0
  62. {utils → mobile_mcp/utils}/logger.py +0 -0
  63. {utils → mobile_mcp/utils}/xml_formatter.py +0 -0
  64. {utils → mobile_mcp/utils}/xml_parser.py +0 -0
  65. {mobile_mcp_ai-2.1.2.dist-info → mobile_mcp_ai-2.5.8.dist-info}/WHEEL +0 -0
@@ -0,0 +1,306 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ iOS设备连接管理 - 使用 tidevice + facebook-wda
5
+
6
+ 优势:
7
+ 1. API风格和 uiautomator2 几乎一样
8
+ 2. 不需要启动 Appium Server
9
+ 3. tidevice 简化设备管理
10
+
11
+ 前置条件:
12
+ 1. 安装 tidevice: pip install tidevice
13
+ 2. 安装 facebook-wda: pip install facebook-wda
14
+ 3. 首次需要用 Xcode 编译 WebDriverAgent 到设备上
15
+
16
+ 用法:
17
+ manager = IOSDeviceManagerWDA()
18
+ devices = manager.list_devices()
19
+ client = manager.connect(device_id="xxx")
20
+ client(text="登录").click() # 和 uiautomator2 风格一样!
21
+ """
22
+ import sys
23
+ import subprocess
24
+ from typing import List, Optional, Dict
25
+
26
+
27
+ class IOSDeviceManagerWDA:
28
+ """
29
+ iOS设备管理器 - 使用 tidevice + facebook-wda
30
+
31
+ 用法:
32
+ manager = IOSDeviceManagerWDA()
33
+ devices = manager.list_devices()
34
+ client = manager.connect()
35
+ client(text="登录").click()
36
+ """
37
+
38
+ def __init__(self):
39
+ """初始化iOS设备管理器"""
40
+ self.client = None
41
+ self.current_device_id = None
42
+ self._wda_proxy_process = None
43
+ self._check_dependencies()
44
+
45
+ def _check_dependencies(self):
46
+ """检查依赖是否安装"""
47
+ try:
48
+ import tidevice
49
+ import wda
50
+ except ImportError as e:
51
+ raise ImportError(
52
+ f"缺少iOS自动化依赖: {e}\n"
53
+ f"请运行以下命令安装:\n"
54
+ f" pip install tidevice facebook-wda\n"
55
+ )
56
+
57
+ def list_devices(self) -> List[Dict[str, str]]:
58
+ """
59
+ 列出所有连接的iOS设备
60
+
61
+ Returns:
62
+ 设备列表,每个设备包含 id, name, type 等信息
63
+ """
64
+ devices = []
65
+
66
+ try:
67
+ # 优先使用 tidevice Python API
68
+ try:
69
+ import tidevice
70
+ for d in tidevice.Usbmux().device_list():
71
+ devices.append({
72
+ 'id': d.udid,
73
+ 'name': d.name if hasattr(d, 'name') else 'iOS Device',
74
+ 'type': 'device',
75
+ 'state': 'connected'
76
+ })
77
+ if devices:
78
+ return devices
79
+ except Exception:
80
+ pass
81
+
82
+ # 回退:使用 subprocess 调用 tidevice
83
+ result = subprocess.run(
84
+ [sys.executable, '-m', 'tidevice', 'list', '--json'],
85
+ capture_output=True,
86
+ text=True,
87
+ timeout=10
88
+ )
89
+
90
+ if result.returncode == 0 and result.stdout.strip():
91
+ import json
92
+ try:
93
+ device_list = json.loads(result.stdout)
94
+ for device in device_list:
95
+ devices.append({
96
+ 'id': device.get('udid', ''),
97
+ 'name': device.get('name', 'iOS Device'),
98
+ 'type': 'device',
99
+ 'model': device.get('model', 'Unknown'),
100
+ 'ios_version': device.get('version', 'Unknown'),
101
+ 'state': 'connected'
102
+ })
103
+ except json.JSONDecodeError:
104
+ # 尝试纯文本解析
105
+ for line in result.stdout.strip().split('\n'):
106
+ if line.strip():
107
+ parts = line.split()
108
+ if len(parts) >= 1:
109
+ devices.append({
110
+ 'id': parts[0],
111
+ 'name': ' '.join(parts[1:]) if len(parts) > 1 else 'iOS Device',
112
+ 'type': 'device',
113
+ 'state': 'connected'
114
+ })
115
+
116
+ # 如果 tidevice 没有找到设备,尝试使用 xcrun simctl 列出模拟器
117
+ if not devices:
118
+ sim_result = subprocess.run(
119
+ ['xcrun', 'simctl', 'list', 'devices', 'booted', '--json'],
120
+ capture_output=True,
121
+ text=True,
122
+ timeout=10
123
+ )
124
+
125
+ if sim_result.returncode == 0 and sim_result.stdout.strip():
126
+ import json
127
+ sim_data = json.loads(sim_result.stdout)
128
+ for runtime, sims in sim_data.get('devices', {}).items():
129
+ for sim in sims:
130
+ if sim.get('state') == 'Booted':
131
+ devices.append({
132
+ 'id': sim.get('udid', ''),
133
+ 'name': sim.get('name', 'Simulator'),
134
+ 'type': 'simulator',
135
+ 'runtime': runtime,
136
+ 'state': 'Booted'
137
+ })
138
+
139
+ return devices
140
+
141
+ except FileNotFoundError:
142
+ print("⚠️ tidevice 未安装,请运行: pip install tidevice", file=sys.stderr)
143
+ return []
144
+ except Exception as e:
145
+ print(f"⚠️ 获取设备列表失败: {e}", file=sys.stderr)
146
+ return []
147
+
148
+ def start_wda_proxy(self, device_id: str, port: int = 8100) -> bool:
149
+ """
150
+ 启动 WDA 代理(如果尚未启动)
151
+
152
+ Args:
153
+ device_id: 设备UDID
154
+ port: WDA代理端口,默认8100
155
+
156
+ Returns:
157
+ 是否成功启动
158
+ """
159
+ try:
160
+ import socket
161
+
162
+ # 检查端口是否已被占用(可能WDA已在运行)
163
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
164
+ result = sock.connect_ex(('localhost', port))
165
+ sock.close()
166
+
167
+ if result == 0:
168
+ print(f" ✅ WDA代理已在运行 (端口 {port})", file=sys.stderr)
169
+ return True
170
+
171
+ # 启动 WDA 代理
172
+ print(f" 🚀 启动 WDA 代理...", file=sys.stderr)
173
+
174
+ # 使用 tidevice 启动 WDA(后台运行)
175
+ self._wda_proxy_process = subprocess.Popen(
176
+ [sys.executable, '-m', 'tidevice', '-u', device_id, 'wdaproxy', '-B',
177
+ 'com.facebook.WebDriverAgentRunner.xctrunner', '--port', str(port)],
178
+ stdout=subprocess.DEVNULL,
179
+ stderr=subprocess.DEVNULL
180
+ )
181
+
182
+ # 等待 WDA 启动
183
+ import time
184
+ for i in range(10): # 最多等待10秒
185
+ time.sleep(1)
186
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
187
+ result = sock.connect_ex(('localhost', port))
188
+ sock.close()
189
+
190
+ if result == 0:
191
+ print(f" ✅ WDA代理启动成功 (端口 {port})", file=sys.stderr)
192
+ return True
193
+
194
+ print(f" ⏳ 等待WDA启动... ({i+1}/10)", file=sys.stderr)
195
+
196
+ print(f" ❌ WDA代理启动超时", file=sys.stderr)
197
+ return False
198
+
199
+ except Exception as e:
200
+ print(f" ❌ 启动WDA代理失败: {e}", file=sys.stderr)
201
+ return False
202
+
203
+ def connect(self, device_id: Optional[str] = None, port: int = 8100) -> 'wda.Client':
204
+ """
205
+ 连接iOS设备
206
+
207
+ Args:
208
+ device_id: 设备UDID,None则自动选择第一个设备
209
+ port: WDA代理端口,默认8100
210
+
211
+ Returns:
212
+ wda.Client 对象(API类似 uiautomator2)
213
+ """
214
+ try:
215
+ import wda
216
+
217
+ # 如果没有指定设备ID,自动选择第一个
218
+ if device_id is None:
219
+ devices = self.list_devices()
220
+ if not devices:
221
+ raise RuntimeError(
222
+ "未找到连接的iOS设备\n"
223
+ "请确保:\n"
224
+ "1. iOS设备已通过USB连接\n"
225
+ "2. 设备已信任此电脑\n"
226
+ "3. tidevice已安装: pip install tidevice"
227
+ )
228
+ device_id = devices[0]['id']
229
+ print(f" 📱 自动选择设备: {device_id}", file=sys.stderr)
230
+
231
+ self.current_device_id = device_id
232
+
233
+ # 尝试启动 WDA 代理
234
+ self.start_wda_proxy(device_id, port)
235
+
236
+ # 连接到 WDA
237
+ self.client = wda.Client(f'http://localhost:{port}')
238
+
239
+ # 测试连接
240
+ try:
241
+ status = self.client.status()
242
+ print(f" ✅ iOS设备连接成功: {device_id}", file=sys.stderr)
243
+ print(f" iOS版本: {status.get('os', {}).get('version', 'Unknown')}", file=sys.stderr)
244
+ except Exception as e:
245
+ print(f" ⚠️ 连接可能不稳定: {e}", file=sys.stderr)
246
+
247
+ return self.client
248
+
249
+ except ImportError:
250
+ raise ImportError(
251
+ "facebook-wda 未安装\n"
252
+ "请运行: pip install facebook-wda"
253
+ )
254
+ except Exception as e:
255
+ error_msg = str(e)
256
+ if "Connection refused" in error_msg:
257
+ raise RuntimeError(
258
+ f"无法连接到WDA (端口 {port})\n"
259
+ f"请确保:\n"
260
+ f"1. WebDriverAgent 已安装到设备上(需要用Xcode首次编译)\n"
261
+ f"2. 运行: tidevice -u {device_id} wdaproxy -B com.facebook.WebDriverAgentRunner.xctrunner\n"
262
+ f"3. 或者检查端口 {port} 是否被占用"
263
+ )
264
+ raise RuntimeError(f"连接iOS设备失败: {e}")
265
+
266
+ def check_device_status(self) -> Dict:
267
+ """
268
+ 检查设备连接状态
269
+
270
+ Returns:
271
+ 设备状态信息
272
+ """
273
+ if not self.client:
274
+ return {'connected': False, 'reason': '设备未连接'}
275
+
276
+ try:
277
+ status = self.client.status()
278
+ return {
279
+ 'connected': True,
280
+ 'device_id': self.current_device_id,
281
+ 'ios_version': status.get('os', {}).get('version', 'Unknown'),
282
+ 'wda_version': status.get('build', {}).get('productBundleIdentifier', 'Unknown'),
283
+ }
284
+ except Exception as e:
285
+ return {
286
+ 'connected': False,
287
+ 'reason': str(e)
288
+ }
289
+
290
+ def disconnect(self):
291
+ """断开设备连接"""
292
+ if self._wda_proxy_process:
293
+ try:
294
+ self._wda_proxy_process.terminate()
295
+ self._wda_proxy_process.wait(timeout=5)
296
+ except:
297
+ pass
298
+ self._wda_proxy_process = None
299
+
300
+ self.client = None
301
+ self.current_device_id = None
302
+ print(" 📱 iOS设备已断开连接", file=sys.stderr)
303
+
304
+
305
+
306
+