mobile-mcp-ai 2.2.6__py3-none-any.whl → 2.5.3__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 (52) hide show
  1. mobile_mcp/config.py +3 -2
  2. mobile_mcp/core/basic_tools_lite.py +3193 -0
  3. mobile_mcp/core/ios_client_wda.py +569 -0
  4. mobile_mcp/core/ios_device_manager_wda.py +306 -0
  5. mobile_mcp/core/mobile_client.py +246 -20
  6. mobile_mcp/core/template_matcher.py +429 -0
  7. mobile_mcp/core/templates/close_buttons/auto_x_0112_151217.png +0 -0
  8. mobile_mcp/core/templates/close_buttons/auto_x_0112_152037.png +0 -0
  9. mobile_mcp/core/templates/close_buttons/auto_x_0112_152840.png +0 -0
  10. mobile_mcp/core/templates/close_buttons/auto_x_0112_153256.png +0 -0
  11. mobile_mcp/core/templates/close_buttons/auto_x_0112_154847.png +0 -0
  12. mobile_mcp/core/templates/close_buttons/gray_x_stock_ad.png +0 -0
  13. mobile_mcp/mcp_tools/__init__.py +10 -0
  14. mobile_mcp/mcp_tools/mcp_server.py +992 -0
  15. mobile_mcp_ai-2.5.3.dist-info/METADATA +456 -0
  16. mobile_mcp_ai-2.5.3.dist-info/RECORD +32 -0
  17. mobile_mcp_ai-2.5.3.dist-info/entry_points.txt +2 -0
  18. mobile_mcp/core/ai/__init__.py +0 -11
  19. mobile_mcp/core/ai/ai_analyzer.py +0 -197
  20. mobile_mcp/core/ai/ai_config.py +0 -116
  21. mobile_mcp/core/ai/ai_platform_adapter.py +0 -399
  22. mobile_mcp/core/ai/smart_test_executor.py +0 -520
  23. mobile_mcp/core/ai/test_generator.py +0 -365
  24. mobile_mcp/core/ai/test_generator_from_history.py +0 -391
  25. mobile_mcp/core/ai/test_generator_standalone.py +0 -293
  26. mobile_mcp/core/assertion/__init__.py +0 -9
  27. mobile_mcp/core/assertion/smart_assertion.py +0 -341
  28. mobile_mcp/core/basic_tools.py +0 -945
  29. mobile_mcp/core/h5/__init__.py +0 -10
  30. mobile_mcp/core/h5/h5_handler.py +0 -548
  31. mobile_mcp/core/ios_client.py +0 -219
  32. mobile_mcp/core/ios_device_manager.py +0 -252
  33. mobile_mcp/core/locator/__init__.py +0 -10
  34. mobile_mcp/core/locator/cursor_ai_auto_analyzer.py +0 -119
  35. mobile_mcp/core/locator/cursor_vision_helper.py +0 -414
  36. mobile_mcp/core/locator/mobile_smart_locator.py +0 -1747
  37. mobile_mcp/core/locator/position_analyzer.py +0 -813
  38. mobile_mcp/core/locator/script_updater.py +0 -157
  39. mobile_mcp/core/nl_test_runner.py +0 -585
  40. mobile_mcp/core/smart_app_launcher.py +0 -421
  41. mobile_mcp/core/smart_tools.py +0 -311
  42. mobile_mcp/mcp/__init__.py +0 -13
  43. mobile_mcp/mcp/mcp_server.py +0 -1126
  44. mobile_mcp/mcp/mcp_server_simple.py +0 -23
  45. mobile_mcp/vision/__init__.py +0 -10
  46. mobile_mcp/vision/vision_locator.py +0 -405
  47. mobile_mcp_ai-2.2.6.dist-info/METADATA +0 -503
  48. mobile_mcp_ai-2.2.6.dist-info/RECORD +0 -49
  49. mobile_mcp_ai-2.2.6.dist-info/entry_points.txt +0 -2
  50. {mobile_mcp_ai-2.2.6.dist-info → mobile_mcp_ai-2.5.3.dist-info}/WHEEL +0 -0
  51. {mobile_mcp_ai-2.2.6.dist-info → mobile_mcp_ai-2.5.3.dist-info}/licenses/LICENSE +0 -0
  52. {mobile_mcp_ai-2.2.6.dist-info → mobile_mcp_ai-2.5.3.dist-info}/top_level.txt +0 -0
