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.
- mobile_mcp/__init__.py +34 -0
- mobile_mcp/config.py +142 -0
- mobile_mcp/core/basic_tools_lite.py +3266 -0
- {core → mobile_mcp/core}/device_manager.py +2 -2
- mobile_mcp/core/dynamic_config.py +272 -0
- mobile_mcp/core/ios_client_wda.py +569 -0
- mobile_mcp/core/ios_device_manager_wda.py +306 -0
- {core → mobile_mcp/core}/mobile_client.py +279 -39
- mobile_mcp/core/template_matcher.py +429 -0
- mobile_mcp/core/templates/close_buttons/auto_x_0112_151217.png +0 -0
- mobile_mcp/core/templates/close_buttons/auto_x_0112_152037.png +0 -0
- mobile_mcp/core/templates/close_buttons/auto_x_0112_152840.png +0 -0
- mobile_mcp/core/templates/close_buttons/auto_x_0112_153256.png +0 -0
- mobile_mcp/core/templates/close_buttons/auto_x_0112_154847.png +0 -0
- mobile_mcp/core/templates/close_buttons/gray_x_stock_ad.png +0 -0
- {core → mobile_mcp/core}/utils/smart_wait.py +3 -3
- mobile_mcp/mcp_tools/__init__.py +10 -0
- mobile_mcp/mcp_tools/mcp_server.py +1071 -0
- mobile_mcp_ai-2.5.8.dist-info/METADATA +469 -0
- mobile_mcp_ai-2.5.8.dist-info/RECORD +32 -0
- mobile_mcp_ai-2.5.8.dist-info/entry_points.txt +2 -0
- mobile_mcp_ai-2.5.8.dist-info/licenses/LICENSE +201 -0
- mobile_mcp_ai-2.5.8.dist-info/top_level.txt +1 -0
- core/ai/__init__.py +0 -11
- core/ai/ai_analyzer.py +0 -197
- core/ai/ai_config.py +0 -116
- core/ai/ai_platform_adapter.py +0 -399
- core/ai/smart_test_executor.py +0 -520
- core/ai/test_generator.py +0 -365
- core/ai/test_generator_from_history.py +0 -391
- core/ai/test_generator_standalone.py +0 -293
- core/assertion/__init__.py +0 -9
- core/assertion/smart_assertion.py +0 -341
- core/basic_tools.py +0 -377
- core/h5/__init__.py +0 -10
- core/h5/h5_handler.py +0 -548
- core/ios_client.py +0 -219
- core/ios_device_manager.py +0 -252
- core/locator/__init__.py +0 -10
- core/locator/cursor_ai_auto_analyzer.py +0 -119
- core/locator/cursor_vision_helper.py +0 -414
- core/locator/mobile_smart_locator.py +0 -1640
- core/locator/position_analyzer.py +0 -813
- core/locator/script_updater.py +0 -157
- core/nl_test_runner.py +0 -585
- core/smart_app_launcher.py +0 -334
- core/smart_tools.py +0 -311
- mcp/__init__.py +0 -8
- mcp/mcp_server.py +0 -1919
- mcp/mcp_server_simple.py +0 -476
- mobile_mcp_ai-2.1.2.dist-info/METADATA +0 -567
- mobile_mcp_ai-2.1.2.dist-info/RECORD +0 -45
- mobile_mcp_ai-2.1.2.dist-info/entry_points.txt +0 -2
- mobile_mcp_ai-2.1.2.dist-info/top_level.txt +0 -4
- vision/__init__.py +0 -10
- vision/vision_locator.py +0 -404
- {core → mobile_mcp/core}/__init__.py +0 -0
- {core → mobile_mcp/core}/utils/__init__.py +0 -0
- {core → mobile_mcp/core}/utils/logger.py +0 -0
- {core → mobile_mcp/core}/utils/operation_history_manager.py +0 -0
- {utils → mobile_mcp/utils}/__init__.py +0 -0
- {utils → mobile_mcp/utils}/logger.py +0 -0
- {utils → mobile_mcp/utils}/xml_formatter.py +0 -0
- {utils → mobile_mcp/utils}/xml_parser.py +0 -0
- {mobile_mcp_ai-2.1.2.dist-info → mobile_mcp_ai-2.5.8.dist-info}/WHEEL +0 -0
|
@@ -208,7 +208,7 @@ class DeviceManager:
|
|
|
208
208
|
|
|
209
209
|
try:
|
|
210
210
|
# 尝试获取页面结构,如果失败可能是无障碍服务未启用
|
|
211
|
-
xml = self.u2.dump_hierarchy()
|
|
211
|
+
xml = self.u2.dump_hierarchy(compressed=False)
|
|
212
212
|
if xml and len(xml) > 100: # 有内容说明无障碍服务正常
|
|
213
213
|
print(f" ✅ 无障碍服务: 已启用", file=sys.stderr)
|
|
214
214
|
return
|
|
@@ -235,7 +235,7 @@ class DeviceManager:
|
|
|
235
235
|
|
|
236
236
|
try:
|
|
237
237
|
# 尝试获取页面结构
|
|
238
|
-
xml = self.u2.dump_hierarchy()
|
|
238
|
+
xml = self.u2.dump_hierarchy(compressed=False)
|
|
239
239
|
if xml and len(xml) > 100:
|
|
240
240
|
return {
|
|
241
241
|
'enabled': True,
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
动态配置管理器 - 运行时可调整的自动化行为配置
|
|
5
|
+
|
|
6
|
+
功能:
|
|
7
|
+
1. 提供默认配置(与硬编码值完全一致,保证兼容性)
|
|
8
|
+
2. 支持运行时动态调整(通过 mobile_configure 工具)
|
|
9
|
+
3. 让 AI 可以根据 App 特性和场景优化配置
|
|
10
|
+
|
|
11
|
+
设计原则:
|
|
12
|
+
- 默认值 = 当前硬编码值(保证向后兼容)
|
|
13
|
+
- 所有配置都可选(不调用就用默认值)
|
|
14
|
+
- 运行时可修改(无需重启)
|
|
15
|
+
"""
|
|
16
|
+
import sys
|
|
17
|
+
from typing import Dict, Any, Optional
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class DynamicConfig:
|
|
21
|
+
"""
|
|
22
|
+
动态配置管理器(运行时可调整)
|
|
23
|
+
|
|
24
|
+
所有默认值都与当前硬编码值完全一致,保证 100% 向后兼容
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
# ==================== 等待时间策略 ====================
|
|
28
|
+
|
|
29
|
+
# 点击后等待时间(秒)- 让页面有时间响应
|
|
30
|
+
wait_after_click: float = 0.3
|
|
31
|
+
|
|
32
|
+
# 输入后等待时间(秒)- 让UI更新
|
|
33
|
+
wait_after_input: float = 0.3
|
|
34
|
+
|
|
35
|
+
# 页面稳定等待时间(秒)- 确保动画/过渡完成
|
|
36
|
+
wait_page_stable: float = 0.8
|
|
37
|
+
|
|
38
|
+
# 元素等待默认超时(秒)
|
|
39
|
+
element_wait_timeout: float = 10.0
|
|
40
|
+
|
|
41
|
+
# 页面变化检测超时(秒)
|
|
42
|
+
page_change_timeout: float = 2.0
|
|
43
|
+
|
|
44
|
+
# ==================== 验证策略 ====================
|
|
45
|
+
|
|
46
|
+
# 是否验证点击操作
|
|
47
|
+
verify_clicks: bool = True
|
|
48
|
+
|
|
49
|
+
# 是否验证输入操作
|
|
50
|
+
verify_inputs: bool = False
|
|
51
|
+
|
|
52
|
+
# 是否验证按键操作
|
|
53
|
+
verify_keys: bool = True
|
|
54
|
+
|
|
55
|
+
# ==================== 页面检测阈值 ====================
|
|
56
|
+
|
|
57
|
+
# 页面变化阈值(0-1)- 百分比
|
|
58
|
+
# 默认 0.05 = 5% 变化就认为页面发生了变化
|
|
59
|
+
page_change_threshold: float = 0.05
|
|
60
|
+
|
|
61
|
+
# 页面稳定阈值(秒)- 连续多久无变化认为稳定
|
|
62
|
+
page_stable_threshold: float = 0.3
|
|
63
|
+
|
|
64
|
+
# ==================== 屏幕方向控制 ====================
|
|
65
|
+
|
|
66
|
+
# 屏幕方向:portrait(竖屏), landscape(横屏), auto(跟随App)
|
|
67
|
+
screen_orientation: str = "portrait"
|
|
68
|
+
|
|
69
|
+
# 是否锁定屏幕方向
|
|
70
|
+
lock_screen_orientation: bool = True
|
|
71
|
+
|
|
72
|
+
# ==================== 广告/弹窗处理 ====================
|
|
73
|
+
|
|
74
|
+
# 是否自动关闭广告
|
|
75
|
+
auto_close_ads: bool = True
|
|
76
|
+
|
|
77
|
+
# 点击关闭按钮前的等待时间(秒)
|
|
78
|
+
wait_before_close_ad: float = 0.3
|
|
79
|
+
|
|
80
|
+
# 最多点击多少个关闭按钮(避免误点击)
|
|
81
|
+
max_close_buttons: int = 1
|
|
82
|
+
|
|
83
|
+
# ==================== 截图策略 ====================
|
|
84
|
+
|
|
85
|
+
# 截图策略:always(总是), on_failure(失败时), never(从不), smart(智能)
|
|
86
|
+
screenshot_strategy: str = "smart"
|
|
87
|
+
|
|
88
|
+
# ==================== 重试策略 ====================
|
|
89
|
+
|
|
90
|
+
# 操作失败时的最大重试次数
|
|
91
|
+
max_retries: int = 3
|
|
92
|
+
|
|
93
|
+
# 重试间隔(秒)
|
|
94
|
+
retry_delay: float = 1.0
|
|
95
|
+
|
|
96
|
+
# ==================== 配置管理 ====================
|
|
97
|
+
|
|
98
|
+
@classmethod
|
|
99
|
+
def update(cls, config: Dict[str, Any]) -> Dict[str, Any]:
|
|
100
|
+
"""
|
|
101
|
+
更新配置
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
config: 配置字典,支持嵌套结构
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
更新后的配置摘要
|
|
108
|
+
|
|
109
|
+
示例:
|
|
110
|
+
DynamicConfig.update({
|
|
111
|
+
"wait_strategy": {
|
|
112
|
+
"click_wait": 0.5,
|
|
113
|
+
"input_wait": 0.5
|
|
114
|
+
},
|
|
115
|
+
"screen_orientation": "landscape",
|
|
116
|
+
"page_change_threshold": 0.1
|
|
117
|
+
})
|
|
118
|
+
"""
|
|
119
|
+
updated = []
|
|
120
|
+
|
|
121
|
+
# 处理嵌套的等待策略
|
|
122
|
+
if "wait_strategy" in config:
|
|
123
|
+
wait = config["wait_strategy"]
|
|
124
|
+
if "click_wait" in wait:
|
|
125
|
+
cls.wait_after_click = float(wait["click_wait"])
|
|
126
|
+
updated.append(f"wait_after_click={cls.wait_after_click}")
|
|
127
|
+
if "input_wait" in wait:
|
|
128
|
+
cls.wait_after_input = float(wait["input_wait"])
|
|
129
|
+
updated.append(f"wait_after_input={cls.wait_after_input}")
|
|
130
|
+
if "page_stable_wait" in wait:
|
|
131
|
+
cls.wait_page_stable = float(wait["page_stable_wait"])
|
|
132
|
+
updated.append(f"wait_page_stable={cls.wait_page_stable}")
|
|
133
|
+
if "element_timeout" in wait:
|
|
134
|
+
cls.element_wait_timeout = float(wait["element_timeout"])
|
|
135
|
+
updated.append(f"element_wait_timeout={cls.element_wait_timeout}")
|
|
136
|
+
if "page_change_timeout" in wait:
|
|
137
|
+
cls.page_change_timeout = float(wait["page_change_timeout"])
|
|
138
|
+
updated.append(f"page_change_timeout={cls.page_change_timeout}")
|
|
139
|
+
|
|
140
|
+
# 处理验证策略
|
|
141
|
+
if "verify_strategy" in config:
|
|
142
|
+
verify = config["verify_strategy"]
|
|
143
|
+
if "verify_clicks" in verify:
|
|
144
|
+
cls.verify_clicks = bool(verify["verify_clicks"])
|
|
145
|
+
updated.append(f"verify_clicks={cls.verify_clicks}")
|
|
146
|
+
if "verify_inputs" in verify:
|
|
147
|
+
cls.verify_inputs = bool(verify["verify_inputs"])
|
|
148
|
+
updated.append(f"verify_inputs={cls.verify_inputs}")
|
|
149
|
+
if "verify_keys" in verify:
|
|
150
|
+
cls.verify_keys = bool(verify["verify_keys"])
|
|
151
|
+
updated.append(f"verify_keys={cls.verify_keys}")
|
|
152
|
+
|
|
153
|
+
# 处理广告策略
|
|
154
|
+
if "ad_handling" in config:
|
|
155
|
+
ad = config["ad_handling"]
|
|
156
|
+
if "auto_close" in ad:
|
|
157
|
+
cls.auto_close_ads = bool(ad["auto_close"])
|
|
158
|
+
updated.append(f"auto_close_ads={cls.auto_close_ads}")
|
|
159
|
+
if "wait_before_close" in ad:
|
|
160
|
+
cls.wait_before_close_ad = float(ad["wait_before_close"])
|
|
161
|
+
updated.append(f"wait_before_close_ad={cls.wait_before_close_ad}")
|
|
162
|
+
if "max_close_buttons" in ad:
|
|
163
|
+
cls.max_close_buttons = int(ad["max_close_buttons"])
|
|
164
|
+
updated.append(f"max_close_buttons={cls.max_close_buttons}")
|
|
165
|
+
|
|
166
|
+
# 处理重试策略
|
|
167
|
+
if "retry_strategy" in config:
|
|
168
|
+
retry = config["retry_strategy"]
|
|
169
|
+
if "max_retries" in retry:
|
|
170
|
+
cls.max_retries = int(retry["max_retries"])
|
|
171
|
+
updated.append(f"max_retries={cls.max_retries}")
|
|
172
|
+
if "retry_delay" in retry:
|
|
173
|
+
cls.retry_delay = float(retry["retry_delay"])
|
|
174
|
+
updated.append(f"retry_delay={cls.retry_delay}")
|
|
175
|
+
|
|
176
|
+
# 处理简单配置项
|
|
177
|
+
simple_configs = {
|
|
178
|
+
"page_change_threshold": (float, "page_change_threshold"),
|
|
179
|
+
"page_stable_threshold": (float, "page_stable_threshold"),
|
|
180
|
+
"screen_orientation": (str, "screen_orientation"),
|
|
181
|
+
"lock_screen_orientation": (bool, "lock_screen_orientation"),
|
|
182
|
+
"screenshot_strategy": (str, "screenshot_strategy"),
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
for key, (type_cast, attr_name) in simple_configs.items():
|
|
186
|
+
if key in config:
|
|
187
|
+
setattr(cls, attr_name, type_cast(config[key]))
|
|
188
|
+
updated.append(f"{attr_name}={getattr(cls, attr_name)}")
|
|
189
|
+
|
|
190
|
+
print(f" ✅ 配置已更新: {', '.join(updated)}", file=sys.stderr)
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
"success": True,
|
|
194
|
+
"updated": updated,
|
|
195
|
+
"message": f"成功更新 {len(updated)} 项配置"
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
@classmethod
|
|
199
|
+
def get_current(cls) -> Dict[str, Any]:
|
|
200
|
+
"""
|
|
201
|
+
获取当前配置
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
当前所有配置的字典
|
|
205
|
+
"""
|
|
206
|
+
return {
|
|
207
|
+
"wait_strategy": {
|
|
208
|
+
"click_wait": cls.wait_after_click,
|
|
209
|
+
"input_wait": cls.wait_after_input,
|
|
210
|
+
"page_stable_wait": cls.wait_page_stable,
|
|
211
|
+
"element_timeout": cls.element_wait_timeout,
|
|
212
|
+
"page_change_timeout": cls.page_change_timeout,
|
|
213
|
+
},
|
|
214
|
+
"verify_strategy": {
|
|
215
|
+
"verify_clicks": cls.verify_clicks,
|
|
216
|
+
"verify_inputs": cls.verify_inputs,
|
|
217
|
+
"verify_keys": cls.verify_keys,
|
|
218
|
+
},
|
|
219
|
+
"thresholds": {
|
|
220
|
+
"page_change_threshold": cls.page_change_threshold,
|
|
221
|
+
"page_stable_threshold": cls.page_stable_threshold,
|
|
222
|
+
},
|
|
223
|
+
"screen": {
|
|
224
|
+
"orientation": cls.screen_orientation,
|
|
225
|
+
"lock_orientation": cls.lock_screen_orientation,
|
|
226
|
+
},
|
|
227
|
+
"ad_handling": {
|
|
228
|
+
"auto_close": cls.auto_close_ads,
|
|
229
|
+
"wait_before_close": cls.wait_before_close_ad,
|
|
230
|
+
"max_close_buttons": cls.max_close_buttons,
|
|
231
|
+
},
|
|
232
|
+
"screenshot_strategy": cls.screenshot_strategy,
|
|
233
|
+
"retry_strategy": {
|
|
234
|
+
"max_retries": cls.max_retries,
|
|
235
|
+
"retry_delay": cls.retry_delay,
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
@classmethod
|
|
240
|
+
def reset(cls) -> Dict[str, str]:
|
|
241
|
+
"""
|
|
242
|
+
重置所有配置为默认值
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
重置结果
|
|
246
|
+
"""
|
|
247
|
+
cls.wait_after_click = 0.3
|
|
248
|
+
cls.wait_after_input = 0.3
|
|
249
|
+
cls.wait_page_stable = 0.8
|
|
250
|
+
cls.element_wait_timeout = 10.0
|
|
251
|
+
cls.page_change_timeout = 2.0
|
|
252
|
+
cls.verify_clicks = True
|
|
253
|
+
cls.verify_inputs = False
|
|
254
|
+
cls.verify_keys = True
|
|
255
|
+
cls.page_change_threshold = 0.05
|
|
256
|
+
cls.page_stable_threshold = 0.3
|
|
257
|
+
cls.screen_orientation = "portrait"
|
|
258
|
+
cls.lock_screen_orientation = True
|
|
259
|
+
cls.auto_close_ads = True
|
|
260
|
+
cls.wait_before_close_ad = 0.3
|
|
261
|
+
cls.max_close_buttons = 1
|
|
262
|
+
cls.screenshot_strategy = "smart"
|
|
263
|
+
cls.max_retries = 3
|
|
264
|
+
cls.retry_delay = 1.0
|
|
265
|
+
|
|
266
|
+
print(" ✅ 配置已重置为默认值", file=sys.stderr)
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
"success": True,
|
|
270
|
+
"message": "所有配置已重置为默认值"
|
|
271
|
+
}
|
|
272
|
+
|