myagent-ai 1.33.1 → 1.33.2

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.
@@ -178,6 +178,67 @@ def _stop_xvfb() -> None:
178
178
  logger.info("Xvfb 虚拟显示已停止")
179
179
 
180
180
 
181
+ def _ensure_display() -> Optional[Dict[str, Any]]:
182
+ """
183
+ 确保有可用的 X11 显示,供浏览器有头模式使用。
184
+
185
+ 优先级:
186
+ 1. 复用 VNC 远程桌面的 Xvfb 显示(用户可在 VNC 中看到浏览器操作)
187
+ 2. 自动启动 VNC(含 Xvfb + x11vnc + websockify)
188
+ 3. VNC 不可用时独立启动 Xvfb
189
+
190
+ Returns:
191
+ {"display": ":99", "vnc": True/False, "xvfb_standalone": True/False}
192
+ 失败返回 None
193
+ """
194
+ # 1. 尝试复用 VNC 远程桌面
195
+ try:
196
+ from core.vnc_manager import get_vnc_manager
197
+ vnc = get_vnc_manager()
198
+
199
+ # VNC 已在运行 → 直接复用其 DISPLAY
200
+ if vnc.is_running:
201
+ display = vnc.display # 默认 :99
202
+ os.environ["DISPLAY"] = display
203
+ logger.info(f"VNC 远程桌面已在运行,复用显示: {display}")
204
+ return {"display": display, "vnc": True, "xvfb_standalone": False}
205
+
206
+ # VNC 未运行 → 尝试自动启动
207
+ try:
208
+ import asyncio
209
+ loop = asyncio.get_event_loop()
210
+ if loop.is_running():
211
+ # 在已有事件循环中,用 run_until_complete 不行,
212
+ # 创建 task 等待完成
213
+ import concurrent.futures
214
+ with concurrent.futures.ThreadPoolExecutor() as pool:
215
+ result = pool.submit(asyncio.run, vnc.start()).result(timeout=30)
216
+ else:
217
+ result = asyncio.run(vnc.start())
218
+
219
+ if result.get("success"):
220
+ display = vnc.display
221
+ os.environ["DISPLAY"] = display
222
+ logger.info(f"VNC 远程桌面已自动启动,显示: {display},可在 VNC 中查看浏览器操作")
223
+ return {"display": display, "vnc": True, "xvfb_standalone": False}
224
+ else:
225
+ logger.warning(f"VNC 自动启动失败: {result.get('message', '')}")
226
+ except Exception as e:
227
+ logger.warning(f"VNC 自动启动异常: {e}")
228
+
229
+ except ImportError:
230
+ logger.debug("VNC 管理器不可用,跳过 VNC 集成")
231
+ except Exception as e:
232
+ logger.warning(f"VNC 检测异常: {e}")
233
+
234
+ # 2. VNC 不可用 → 独立启动 Xvfb
235
+ xvfb_display = _start_xvfb()
236
+ if xvfb_display:
237
+ return {"display": xvfb_display, "vnc": False, "xvfb_standalone": True}
238
+
239
+ return None
240
+
241
+
181
242
  # ── 反检测浏览器管理器 ──────────────────────────────────────
182
243
 
183
244
 
@@ -214,7 +275,8 @@ class StealthBrowser:
214
275
  self._browser = None # DrissionPage Chromium 实例
215
276
  self._started = False
216
277
  self._user_data_dir = ""
217
- self._xvfb_started_by_us = False # 是否由本实例启动的 Xvfb
278
+ self._xvfb_started_by_us = False # 是否由本实例独立启动的 Xvfb
279
+ self._vnc_used = False # 是否使用了 VNC 远程桌面的显示
218
280
 
219
281
  def _get_user_data_dir(self) -> str:
220
282
  """获取用户数据目录路径"""
@@ -266,17 +328,23 @@ class StealthBrowser:
266
328
  co.set_argument("--disable-gpu")
267
329
 
