mirror of
https://git.openapi.site/https://github.com/desirecore/market.git
synced 2026-09-05 20:03:43 +08:00
fix(web-access): 增加外部浏览器确定性前置探测 (#94)
## 中文 ### 背景 L3-external 过去只用 curl 判断 9222,无法区分浏览器未安装、端口未开启、错误产品或伪 CDP 服务,也可能静默回落内置浏览器。 ### 变更 - web-access 升级到 3.4.0,并要求先调用 BrowserExternalProbe - 结构化处理未安装、端口关闭、多候选、产品不匹配、无效 CDP 与宿主不可用 - 显式浏览器不可自动替换;泛指 external 在多候选且无 ready 端口时先询问 - 所有 Playwright 配方使用 probe 返回端口和 DesireCore 隔离 Profile - 纳入并修复 #93 的浏览器收尾规则与重复编号 finding ### 验证 - validate-i18n:通过 - translate --check:通过 - validate-i18n --online:通过 - source hash:sha256:1704b973e3a90e89 - 双语结构:31 个标题、20 个代码围栏 取代 #93;关联 desirecore/desirecore#2369。 ## English ### Background L3-external previously relied on curl against port 9222, which could not distinguish a missing browser, a closed debug port, a different product, or a fake CDP service, and could silently fall back to the built-in browser. ### Changes - Bump web-access to 3.4.0 and require BrowserExternalProbe before CDP attach - Handle missing installation, closed port, multiple candidates, product mismatch, invalid CDP, and unavailable host as separate states - Never replace an explicitly requested browser; ask when a generic external request has multiple candidates and no ready endpoint - Use the probe result port and a DesireCore-isolated profile in all Playwright recipes - Incorporate #93 browser wrap-up guidance and fix its duplicate numbering review finding ### Validation - i18n validation passed - translation freshness check passed - online validation passed - source hash: sha256:1704b973e3a90e89 - bilingual structure: 31 headings and 20 code fences Supersedes #93; related to desirecore/desirecore#2369. --------- Co-authored-by: xieyuanxiang <124608760@qq.com>
This commit is contained in:
@@ -9,13 +9,13 @@ description: >-
|
||||
for public pages, (2) Jina Reader as the default token-optimization layer for
|
||||
heavy/JS-rendered pages, and (3) the governed built-in browser (isolated
|
||||
BrowserSpace + Cookie import + bulk text extraction + waits + code mode) to
|
||||
reach, interact with, and read login-gated sites, and (4) the user's own Chrome over CDP when they ask for it by name. Always cite source URLs.
|
||||
reach, interact with, and read login-gated sites, and (4) the user's named external Chromium browser (Chrome/Edge/Chromium) over CDP when requested. Always cite source URLs.
|
||||
Use when 用户提到 联网搜索、上网查、
|
||||
查资料、抓取网页、研究、调研、最新资讯、文档查询、对比、竞品、技术文档、
|
||||
新闻、网址、URL、找一下、搜一下、查一下、小红书、B站、微博、飞书、Twitter、
|
||||
推特、X、知乎、公众号、已登录、登录状态。
|
||||
license: Complete terms in LICENSE.txt
|
||||
version: 3.2.0
|
||||
version: 3.4.0
|
||||
type: procedural
|
||||
risk_level: low
|
||||
status: enabled
|
||||
@@ -31,6 +31,7 @@ tags:
|
||||
provides:
|
||||
tools:
|
||||
- BrowserManage
|
||||
- BrowserExternalProbe
|
||||
- BrowserSnapshot
|
||||
- BrowserAct
|
||||
- BrowserScript
|
||||
@@ -41,7 +42,7 @@ provides:
|
||||
- LocalBookmarks
|
||||
metadata:
|
||||
author: desirecore
|
||||
updated_at: '2026-08-21'
|
||||
updated_at: '2026-08-29'
|
||||
i18n:
|
||||
default_locale: en-US
|
||||
source_locale: zh-CN
|
||||
@@ -51,16 +52,16 @@ metadata:
|
||||
zh-CN:
|
||||
name: 联网访问
|
||||
short_desc: 联网搜索、网页抓取、内置受管浏览器登录态访问与取文、研究调研工作流
|
||||
description: 联网访问工具包——搜索公开页面、Jina 优化抓取、内置受管浏览器完成登录态访问与取文,以及用户点名时接管他自己的 Chrome。
|
||||
description: 联网访问工具包——搜索公开页面、Jina 优化抓取、内置受管浏览器完成登录态访问与取文,以及用户点名时接管他自己的 Chrome/Edge/Chromium。
|
||||
body: ./SKILL.zh-CN.md
|
||||
source_hash: sha256:4d3bc4221b2d6b09
|
||||
source_hash: sha256:1704b973e3a90e89
|
||||
translated_by: human
|
||||
en-US:
|
||||
name: Web Access
|
||||
short_desc: Web search, page fetching, logged-in access via the governed built-in browser, research workflows
|
||||
description: A web-access toolkit — search public pages, fetch heavy pages via Jina Reader, reach and read logged-in sites through the governed built-in browser, and drive the user's own Chrome over CDP on request.
|
||||
description: A web-access toolkit — search public pages, fetch heavy pages via Jina Reader, reach and read logged-in sites through the governed built-in browser, and drive the user's named Chrome/Edge/Chromium over CDP on request.
|
||||
body: ./SKILL.md
|
||||
source_hash: sha256:4d3bc4221b2d6b09
|
||||
source_hash: sha256:1704b973e3a90e89
|
||||
translated_by: human
|
||||
market:
|
||||
icon: >-
|
||||
@@ -78,7 +79,7 @@ market:
|
||||
fill-opacity="0.12"/><path d="M20.5 20.5l2 2" stroke="#34C759"
|
||||
stroke-width="1.8" stroke-linecap="round"/></svg>
|
||||
category: research
|
||||
required_client_version: 10.0.98
|
||||
required_client_version: 10.0.128
|
||||
maintainer:
|
||||
name: DesireCore Official
|
||||
verified: true
|
||||
@@ -89,7 +90,7 @@ market:
|
||||
|
||||
## L0: One-line Summary
|
||||
|
||||
A web-access toolkit — search public pages, optimize fetches via Jina Reader, reach/interact with/read login-gated sites through the governed built-in browser, and drive the user's own Chrome over CDP when they ask for it by name.
|
||||
A web-access toolkit — search public pages, optimize fetches via Jina Reader, reach/interact with/read login-gated sites through the governed built-in browser, and drive the user's named Chrome/Edge/Chromium over CDP when requested.
|
||||
|
||||
## L1: Overview & Use Cases
|
||||
|
||||
@@ -101,17 +102,18 @@ web-access is a **procedural skill** that provides four complementary layers of
|
||||
- **L2** (Jina Reader): JS-rendered heavy pages, saving tokens by default
|
||||
- **L3** (governed built-in browser, capability surface completed in v3.0): reach, *interact with*, and **read** logged-in / interactive sites — isolated BrowserSpace per task, zero Python dependency, every action carries a signed receipt. Bulk text extraction (`page.extract-text`), discriminated waits (`page.wait`), and code mode (`BrowserScript`) all close the loop inside this layer
|
||||
|
||||
- **L3-external** (the user's own Chrome, attached via CDP + Python Playwright): **take this route when the user names their own browser** — their login state, their window, visible to them the whole time and theirs to take over at any moment
|
||||
- **L3-external** (the user's named Chrome/Edge/Chromium, attached via CDP + Python Playwright): **take this route only when the user names their own browser or explicitly accepts it after you explain why** — its isolated DesireCore profile login state, its visible window, and the user's ability to take over at any moment
|
||||
|
||||
A note of history on L3-external: v3.0 deleted it outright, on the grounds that "every technical reason it existed for (no bulk text channel, evaluate unusable, screenshots must activate-serialize) is now covered by the built-in browser". That technical judgement was correct — **as a fallback for when the built-in browser isn't enough, it genuinely isn't needed any more**. But the deletion took with it a completely different use case: the user wanting *their own* browser. That has nothing to do with capability, and the built-in browser cannot stand in for it, so v3.2 restores it as a peer option **triggered by user intent**. Note it is no longer a fallback; see "Two browsers — pick by user intent" below.
|
||||
|
||||
### v3.0: governed built-in browser (default-hidden, exposed only after Skill activation)
|
||||
|
||||
When you call `Skill('web-access')`, the following 9 tools are injected into the current session so the LLM can drive the built-in browser directly:
|
||||
When you call `Skill('web-access')`, the following tools are injected into the current session. The `Browser*` tools drive the built-in browser; `BrowserExternalProbe` only inspects external-browser prerequisites:
|
||||
|
||||
| Tool | Purpose |
|
||||
|------|---------|
|
||||
| BrowserManage | Create/destroy isolated BrowserSpace, start sessions, manage tabs |
|
||||
| BrowserExternalProbe | Read-only check for installed Chrome/Edge/Chromium and loopback CDP readiness; never launches or reads a profile |
|
||||
| BrowserSnapshot | `semantic` / `text` / `accessibility` / `visual` snapshots — the primary way to read a page |
|
||||
| BrowserAct | One governed action per call: navigate, input, extract text, wait, element ops, screenshot, … |
|
||||
| BrowserScript | **Code mode**: one async JS script issues browser commands back-to-back, eliminating per-action round trips (trust level equals Bash) |
|
||||
@@ -122,7 +124,7 @@ When you call `Skill('web-access')`, the following 9 tools are injected into the
|
||||
|
||||
> **Important**: before `Skill('web-access')` is called, none of these tools appear in the LLM tools list — default conversations don't pay their token cost. See [references/browser-tools.md](references/browser-tools.md).
|
||||
>
|
||||
> **Removed in v2.1**: `BrowserListTabs` / `BrowserNavigate` / `BrowserEval` / `BrowserClick` / `BrowserScreenshot` / `BrowserScroll` / `BrowserSetFiles` / `BrowserCloseTab` and the cdp-proxy behind them are retired. Calling them now returns "该旧 BrowserXxx/cdp-proxy 入口已停用". Requires client v10.0.98+; `page.extract-text` / `page.element` / `page.wait` / inline wait blocks / cross-origin iframe snapshots need v10.0.112+, and `BrowserScript` needs a build containing S17/S18.
|
||||
> **Removed in v2.1**: `BrowserListTabs` / `BrowserNavigate` / `BrowserEval` / `BrowserClick` / `BrowserScreenshot` / `BrowserScroll` / `BrowserSetFiles` / `BrowserCloseTab` and the cdp-proxy behind them are retired. Calling them now returns "该旧 BrowserXxx/cdp-proxy 入口已停用". This skill version requires client v10.0.128+; `page.extract-text` / `page.element` / `page.wait` / inline wait blocks / cross-origin iframe snapshots were introduced in v10.0.112+, and `BrowserScript` needs a build containing S17/S18.
|
||||
|
||||
### Use Cases
|
||||
|
||||
@@ -137,7 +139,7 @@ When you call `Skill('web-access')`, the following 9 tools are injected into the
|
||||
- **Layered progression**: from lightweight search to heavy JS rendering to logged-in access — pick on demand; plus the user's own browser whenever they name it
|
||||
- **Token optimization**: Jina Reader cuts token usage by 50–80% by default; `page.extract-text`'s maxBytes/cursor paging keeps even long logged-in articles under control
|
||||
- **Logged-in session reuse**: where the Host has granted `browser.import.*`, BrowserImport brings the user's Cookies into an isolated Space — no re-login required
|
||||
- **Zero external dependencies by default**: the built-in browser needs no Python/Playwright install and no manually launched debug Chrome (L3-external does, and only when the user asks for it)
|
||||
- **Zero external dependencies by default**: the built-in browser needs no Python/Playwright install and no manually launched external Chromium browser (L3-external does, and only after explicit user intent)
|
||||
|
||||
## L2: Detailed Specification
|
||||
|
||||
@@ -149,51 +151,41 @@ When you complete a research task, you **MUST** cite all source URLs in your res
|
||||
|
||||
If any fetch fails, explicitly tell the user which URL failed and which fallback you used.
|
||||
|
||||
## Prerequisites: Chrome CDP Setup (L3-external only)
|
||||
## Prerequisites: external browser + CDP (L3-external only)
|
||||
|
||||
**Only needed when taking the L3-external route** (the user named their own browser). The built-in
|
||||
browser has no prerequisites.
|
||||
**Only needed when taking the L3-external route.** The built-in browser has no prerequisites.
|
||||
|
||||
### One-time setup
|
||||
### Always probe before attaching
|
||||
|
||||
Have the user launch Chrome with remote debugging enabled:
|
||||
Map the user's words to an exact probe request:
|
||||
|
||||
**macOS**:
|
||||
```bash
|
||||
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
|
||||
--remote-debugging-port=9222 \
|
||||
--user-data-dir="${DESIRECORE_ROOT}/chrome-profile"
|
||||
```
|
||||
- “my Chrome” → `BrowserExternalProbe({ requestedBrowser: 'chrome' })`
|
||||
- “my Edge” → `BrowserExternalProbe({ requestedBrowser: 'edge' })`
|
||||
- “my external/system browser” without a product name → `BrowserExternalProbe({ requestedBrowser: 'any' })`
|
||||
- “local browser” is ambiguous → ask whether they mean the built-in browser or their external browser before probing
|
||||
|
||||
**Linux**:
|
||||
```bash
|
||||
google-chrome \
|
||||
--remote-debugging-port=9222 \
|
||||
--user-data-dir="${DESIRECORE_ROOT}/chrome-profile"
|
||||
```
|
||||
Handle the structured result exactly:
|
||||
|
||||
**Windows (PowerShell)**:
|
||||
```powershell
|
||||
& "C:\Program Files\Google\Chrome\Application\chrome.exe" `
|
||||
--remote-debugging-port=9222 `
|
||||
--user-data-dir="$env:USERPROFILE\.desirecore\chrome-profile"
|
||||
```
|
||||
| status | Required response |
|
||||
|---|---|
|
||||
| `ready` | Continue with Playwright `connect_over_cdp`; name the detected external browser honestly. For `any`, the already-ready endpoint is the prepared choice even if other products are installed |
|
||||
| `browser_not_installed` | Say the requested browser was not detected. If `alternatives` is non-empty, ask whether the user wants one of them; **never switch automatically** |
|
||||
| `debug_port_closed` | Show the returned `launchCommand`, ask the user to launch it and log in manually, then wait and probe again |
|
||||
| `browser_choice_required` | No endpoint is ready and multiple external browsers are installed. List id/name only and ask which one the user wants; probe that exact choice next |
|
||||
| `browser_mismatch` | Say which browser is actually on the port and which one was requested; ask the user to correct the port or explicitly approve the other browser |
|
||||
| `invalid_cdp_endpoint` | Explain that something is listening on the port but it is not a valid Chrome DevTools endpoint; do not attach |
|
||||
| `host_unavailable` | Explain that this Agent Service cannot inspect the user's desktop host; do not assume a browser is installed or silently use the built-in browser |
|
||||
|
||||
After launch:
|
||||
1. The user logs in manually to the sites they need
|
||||
2. That Chrome window stays open
|
||||
3. Verify the debug endpoint: `curl -s http://localhost:9222/json/version` should return JSON
|
||||
`launchCommand` uses an isolated DesireCore profile. After launch:
|
||||
|
||||
### Verify readiness before every operation
|
||||
1. The user logs in manually to the sites they need.
|
||||
2. That external browser window stays open.
|
||||
3. Call `BrowserExternalProbe` again. Only `ready` authorizes the CDP attach attempt.
|
||||
|
||||
```bash
|
||||
curl -s http://localhost:9222/json/version | python3 -c "import sys,json; d=json.load(sys.stdin); print('CDP ready:', d.get('Browser'))"
|
||||
```
|
||||
Do not replace this probe with `curl`: a refused connection cannot distinguish “browser not installed”
|
||||
from “browser installed but debugging disabled”, and a random HTTP service must not be accepted as CDP.
|
||||
|
||||
If it fails, tell the user: "请先启动 Chrome 并开启远程调试端口(见 web-access 技能的 Prerequisites 部分)"
|
||||
— **then wait for them.** Don't switch to the built-in browser just because it could also do the job.
|
||||
|
||||
⚠️ When attached over CDP, **never call `browser.close()`** — that would close the user's own Chrome.
|
||||
⚠️ When attached over CDP, **never call `browser.close()`** — that would close the user's own external browser.
|
||||
Only close the page you opened. Full recipes in [references/cdp-browser.md](references/cdp-browser.md).
|
||||
|
||||
---
|
||||
@@ -229,8 +221,8 @@ User intent
|
||||
│
|
||||
└─ "Real-time interactive task" (click, fill form, scroll, screenshot)
|
||||
├─→ **User named "my own / my machine's / the external browser"** → L3-external:
|
||||
│ verify CDP is ready (see Prerequisites), then python3 playwright.connect_over_cdp()
|
||||
│ If it isn't ready, give them the launch command and wait — don't quietly switch to the built-in one
|
||||
│ BrowserExternalProbe(exact requested browser), then connect_over_cdp() only on `ready`
|
||||
│ Otherwise follow the status guidance and wait — don't quietly switch to the built-in one
|
||||
└─→ **Otherwise (default)**: built-in browser (BrowserManage → BrowserAct → BrowserSnapshot —
|
||||
see references/browser-tools.md, no Python needed)
|
||||
```
|
||||
@@ -241,10 +233,10 @@ DesireCore can drive **two** browsers. They are peer options:
|
||||
|
||||
| | L3 built-in governed browser | L3-external — the user's own browser |
|
||||
|---|---|---|
|
||||
| What it is | A browser instance inside the app (the `Browser*` tools) | The Chrome installed on the user's machine, attached via CDP + Python Playwright |
|
||||
| What it is | A browser instance inside the app (the `Browser*` tools) | The user-named Chrome/Edge/Chromium on their machine, attached via CDP + Python Playwright |
|
||||
| Login state | Isolated; needs `browser.import.*` granted by the Host before `BrowserImport` can pull cookies | **Literally the user's own session** — nothing to import |
|
||||
| Can the user see it | Agent tabs are offscreen by default; must be presented to the workbench | **It's their own window** — visible throughout, theirs to take over |
|
||||
| Prerequisite | None | User must launch Chrome with `--remote-debugging-port=9222` (see Prerequisites) |
|
||||
| Prerequisite | None | `BrowserExternalProbe` must report `ready`; otherwise follow its exact status guidance |
|
||||
| Default | ✅ yes | When the user names it |
|
||||
|
||||
> The login-state row is easy to misread as "the built-in browser can't reuse the user's login
|
||||
@@ -258,13 +250,13 @@ DesireCore can drive **two** browsers. They are peer options:
|
||||
**The layer is chosen by user intent, not by technical difficulty.** v3.0 deleted this layer as
|
||||
"a fallback for when the built-in browser isn't enough" — and as a fallback, it really isn't needed
|
||||
any more. But that deletion also removed a **different** use case: the user wanting *their own*
|
||||
browser. That has nothing to do with capability — their login state lives in their Chrome, and they
|
||||
browser. That has nothing to do with capability — the login state they establish lives in their named external browser, and they
|
||||
want to watch it happen and take over when they choose. The built-in browser cannot stand in for that.
|
||||
|
||||
**If the user named one, use the one they named:**
|
||||
|
||||
- "my own / my machine's / the external browser / my Chrome" → **L3-external**. Verify CDP readiness
|
||||
per Prerequisites first; if it isn't ready, give them the launch command and wait. **Do not switch
|
||||
- "my own / my machine's / the external browser / my Chrome" → **L3-external**. Probe the exact
|
||||
requested browser first; if it isn't ready, follow the structured status and wait. **Do not switch
|
||||
to the built-in browser just because it could also do the job**
|
||||
- "the built-in browser", or nothing named → **L3 built-in** (default, no prerequisites)
|
||||
- Genuinely unclear which they mean → ask, don't guess
|
||||
@@ -280,7 +272,7 @@ When what they asked for and what you're giving differ, the wording has to make
|
||||
| L1 | Public, static | `WebFetch` | Low |
|
||||
| L2 | JS-heavy, long articles, token savings | `Bash curl r.jina.ai` | **Lowest** (Markdown pre-cleaned) |
|
||||
| **L3** | **Login-gated navigation, interaction & extraction (PRIMARY)** | **built-in browser (BrowserManage / BrowserAct / BrowserSnapshot / BrowserScript)** | Medium |
|
||||
| L3-external | **User named their own browser**; or their personal login state is needed and `BrowserImport` is unavailable | `Bash + Python Playwright connect_over_cdp` (see references/cdp-browser.md) | Medium |
|
||||
| L3-external | **User named their own browser**, or explicitly accepted this route after you explained why it is needed | `BrowserExternalProbe` → `Bash + Python Playwright connect_over_cdp` (see references/cdp-browser.md) | Medium |
|
||||
|
||||
**Default priority**: L1 for simple public pages → L2 for heavy → **L3 for login-gated (body text and in-site API data included)**.
|
||||
|
||||
@@ -354,7 +346,36 @@ The full command surface, capability tiers, and boundaries are in [references/br
|
||||
4. `BrowserSnapshot(semantic)` for interactive-element `ref` handles (cross-origin iframe elements are in the same tree with globally sequential refs)
|
||||
5. Interact via `input.*` (humanized trajectories) or `page.element` (bulk form writes); wait for results via `page.wait` or inline wait blocks
|
||||
6. Read body text via `BrowserSnapshot(text)` or `BrowserAct(page.extract-text)`; pull API data via the fetch.browser recipe
|
||||
7. `BrowserManage(close_session)` when done
|
||||
7. Wrap up: **confirm first, then decide whether to close** — see "Wrapping up: before you report, before you close" below
|
||||
|
||||
### Wrapping up: before you report, before you close
|
||||
|
||||
**1. Before reporting a browser result to the user, take one more snapshot to confirm the page is still there.**
|
||||
|
||||
Never report based on an earlier successful navigation. The session may have been terminated since —
|
||||
most often by the resource quota guard (`BROWSER_RESOURCE_QUOTA_EXCEEDED`), which heavy JS sites hit
|
||||
easily. This happened in the field: the agent navigated successfully, reported "✅ opened, the page is
|
||||
visible in the built-in browser panel", while the session had already crashed and the user was staring
|
||||
at a blank panel.
|
||||
|
||||
One `BrowserSnapshot` (`text` or `visual`) before reporting surfaces this: if the session is gone, the
|
||||
tool returns `BROWSER_TOOL_SESSION_TERMINATED` with the reason. When that happens, **tell the user the
|
||||
session was interrupted**, then decide whether to retry, switch to a lighter page, or hand it back —
|
||||
never present the earlier success as the current state.
|
||||
|
||||
**2. Do not close the session unconditionally. The test is whether the user still needs to look at it.**
|
||||
|
||||
| Task nature | Wrap-up |
|
||||
|---|---|
|
||||
| Demo / interactive / user wants to see the result | **Keep the session** and tell them where the page is, so they can take over |
|
||||
| Pure data extraction, text already retrieved | Close it and free the resources |
|
||||
| User explicitly said "close it when done" | Close it |
|
||||
|
||||
Showing the built-in browser to the user depends on the workbench presenting that session; close it and
|
||||
there is nothing left to show. This happened in the field: the agent finished a two-minute demo and
|
||||
immediately called `close_session`, leaving the user with an empty panel — nothing it did was wrong, it
|
||||
just left no chance to look. **When in doubt, keep it**: keeping costs a little resource, closing too
|
||||
early costs the user the whole run.
|
||||
|
||||
Multi-step sequences (navigate→snapshot→click→wait→extract) can be done in one `BrowserScript` run, skipping the per-action IPC round trips.
|
||||
|
||||
@@ -427,8 +448,9 @@ Notes:
|
||||
← body text read out directly; if too long, pass back the returned nextCursor to continue
|
||||
7. Need to confirm rendering → BrowserSnapshot({ mode: 'visual' }) (pixels readable in place)
|
||||
8. SitePatternRead({ domain: 'xiaohongshu.com' }) ← read accumulated experience
|
||||
9. At task end → BrowserManage({ action: 'close_session', sessionId })
|
||||
10. If you find a new pitfall → SitePatternWrite({ domain, scope: 'agent', mode: 'merge', content })
|
||||
9. Before reporting → one more BrowserSnapshot to confirm the page is still there (the session may have been quota-terminated)
|
||||
10. At task end → if the user still wants to look, **keep the session** and say where the page is; close only for pure extraction
|
||||
11. If you find a new pitfall → SitePatternWrite({ domain, scope: 'agent', mode: 'merge', content })
|
||||
```
|
||||
|
||||
## Site Experience Accumulation
|
||||
|
||||
Reference in New Issue
Block a user