myagent-ai 1.47.24 → 1.47.25

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.
@@ -2181,19 +2181,16 @@ class StealthBrowser:
2181
2181
  f"{result.stderr.decode(errors='ignore')[:200]}"
2182
2182
  )
2183
2183
 
2184
- # 方法4: 使用 xdotool + xwd + ffmpeg (proot 兼容)
2185
- xdotool_cmd = shutil.which("xdotool")
2184
+ # 方法4: 使用 xwd + ffmpeg (proot 兼容)
2186
2185
  ffmpeg_cmd = shutil.which("ffmpeg")
2187
- if xdotool_cmd and ffmpeg_cmd:
2186
+ if xwd_cmd and ffmpeg_cmd:
2188
2187
  xwd_tmp = save_path + ".xwd"
2189
- # 用 xdotool 获取活动窗口并截图
2190
2188
  result = subprocess.run(
2191
- [xwd_cmd or "xwd", "-root", "-display", display, "-out", xwd_tmp],
2189
+ [xwd_cmd, "-root", "-display", display, "-out", xwd_tmp],
2192
2190
  capture_output=True, timeout=10,
2193
2191
  env=env, start_new_session=True,
2194
2192
  )
2195
2193
  if result.returncode == 0 and os.path.isfile(xwd_tmp):
2196
- # 用 ffmpeg 转换 xwd 到 png
2197
2194
  result2 = subprocess.run(
2198
2195
  [ffmpeg_cmd, "-y", "-i", xwd_tmp, save_path],
2199
2196
  capture_output=True, timeout=10,
@@ -2210,18 +2207,49 @@ class StealthBrowser:
2210
2207
  message=f"Firefox+VNC 截图已保存",
2211
2208
  data={"path": save_path},
2212
2209
  )
2210
+ logger.warning(
2211
+ f"[_firefox_screenshot] xwd+ffmpeg 截图失败: xwd_rc={result.returncode}"
2212
+ )
2213
+
2214
+ # 方法5: 使用 Python Pillow + xwd
2215
+ try:
2216
+ from PIL import Image
2217
+ if xwd_cmd:
2218
+ xwd_tmp = save_path + ".xwd"
2219
+ result = subprocess.run(
2220
+ [xwd_cmd, "-root", "-display", display, "-out", xwd_tmp],
2221
+ capture_output=True, timeout=10,
2222
+ env=env, start_new_session=True,
2223
+ )
2224
+ if result.returncode == 0 and os.path.isfile(xwd_tmp):
2225
+ try:
2226
+ img = Image.open(xwd_tmp)
2227
+ img.save(save_path, "PNG")
2228
+ os.unlink(xwd_tmp)
2229
+ logger.info(f"Firefox 截图已保存 (xwd+Pillow): {save_path}")
2230
+ return SkillResult(
2231
+ success=True,
2232
+ message=f"Firefox+VNC 截图已保存",
2233
+ data={"path": save_path},
2234
+ )
2235
+ except Exception as pil_err:
2236
+ logger.warning(f"[_firefox_screenshot] Pillow 转换失败: {pil_err}")
2237
+ try: os.unlink(xwd_tmp)
2238
+ except Exception: pass
2239
+ except ImportError:
2240
+ pass
2213
2241
 
2214
2242
  _available = []
2215
2243
  if import_cmd: _available.append("import")
2216
2244
  if scrot_cmd: _available.append("scrot")
2217
2245
  if xwd_cmd and convert_cmd: _available.append("xwd+convert")
2218
- if xdotool_cmd and ffmpeg_cmd: _available.append("xdotool+ffmpeg")
2246
+ if xwd_cmd and ffmpeg_cmd: _available.append("xwd+ffmpeg")
2219
2247
  _tried = ", ".join(_available) if _available else "无"
2220
2248
 
2221
2249
  return SkillResult(
2222
2250
  success=False,
2223
2251
  error=f"Firefox+VNC 截图失败 (尝试过: {_tried}) "
2224
- f"— 需要 ImageMagick import / scrot / xwd+convert",
2252
+ f"— 需要 ImageMagick / scrot / xwd+convert/ffmpeg",
2225
2253
  )
2226
2254
  except Exception as e:
2227
2255
  return SkillResult(success=False, error=f"Firefox+VNC 截图失败: {e}")
