myagent-ai 1.47.16 → 1.47.17

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.
@@ -544,6 +544,73 @@ class StealthBrowser:
544
544
 
545
545
  async def start(self) -> SkillResult:
546
546
  """启动反检测浏览器"""
547
+ # [v1.47.16] VNC/Termux 模式下直接使用 Firefox,不走 DrissionPage/Chromium
548
+ # Chromium 在 proot ARM64 下与 DrissionPage 不兼容,
549
+ # 所以 VNC 模式下先检测环境,直接走 Firefox 路径,不需要 import DrissionPage
550
+ _is_vnc_mode = False
551
+ _is_termux_env = False
552
+ try:
553
+ from core.env_detect import is_termux
554
+ _is_termux_env = is_termux()
555
+ except ImportError:
556
+ pass
557
+ try:
558
+ from core.env_detect import is_desktop
559
+ if not is_desktop():
560
+ _is_vnc_mode = True
561
+ except ImportError:
562
+ if not _has_display():
563
+ _is_vnc_mode = True
564
+
565
+ # VNC 模式:跳过所有 Chrome/DrissionPage 逻辑,直接用 Firefox
566
+ if _is_vnc_mode:
567
+ logger.info("VNC/非桌面环境: 直接启动 Firefox(跳过 Chromium 检测)")
568
+ if not self._headless:
569
+ display = _ensure_display()
570
+ if display:
571
+ self._vnc_used = display.get("vnc", False)
572
+ self._xvfb_started_by_us = display.get("xvfb_standalone", False)
573
+ if self._vnc_used:
574
+ return self._start_firefox_in_vnc()
575
+ # VNC/Xvfb 不可用
576
+ if _is_termux_env:
577
+ return SkillResult(
578
+ success=False,
579
+ error=(
580
+ "Termux+Ubuntu 环境仅支持通过 VNC 启动浏览器,"
581
+ "VNC 启动失败。请先启动 VNC 远程桌面后再使用浏览器功能。"
582
+ ),
583
+ )
584
+ # 非 Termux:尝试 Xvfb
585
+ if display and display.get("xvfb_standalone"):
586
+ # 有 Xvfb 但没有 VNC → 用 Xvfb + Chromium(走下面的正常流程)
587
+ pass
588
+ else:
589
+ # 没有 Xvfb 也没有 VNC → 降级 headless 或报错
590
+ logger.warning("无显示环境且 VNC/Xvfb 均不可用,尝试 Firefox headless 模式")
591
+ return self._start_firefox_in_vnc()
592
+ else:
593
+ # headless=True 被显式请求
594
+ if _is_termux_env:
595
+ logger.info("Termux+Ubuntu 环境: 忽略 headless 请求,强制使用 VNC 模式")
596
+ self._headless = False
597
+ display = _ensure_display()
598
+ if display:
599
+ self._vnc_used = display.get("vnc", False)
600
+ self._xvfb_started_by_us = display.get("xvfb_standalone", False)
601
+ if self._vnc_used:
602
+ return self._start_firefox_in_vnc()
603
+ return SkillResult(
604
+ success=False,
605
+ error=(
606
+ "Termux+Ubuntu 环境仅支持通过 VNC 启动浏览器,"
607
+ "VNC 启动失败。headless 模式在此环境下不可用。"
608
+ ),
609
+ )
610
+ # 非 Termux headless → 仍然尝试 Firefox
611
+ return self._start_firefox_in_vnc()
612
+
613
+ # ── 桌面环境:使用 DrissionPage + Chromium ──
547
614
  try:
548
615
  from DrissionPage import Chromium, ChromiumOptions
549
616
  except ImportError:
@@ -613,98 +680,10 @@ class StealthBrowser:
613
680
  else:
614
681
  logger.info("桌面环境,使用系统 Chrome 原生参数")
615
682
 
616
- # ── 无显示环境处理 ──
617
- # 桌面环境 (Windows/macOS/Linux有真实显示器): 直接用系统 Chrome
618
- # Termux+Ubuntu: 仅支持 VNC,不降级到 headless
619
- # 非 Termux 容器: VNC > Xvfb > headless 降级
620
- _is_termux_env = False
621
- try:
622
- from core.env_detect import is_termux
623
- _is_termux_env = is_termux()
624
- except ImportError:
625
- pass
626
-
683
+ # ── 显示环境处理(仅桌面环境到达此代码路径)──
684
+ # VNC/Termux 模式已在方法开头走 Firefox 分支,这里只处理桌面环境
627
685
  if not self._headless:
628
- try:
629
- from core.env_detect import is_desktop
630
- if is_desktop():
631
- # 桌面环境: 直接用系统 Chrome
632
- logger.info("桌面环境,直接使用系统浏览器,跳过 VNC/Xvfb")
633
- else:
634
- # 非桌面环境 (容器/Termux): 通过 _ensure_display() 获取显示
635
- display = _ensure_display()
636
- if display:
637
- self._vnc_used = display.get("vnc", False)
638
- self._xvfb_started_by_us = display.get("xvfb_standalone", False)
639
- if self._vnc_used:
640
- logger.info(f"复用 VNC 远程桌面显示 ({display['display']}),可在 VNC 中查看浏览器操作")
641
- # [v1.47.16] VNC 模式下直接用 Firefox,不走 DrissionPage/Chromium
642
- # Chromium 在 proot ARM64 下与 DrissionPage 不兼容
643
- logger.info("VNC 模式: 直接启动 Firefox(跳过 Chromium 检测)")
644
- return self._start_firefox_in_vnc()
645
- else:
646
- # ── Termux+Ubuntu: VNC 失败 → 直接报错,不降级 headless ──
647
- if _is_termux_env:
648
- return SkillResult(
649
- success=False,
650
- error=(
651
- "Termux+Ubuntu 环境仅支持通过 VNC 启动浏览器,"
652
- "VNC 启动失败。请先启动 VNC 远程桌面,"
653
- "或通过 Web 管理面板打开 VNC 后再使用浏览器功能。"
654
- ),
655
- )
656
- # ── 非 Termux 容器: 降级到 headless ──
657
- self._headless = True
658
- logger.warning(
659
- "无显示环境且 VNC/Xvfb 均不可用,自动降级为 headless 模式"
660
- )
661
- except ImportError:
662
- # env_detect 不可用时,降级为原有 X11 检测逻辑
663
- if not _has_display():
664
- display = _ensure_display()
665
- if display:
666
- self._vnc_used = display.get("vnc", False)
667
- self._xvfb_started_by_us = display.get("xvfb_standalone", False)
668
- if self._vnc_used:
669
- # [v1.47.16] VNC 模式下直接用 Firefox
670
- logger.info("VNC 模式 (env_detect不可用): 直接启动 Firefox")
671
- return self._start_firefox_in_vnc()
672
- else:
673
- if _is_termux_env:
674
- return SkillResult(
675
- success=False,
676
- error=(
677
- "Termux+Ubuntu 环境仅支持通过 VNC 启动浏览器,"
678
- "VNC 启动失败。请先启动 VNC 远程桌面后再使用浏览器功能。"
679
- ),
680
- )
681
- self._headless = True
682
- logger.warning(
683
- "无 DISPLAY 环境且 VNC/Xvfb 均不可用,自动降级为 headless 模式"
684
- )
685
- else:
686
- # headless=True 被显式请求,但 Termux 环境下仍强制使用 VNC
687
- # 因为 headless Chromium 在 Termux 下容易被 OOM Kill
688
- if _is_termux_env:
689
- logger.info("Termux+Ubuntu 环境: 忽略 headless 请求,强制使用 VNC 模式")
690
- self._headless = False
691
- display = _ensure_display()
692
- if display:
693
- self._vnc_used = display.get("vnc", False)
694
- self._xvfb_started_by_us = display.get("xvfb_standalone", False)
695
- if self._vnc_used:
696
- # [v1.47.16] VNC 模式下直接用 Firefox
697
- logger.info(f"Termux+Ubuntu VNC 模式: 直接启动 Firefox")
698
- return self._start_firefox_in_vnc()
699
- else:
700
- return SkillResult(
701
- success=False,
702
- error=(
703
- "Termux+Ubuntu 环境仅支持通过 VNC 启动浏览器,"
704
- "VNC 启动失败。headless 模式在此环境下不可用(容易被 OOM Kill)。"
705
- "请先启动 VNC 远程桌面后再使用浏览器功能。"
706
- ),
707
- )
686
+ logger.info("桌面环境,直接使用系统浏览器,跳过 VNC/Xvfb")
708
687
 
709
688
  # 无头模式(co.headless() 内部设置 --headless=new)
710
689
  if self._headless:
@@ -1273,8 +1252,11 @@ class StealthBrowser:
1273
1252
  if not self._ensure_page():
1274
1253
  return SkillResult(success=False, error="浏览器未启动")
1275
1254
 
1255
+ # [v1.47.16] Firefox+VNC 模式:通过 xdotool type 输入
1256
+ if self._firefox_mode:
1257
+ return self._firefox_fill(selector or "", text, clear, wait)
1258
+
1276
1259
  try:
1277
- # 如果提供了 selector,先点击聚焦
1278
1260
  if selector:
1279
1261
  ele = self._find_element(selector, timeout=10)
1280
1262
  if not ele:
@@ -1366,8 +1348,11 @@ class StealthBrowser:
1366
1348
  if not self._ensure_page():
1367
1349
  return SkillResult(success=False, error="浏览器未启动")
1368
1350
 
1351
+ # [v1.47.16] Firefox+VNC 模式:通过 xdotool key 按键
1352
+ if self._firefox_mode:
1353
+ return self._firefox_press_key(key, selector, wait)
1354
+
1369
1355
  try:
1370
- # 如果提供了 selector,先聚焦
1371
1356
  if selector:
1372
1357
  ele = self._find_element(selector, timeout=10)
1373
1358
  if ele:
@@ -1490,6 +1475,13 @@ class StealthBrowser:
1490
1475
  if not self._ensure_page():
1491
1476
  return SkillResult(success=False, error="浏览器未启动")
1492
1477
 
1478
+ # [v1.47.16] Firefox+VNC 模式:无法通过 CDP 执行 JS
1479
+ if self._firefox_mode:
1480
+ return SkillResult(
1481
+ success=False,
1482
+ error="Firefox+VNC 模式下不支持 JS 执行。请在 VNC 中手动操作,或切换到桌面环境使用 Chromium。",
1483
+ )
1484
+
1493
1485
  try:
1494
1486
  _script = script.strip()
1495
1487
  # 检测是否包含 return 语句
@@ -1546,6 +1538,13 @@ class StealthBrowser:
1546
1538
  if not self._ensure_page():
1547
1539
  return SkillResult(success=False, error="浏览器未启动")
1548
1540
 
1541
+ # [v1.47.16] Firefox+VNC 模式:无法获取页面内容
1542
+ if self._firefox_mode:
1543
+ return SkillResult(
1544
+ success=False,
1545
+ error="Firefox+VNC 模式下不支持获取页面内容。请在 VNC 中手动查看,或切换到桌面环境使用 Chromium。",
1546
+ )
1547
+
1549
1548
  try:
1550
1549
  # Bug Fix: DrissionPage 没有 page.text 属性
1551
1550
  # 需要通过 page.ele('tag:html').text 获取页面文本
@@ -1584,6 +1583,13 @@ class StealthBrowser:
1584
1583
  if not self._ensure_page():
1585
1584
  return SkillResult(success=False, error="浏览器未启动")
1586
1585
 
1586
+ # [v1.47.16] Firefox+VNC 模式:无法获取页面 HTML
1587
+ if self._firefox_mode:
1588
+ return SkillResult(
1589
+ success=False,
1590
+ error="Firefox+VNC 模式下不支持获取页面 HTML。请在 VNC 中手动查看,或切换到桌面环境使用 Chromium。",
1591
+ )
1592
+
1587
1593
  try:
1588
1594
  html = self._page.html or ""
1589
1595
  output_html = html[:50000] if len(html) > 50000 else html
@@ -1608,6 +1614,13 @@ class StealthBrowser:
1608
1614
  if not self._ensure_page():
1609
1615
  return SkillResult(success=False, error="浏览器未启动")
1610
1616
 
1617
+ # [v1.47.16] Firefox+VNC 模式:无法等待元素
1618
+ if self._firefox_mode:
1619
+ return SkillResult(
1620
+ success=False,
1621
+ error="Firefox+VNC 模式下不支持等待元素。请在 VNC 中手动操作。",
1622
+ )
1623
+
1611
1624
  try:
1612
1625
  ele = self._find_element(selector, timeout=timeout)
1613
1626
  if ele:
@@ -1662,6 +1675,13 @@ class StealthBrowser:
1662
1675
  if not self._ensure_page():
1663
1676
  return SkillResult(success=False, error="浏览器未启动")
1664
1677
 
1678
+ # [v1.47.16] Firefox+VNC 模式:Cookie 已由 Firefox 自动保存到 profile 目录
1679
+ if self._firefox_mode:
1680
+ return SkillResult(
1681
+ success=True,
1682
+ message="Firefox+VNC 模式: Cookie 由 Firefox 自动保存到 profile 目录",
1683
+ )
1684
+
1665
1685
  try:
1666
1686
  cookies = self._page.cookies()
1667
1687
  cookie_list = []
@@ -1691,6 +1711,13 @@ class StealthBrowser:
1691
1711
  if not self._ensure_page():
1692
1712
  return SkillResult(success=False, error="浏览器未启动")
1693
1713
 
1714
+ # [v1.47.16] Firefox+VNC 模式:Cookie 由 Firefox 自动从 profile 目录加载
1715
+ if self._firefox_mode:
1716
+ return SkillResult(
1717
+ success=True,
1718
+ message="Firefox+VNC 模式: Cookie 由 Firefox 自动从 profile 目录加载",
1719
+ )
1720
+
1694
1721
  try:
1695
1722
  from core.browser_profile import get_browser_profile_manager
1696
1723
  mgr = get_browser_profile_manager()
@@ -1970,6 +1997,74 @@ class StealthBrowser:
1970
1997
  error=f"Firefox+VNC 输入失败: {e}",
1971
1998
  )
1972
1999
 
2000
+ def _firefox_press_key(self, key: str, selector: str = "", wait: float = 0.5) -> SkillResult:
2001
+ """[v1.47.16] Firefox+VNC 模式下通过 xdotool key 按键。"""
2002
+ display = os.environ.get("DISPLAY", ":99")
2003
+ env = {**os.environ, "DISPLAY": display}
2004
+
2005
+ try:
2006
+ # 查找 Firefox 窗口
2007
+ result = subprocess.run(
2008
+ ["xdotool", "search", "--onlyvisible", "--class", "firefox"],
2009
+ capture_output=True, text=True, timeout=5,
2010
+ env=env, start_new_session=True,
2011
+ )
2012
+ if result.returncode != 0 or not result.stdout.strip():
2013
+ return SkillResult(
2014
+ success=False,
2015
+ error="Firefox+VNC: 未找到 Firefox 窗口。请在 VNC 中确认 Firefox 已打开。",
2016
+ )
2017
+
2018
+ window_id = result.stdout.strip().split()[0]
2019
+ subprocess.run(
2020
+ ["xdotool", "windowactivate", "--sync", window_id],
2021
+ capture_output=True, timeout=5, env=env, start_new_session=True,
2022
+ )
2023
+
2024
+ # 将 JS 按键名称映射为 xdotool 按键名称
2025
+ xdotool_key_map = {
2026
+ 'enter': 'Return', 'tab': 'Tab', 'escape': 'Escape', 'esc': 'Escape',
2027
+ 'backspace': 'BackSpace', 'delete': 'Delete', 'del': 'Delete',
2028
+ 'arrowup': 'Up', 'arrowdown': 'Down', 'arrowleft': 'Left', 'arrowright': 'Right',
2029
+ 'up': 'Up', 'down': 'Down', 'left': 'Left', 'right': 'Right',
2030
+ 'home': 'Home', 'end': 'End',
2031
+ 'pageup': 'Page_Up', 'pagedown': 'Page_Down',
2032
+ 'space': 'space', ' ': 'space',
2033
+ 'ctrl': 'ctrl', 'shift': 'shift', 'alt': 'alt', 'meta': 'super',
2034
+ 'cmd': 'super', 'command': 'super',
2035
+ }
2036
+ # F1-F12
2037
+ for i in range(1, 13):
2038
+ xdotool_key_map[f'f{i}'] = f'F{i}'
2039
+
2040
+ # 解析组合键
2041
+ parts = key.strip().split('+')
2042
+ xdotool_keys = []
2043
+ for part in parts:
2044
+ p = part.strip()
2045
+ mapped = xdotool_key_map.get(p.lower(), p)
2046
+ xdotool_keys.append(mapped)
2047
+
2048
+ # 构建 xdotool key 参数
2049
+ xdotool_key_str = '+'.join(xdotool_keys)
2050
+ subprocess.run(
2051
+ ["xdotool", "key", "--window", window_id, xdotool_key_str],
2052
+ capture_output=True, timeout=5, env=env, start_new_session=True,
2053
+ )
2054
+
2055
+ if wait > 0:
2056
+ time.sleep(wait)
2057
+
2058
+ return SkillResult(
2059
+ success=True,
2060
+ message=f"Firefox+VNC: 已按键: {key}",
2061
+ )
2062
+ except Exception as e:
2063
+ return SkillResult(
2064
+ success=False,
2065
+ error=f"Firefox+VNC 按键失败: {e}",
2066
+ )
2067
+
1973
2068
  def _firefox_screenshot(self, save_path: str = "") -> SkillResult:
1974
2069
  """Firefox+VNC 模式下通过 xdotool + import 截图。"""
1975
2070
  display = os.environ.get("DISPLAY", ":99")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myagent-ai",
3
- "version": "1.47.16",
3
+ "version": "1.47.17",
4
4
  "description": "本地桌面端执行型AI助手 - Open Interpreter 风格 | Local Desktop Execution-Oriented AI Assistant",
5
5
  "main": "main.py",
6
6
  "bin": {