@@ -1,116 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- """
4
- AI配置模块 - 从根目录.env读取配置
5
- 支持通义千问API
6
- """
7
- import os
8
- from pathlib import Path
9
- from typing import Optional
10
-
11
-
12
- class AIConfig:
13
- """AI配置类"""
14
-
15
- def __init__(self):
16
- """初始化配置"""
17
- self._load_env()
18
-
19
- def _load_env(self):
20
- """从根目录.env加载配置"""
21
- # 查找根目录.env文件
22
- # 当前路径: backend/mobile_mcp/core/ai/ai_config.py
23
- # 项目根目录: douzi-ai/
24
- current_file = Path(__file__)
25
- # 向上5层: ai/ -> core/ -> mobile_mcp/ -> backend/ -> douzi-ai/
26
- project_root = current_file.parent.parent.parent.parent.parent
27
- env_file = project_root / '.env'
28
-
29
- # 如果项目根目录没有,尝试backend目录
30
- if not env_file.exists():
31
- backend_root = current_file.parent.parent.parent.parent
32
- env_file = backend_root / '.env'
33
-
34
- if not env_file.exists():
35
- print(f"⚠️ 未找到.env文件: {env_file}")
36
- return
37
-
38
- # 读取.env文件
39
- with open(env_file, 'r', encoding='utf-8') as f:
40
- for line in f:
41
- line = line.strip()
42
- if not line or line.startswith('#'):
43
- continue
44
-
45
- # 解析键值对
46
- if '=' in line:
47
- key, value = line.split('=', 1)
48
- key = key.strip()
49
- value = value.strip().strip('"').strip("'")
50
-
51
- # 设置环境变量(如果尚未设置)
52
- if key not in os.environ:
53
- os.environ[key] = value
54
-
55
- @property
56
- def api_key(self) -> Optional[str]:
57
- """获取API密钥"""
58
- # 优先级:QWEN_API_KEY > OPENAI_API_KEY
59
- return (
60
- os.getenv('QWEN_API_KEY') or
61
- os.getenv('DASHSCOPE_API_KEY') or
62
- os.getenv('OPENAI_API_KEY')
63
- )
64
-
65
- @property
66
- def api_base(self) -> Optional[str]:
67
- """获取API基础URL"""
68
- return (
69
- os.getenv('QWEN_API_BASE') or
70
- os.getenv('OPENAI_API_BASE') or
71
- 'https://dashscope.aliyuncs.com/compatible-mode/v1' # 通义千问默认地址
72
- )
73
-
74
- @property
75
- def model(self) -> str:
76
- """获取模型名称"""
77
- return (
78
- os.getenv('QWEN_MODEL') or
79
- os.getenv('OPENAI_MODEL') or
80
- 'qwen-plus' # 通义千问默认模型
81
- )
82
-
83
- @property
84
- def timeout(self) -> int:
85
- """获取超时时间(秒)"""
86
- return int(os.getenv('AI_TIMEOUT', '30'))
87
-
88
- def is_configured(self) -> bool:
89
- """检查是否已配置"""
90
- return bool(self.api_key)
91
-
92
- def __repr__(self):
93
- """字符串表示(安全:不显示 API Key 信息)"""
94
- return (
95
- f"AIConfig(\n"
96
- f" api_base={self.api_base}\n"
97
- f" model={self.model}\n"
98
- f" api_key_configured={'✅ Yes' if self.api_key else '❌ No'}\n"
99
- f" timeout={self.timeout}s\n"
100
- f")"
101
- )
102
-
103
-
104
- # 全局配置实例
105
- ai_config = AIConfig()
106
-
107
-
108
- if __name__ == '__main__':
109
- # 测试配置
110
- print("=" * 60)
111
- print("AI配置测试")
112
- print("=" * 60)
113
- print(ai_config)
114
- print()
115
- print(f"是否已配置: {ai_config.is_configured()}")
116
-
@@ -1,399 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- """
4
- AI平台适配器 - 支持多种AI平台的可选增强功能
5
-
6
- 支持的平台:
7
- 1. Cursor AI - 多模态视觉识别
8
- 2. Claude (Anthropic) - 通用AI能力
9
- 3. OpenAI GPT-4V - 视觉识别
10
- 4. 其他支持MCP的AI平台
11
-
12
- 设计理念:
13
- - 基础功能不依赖AI平台(通用)
14
- - AI增强功能作为可选插件
15
- - 自动检测可用的AI平台
16
- - 优雅降级(AI不可用时使用基础功能)
17
- """
18
- import os
19
- from typing import Optional, Dict, Any, List
20
- from enum import Enum
21
- from pathlib import Path
22
-
23
-
24
- class AIPlatform(Enum):
25
- """支持的AI平台"""
26
- CURSOR = "cursor"
27
- CLAUDE = "claude"
28
- OPENAI = "openai"
29
- GEMINI = "gemini"
30
- NONE = "none" # 无AI平台(仅基础功能)
31
-
32
-
33
- class AIPlatformAdapter:
34
- """
35
- AI平台适配器
36
-
37
- 功能:
38
- 1. 自动检测可用的AI平台
39
- 2. 提供统一的AI能力接口
40
- 3. 支持多平台切换
41
- 4. 优雅降级
42
- """
43
-
44
- def __init__(self):
45
- """初始化AI平台适配器"""
46
- self.detected_platform: AIPlatform = self._detect_platform()
47
- self.platform_config: Dict[str, Any] = {}
48
- self._initialize_platform()
49
-
50
- def _detect_platform(self) -> AIPlatform:
51
- """
52
- 自动检测可用的AI平台
53
-
54
- 检测顺序:
55
- 1. Cursor AI (通过环境变量或MCP上下文)
56
- 2. Claude (通过环境变量)
57
- 3. OpenAI (通过环境变量)
58
- 4. 其他平台
59
- """
60
- # 检测 Cursor AI
61
- if self._is_cursor_available():
62
- return AIPlatform.CURSOR
63
-
64
- # 检测 Claude
65
- if os.getenv("ANTHROPIC_API_KEY"):
66
- return AIPlatform.CLAUDE
67
-
68
- # 检测 OpenAI
69
- if os.getenv("OPENAI_API_KEY"):
70
- return AIPlatform.OPENAI
71
-
72
- # 检测 Gemini
73
- if os.getenv("GOOGLE_API_KEY"):
74
- return AIPlatform.GEMINI
75
-
76
- return AIPlatform.NONE
77
-
78
- def _is_cursor_available(self) -> bool:
79
- """检测 Cursor AI 是否可用"""
80
- # 方法1: 检查环境变量
81
- if os.getenv("CURSOR_AI_ENABLED", "").lower() == "true":
82
- return True
83
-
84
- # 方法2: 检查MCP上下文(在MCP Server中)
85
- # 如果是在MCP Server中运行,Cursor AI通常可用
86
- try:
87
- # 检查是否有MCP相关的环境
88
- mcp_server = os.getenv("MCP_SERVER_NAME", "")
89
- if "cursor" in mcp_server.lower():
90
- return True
91
- except:
92
- pass
93
-
94
- # 方法3: 🎯 在 MCP Server 环境中默认启用 Cursor AI
95
- # 如果没有配置其他 AI 平台,且在 MCP 环境中,默认使用 Cursor
96
- if self._is_running_in_mcp() and not self._has_other_ai_platform():
97
- return True
98
-
99
- return False
100
-
101
- def _is_running_in_mcp(self) -> bool:
102
- """检测是否在 MCP Server 环境中运行"""
103
- # 检查是否通过 MCP 协议运行(stdin/stdout)
104
- import sys
105
- return not sys.stdin.isatty() or os.getenv("MCP_MODE") == "1"
106
-
107
- def _has_other_ai_platform(self) -> bool:
108
- """检测是否配置了其他 AI 平台"""
109
- return bool(
110
- os.getenv("AI_PROVIDER") or
111
- os.getenv("ANTHROPIC_API_KEY") or
112
- os.getenv("OPENAI_API_KEY") or
113
- os.getenv("GOOGLE_API_KEY") or
114
- os.getenv("QWEN_API_KEY")
115
- )
116
-
117
- def _initialize_platform(self):
118
- """初始化检测到的平台"""
119
- if self.detected_platform == AIPlatform.CURSOR:
120
- self.platform_config = {
121
- "name": "Cursor AI",
122
- "multimodal": True, # 支持多模态
123
- "vision": True, # 支持视觉识别
124
- "free": True, # Cursor AI免费使用
125
- }
126
- elif self.detected_platform == AIPlatform.CLAUDE:
127
- self.platform_config = {
128
- "name": "Claude (Anthropic)",
129
- "multimodal": True,
130
- "vision": True,
131
- "free": False,
132
- }
133
- elif self.detected_platform == AIPlatform.OPENAI:
134
- self.platform_config = {
135
- "name": "OpenAI GPT-4V",
136
- "multimodal": True,
137
- "vision": True,
138
- "free": False,
139
- }
140
- elif self.detected_platform == AIPlatform.GEMINI:
141
- self.platform_config = {
142
- "name": "Google Gemini",
143
- "multimodal": True,
144
- "vision": True,
145
- "free": True, # Gemini有免费额度
146
- }
147
- else:
148
- self.platform_config = {
149
- "name": "None (基础模式)",
150
- "multimodal": False,
151
- "vision": False,
152
- "free": True,
153
- }
154
-
155
- def is_vision_available(self) -> bool:
156
- """检查是否支持视觉识别"""
157
- return self.platform_config.get("vision", False)
158
-
159
- def is_multimodal_available(self) -> bool:
160
- """检查是否支持多模态"""
161
- return self.platform_config.get("multimodal", False)
162
-
163
- def get_platform_name(self) -> str:
164
- """获取平台名称"""
165
- return self.platform_config.get("name", "Unknown")
166
-
167
- async def analyze_screenshot(
168
- self,
169
- screenshot_path: str,
170
- element_desc: str,
171
- **kwargs
172
- ) -> Optional[Dict[str, Any]]:
173
- """
174
- 分析截图(统一接口)
175
-
176
- Args:
177
- screenshot_path: 截图路径
178
- element_desc: 元素描述
179
- **kwargs: 平台特定参数
180
-
181
- Returns:
182
- 坐标信息或None
183
- """
184
- if not self.is_vision_available():
185
- return None
186
-
187
- if self.detected_platform == AIPlatform.CURSOR:
188
- return await self._analyze_with_cursor(screenshot_path, element_desc, **kwargs)
189
- elif self.detected_platform == AIPlatform.CLAUDE:
190
- return await self._analyze_with_claude(screenshot_path, element_desc, **kwargs)
191
- elif self.detected_platform == AIPlatform.OPENAI:
192
- return await self._analyze_with_openai(screenshot_path, element_desc, **kwargs)
193
- elif self.detected_platform == AIPlatform.GEMINI:
194
- return await self._analyze_with_gemini(screenshot_path, element_desc, **kwargs)
195
-
196
- return None
197
-
198
- async def _analyze_with_cursor(
199
- self,
200
- screenshot_path: str,
201
- element_desc: str,
202
- **kwargs
203
- ) -> Optional[Dict[str, Any]]:
204
- """
205
- 使用 Cursor AI 分析截图
206
-
207
- Cursor AI 通过 MCP 工具调用,返回结果文件路径
208
- """
209
- # Cursor AI 的特殊处理:
210
- # 1. 创建请求文件
211
- # 2. 返回提示信息,让 Cursor AI 通过 MCP 工具分析
212
- # 3. 轮询结果文件
213
-
214
- request_id = kwargs.get("request_id")
215
- if request_id:
216
- # 自动模式:等待 Cursor AI 写入结果文件
217
- result_file = kwargs.get("result_file")
218
- if result_file and Path(result_file).exists():
219
- import json
220
- with open(result_file, 'r', encoding='utf-8') as f:
221
- result_data = json.load(f)
222
- if result_data.get("status") == "completed":
223
- coord = result_data.get("coordinate")
224
- if coord:
225
- return {
226
- "x": coord.get("x"),
227
- "y": coord.get("y"),
228
- "confidence": coord.get("confidence", 90),
229
- "platform": "cursor"
230
- }
231
-
232
- # 手动模式:返回提示信息
233
- return {
234
- "platform": "cursor",
235
- "instruction": f"请使用多模态能力分析截图 {screenshot_path},找到元素 '{element_desc}' 并返回坐标",
236
- "screenshot_path": screenshot_path,
237
- "element_desc": element_desc
238
- }
239
-
240
- async def _analyze_with_claude(
241
- self,
242
- screenshot_path: str,
243
- element_desc: str,
244
- **kwargs
245
- ) -> Optional[Dict[str, Any]]:
246
- """使用 Claude API 分析截图"""
247
- # TODO: 实现 Claude API 调用
248
- # 需要安装 anthropic SDK
249
- try:
250
- from anthropic import Anthropic
251
-
252
- client = Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
253
-
254
- # 读取截图
255
- with open(screenshot_path, 'rb') as f:
256
- image_data = f.read()
257
-
258
- # 调用 Claude Vision API
259
- message = client.messages.create(
260
- model="claude-3-5-sonnet-20241022",
261
- max_tokens=1024,
262
- messages=[{
263
- "role": "user",
264
- "content": [
265
- {
266
- "type": "image",
267
- "source": {
268
- "type": "base64",
269
- "media_type": "image/png",
270
- "data": image_data.hex() # 需要base64编码
271
- }
272
- },
273
- {
274
- "type": "text",
275
- "text": f"分析这个移动端截图,找到元素 '{element_desc}' 并返回其中心点坐标,格式:{{\"x\": 100, \"y\": 200}}"
276
- }
277
- ]
278
- }]
279
- )
280
-
281
- # 解析响应
282
- # TODO: 解析 Claude 返回的坐标
283
- return None
284
-
285
- except ImportError:
286
- return None
287
- except Exception as e:
288
- print(f"⚠️ Claude API 调用失败: {e}")
289
- return None
290
-
291
- async def _analyze_with_openai(
292
- self,
293
- screenshot_path: str,
294
- element_desc: str,
295
- **kwargs
296
- ) -> Optional[Dict[str, Any]]:
297
- """使用 OpenAI GPT-4V 分析截图"""
298
- # TODO: 实现 OpenAI Vision API 调用
299
- try:
300
- import base64
301
- from openai import OpenAI
302
-
303
- client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
304
-
305
- # 读取并编码截图
306
- with open(screenshot_path, 'rb') as f:
307
- image_data = base64.b64encode(f.read()).decode('utf-8')
308
-
309
- # 调用 GPT-4V
310
- response = client.chat.completions.create(
311
- model="gpt-4-vision-preview",
312
- messages=[{
313
- "role": "user",
314
- "content": [
315
- {
316
- "type": "text",
317
- "text": f"分析这个移动端截图,找到元素 '{element_desc}' 并返回其中心点坐标,格式:{{\"x\": 100, \"y\": 200}}"
318
- },
319
- {
320
- "type": "image_url",
321
- "image_url": {
322
- "url": f"data:image/png;base64,{image_data}"
323
- }
324
- }
325
- ]
326
- }],
327
- max_tokens=300
328
- )
329
-
330
- # 解析响应
331
- # TODO: 解析 OpenAI 返回的坐标
332
- return None
333
-
334
- except ImportError:
335
- return None
336
- except Exception as e:
337
- print(f"⚠️ OpenAI API 调用失败: {e}")
338
- return None
339
-
340
- async def _analyze_with_gemini(
341
- self,
342
- screenshot_path: str,
343
- element_desc: str,
344
- **kwargs
345
- ) -> Optional[Dict[str, Any]]:
346
- """使用 Google Gemini 分析截图"""
347
- # TODO: 实现 Gemini Vision API 调用
348
- return None
349
-
350
- def get_enhanced_tools(self) -> List[Dict[str, Any]]:
351
- """
352
- 获取AI增强的工具列表
353
-
354
- Returns:
355
- AI增强工具的定义列表
356
- """
357
- tools = []
358
-
359
- if self.is_vision_available():
360
- # 视觉识别工具(根据平台调整描述)
361
- platform_name = self.get_platform_name()
362
- tools.append({
363
- "name": "mobile_analyze_screenshot",
364
- "description": f"分析截图并返回元素坐标。使用{platform_name}的多模态能力分析截图,找到指定元素并返回坐标。",
365
- "platform": self.detected_platform.value,
366
- "enhanced": True
367
- })
368
-
369
- return tools
370
-
371
- def get_capabilities(self) -> Dict[str, Any]:
372
- """获取当前平台的AI能力"""
373
- return {
374
- "platform": self.detected_platform.value,
375
- "platform_name": self.get_platform_name(),
376
- "vision": self.is_vision_available(),
377
- "multimodal": self.is_multimodal_available(),
378
- "free": self.platform_config.get("free", False),
379
- "enhanced_tools": [t["name"] for t in self.get_enhanced_tools()]
380
- }
381
-
382
-
383
- # 全局实例
384
- _ai_adapter: Optional[AIPlatformAdapter] = None
385
-
386
-
387
- def get_ai_adapter() -> AIPlatformAdapter:
388
- """获取全局AI适配器实例"""
389
- global _ai_adapter
390
- if _ai_adapter is None:
391
- _ai_adapter = AIPlatformAdapter()
392
- return _ai_adapter
393
-
394
-
395
- def reset_ai_adapter():
396
- """重置AI适配器(用于测试)"""
397
- global _ai_adapter
398
- _ai_adapter = None
399
-