@@ -189,55 +189,27 @@ def _ensure_display() -> Optional[str]:
189
189
  logger.info(f"VNC 远程桌面已在运行,复用显示: {display}")
190
190
  return display
191
191
 
192
- # [v1.47.13] VNC 未运行 → 尝试自动启动
193
- # 修复: 不能在 ThreadPoolExecutor + asyncio.run 中直接调用 vnc.start()!
194
- # 因为 vnc.start() 内部用 asyncio.Lock(),跨 event loop 会导致死锁。
195
- # 正确做法:在单独线程中用 asyncio.run 启动,然后轮询等待 VNC 就绪。
192
+ # VNC 未运行 → 尝试自动启动
196
193
  try:
197
194
  import asyncio
198
- import concurrent.futures
199
-
200
- def _start_vnc_in_thread():
201
- try:
202
- return asyncio.run(vnc.start())
203
- except Exception as e:
204
- return {"success": False, "message": str(e)}
205
-
206
- with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
207
- future = pool.submit(_start_vnc_in_thread)
208
-
209
- max_wait = 60
210
- check_interval = 2
211
- elapsed = 0
212
- while elapsed < max_wait:
213
- if vnc.is_running:
214
- display = vnc.display
215
- os.environ["DISPLAY"] = display
216
- logger.info(f"VNC 远程桌面已启动,复用显示: {display}")
217
- return display
218
-
219
- try:
220
- result = future.result(timeout=0.1)
221
- if result.get("success") or vnc.is_running:
222
- display = vnc.display
223
- os.environ["DISPLAY"] = display
224
- logger.info(f"VNC 远程桌面已启动,显示: {display}")
225
- return display
226
- else:
227
- logger.warning(f"VNC 启动失败: {result.get('message', '')}")
228
- break
229
- except concurrent.futures.TimeoutError:
230
- pass
231
-
232
- import time
233
- time.sleep(check_interval)
234
- elapsed += check_interval
235
-
236
- if vnc.is_running:
237
- display = vnc.display
238
- os.environ["DISPLAY"] = display
239
- return display
240
-
195
+ try:
196
+ loop = asyncio.get_event_loop()
197
+ if loop.is_running():
198
+ import concurrent.futures
199
+ with concurrent.futures.ThreadPoolExecutor() as pool:
200
+ result = pool.submit(asyncio.run, vnc.start()).result(timeout=30)
201
+ else:
202
+ result = asyncio.run(vnc.start())
203
+ except RuntimeError:
204
+ result = asyncio.run(vnc.start())
205
+
206
+ if result.get("success"):
207
+ display = vnc.display
208
+ os.environ["DISPLAY"] = display
209
+ logger.info(f"VNC 远程桌面已自动启动,显示: {display}")
210
+ return display
211
+ else:
212
+ logger.warning(f"VNC 自动启动失败: {result.get('message', '')}")
241
213
  except Exception as e:
242
214
  logger.warning(f"VNC 自动启动异常: {e}")
243
215
 
@@ -1372,26 +1344,6 @@ class BrowserOpenSkill(Skill):
1372
1344
  if not url:
1373
1345
  return SkillResult(success=False, error="缺少必需参数: url")
1374
1346
 
1375
- # [v1.47.20] VNC 模式下:Chromium 不可用时回退到 stealth_browser_navigate
1376
- try:
1377
- from core.vnc_manager import get_vnc_manager
1378
- vnc_mgr = get_vnc_manager()
1379
- if vnc_mgr.is_running:
1380
- # 检查是否有可用的 Chromium
1381
- import shutil
1382
- has_chrome = bool(shutil.which("chromium-browser") or shutil.which("chromium")
1383
- or shutil.which("google-chrome"))
1384
- if not has_chrome:
1385
- # VNC 模式下无 Chromium,回退到 stealth_browser
1386
- return SkillResult(
1387
- success=False,
1388
- error="VNC 远程桌面模式下没有 Chromium 浏览器,无法使用 browser_open。"
1389
- "请改用 stealth_browser_start + stealth_browser_navigate 操作 Firefox。"
1390
- "Firefox 在 VNC 模式下已可用。",
1391
- )
1392
- except ImportError:
1393
- pass # vnc_manager 不可用,跳过检测
1394
-
1395
1347
  # 检查依赖
1396
1348
  dep_err = await asyncio.get_event_loop().run_in_executor(None, _ensure_node_deps)
1397
1349
  if dep_err: