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.
Files changed (36) hide show
  1. package/agents/main_agent.py +11 -7
  2. package/core/deps_checker.py +7 -2
  3. package/core/logger.py +20 -8
  4. package/package.json +2 -2
  5. package/requirements.txt +6 -0
  6. package/setup.py +8 -2
  7. package/skills/chromedev_mcp.py +201 -10
  8. package/skills/frontend-dev/SKILL.md +567 -0
  9. package/skills/frontend-dev/references/asset-prompt-guide.md +43 -0
  10. package/skills/frontend-dev/references/env-setup.md +33 -0
  11. package/skills/frontend-dev/references/minimax-cli-reference.md +133 -0
  12. package/skills/frontend-dev/references/minimax-image-guide.md +65 -0
  13. package/skills/frontend-dev/references/minimax-music-guide.md +216 -0
  14. package/skills/frontend-dev/references/minimax-tts-guide.md +78 -0
  15. package/skills/frontend-dev/references/minimax-video-guide.md +82 -0
  16. package/skills/frontend-dev/references/minimax-voice-catalog.md +686 -0
  17. package/skills/frontend-dev/references/motion-recipes.md +407 -0
  18. package/skills/frontend-dev/references/troubleshooting.md +85 -0
  19. package/skills/frontend-dev/scripts/minimax_image.py +137 -0
  20. package/skills/frontend-dev/scripts/minimax_music.py +157 -0
  21. package/skills/frontend-dev/scripts/minimax_tts.py +127 -0
  22. package/skills/frontend-dev/scripts/minimax_video.py +187 -0
  23. package/skills/frontend-dev/templates/generator_template.js +223 -0
  24. package/skills/frontend-dev/templates/viewer.html +599 -0
  25. package/skills/fullstack-dev/SKILL.md +1037 -0
  26. package/skills/fullstack-dev/references/api-design.md +444 -0
  27. package/skills/fullstack-dev/references/auth-flow.md +165 -0
  28. package/skills/fullstack-dev/references/db-schema.md +706 -0
  29. package/skills/fullstack-dev/references/django-best-practices.md +466 -0
  30. package/skills/fullstack-dev/references/environment-management.md +78 -0
  31. package/skills/fullstack-dev/references/release-checklist.md +278 -0
  32. package/skills/fullstack-dev/references/technology-selection.md +254 -0
  33. package/skills/fullstack-dev/references/testing-strategy.md +404 -0
  34. package/skills/xlsx_skill.py +39 -3
  35. package/web/api_server.py +64 -48
  36. package/web/ui/index.html +78 -19
@@ -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
- if _fresult.get("success") and _fresult.get("file_id"):
1593
- _sent_files.append({
1594
- "id": _fresult["file_id"],
1595
- "name": _fresult.get("name", ""),
1596
- "type": _fresult.get("type", ""),
1597
- "size": _fresult.get("size", 0),
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}")
@@ -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.7] SenseVoice 中文语音识别(推荐,需 torch+torchaudio)"),
109
+ note="[v1.18.8] SenseVoice 中文语音识别(首选,阿里达摩院)"),
105
110
  DepInfo("faster_whisper", "faster-whisper", "1.0.0", "stt", "all",
106
- note="Whisper 本地语音识别引擎 (需要 C++ 编译)"),
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
- # logger 继承父 logger 的 handlers(如文件 handler
226
- parent_logger = logging.getLogger()
227
- if parent_logger.handlers:
228
- for handler in parent_logger.handlers:
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(parent_logger.level)
231
- logger.propagate = parent_logger.propagate
242
+ logger.setLevel(parent_found.level)
243
+ logger.propagate = False # 复制 handlers 后禁止再向上传播(避免重复)
232
244
  else:
233
- # 没有父 logger,创建默认配置
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.18.8",
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
  },
@@ -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@latest"]
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
- # 注意: stderr 必须用 DEVNULL 或在后台线程中持续读取
164
- # 否则 stderr 管道缓冲区满后子进程会阻塞,导致 JSON-RPC 超时
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.DEVNULL,
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,请确认 Node.js 已安装")
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
- return SkillResult(success=False, error=f"ChromeDev MCP 依赖缺失: {dep_err}")
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
- client = await get_mcp_client()
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="浏览器启动失败。系统未安装 Chrome/Chromium,自动安装也未成功。"
916
- "请手动执行: apt install -y chromium-browser apt install -y chromium"
1103
+ error=(
1104
+ "浏览器启动失败。系统未安装 Chrome/ChromiumMCP 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