项目文件夹

文件
wehub-resource-sync b4fbd6fe9f
Deploy Site / deploy-vercel (push) Has been skipped
Deploy Site / deploy-docs (push) Has been skipped
Build Skills Index / build-index (push) Has been skipped
CI / Deny unrelated histories (push) Has been skipped
CI / Detect affected areas (push) Successful in 27m35s
CI / OSV scan (push) Failing after 4s
CI / Build&Test Docker image (push) Successful in 9s
CI / Supply-chain scan (push) Has been skipped
CI / Lint Docker scripts (push) Failing after 5m13s
CI / Check contributors (push) Failing after 12m8s
CI / Docs Site (push) Failing after 12m8s
CI / TypeScript (push) Failing after 12m8s
CI / Python lints (push) Failing after 12m9s
CI / Python tests (push) Failing after 12m9s
CI / Check uv.lock (push) Failing after 23m22s
CI / CI timing report (push) Has been cancelled
Build Skills Index / trigger-deploy (push) Has been cancelled
CI / All required checks pass (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 11:56:03 +08:00

145 行
5.1 KiB
Python

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
"""Regression for the X11 null-PID `list_windows` crash.
On X11 a window's PID comes from the *optional* ``_NET_WM_PID`` property, so
the cua-driver legitimately reports ``pid: null`` for windows that don't set
it (the desktop root, panels, override-redirect popups, …). Both
``capture()`` and ``focus_app()`` previously coerced *every* window's pid via
``int(w["pid"])`` inside a list comprehension, so a single null-pid window
raised::
TypeError: int() argument must be a string, a bytes-like object or a
real number, not 'NoneType'
…aborting the whole enumeration before any screenshot was taken — i.e. the
agent could never capture the screen at all on an X11 desktop that had even
one such window.
The fix routes both ingestion sites through ``_ingest_windows``, which skips
windows lacking a usable pid/window_id (uncapturable anyway) and coerces the
rest, so real targetable windows survive.
"""
from __future__ import annotations
import base64
from unittest.mock import MagicMock
# 8×8 transparent PNG — decodes cleanly so capture() can size it.
_PNG_B64 = (
"iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAADUlEQVR4nG"
"NgGAUgAAABCAABgukLHQAAAABJRU5ErkJggg=="
)
# ---------------------------------------------------------------------------
# _ingest_windows: the fix locus (pure function, no session needed)
# ---------------------------------------------------------------------------
class TestIngestWindows:
def test_skips_window_with_null_pid(self):
from tools.computer_use.cua_backend import _ingest_windows
raw = [
{"app_name": "Desktop", "pid": None, "window_id": 1, "z_index": 0},
{"app_name": "Firefox", "pid": 4321, "window_id": 77, "z_index": 1},
]
out = _ingest_windows(raw)
assert [w["app_name"] for w in out] == ["Firefox"]
assert out[0]["pid"] == 4321
assert out[0]["window_id"] == 77
def test_skips_window_with_null_window_id(self):
from tools.computer_use.cua_backend import _ingest_windows
raw = [
{"app_name": "Panel", "pid": 10, "window_id": None, "z_index": 0},
{"app_name": "Firefox", "pid": 4321, "window_id": 77, "z_index": 1},
]
out = _ingest_windows(raw)
assert [w["app_name"] for w in out] == ["Firefox"]
def test_coerces_numeric_strings_like_the_original_int_call(self):
# The original `int(w["pid"])` accepted numeric strings; preserve that.
from tools.computer_use.cua_backend import _ingest_windows
out = _ingest_windows(
[{"app_name": "Term", "pid": "200", "window_id": "9", "z_index": 0}]
)
assert out[0]["pid"] == 200
assert out[0]["window_id"] == 9
assert isinstance(out[0]["pid"], int)
def test_preserves_fields_capture_relies_on(self):
from tools.computer_use.cua_backend import _ingest_windows
out = _ingest_windows([
{
"app_name": "Firefox",
"pid": 1,
"window_id": 2,
"is_on_screen": False,
"title": "Mozilla Firefox",
"z_index": 3,
}
])
w = out[0]
assert w["off_screen"] is True # derived from is_on_screen
assert w["title"] == "Mozilla Firefox"
assert w["z_index"] == 3
# ---------------------------------------------------------------------------
# capture(): end-to-end proof the null-pid window no longer crashes capture
# ---------------------------------------------------------------------------
def _backend_with_windows(raw_windows):
"""A CuaDriverBackend whose session returns `raw_windows` from
list_windows and a valid PNG from screenshot."""
from tools.computer_use.cua_backend import CuaDriverBackend
backend = CuaDriverBackend()
session = MagicMock()
session.capabilities_discovered = True
session._has_tool.return_value = True
def _call_tool(name, args, *a, **k):
if name == "list_windows":
return {"structuredContent": {"windows": raw_windows}}
if name == "screenshot":
return {
"structuredContent": {
"screenshot_png_b64": _PNG_B64,
"screenshot_mime_type": "image/png",
}
}
return {}
session.call_tool.side_effect = _call_tool
backend._session = session
return backend
def test_capture_vision_survives_null_pid_window():
raw = [
{"app_name": "Desktop", "pid": None, "window_id": 1, "z_index": 0},
{"app_name": "Firefox", "pid": 4321, "window_id": 77,
"is_on_screen": True, "title": "Mozilla Firefox", "z_index": 1},
]
backend = _backend_with_windows(raw)
cap = backend.capture(mode="vision")
# The real, targetable window is selected rather than the whole capture
# crashing on the null-pid desktop window.
assert cap.app == "Firefox"
assert cap.png_b64 == _PNG_B64
assert backend._active_pid == 4321
assert backend._active_window_id == 77
assert base64.b64decode(cap.png_b64) # decodes cleanly