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.
- package/aiskills/browser_stealth.py +36 -8
- package/aiskills/chromedev_mcp.py +19 -67
- package/core/output_parser.py +730 -0
- package/core/vnc_manager.py +51 -1111
- package/main.py +1 -19
- package/package.json +3 -3
- package/requirements.txt +1 -0
- package/start.js +1 -5
- package/tray_manager.py +2 -20
- package/web/ui/admin/admin-sites.js +15 -6
- package/web/ui/chat/chat_container.html +2 -2
- package/worklog.md +0 -75
|
@@ -2181,19 +2181,16 @@ class StealthBrowser:
|
|
|
2181
2181
|
f"{result.stderr.decode(errors='ignore')[:200]}"
|
|
2182
2182
|
)
|
|
2183
2183
|
|
|
2184
|
-
# 方法4: 使用
|
|
2185
|
-
xdotool_cmd = shutil.which("xdotool")
|
|
2184
|
+
# 方法4: 使用 xwd + ffmpeg (proot 兼容)
|
|
2186
2185
|
ffmpeg_cmd = shutil.which("ffmpeg")
|
|
2187
|
-
if
|
|
2186
|
+
if xwd_cmd and ffmpeg_cmd:
|
|
2188
2187
|
xwd_tmp = save_path + ".xwd"
|
|
2189
|
-
# 用 xdotool 获取活动窗口并截图
|
|
2190
2188
|
result = subprocess.run(
|
|
2191
|
-
[xwd_cmd
|
|
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
|
|
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
|
|
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
|
-
#
|
|
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
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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:
|