myagent-ai 1.18.8 → 1.19.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/agents/main_agent.py +11 -7
- package/core/deps_checker.py +7 -2
- package/core/logger.py +20 -8
- package/package.json +2 -2
- package/requirements.txt +6 -0
- package/setup.py +8 -2
- package/skills/chromedev_mcp.py +201 -10
- package/skills/frontend-dev/SKILL.md +567 -0
- package/skills/frontend-dev/references/asset-prompt-guide.md +43 -0
- package/skills/frontend-dev/references/env-setup.md +33 -0
- package/skills/frontend-dev/references/minimax-cli-reference.md +133 -0
- package/skills/frontend-dev/references/minimax-image-guide.md +65 -0
- package/skills/frontend-dev/references/minimax-music-guide.md +216 -0
- package/skills/frontend-dev/references/minimax-tts-guide.md +78 -0
- package/skills/frontend-dev/references/minimax-video-guide.md +82 -0
- package/skills/frontend-dev/references/minimax-voice-catalog.md +686 -0
- package/skills/frontend-dev/references/motion-recipes.md +407 -0
- package/skills/frontend-dev/references/troubleshooting.md +85 -0
- package/skills/frontend-dev/scripts/minimax_image.py +137 -0
- package/skills/frontend-dev/scripts/minimax_music.py +157 -0
- package/skills/frontend-dev/scripts/minimax_tts.py +127 -0
- package/skills/frontend-dev/scripts/minimax_video.py +187 -0
- package/skills/frontend-dev/templates/generator_template.js +223 -0
- package/skills/frontend-dev/templates/viewer.html +599 -0
- package/skills/fullstack-dev/SKILL.md +1037 -0
- package/skills/fullstack-dev/references/api-design.md +444 -0
- package/skills/fullstack-dev/references/auth-flow.md +165 -0
- package/skills/fullstack-dev/references/db-schema.md +706 -0
- package/skills/fullstack-dev/references/django-best-practices.md +466 -0
- package/skills/fullstack-dev/references/environment-management.md +78 -0
- package/skills/fullstack-dev/references/release-checklist.md +278 -0
- package/skills/fullstack-dev/references/technology-selection.md +254 -0
- package/skills/fullstack-dev/references/testing-strategy.md +404 -0
- package/skills/xlsx_skill.py +39 -3
- package/web/api_server.py +64 -48
- package/web/ui/index.html +78 -19
package/agents/main_agent.py
CHANGED
|
@@ -1589,13 +1589,17 @@ class MainAgent(BaseAgent):
|
|
|
1589
1589
|
_fresult = await _fskill.execute(_fpath, _fdesc, stream_callback=stream_callback)
|
|
1590
1590
|
result = {"success": True, "output": json.dumps(_fresult, ensure_ascii=False, indent=2), "data": _fresult}
|
|
1591
1591
|
# [v1.18.5] 追踪发送的文件,用于持久化到会话记忆
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1592
|
+
# [v1.18.9] 修复:_sent_files 可能不在作用域内,安全处理
|
|
1593
|
+
try:
|
|
1594
|
+
if _fresult.get("success") and _fresult.get("file_id"):
|
|
1595
|
+
_sent_files.append({
|
|
1596
|
+
"id": _fresult["file_id"],
|
|
1597
|
+
"name": _fresult.get("name", ""),
|
|
1598
|
+
"type": _fresult.get("type", ""),
|
|
1599
|
+
"size": _fresult.get("size", 0),
|
|
1600
|
+
})
|
|
1601
|
+
except NameError:
|
|
1602
|
+
pass # _sent_files 不在当前作用域,跳过文件追踪
|
|
1599
1603
|
except Exception as _fse:
|
|
1600
1604
|
result = {"success": False, "error": f"文件发送失败: {_fse}"}
|
|
1601
1605
|
logger.warning(f"[{task_id}] file_send 工具异常: {_fse}")
|
package/core/deps_checker.py
CHANGED
|
@@ -100,10 +100,15 @@ DEPENDENCIES: List[DepInfo] = [
|
|
|
100
100
|
DepInfo("edge_tts", "edge-tts", "6.1.0", "tts", "all"),
|
|
101
101
|
|
|
102
102
|
# ── 语音识别 (STT) ──
|
|
103
|
+
# [v1.18.8] torch/torchaudio 是 SenseVoice (funasr) 的必需依赖
|
|
104
|
+
DepInfo("torch", "torch", "2.0.0", "stt", "all",
|
|
105
|
+
note="PyTorch 深度学习框架 (SenseVoice 必需, CPU版约200MB)"),
|
|
106
|
+
DepInfo("torchaudio", "torchaudio", "2.0.0", "stt", "all",
|
|
107
|
+
note="PyTorch 音频处理库 (SenseVoice 必需)"),
|
|
103
108
|
DepInfo("funasr", "funasr", "1.1.0", "stt", "all",
|
|
104
|
-
note="[v1.18.
|
|
109
|
+
note="[v1.18.8] SenseVoice 中文语音识别(首选,阿里达摩院)"),
|
|
105
110
|
DepInfo("faster_whisper", "faster-whisper", "1.0.0", "stt", "all",
|
|
106
|
-
note="Whisper 本地语音识别引擎 (
|
|
111
|
+
note="Whisper 本地语音识别引擎 (备选,需 C++ 编译)"),
|
|
107
112
|
DepInfo("speech_recognition", "SpeechRecognition", "3.10.0", "stt", "all",
|
|
108
113
|
note="在线语音识别 (Google API,纯 Python 无需编译,Termux 兼容)"),
|
|
109
114
|
|
package/core/logger.py
CHANGED
|
@@ -219,18 +219,30 @@ def setup_logger(
|
|
|
219
219
|
|
|
220
220
|
|
|
221
221
|
def get_logger(name: str = "myagent") -> logging.Logger:
|
|
222
|
-
"""获取已存在的 Logger
|
|
222
|
+
"""获取已存在的 Logger,如果不存在则创建默认的。
|
|
223
|
+
|
|
224
|
+
[v1.18.8] 子 logger 不再创建独立文件,而是继承最近有 handlers 的父 logger。
|
|
225
|
+
例如 get_logger("myagent.api") 会复用 "myagent" 的文件 handler,
|
|
226
|
+
确保所有日志集中写入同一个 myagent.log 文件。
|
|
227
|
+
"""
|
|
223
228
|
logger = logging.getLogger(name)
|
|
224
229
|
if not logger.handlers:
|
|
225
|
-
#
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
230
|
+
# 向上查找最近有 handlers 的父 logger(如 "myagent")
|
|
231
|
+
parts = name.split(".")
|
|
232
|
+
parent_found = None
|
|
233
|
+
for i in range(len(parts) - 1, 0, -1):
|
|
234
|
+
parent_name = ".".join(parts[:i])
|
|
235
|
+
parent = logging.getLogger(parent_name)
|
|
236
|
+
if parent.handlers:
|
|
237
|
+
parent_found = parent
|
|
238
|
+
break
|
|
239
|
+
if parent_found:
|
|
240
|
+
for handler in parent_found.handlers:
|
|
229
241
|
logger.addHandler(handler)
|
|
230
|
-
logger.setLevel(
|
|
231
|
-
logger.propagate =
|
|
242
|
+
logger.setLevel(parent_found.level)
|
|
243
|
+
logger.propagate = False # 复制 handlers 后禁止再向上传播(避免重复)
|
|
232
244
|
else:
|
|
233
|
-
#
|
|
245
|
+
# 没有找到有 handlers 的父 logger,创建默认配置
|
|
234
246
|
return setup_logger(name)
|
|
235
247
|
return logger
|
|
236
248
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "myagent-ai",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.19.0",
|
|
4
4
|
"description": "本地桌面端执行型AI助手 - Open Interpreter 风格 | Local Desktop Execution-Oriented AI Assistant",
|
|
5
5
|
"main": "main.py",
|
|
6
6
|
"bin": {
|
|
@@ -43,4 +43,4 @@
|
|
|
43
43
|
"python": ">=3.10",
|
|
44
44
|
"node": ">=18"
|
|
45
45
|
}
|
|
46
|
-
}
|
|
46
|
+
}
|
package/requirements.txt
CHANGED
|
@@ -72,6 +72,12 @@ xlrd>=2.0.0
|
|
|
72
72
|
# ============================================================
|
|
73
73
|
# 语音识别 (本地 STT,默认启用)
|
|
74
74
|
# ============================================================
|
|
75
|
+
# [v1.18.8] SenseVoice (funasr) 作为首选引擎,中文识别极佳
|
|
76
|
+
# torch/torchaudio 约 200MB (CPU版),funasr 约 100MB
|
|
77
|
+
# 若仅需 Whisper 备选引擎,可注释下面三行,保留 faster-whisper
|
|
78
|
+
funasr>=1.1.0
|
|
79
|
+
torch>=2.0.0
|
|
80
|
+
torchaudio>=2.0.0
|
|
75
81
|
faster-whisper>=1.0.0
|
|
76
82
|
pydub>=0.25.1
|
|
77
83
|
|
package/setup.py
CHANGED
|
@@ -37,7 +37,10 @@ setup(
|
|
|
37
37
|
"Pillow>=10.0.0",
|
|
38
38
|
# 语音合成
|
|
39
39
|
"edge-tts>=6.1.0",
|
|
40
|
-
# 语音识别 (本地 STT)
|
|
40
|
+
# 语音识别 (本地 STT) - [v1.18.8] SenseVoice 首选
|
|
41
|
+
"funasr>=1.1.0",
|
|
42
|
+
"torch>=2.0.0",
|
|
43
|
+
"torchaudio>=2.0.0",
|
|
41
44
|
"faster-whisper>=1.0.0",
|
|
42
45
|
# 浏览器自动化 (ChromeDev MCP, 无需 Playwright)
|
|
43
46
|
# 桌面 GUI 自动化 (内置技能)
|
|
@@ -50,13 +53,16 @@ setup(
|
|
|
50
53
|
"discord": ["discord.py>=2.3.0"],
|
|
51
54
|
"anthropic": ["anthropic>=0.18.0"],
|
|
52
55
|
"communication": ["cryptography>=41.0.0", "websockets>=12.0"],
|
|
53
|
-
"voice": ["faster-whisper>=1.0.0"],
|
|
56
|
+
"voice": ["funasr>=1.1.0", "torch>=2.0.0", "torchaudio>=2.0.0", "faster-whisper>=1.0.0"],
|
|
54
57
|
"all": [
|
|
55
58
|
"python-telegram-bot>=21.0",
|
|
56
59
|
"discord.py>=2.3.0",
|
|
57
60
|
"anthropic>=0.18.0",
|
|
58
61
|
"cryptography>=41.0.0",
|
|
59
62
|
"websockets>=12.0",
|
|
63
|
+
"funasr>=1.1.0",
|
|
64
|
+
"torch>=2.0.0",
|
|
65
|
+
"torchaudio>=2.0.0",
|
|
60
66
|
"faster-whisper>=1.0.0",
|
|
61
67
|
],
|
|
62
68
|
},
|
package/skills/chromedev_mcp.py
CHANGED
|
@@ -109,6 +109,8 @@ class MCPClient:
|
|
|
109
109
|
self._lock = asyncio.Lock()
|
|
110
110
|
# [v1.17.0] 有头模式 DISPLAY 覆盖
|
|
111
111
|
self._display_override: Optional[str] = None
|
|
112
|
+
# [v1.19.0] stderr 日志收集(用于调试 MCP Server 启动失败)
|
|
113
|
+
self._stderr_buffer: List[str] = []
|
|
112
114
|
|
|
113
115
|
async def start(self) -> bool:
|
|
114
116
|
"""启动 MCP Server 子进程"""
|
|
@@ -120,7 +122,7 @@ class MCPClient:
|
|
|
120
122
|
logger.error("npx 不可用,请安装 Node.js (>= 20.19)")
|
|
121
123
|
return False
|
|
122
124
|
|
|
123
|
-
args = ["npx", "-y", "chrome-devtools-mcp@
|
|
125
|
+
args = ["npx", "-y", "chrome-devtools-mcp@0.21.0"]
|
|
124
126
|
if self._headless:
|
|
125
127
|
args.append("--headless")
|
|
126
128
|
if self._slim:
|
|
@@ -160,32 +162,48 @@ class MCPClient:
|
|
|
160
162
|
|
|
161
163
|
try:
|
|
162
164
|
# 启动 MCP Server 子进程
|
|
163
|
-
#
|
|
164
|
-
|
|
165
|
+
# [v1.19.0] stderr 使用 PIPE 并在后台线程中持续读取,避免管道缓冲区满导致子进程阻塞
|
|
166
|
+
self._stderr_buffer = []
|
|
165
167
|
self._process = subprocess.Popen(
|
|
166
168
|
args,
|
|
167
169
|
stdin=subprocess.PIPE,
|
|
168
170
|
stdout=subprocess.PIPE,
|
|
169
|
-
stderr=subprocess.
|
|
171
|
+
stderr=subprocess.PIPE,
|
|
170
172
|
env=env,
|
|
171
173
|
)
|
|
172
174
|
|
|
173
|
-
#
|
|
175
|
+
# 启动读取线程(stdout 和 stderr 各一个,防止管道阻塞)
|
|
174
176
|
loop = asyncio.get_event_loop()
|
|
175
177
|
loop.run_in_executor(None, self._read_stdout)
|
|
178
|
+
loop.run_in_executor(None, self._read_stderr)
|
|
176
179
|
|
|
177
180
|
# 执行 MCP 握手
|
|
178
181
|
success = await self._handshake()
|
|
179
182
|
if success:
|
|
180
183
|
self._initialized = True
|
|
181
184
|
logger.info("chrome-devtools-mcp 已连接并初始化")
|
|
185
|
+
else:
|
|
186
|
+
# 握手失败时,收集 stderr 输出用于诊断
|
|
187
|
+
stderr_output = self._get_stderr_tail()
|
|
188
|
+
if stderr_output:
|
|
189
|
+
logger.error(f"MCP 握手失败,stderr 输出:\n{stderr_output}")
|
|
190
|
+
else:
|
|
191
|
+
logger.error(
|
|
192
|
+
"MCP 握手失败,无 stderr 输出。可能原因: "
|
|
193
|
+
"1) Node.js 版本过低 (需要 >= 20.19); "
|
|
194
|
+
"2) chrome-devtools-mcp@0.21.0 下载失败; "
|
|
195
|
+
"3) Chrome/Chromium 未找到且自动安装失败"
|
|
196
|
+
)
|
|
182
197
|
return success
|
|
183
198
|
|
|
184
199
|
except FileNotFoundError:
|
|
185
|
-
logger.error("无法启动 chrome-devtools-mcp
|
|
200
|
+
logger.error("无法启动 chrome-devtools-mcp: npx 命令未找到。请安装 Node.js >= 20.19: https://nodejs.org/")
|
|
186
201
|
return False
|
|
187
202
|
except Exception as e:
|
|
203
|
+
stderr_output = self._get_stderr_tail()
|
|
188
204
|
logger.error(f"启动 chrome-devtools-mcp 失败: {e}")
|
|
205
|
+
if stderr_output:
|
|
206
|
+
logger.error(f"MCP Server stderr:\n{stderr_output}")
|
|
189
207
|
self._cleanup()
|
|
190
208
|
return False
|
|
191
209
|
|
|
@@ -389,6 +407,34 @@ class MCPClient:
|
|
|
389
407
|
self._initialized = False
|
|
390
408
|
logger.warning("MCP stdout 读取结束(Server 或 Chrome 已断开)")
|
|
391
409
|
|
|
410
|
+
def _read_stderr(self):
|
|
411
|
+
"""[v1.19.0] 持续读取 MCP Server 的 stderr 并记录到日志。
|
|
412
|
+
|
|
413
|
+
必须在后台线程中持续读取 stderr,否则管道缓冲区满后
|
|
414
|
+
子进程会阻塞,导致 JSON-RPC 通信超时。
|
|
415
|
+
stderr 输出会同时收集到 _stderr_buffer 中,用于启动失败时的诊断。
|
|
416
|
+
"""
|
|
417
|
+
if not self._process or not self._process.stderr:
|
|
418
|
+
return
|
|
419
|
+
try:
|
|
420
|
+
for raw_line in self._process.stderr:
|
|
421
|
+
line_text = raw_line.decode("utf-8", errors="replace").rstrip("\n")
|
|
422
|
+
if line_text:
|
|
423
|
+
self._stderr_buffer.append(line_text)
|
|
424
|
+
# 只保留最近的 200 行,防止内存无限增长
|
|
425
|
+
if len(self._stderr_buffer) > 200:
|
|
426
|
+
self._stderr_buffer = self._stderr_buffer[-100:]
|
|
427
|
+
logger.debug(f"MCP stderr: {line_text}")
|
|
428
|
+
except Exception:
|
|
429
|
+
pass
|
|
430
|
+
|
|
431
|
+
def _get_stderr_tail(self, max_lines: int = 30) -> str:
|
|
432
|
+
"""[v1.19.0] 获取最近的 stderr 输出(用于错误诊断)"""
|
|
433
|
+
if not self._stderr_buffer:
|
|
434
|
+
return ""
|
|
435
|
+
lines = self._stderr_buffer[-max_lines:]
|
|
436
|
+
return "\n".join(lines)
|
|
437
|
+
|
|
392
438
|
async def _handshake(self) -> bool:
|
|
393
439
|
"""执行 MCP 初始化握手"""
|
|
394
440
|
try:
|
|
@@ -652,6 +698,120 @@ class MCPClient:
|
|
|
652
698
|
"""检查 MCP Server 是否正在运行"""
|
|
653
699
|
return self._process is not None and self._process.poll() is None
|
|
654
700
|
|
|
701
|
+
@staticmethod
|
|
702
|
+
def diagnose() -> Dict[str, Any]:
|
|
703
|
+
"""[v1.19.0] 检查浏览器自动化环境是否就绪,返回诊断信息。
|
|
704
|
+
|
|
705
|
+
用于在浏览器启动失败时提供详细的错误原因。
|
|
706
|
+
检查项:
|
|
707
|
+
1. Node.js 是否安装及版本
|
|
708
|
+
2. npx 是否可用
|
|
709
|
+
3. Chrome/Chromium 是否找到
|
|
710
|
+
4. chrome-devtools-mcp npm 包是否可访问
|
|
711
|
+
|
|
712
|
+
Returns:
|
|
713
|
+
dict with keys: ok (bool), checks (list), summary (str)
|
|
714
|
+
"""
|
|
715
|
+
results: Dict[str, Any] = {"ok": True, "checks": [], "summary": ""}
|
|
716
|
+
|
|
717
|
+
# 1. 检查 Node.js
|
|
718
|
+
node_path = shutil.which("node")
|
|
719
|
+
if not node_path:
|
|
720
|
+
results["ok"] = False
|
|
721
|
+
results["checks"].append({
|
|
722
|
+
"name": "Node.js", "status": "missing",
|
|
723
|
+
"message": "Node.js 未安装。需要 >= 20.19。请安装: https://nodejs.org/",
|
|
724
|
+
})
|
|
725
|
+
else:
|
|
726
|
+
try:
|
|
727
|
+
ver_result = subprocess.run(
|
|
728
|
+
["node", "--version"], capture_output=True, text=True, timeout=5,
|
|
729
|
+
)
|
|
730
|
+
version = ver_result.stdout.strip()
|
|
731
|
+
results["checks"].append({
|
|
732
|
+
"name": "Node.js", "status": "ok",
|
|
733
|
+
"message": f"Node.js {version} ({node_path})",
|
|
734
|
+
})
|
|
735
|
+
except Exception as e:
|
|
736
|
+
results["checks"].append({
|
|
737
|
+
"name": "Node.js", "status": "error", "message": str(e),
|
|
738
|
+
})
|
|
739
|
+
|
|
740
|
+
# 2. 检查 npx
|
|
741
|
+
npx_path = shutil.which("npx")
|
|
742
|
+
if not npx_path:
|
|
743
|
+
results["ok"] = False
|
|
744
|
+
results["checks"].append({
|
|
745
|
+
"name": "npx", "status": "missing",
|
|
746
|
+
"message": "npx 未安装(通常随 Node.js 一起安装)",
|
|
747
|
+
})
|
|
748
|
+
else:
|
|
749
|
+
results["checks"].append({
|
|
750
|
+
"name": "npx", "status": "ok",
|
|
751
|
+
"message": f"npx 可用 ({npx_path})",
|
|
752
|
+
})
|
|
753
|
+
|
|
754
|
+
# 3. 检查 Chrome/Chromium
|
|
755
|
+
browser_path = MCPClient._detect_browser()
|
|
756
|
+
if browser_path:
|
|
757
|
+
results["checks"].append({
|
|
758
|
+
"name": "Chrome/Chromium", "status": "ok",
|
|
759
|
+
"message": f"浏览器已找到: {browser_path}",
|
|
760
|
+
})
|
|
761
|
+
else:
|
|
762
|
+
results["ok"] = False
|
|
763
|
+
results["checks"].append({
|
|
764
|
+
"name": "Chrome/Chromium", "status": "missing",
|
|
765
|
+
"message": (
|
|
766
|
+
"未找到 Chrome/Chromium。系统会尝试自动安装,"
|
|
767
|
+
"或手动安装: apt install -y chromium-browser 或 apt install -y chromium"
|
|
768
|
+
),
|
|
769
|
+
})
|
|
770
|
+
|
|
771
|
+
# 4. 检查 npm / chrome-devtools-mcp 包可访问性
|
|
772
|
+
npm_path = shutil.which("npm")
|
|
773
|
+
if not npm_path:
|
|
774
|
+
results["checks"].append({
|
|
775
|
+
"name": "chrome-devtools-mcp (npm)", "status": "error",
|
|
776
|
+
"message": "npm 未安装,无法验证 chrome-devtools-mcp 包",
|
|
777
|
+
})
|
|
778
|
+
else:
|
|
779
|
+
try:
|
|
780
|
+
pkg_result = subprocess.run(
|
|
781
|
+
["npm", "view", "chrome-devtools-mcp", "version"],
|
|
782
|
+
capture_output=True, text=True, timeout=30,
|
|
783
|
+
)
|
|
784
|
+
if pkg_result.returncode == 0 and pkg_result.stdout.strip():
|
|
785
|
+
ver = pkg_result.stdout.strip()
|
|
786
|
+
results["checks"].append({
|
|
787
|
+
"name": "chrome-devtools-mcp (npm)", "status": "ok",
|
|
788
|
+
"message": f"npm 注册表中可用,最新版本: {ver} (当前固定: 0.21.0)",
|
|
789
|
+
})
|
|
790
|
+
else:
|
|
791
|
+
results["checks"].append({
|
|
792
|
+
"name": "chrome-devtools-mcp (npm)", "status": "warning",
|
|
793
|
+
"message": (
|
|
794
|
+
f"无法查询 npm 注册表: "
|
|
795
|
+
f"{pkg_result.stderr[:200] if pkg_result.stderr else 'unknown error'}"
|
|
796
|
+
),
|
|
797
|
+
})
|
|
798
|
+
except Exception as e:
|
|
799
|
+
results["checks"].append({
|
|
800
|
+
"name": "chrome-devtools-mcp (npm)", "status": "error",
|
|
801
|
+
"message": str(e),
|
|
802
|
+
})
|
|
803
|
+
|
|
804
|
+
# 构建摘要
|
|
805
|
+
failed = [c for c in results["checks"] if c["status"] in ("missing", "error")]
|
|
806
|
+
if not failed:
|
|
807
|
+
results["summary"] = "浏览器自动化环境就绪"
|
|
808
|
+
else:
|
|
809
|
+
results["summary"] = "环境检查发现问题: " + "; ".join(
|
|
810
|
+
f"{c['name']}({c['status']})" for c in failed
|
|
811
|
+
)
|
|
812
|
+
|
|
813
|
+
return results
|
|
814
|
+
|
|
655
815
|
|
|
656
816
|
# ── 全局 MCP 客户端单例 ──────────────────────────────────────
|
|
657
817
|
|
|
@@ -902,18 +1062,49 @@ class BrowserOpenSkill(Skill):
|
|
|
902
1062
|
# 检查依赖
|
|
903
1063
|
dep_err = await asyncio.get_event_loop().run_in_executor(None, _ensure_node_deps)
|
|
904
1064
|
if dep_err:
|
|
905
|
-
|
|
1065
|
+
# [v1.19.0] 依赖缺失时也运行诊断,给出完整信息
|
|
1066
|
+
diag = await asyncio.get_event_loop().run_in_executor(None, MCPClient.diagnose)
|
|
1067
|
+
diag_lines = [f" - {c['name']}: {c['message']}" for c in diag.get("checks", [])]
|
|
1068
|
+
return SkillResult(
|
|
1069
|
+
success=False,
|
|
1070
|
+
error=f"ChromeDev MCP 依赖缺失: {dep_err}\n环境诊断:\n" + "\n".join(diag_lines),
|
|
1071
|
+
)
|
|
906
1072
|
|
|
907
1073
|
global _mcp_client
|
|
908
1074
|
max_attempts = 2 # 最多重试 1 次
|
|
909
1075
|
for attempt in range(max_attempts):
|
|
910
1076
|
try:
|
|
911
|
-
|
|
1077
|
+
# [v1.19.0] 添加超时保护,防止 get_mcp_client() 永久阻塞
|
|
1078
|
+
# 首次启动可能需要下载 chrome-devtools-mcp 和 Chrome,给 90 秒
|
|
1079
|
+
mcp_timeout = 90 if attempt == 0 else 60
|
|
1080
|
+
try:
|
|
1081
|
+
client = await asyncio.wait_for(
|
|
1082
|
+
get_mcp_client(), timeout=mcp_timeout,
|
|
1083
|
+
)
|
|
1084
|
+
except asyncio.TimeoutError:
|
|
1085
|
+
logger.error(f"获取 MCP 客户端超时 ({mcp_timeout}s)")
|
|
1086
|
+
diag = await asyncio.get_event_loop().run_in_executor(None, MCPClient.diagnose)
|
|
1087
|
+
diag_lines = [f" - {c['name']}: {c['message']}" for c in diag.get("checks", [])]
|
|
1088
|
+
return SkillResult(
|
|
1089
|
+
success=False,
|
|
1090
|
+
error=(
|
|
1091
|
+
f"浏览器启动超时 ({mcp_timeout}s)。"
|
|
1092
|
+
f"首次启动可能需要下载 chrome-devtools-mcp 和 Chrome,请稍后重试。\n"
|
|
1093
|
+
f"环境诊断:\n" + "\n".join(diag_lines)
|
|
1094
|
+
),
|
|
1095
|
+
)
|
|
1096
|
+
|
|
912
1097
|
if not client or not client.is_running() or not client._initialized:
|
|
1098
|
+
# [v1.19.0] 启动失败时运行完整诊断
|
|
1099
|
+
diag = await asyncio.get_event_loop().run_in_executor(None, MCPClient.diagnose)
|
|
1100
|
+
diag_lines = [f" - {c['name']}: {c['message']}" for c in diag.get("checks", [])]
|
|
913
1101
|
return SkillResult(
|
|
914
1102
|
success=False,
|
|
915
|
-
error=
|
|
916
|
-
|
|
1103
|
+
error=(
|
|
1104
|
+
"浏览器启动失败。系统未安装 Chrome/Chromium 或 MCP Server 初始化失败。\n"
|
|
1105
|
+
"环境诊断:\n" + "\n".join(diag_lines) + "\n\n"
|
|
1106
|
+
"建议: apt install -y chromium-browser 或 apt install -y chromium"
|
|
1107
|
+
),
|
|
917
1108
|
)
|
|
918
1109
|
|
|
919
1110
|
# 1. 导航到目标 URL
|