268
330
  # ── 无 DISPLAY 环境处理 ──
269
- # headless=False 但没有可用显示时,尝试启动 Xvfb 虚拟显示;
270
- # 如果 Xvfb 不可用则自动降级为 headless 模式
331
+ # 优先级: VNC远程桌面 > 自启动Xvfb > 降级headless
332
+ # headless=False 但没有可用显示时:
333
+ # 1. 优先复用 VNC 远程桌面的 Xvfb 显示(用户可在 VNC 中看到浏览器操作)
334
+ # 2. VNC 未运行时自动启动 VNC(含 Xvfb + x11vnc + websockify)
335
+ # 3. VNC 启动失败则尝试独立启动 Xvfb
336
+ # 4. 都不可用则降级为 headless 模式
271
337
  if not self._headless and not _has_display():
272
- xvfb_display = _start_xvfb()
273
- if xvfb_display:
274
- self._xvfb_started_by_us = True
275
- logger.info(f"无 DISPLAY 环境,已启动 Xvfb 虚拟显示 ({xvfb_display})")
338
+ display = _ensure_display()
339
+ if display:
340
+ self._vnc_used = display.get("vnc", False)
341
+ self._xvfb_started_by_us = display.get("xvfb_standalone", False)
342
+ if self._vnc_used:
343
+ logger.info(f"复用 VNC 远程桌面显示 ({display['display']}),可在 VNC 中查看浏览器操作")
276
344
  else:
277
345
  self._headless = True
278
346
  logger.warning(
279
- "无 DISPLAY 环境且 Xvfb 不可用,自动降级为 headless 模式"
347
+ "无 DISPLAY 环境且 VNC/Xvfb 均不可用,自动降级为 headless 模式"
280
348
  )
281
349
 
282
350
  # 无头模式(co.headless() 内部设置 --headless=new)
@@ -369,7 +437,8 @@ class StealthBrowser:
369
437
  self._browser = None
370
438
  self._page = None
371
439
 
372
- # 如果由本实例启动了 Xvfb,关闭浏览器后停止虚拟显示
440
+ # 如果由本实例独立启动了 Xvfb(非 VNC),关闭浏览器后停止虚拟显示
441
+ # 注意:VNC 管理自己的 Xvfb 生命周期,不在此停止
373
442
  if self._xvfb_started_by_us:
374
443
  self._xvfb_started_by_us = False
375
444
  # 检查是否还有其他浏览器实例在使用 Xvfb
@@ -381,6 +450,9 @@ class StealthBrowser:
381
450
  if not other_active:
382
451
  _stop_xvfb()
383
452
 
453
+ # VNC 显示不由浏览器管理,无需停止
454
+ self._vnc_used = False
455
+
384
456
  return SkillResult(success=True, message="浏览器已关闭")
385
457
 
386
458
  async def navigate(self, url: str, wait: float = 2.0) -> SkillResult:
@@ -902,8 +974,11 @@ def close_stealth_browser(profile_name: str = "") -> None:
902
974
  pass
903
975
  _browsers.clear()
904
976
 
905
- # 所有浏览器关闭后,停止 Xvfb(如果有)
906
- _stop_xvfb()
977
+ # 所有浏览器关闭后,仅在独立 Xvfb(非 VNC)时才停止
978
+ # VNC 管理自己的 Xvfb 生命周期
979
+ any_vnc = any(b._vnc_used for b in _browsers.values()) if _browsers else False
980
+ if not any_vnc:
981
+ _stop_xvfb()
907
982
 
908
983
 
909
984
  async def close_stealth_browser_async(profile_name: str = "") -> None:
@@ -165,6 +165,63 @@ def _stop_xvfb() -> None:
165
165
  logger.info("Xvfb 虚拟显示已停止 (chromedev_mcp)")
166
166
 
167
167
 
