소스 정보
- 저장소
- agentscope-ai/QwenPaw
- 최근 소스 활동
- 2026년 7월 30일 08:55
- 감지된 SKILL.md 언어
- 영어
- 스타
- 34,066
- 포크
- 3,008
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/agentscope-ai/QwenPaw --skill browser명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
Use this skill whenever the user needs ANY mailbox/email operation — checking, reading, searching, sending, replying, forwarding, organizing or deleting email, managing threads, connecting a personal mailbox, or registering a new mailbox. This skill is the single entry point for email tasks and orchestrates qwenpawmail-mcp for nine supported personal-mail domains.
当用户需要任何邮箱/邮件操作时使用此技能——包括查看、阅读、搜索、发送、回复、转发、整理或删除邮件,管理会话线程,绑定个人邮箱或注册新邮箱。此技能是邮件任务的统一且唯一入口,通过 qwenpawmail-mcp 编排操作,当前支持 9 个个人邮箱域名。
Use this skill when you need to consult another agent, ask for help, or involve a specific agent the user asked for.
SOC 직업 분류 기준
SKILL.md 표시 중
| name | browser |
| description | 用异步 Python 调用 QwenPaw 内置 Browser SDK 驱动真实浏览器。完整参考在下方;上下文压缩后请重新加载此 browser skill。 |
| metadata | {"builtin_skill_version":"0.3","qwenpaw":{"emoji":"🌐","requires":{}}} |
保持工作纪律:先感知当前页面,再通过已列出的 API 动作,最后重新感知后才可声明成功。 只能陈述本轮实际观察到的事实。卡住时的合格交付 = 说明卡在哪一步 + 已亲眼验证的部分结果; 不要为了给出完整答案而补全你没有看到的内容。
尊重人工边界:登录、验证码、2FA 或任何必须由人完成的步骤,调用
await browser.handoff(...) 后停止,绝不自动化这些流程。
浏览器完不成时,不要拿其他渠道(如 web_search)的数据顶替并仍说成浏览器结果——
如实写明每个数据的来源。
这是 QwenPaw 内置的 Browser SDK,不是 Playwright。它是封闭的 API 表面: 未列出的方法不存在。完整参考在下方;若上下文中不再保留,请用 Skill 工具重新加载 此 browser skill。
QwenPaw Browser SDK — complete reference. This is QwenPaw's OWN internal SDK and this is the ENTIRE API; these are all the entrypoints. The SDK is already in scope as Browser — call the methods below directly. Write async Python. Work in a loop: perceive → act → verify.
browser = await Browser.connect() # connect once; reused all session page = await browser.open("https://example.com") # open a page obs = await page.snapshot() # PERCEIVE — page text is obs.text if len(obs.text) < 6000: print(obs.text) else: # Large page: read selectively instead of dumping everything. lines = [line for line in obs.text.splitlines() if "keyword" in line] print(f"{len(obs.text)} chars total; {len(lines)} matching lines:") print("\n".join(lines[:80]))
await page.get_by_role("textbox", name="Search").fill("laptop") # ACT await page.get_by_role("button", name="Search").click() # ACT obs = await page.snapshot() # VERIFY — re-perceive to confirm print("Verified; inspect obs.text with the selective pattern above.")
Session state: this is a stateful session — variables you assign (browser, page) persist across calls, so connect once and reuse them. If a call reports the session was reset, re-run await Browser.connect().
Chrome backend caveat: with backend=chrome you operate inside the user's real browser. A session is a tab-ownership group — tabs are isolated per session, but identity (cookies, logins, storage) is shared with the user's profile and with every other session. Do not rely on session-level identity isolation on this backend.
browser (orchestration): await Browser.connect(*, identity: "auto"|"user"|"avatar"|"guest" = "auto") -> browser Connect as an identity: user, avatar, guest, or auto.
``auto`` picks ``user`` when Chrome is connected, otherwise ``guest``.
An unavailable explicit identity raises instead of substituting.
await browser.open(url: str | None = None) -> page
Open a page at url and return it.
Reuses this session's active page when one exists; otherwise a
new page is created. Pages are released when the response cycle ends;
start each cycle by calling ``open(url)`` again.
await browser.pages() -> list of page ref (.id, .url, .title, .active) List open pages with URL, title, and active-state details. await browser.switch_page(page: page ref (.id, .url, .title, .active)) -> none Make the given page ref active for later operations. await browser.close_page(page: page ref (.id, .url, .title, .active)) -> none Close the given page ref in this session. await browser.session_status() -> session status (.owner, .variant, .context, .connected) Report the owner, variant, context, and connected state. await browser.handoff(reason: str, instructions: str = "") -> a result dict Hand a step back to a human (captcha, login, 2FA).
Pass a short reason and instructions; the run stops on this signal —
never automate these flows. The active cycle-scoped page is retained
for one extra response cycle after the handoff.
await browser.present(url: str | None = None) -> page Open a page retained for the chat lifetime. await browser.close() -> none Close this session's browser and release its context.
page (operation):
await page.goto(url: str) -> a result dict
Navigate this page to url and return raw navigation facts.
await page.go_back() -> a result dict
Navigate back to the previous page in history.
await page.go_forward() -> a result dict
Navigate forward to the next page in history.
await page.reload() -> a result dict
Reload the current page.
await page.keep() -> none
Retain this page across response cycles for the current chat.
await page.wait_for_load_state(state: str = "load", *, timeout: float | None = None) -> none
Wait until the page reaches the requested load state.
``networkidle`` semantics depend on the backend: the Playwright
backend waits for true network quiescence, while CDP-based
backends (cdp, chrome) degrade to ``document.readyState ==
"complete"`` plus a fixed 500 ms quiet delay and do NOT track
in-flight requests — content loaded by late XHR may still be
missing when this returns.
await page.wait_for_timeout(timeout: float) -> none Sleep unconditionally for timeout milliseconds (capped at 30 000).
Prefer :py:meth:`locator.wait_for(state, timeout)
<LocatorView.wait_for>` when waiting for a specific DOM condition
— it returns as soon as the condition is met and is both faster
and more reliable than an unconditional sleep.
await page.screenshot() -> a result dict
Capture this page to a PNG file in the active workspace.
page.get_by_role(role: str, *, name: str | None = None) -> locator
Locate elements by accessible role and optional name.
page.get_by_text(text: str) -> locator
Locate elements by their visible text.
page.get_by_label(text: str) -> locator
Locate a form control by its associated label text.
page.get_by_placeholder(text: str) -> locator
Locate an input by its placeholder text.
page.locator(selector: str) -> locator
Locate elements by a CSS selector when no semantic locator fits.
page.frame_locator(selector: str) -> locator
Scope subsequent locators to the iframe matching selector.
await page.snapshot(query: str | None = None) -> observation (read .text; .match_count when you pass a query)
Perceive the page and return readable content in .text.
Pass ``query`` to also report ``.match_count``.
await page.current_surface() -> surface facts (.url, .title, .load_state) Return this page's current URL, title, and load facts. page.mouse -> coordinate/keyboard input surface (see methods below) Viewport-coordinate input surface.
click(x, y) -> a result dict; verify the effect with snapshot().
page.keyboard -> coordinate/keyboard input surface (see methods below) Keyboard input surface.
press(key) -> a result dict; verify the effect with snapshot().
page.get_by_* / page.locator(...) return a locator that mirrors a SUBSET of Playwright's Python locator API — the Playwright-shaped part of this SDK: compose/scope (chainable): get_by_role/get_by_text/get_by_label/ get_by_placeholder, locator(sel), filter(...), nth(i), first, last (properties) iframe scope: page.frame_locator(sel).locator(...) (one frame; no nested frames) read (await): count()->int, inner_text()->str, text_content()->str|None, all_text_contents()->list, get_attribute(name)->str|None, input_value()->str, is_visible()->bool, is_enabled()->bool act (await; returns a short evidence line — read .evidence): click(), fill(v), type(t), press(key), check(), uncheck(), set_checked(b), select_option(*v), hover(), dblclick(), scroll(), focus(), blur(), clear(), wait_for(state), screenshot(), bounding_box()->dict|None (viewport ["x"] ["y"] ["width"] ["height"]; None when the element is not visible) strict-mode uniqueness is enforced — act only when the locator resolves to exactly one element (use count() to check). element_handle / raw CDP unavailable.
Backend differences (chrome/cdp vs playwright): on chrome/cdp the accessible name is a heuristic (aria-labelledby > aria-label > alt > title > text content) - container elements may match get_by_role(name=) more broadly than under playwright, so strict-mode errors are more likely there; narrow with filter(has_text=) or a more specific role. is_enabled() reflects only the disabled property, not aria-disabled. press() supports a fixed key set: printable characters, Enter, Tab, Escape, Backspace, Delete, Arrow keys, Home/End/PageUp/PageDown, and Control/Shift/Alt/Meta combos - anything else fails with guidance. type() sets the value directly and fires an input event; editors that need real per-key events may not react - prefer fill() where possible.
Reading results (read these fields; the type names don't matter): snapshot() -> .text (page text), .match_count (when you pass query) current_surface() -> .url, .title, .load_state page refs -> .id, .url, .title, .active screenshot() -> result dict; read ["path"] bounding_box() -> viewport ["x"] ["y"] ["width"] ["height"]; None when the element is not visible mouse.click()/keyboard.press() -> result dict; fields depend on the backend, so verify with snapshot() actions -> .evidence (a short line saying what happened) locator reads return plain str/int/bool/list directly.
If a locator fails, step DOWN one rung (don't jump):