feat: 浏览器一期:坐标点击 + live 截图
- browser_click_at(x, y):视口坐标点击(1280x800、左上角 原点),越界与非数字拒绝;live 视图里用户在截图上的 点击落点就走这个工具。 - browser_live_shot:固定文件 browser-live.png + 状态 sidecar browser-live.json(URL/标题/时间戳),watcher 轮询一个稳定路径即可;技能要求每次动作后都调一次, 保持画面最新。 - MCP 新增 browser_click_at / browser_live_shot 两个工具; 技能补 live 视图一节;测试 9 项(含 fake mouse、越界、 live 文件与状态读回)。
This commit is contained in:
1 parent
cb6de0e3aa
commit
814fe8f7c2
4 files changed
+151
-1
No files matched your search
@@ -148,6 +148,59 @@ class Browser:
|
||||
result["ok"] = True
|
||||
return result
|
||||
|
||||
def click_at(self, x, y):
|
||||
"""Click by viewport coordinates (CSS pixels, origin at the
|
||||
top-left of the 1280x800 viewport). This is the tool the
|
||||
console live view drives: the user clicks on a screenshot
|
||||
and the coordinates arrive here. Bounds-checked so a
|
||||
mis-scaled click cannot scroll the page into nowhere."""
|
||||
try:
|
||||
fx = float(x)
|
||||
fy = float(y)
|
||||
except (TypeError, ValueError):
|
||||
raise BrowserError("click_at needs numeric x/y, got %r/%r"
|
||||
% (x, y))
|
||||
if not (0 <= fx <= 1280 and 0 <= fy <= 800):
|
||||
raise BrowserError(
|
||||
"click_at out of viewport 1280x800: %s,%s" % (x, y))
|
||||
self._ensure()
|
||||
try:
|
||||
self._page.mouse.click(fx, fy)
|
||||
self._current_url = self._page.url
|
||||
except Exception as exc: # noqa: BLE001
|
||||
raise BrowserError("click_at failed: %s" % exc)
|
||||
result = self._info()
|
||||
result["ok"] = True
|
||||
result["x"] = fx
|
||||
result["y"] = fy
|
||||
return result
|
||||
|
||||
def live_shot(self, output_filename=None):
|
||||
"""Screenshot to a FIXED live-view file plus a JSON status
|
||||
sidecar, so a watcher (console, user) can poll one stable
|
||||
path instead of chasing per-shot filenames. The agent
|
||||
should call this after every navigate/click/fill while a
|
||||
live view is wanted. Returns path, bytes, url, title and
|
||||
a unix timestamp."""
|
||||
self._ensure()
|
||||
name = _validate_basename(
|
||||
output_filename or "browser-live.png", "output_filename")
|
||||
path = os.path.join(self.cwd, name)
|
||||
try:
|
||||
self._page.screenshot(path=path)
|
||||
except Exception as exc: # noqa: BLE001
|
||||
raise BrowserError("live_shot failed: %s" % exc)
|
||||
info = self._info()
|
||||
status = {"image": name, "path": path,
|
||||
"bytes": os.path.getsize(path),
|
||||
"url": info["url"], "title": info["title"],
|
||||
"timestamp": int(time.time())}
|
||||
status_path = os.path.join(self.cwd, "browser-live.json")
|
||||
with open(status_path, "w", encoding="utf-8") as fh:
|
||||
json.dump(status, fh, ensure_ascii=False)
|
||||
status["status_path"] = status_path
|
||||
return status
|
||||
|
||||
def evaluate(self, expression):
|
||||
if not expression or not str(expression).strip():
|
||||
raise BrowserError("expression is required")
|
||||
|
||||
Reference in new issue
Block a user