168
+ def _ensure_display() -> Optional[str]:
169
+ """
170
+ 确保有可用的 X11 显示,供浏览器有头模式使用。
171
+
172
+ 优先级:
173
+ 1. 复用 VNC 远程桌面的 Xvfb 显示(用户可在 VNC 中看到浏览器操作)
174
+ 2. 自动启动 VNC(含 Xvfb + x11vnc + websockify)
175
+ 3. VNC 不可用时独立启动 Xvfb
176
+
177
+ Returns:
178
+ DISPLAY 值(如 ":99"),失败返回 None
179
+ """
180
+ # 1. 尝试复用 VNC 远程桌面
181
+ try:
182
+ from core.vnc_manager import get_vnc_manager
183
+ vnc = get_vnc_manager()
184
+
185
+ # VNC 已在运行 → 直接复用其 DISPLAY
186
+ if vnc.is_running:
187
+ display = vnc.display # 默认 :99
188
+ os.environ["DISPLAY"] = display
189
+ logger.info(f"VNC 远程桌面已在运行,复用显示: {display}")
190
+ return display
191
+
192
+ # VNC 未运行 → 尝试自动启动
193
+ try:
194
+ import asyncio
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', '')}")
213
+ except Exception as e:
214
+ logger.warning(f"VNC 自动启动异常: {e}")
215
+
216
+ except ImportError:
217
+ logger.debug("VNC 管理器不可用,跳过 VNC 集成")
218
+ except Exception as e:
219
+ logger.warning(f"VNC 检测异常: {e}")
220
+
221
+ # 2. VNC 不可用 → 独立启动 Xvfb
222
+ return _start_xvfb()
223
+
224
+
168
225
  # ── MCP 通信常量 ──────────────────────────────────────────────
169
226
 
170
227
  # chrome-devtools-mcp 支持的完整工具列表 (v0.21.0)
@@ -280,22 +337,23 @@ class MCPClient:
280
337
  args.extend(["--chromeArg", "--no-sandbox", "--chromeArg", "--disable-setuid-sandbox"])
281
338
 
282
339
  # [v1.17.0] 有头模式: 设置 DISPLAY 环境变量
340
+ # 优先级: 用户指定 DISPLAY > VNC 远程桌面 > 独立 Xvfb > 降级 headless
283
341
  if not self._headless:
284
342
  # 优先使用用户指定的 DISPLAY
285
343
  if self._display_override:
286
344
  env["DISPLAY"] = self._display_override
287
345
  logger.info(f"有头模式: DISPLAY={self._display_override}")
288
- # 无可用显示时自动启动 Xvfb
346
+ # 无可用显示时,优先尝试 VNC 远程桌面,再尝试独立 Xvfb
289
347
  elif not _has_display():
290
- xvfb_display = _start_xvfb()
291
- if xvfb_display:
292
- env["DISPLAY"] = xvfb_display
293
- logger.info(f" DISPLAY 环境,已启动 Xvfb: {xvfb_display}")
348
+ display = _ensure_display()
349
+ if display:
350
+ env["DISPLAY"] = display
351
+ logger.info(f"有头模式: DISPLAY={display}(通过 VNC/Xvfb 提供)")
294
352
  else:
295
- # Xvfb 不可用,降级为 headless 模式
353
+ # 都不可用,降级为 headless 模式
296
354
  self._headless = True
297
355
  args.append("--headless")
298
- logger.warning("无 DISPLAY 环境且 Xvfb 不可用,自动降级为 headless 模式")
356
+ logger.warning("无 DISPLAY 环境且 VNC/Xvfb 均不可用,自动降级为 headless 模式")
299
357
 
300
358
  logger.info(f"启动 chrome-devtools-mcp: {' '.join(args)}")
301
359
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myagent-ai",
3
- "version": "1.33.1",
3
+ "version": "1.33.2",
4
4
  "description": "本地桌面端执行型AI助手 - Open Interpreter 风格 | Local Desktop Execution-Oriented AI Assistant",
5
5
  "main": "main.py",
6
6
  "bin": {