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
|
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
|
heavy/JS-rendered pages, and (3) the governed built-in browser (isolated
|
||||||
BrowserSpace + Cookie import + bulk text extraction + waits + code mode) to
|
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 用户提到 联网搜索、上网查、
|
Use when 用户提到 联网搜索、上网查、
|
||||||
查资料、抓取网页、研究、调研、最新资讯、文档查询、对比、竞品、技术文档、
|
查资料、抓取网页、研究、调研、最新资讯、文档查询、对比、竞品、技术文档、
|
||||||
新闻、网址、URL、找一下、搜一下、查一下、小红书、B站、微博、飞书、Twitter、
|
新闻、网址、URL、找一下、搜一下、查一下、小红书、B站、微博、飞书、Twitter、
|
||||||
推特、X、知乎、公众号、已登录、登录状态。
|
推特、X、知乎、公众号、已登录、登录状态。
|
||||||
license: Complete terms in LICENSE.txt
|
license: Complete terms in LICENSE.txt
|
||||||
version: 3.2.0
|
version: 3.4.0
|
||||||
type: procedural
|
type: procedural
|
||||||
risk_level: low
|
risk_level: low
|
||||||
status: enabled
|
status: enabled
|
||||||
@@ -31,6 +31,7 @@ tags:
|
|||||||
provides:
|
provides:
|
||||||
tools:
|
tools:
|
||||||
- BrowserManage
|
- BrowserManage
|
||||||
|
- BrowserExternalProbe
|
||||||
- BrowserSnapshot
|
- BrowserSnapshot
|
||||||
- BrowserAct
|
- BrowserAct
|
||||||
- BrowserScript
|
- BrowserScript
|
||||||
@@ -41,7 +42,7 @@ provides:
|
|||||||
- LocalBookmarks
|
- LocalBookmarks
|
||||||
metadata:
|
metadata:
|
||||||
author: desirecore
|
author: desirecore
|
||||||
updated_at: '2026-08-21'
|
updated_at: '2026-08-29'
|
||||||
i18n:
|
i18n:
|
||||||
default_locale: en-US
|
default_locale: en-US
|
||||||
source_locale: zh-CN
|
source_locale: zh-CN
|
||||||
@@ -51,16 +52,16 @@ metadata:
|
|||||||
zh-CN:
|
zh-CN:
|
||||||
name: 联网访问
|
name: 联网访问
|
||||||
short_desc: 联网搜索、网页抓取、内置受管浏览器登录态访问与取文、研究调研工作流
|
short_desc: 联网搜索、网页抓取、内置受管浏览器登录态访问与取文、研究调研工作流
|
||||||
description: 联网访问工具包——搜索公开页面、Jina 优化抓取、内置受管浏览器完成登录态访问与取文,以及用户点名时接管他自己的 Chrome。
|
description: 联网访问工具包——搜索公开页面、Jina 优化抓取、内置受管浏览器完成登录态访问与取文,以及用户点名时接管他自己的 Chrome/Edge/Chromium。
|
||||||
body: ./SKILL.zh-CN.md
|
body: ./SKILL.zh-CN.md
|
||||||
source_hash: sha256:4d3bc4221b2d6b09
|
source_hash: sha256:1704b973e3a90e89
|
||||||
translated_by: human
|
translated_by: human
|
||||||
en-US:
|
en-US:
|
||||||
name: Web Access
|
name: Web Access
|
||||||
short_desc: Web search, page fetching, logged-in access via the governed built-in browser, research workflows
|
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
|
body: ./SKILL.md
|
||||||
source_hash: sha256:4d3bc4221b2d6b09
|
source_hash: sha256:1704b973e3a90e89
|
||||||
translated_by: human
|
translated_by: human
|
||||||
market:
|
market:
|
||||||
icon: >-
|
icon: >-
|
||||||
@@ -78,7 +79,7 @@ market:
|
|||||||
fill-opacity="0.12"/><path d="M20.5 20.5l2 2" stroke="#34C759"
|
fill-opacity="0.12"/><path d="M20.5 20.5l2 2" stroke="#34C759"
|
||||||
stroke-width="1.8" stroke-linecap="round"/></svg>
|
stroke-width="1.8" stroke-linecap="round"/></svg>
|
||||||
category: research
|
category: research
|
||||||
required_client_version: 10.0.98
|
required_client_version: 10.0.128
|
||||||
maintainer:
|
maintainer:
|
||||||
name: DesireCore Official
|
name: DesireCore Official
|
||||||
verified: true
|
verified: true
|
||||||
@@ -89,7 +90,7 @@ market:
|
|||||||
|
|
||||||
## L0: One-line Summary
|
## 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
|
## 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
|
- **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** (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.
|
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)
|
### 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 |
|
| Tool | Purpose |
|
||||||
|------|---------|
|
|------|---------|
|
||||||
| BrowserManage | Create/destroy isolated BrowserSpace, start sessions, manage tabs |
|
| 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 |
|
| 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, … |
|
| 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) |
|
| 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).
|
> **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
|
### 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
|
- **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
|
- **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
|
- **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
|
## 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.
|
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
|
**Only needed when taking the L3-external route.** The built-in browser has no prerequisites.
|
||||||
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**:
|
- “my Chrome” → `BrowserExternalProbe({ requestedBrowser: 'chrome' })`
|
||||||
```bash
|
- “my Edge” → `BrowserExternalProbe({ requestedBrowser: 'edge' })`
|
||||||
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
|
- “my external/system browser” without a product name → `BrowserExternalProbe({ requestedBrowser: 'any' })`
|
||||||
--remote-debugging-port=9222 \
|
- “local browser” is ambiguous → ask whether they mean the built-in browser or their external browser before probing
|
||||||
--user-data-dir="${DESIRECORE_ROOT}/chrome-profile"
|
|
||||||
```
|
|
||||||
|
|
||||||
**Linux**:
|
Handle the structured result exactly:
|
||||||
```bash
|
|
||||||
google-chrome \
|
|
||||||
--remote-debugging-port=9222 \
|
|
||||||
--user-data-dir="${DESIRECORE_ROOT}/chrome-profile"
|
|
||||||
```
|
|
||||||
|
|
||||||
**Windows (PowerShell)**:
|
| status | Required response |
|
||||||
```powershell
|
|---|---|
|
||||||
& "C:\Program Files\Google\Chrome\Application\chrome.exe" `
|
| `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 |
|
||||||
--remote-debugging-port=9222 `
|
| `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** |
|
||||||
--user-data-dir="$env:USERPROFILE\.desirecore\chrome-profile"
|
| `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:
|
`launchCommand` uses an isolated DesireCore profile. 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
|
|
||||||
|
|
||||||
### 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
|
Do not replace this probe with `curl`: a refused connection cannot distinguish “browser not installed”
|
||||||
curl -s http://localhost:9222/json/version | python3 -c "import sys,json; d=json.load(sys.stdin); print('CDP ready:', d.get('Browser'))"
|
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 部分)"
|
⚠️ When attached over CDP, **never call `browser.close()`** — that would close the user's own external browser.
|
||||||
— **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.
|
|
||||||
Only close the page you opened. Full recipes in [references/cdp-browser.md](references/cdp-browser.md).
|
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)
|
└─ "Real-time interactive task" (click, fill form, scroll, screenshot)
|
||||||
├─→ **User named "my own / my machine's / the external browser"** → L3-external:
|
├─→ **User named "my own / my machine's / the external browser"** → L3-external:
|
||||||
│ verify CDP is ready (see Prerequisites), then python3 playwright.connect_over_cdp()
|
│ BrowserExternalProbe(exact requested browser), then connect_over_cdp() only on `ready`
|
||||||
│ If it isn't ready, give them the launch command and wait — don't quietly switch to the built-in one
|
│ Otherwise follow the status guidance and wait — don't quietly switch to the built-in one
|
||||||
└─→ **Otherwise (default)**: built-in browser (BrowserManage → BrowserAct → BrowserSnapshot —
|
└─→ **Otherwise (default)**: built-in browser (BrowserManage → BrowserAct → BrowserSnapshot —
|
||||||
see references/browser-tools.md, no Python needed)
|
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 |
|
| | 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 |
|
| 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 |
|
| 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 |
|
| 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
|
> 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
|
**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
|
"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*
|
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.
|
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:**
|
**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
|
- "my own / my machine's / the external browser / my Chrome" → **L3-external**. Probe the exact
|
||||||
per Prerequisites first; if it isn't ready, give them the launch command and wait. **Do not switch
|
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**
|
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)
|
- "the built-in browser", or nothing named → **L3 built-in** (default, no prerequisites)
|
||||||
- Genuinely unclear which they mean → ask, don't guess
|
- 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 |
|
| L1 | Public, static | `WebFetch` | Low |
|
||||||
| L2 | JS-heavy, long articles, token savings | `Bash curl r.jina.ai` | **Lowest** (Markdown pre-cleaned) |
|
| 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** | **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)**.
|
**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)
|
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
|
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
|
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.
|
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
|
← 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)
|
7. Need to confirm rendering → BrowserSnapshot({ mode: 'visual' }) (pixels readable in place)
|
||||||
8. SitePatternRead({ domain: 'xiaohongshu.com' }) ← read accumulated experience
|
8. SitePatternRead({ domain: 'xiaohongshu.com' }) ← read accumulated experience
|
||||||
9. At task end → BrowserManage({ action: 'close_session', sessionId })
|
9. Before reporting → one more BrowserSnapshot to confirm the page is still there (the session may have been quota-terminated)
|
||||||
10. If you find a new pitfall → SitePatternWrite({ domain, scope: 'agent', mode: 'merge', content })
|
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
|
## Site Experience Accumulation
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
## L0:一句话摘要
|
## L0:一句话摘要
|
||||||
|
|
||||||
联网访问工具包——搜索公开页面、Jina 优化抓取、内置受管浏览器完成登录态访问与交互,以及用户点名时接管他自己的 Chrome。
|
联网访问工具包——搜索公开页面、Jina 优化抓取、内置受管浏览器完成登录态访问与交互,以及用户点名时接管他自己的 Chrome/Edge/Chromium。
|
||||||
|
|
||||||
## L1:概述与使用场景
|
## L1:概述与使用场景
|
||||||
|
|
||||||
@@ -16,17 +16,18 @@ web-access 是一个**流程型技能(Procedural Skill)**,提供四层互
|
|||||||
- **L2**(Jina Reader):JS 渲染的重页面,默认节省 Token
|
- **L2**(Jina Reader):JS 渲染的重页面,默认节省 Token
|
||||||
- **L3**(内置受管浏览器,v3.0 能力面补全):到达、操作并**读取**登录态/交互站点——每个任务独立 BrowserSpace 隔离、零 Python 依赖、每次动作都有可审计回执。批量取文(`page.extract-text`)、判别式等待(`page.wait`)、代码模式(`BrowserScript`)都在本层内闭环
|
- **L3**(内置受管浏览器,v3.0 能力面补全):到达、操作并**读取**登录态/交互站点——每个任务独立 BrowserSpace 隔离、零 Python 依赖、每次动作都有可审计回执。批量取文(`page.extract-text`)、判别式等待(`page.wait`)、代码模式(`BrowserScript`)都在本层内闭环
|
||||||
|
|
||||||
- **L3-external**(用户自己的 Chrome,经 CDP + Python Playwright 接管):**用户点名要用他自己那个浏览器时走这条**——他的登录态、他的窗口、他能全程看着并随时接管
|
- **L3-external**(用户点名的 Chrome/Edge/Chromium,经 CDP + Python Playwright 接管):**仅在用户点名,或 Agent 解释原因后用户明确同意时走这条**——DesireCore 隔离外部 Profile 中由用户手工登录形成的状态、可见窗口,以及用户随时接管的能力
|
||||||
|
|
||||||
关于 L3-external 的一段历史:v3.0 曾把它整个删掉,理由是「它存在的每一条技术理由(无批量取文通道、evaluate 不可用、截图必须串行 activate)都已被内置浏览器覆盖」。那个技术判断没错,**作为「内置浏览器不够用时的兜底」它确实不再需要**。但删除时顺带丢掉了一个完全不同的用例:用户想用**他自己那个**浏览器。这跟能力够不够无关,内置浏览器替代不了,所以 v3.2 把它作为一条**由用户意图触发**的平级选择恢复回来——注意它不再是 fallback,判据见下方「两个浏览器,按用户意图选」。
|
关于 L3-external 的一段历史:v3.0 曾把它整个删掉,理由是「它存在的每一条技术理由(无批量取文通道、evaluate 不可用、截图必须串行 activate)都已被内置浏览器覆盖」。那个技术判断没错,**作为「内置浏览器不够用时的兜底」它确实不再需要**。但删除时顺带丢掉了一个完全不同的用例:用户想用**他自己那个**浏览器。这跟能力够不够无关,内置浏览器替代不了,所以 v3.2 把它作为一条**由用户意图触发**的平级选择恢复回来——注意它不再是 fallback,判据见下方「两个浏览器,按用户意图选」。
|
||||||
|
|
||||||
### v3.0:内置受管浏览器(默认隐藏,激活后才暴露)
|
### v3.0:内置受管浏览器(默认隐藏,激活后才暴露)
|
||||||
|
|
||||||
调用 `Skill('web-access')` 加载本技能时,以下 9 个工具被注入到当前会话,让 LLM 直接驱动内置浏览器:
|
调用 `Skill('web-access')` 加载本技能时会注入以下工具。`Browser*` 工具驱动内置浏览器;`BrowserExternalProbe` 只检查外部浏览器前置条件:
|
||||||
|
|
||||||
| 工具 | 用途 |
|
| 工具 | 用途 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| BrowserManage | 建/销隔离 BrowserSpace、启动会话、管理标签页 |
|
| BrowserManage | 建/销隔离 BrowserSpace、启动会话、管理标签页 |
|
||||||
|
| BrowserExternalProbe | 只读检查 Chrome/Edge/Chromium 安装与 loopback CDP 就绪状态;绝不启动浏览器或读取 Profile |
|
||||||
| BrowserSnapshot | `semantic` / `text` / `accessibility` / `visual` 四种快照——读页面的主通道 |
|
| BrowserSnapshot | `semantic` / `text` / `accessibility` / `visual` 四种快照——读页面的主通道 |
|
||||||
| BrowserAct | 一次调用一个受管动作:导航、输入、取文、等待、元素操作、截图…… |
|
| BrowserAct | 一次调用一个受管动作:导航、输入、取文、等待、元素操作、截图…… |
|
||||||
| BrowserScript | **代码模式**:一段异步 JS 连续下发浏览器命令,消除逐动作往返(信任级别等同 Bash) |
|
| BrowserScript | **代码模式**:一段异步 JS 连续下发浏览器命令,消除逐动作往返(信任级别等同 Bash) |
|
||||||
@@ -37,7 +38,7 @@ web-access 是一个**流程型技能(Procedural Skill)**,提供四层互
|
|||||||
|
|
||||||
> **重要**:未调用 Skill('web-access') 之前,这些工具**不会**出现在 LLM 的 tools 列表里——默认对话不消耗其 token。详见 [references/browser-tools.md](references/browser-tools.md)。
|
> **重要**:未调用 Skill('web-access') 之前,这些工具**不会**出现在 LLM 的 tools 列表里——默认对话不消耗其 token。详见 [references/browser-tools.md](references/browser-tools.md)。
|
||||||
>
|
>
|
||||||
> **v2.1 已移除**:`BrowserListTabs` / `BrowserNavigate` / `BrowserEval` / `BrowserClick` / `BrowserScreenshot` / `BrowserScroll` / `BrowserSetFiles` / `BrowserCloseTab` 及其背后的 cdp-proxy 已停用,调用会返回「该旧 BrowserXxx/cdp-proxy 入口已停用」。本版要求客户端 v10.0.98+;`page.extract-text` / `page.element` / `page.wait` / 内联 wait 块 / 跨源 iframe 快照需 v10.0.112+,`BrowserScript` 需包含 S17/S18 的更新版本。
|
> **v2.1 已移除**:`BrowserListTabs` / `BrowserNavigate` / `BrowserEval` / `BrowserClick` / `BrowserScreenshot` / `BrowserScroll` / `BrowserSetFiles` / `BrowserCloseTab` 及其背后的 cdp-proxy 已停用,调用会返回「该旧 BrowserXxx/cdp-proxy 入口已停用」。本技能版本要求客户端 v10.0.128+;`page.extract-text` / `page.element` / `page.wait` / 内联 wait 块 / 跨源 iframe 快照自 v10.0.112 起可用,`BrowserScript` 需包含 S17/S18 的更新版本。
|
||||||
|
|
||||||
### 使用场景
|
### 使用场景
|
||||||
|
|
||||||
@@ -52,7 +53,7 @@ web-access 是一个**流程型技能(Procedural Skill)**,提供四层互
|
|||||||
- **分层递进**:从轻量搜索到重度 JS 渲染到登录态访问,按需选择;用户点名时还可直接用他自己的浏览器
|
- **分层递进**:从轻量搜索到重度 JS 渲染到登录态访问,按需选择;用户点名时还可直接用他自己的浏览器
|
||||||
- **Token 优化**:Jina Reader 默认减少 50-80% Token 消耗;`page.extract-text` 的 maxBytes/cursor 分页让登录态长文也可控
|
- **Token 优化**:Jina Reader 默认减少 50-80% Token 消耗;`page.extract-text` 的 maxBytes/cursor 分页让登录态长文也可控
|
||||||
- **登录态复用**:Host 授予 `browser.import.*` 时用 BrowserImport 把 Cookie 导入隔离 Space,不必重新登录
|
- **登录态复用**:Host 授予 `browser.import.*` 时用 BrowserImport 把 Cookie 导入隔离 Space,不必重新登录
|
||||||
- **默认零外部依赖**:内置浏览器不要求 Python/Playwright,也不要求用户手工启动调试 Chrome(L3-external 需要,且仅在用户点名时才用)
|
- **默认零外部依赖**:内置浏览器不要求 Python/Playwright,也不要求用户手工启动调试外部 Chromium 浏览器(L3-external 需要,且仅在明确用户意图后才用)
|
||||||
|
|
||||||
## L2:详细规范
|
## L2:详细规范
|
||||||
|
|
||||||
@@ -64,50 +65,42 @@ 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.
|
If any fetch fails, explicitly tell the user which URL failed and which fallback you used.
|
||||||
|
|
||||||
## Prerequisites: Chrome CDP Setup(仅 L3-external 需要)
|
## Prerequisites:外部浏览器 + CDP(仅 L3-external 需要)
|
||||||
|
|
||||||
**只有走 L3-external(用户点名要用他自己的浏览器)时才需要这一步。** 内置浏览器零前置条件。
|
**只有走 L3-external 时才需要。** 内置浏览器零前置条件。
|
||||||
|
|
||||||
### One-time setup
|
### 每次连接前必须先结构化探测
|
||||||
|
|
||||||
让用户带远程调试端口启动 Chrome:
|
按用户原话映射精确请求:
|
||||||
|
|
||||||
**macOS**:
|
- 「我的 Chrome」→ `BrowserExternalProbe({ requestedBrowser: 'chrome' })`
|
||||||
```bash
|
- 「我的 Edge」→ `BrowserExternalProbe({ requestedBrowser: 'edge' })`
|
||||||
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
|
- 只说「我的外部/系统浏览器」、没点产品名 → `BrowserExternalProbe({ requestedBrowser: 'any' })`
|
||||||
--remote-debugging-port=9222 \
|
- 「本地浏览器」语义两可 → 先问是内置浏览器还是用户自己的外部浏览器,再探测
|
||||||
--user-data-dir="${DESIRECORE_ROOT}/chrome-profile"
|
|
||||||
```
|
|
||||||
|
|
||||||
**Linux**:
|
严格按结构化状态处理:
|
||||||
```bash
|
|
||||||
google-chrome \
|
|
||||||
--remote-debugging-port=9222 \
|
|
||||||
--user-data-dir="${DESIRECORE_ROOT}/chrome-profile"
|
|
||||||
```
|
|
||||||
|
|
||||||
**Windows (PowerShell)**:
|
| status | 必须采取的动作 |
|
||||||
```powershell
|
|---|---|
|
||||||
& "C:\Program Files\Google\Chrome\Application\chrome.exe" `
|
| `ready` | 才能继续 Playwright `connect_over_cdp`,并如实说出检测到的外部浏览器;`any` 已有 ready 端口时,即使还安装了其他产品,也以该端口作为用户已准备的选择 |
|
||||||
--remote-debugging-port=9222 `
|
| `browser_not_installed` | 明确说未检测到用户点名的浏览器;若有 `alternatives`,询问是否改用其中之一,**绝不自动替换** |
|
||||||
--user-data-dir="$env:USERPROFILE\.desirecore\chrome-profile"
|
| `debug_port_closed` | 展示返回的 `launchCommand`,请用户启动并手工登录,然后等待并重新 probe |
|
||||||
```
|
| `browser_choice_required` | 没有 ready 端口且检测到多个外部浏览器;只列 id/name 并询问用户选哪个,再 probe 精确选择 |
|
||||||
|
| `browser_mismatch` | 说明端口上实际是什么、用户点名的是什么;让用户修正端口或明确同意改用实际浏览器 |
|
||||||
|
| `invalid_cdp_endpoint` | 说明端口虽有服务但不是合法 Chrome DevTools 端点;不得连接 |
|
||||||
|
| `host_unavailable` | 说明当前 Agent Service 无法探测用户桌面宿主;不得猜已安装浏览器,也不得静默改用内置浏览器 |
|
||||||
|
|
||||||
启动后:
|
`launchCommand` 使用 DesireCore 专属隔离 Profile。启动后:
|
||||||
1. 用户在这个 Chrome 里手工登录需要的站点
|
|
||||||
2. 这个 Chrome 窗口保持开着
|
|
||||||
3. 验证调试端点:`curl -s http://localhost:9222/json/version` 应返回 JSON
|
|
||||||
|
|
||||||
### 每次操作前先验就绪
|
1. 用户在该外部浏览器里手工登录所需站点。
|
||||||
|
2. 外部浏览器窗口保持打开。
|
||||||
|
3. 再调用一次 `BrowserExternalProbe`;只有 `ready` 才允许尝试 CDP attach。
|
||||||
|
|
||||||
```bash
|
不得用 `curl` 替代本探测:连接拒绝无法区分「未安装浏览器」与「已安装但没开调试」,普通 HTTP 服务也不能冒充 CDP。
|
||||||
curl -s http://localhost:9222/json/version | python3 -c "import sys,json; d=json.load(sys.stdin); print('CDP ready:', d.get('Browser'))"
|
|
||||||
```
|
|
||||||
|
|
||||||
失败就告诉用户:「请先启动 Chrome 并开启远程调试端口(见 web-access 技能的 Prerequisites 部分)」,
|
必须按上表的结构化状态提示用户并等待;不要因为内置浏览器也能做就擅自改用内置的。
|
||||||
**然后等他**——不要因为内置浏览器也能做就擅自改用内置的。
|
|
||||||
|
|
||||||
⚠️ 用 CDP attach 时**绝不能调 `browser.close()`**,那会关掉用户自己的 Chrome;只关你开的 page。
|
⚠️ 用 CDP attach 时**绝不能调 `browser.close()`**,那会关掉用户自己的外部浏览器;只关你开的 page。
|
||||||
完整配方见 [references/cdp-browser.md](references/cdp-browser.md)。
|
完整配方见 [references/cdp-browser.md](references/cdp-browser.md)。
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -143,8 +136,8 @@ User intent
|
|||||||
│
|
│
|
||||||
└─ "Real-time interactive task" (click, fill form, scroll, screenshot)
|
└─ "Real-time interactive task" (click, fill form, scroll, screenshot)
|
||||||
├─→ **用户点名「我本机的 / 我自己的 / 外部的浏览器」** → L3-external:
|
├─→ **用户点名「我本机的 / 我自己的 / 外部的浏览器」** → L3-external:
|
||||||
│ 先验 CDP 就绪(见 Prerequisites),再 python3 playwright.connect_over_cdp()
|
│ BrowserExternalProbe(精确点名的浏览器),仅 `ready` 后 connect_over_cdp()
|
||||||
│ 没就绪就给启动命令并等他,不要擅自改用内置浏览器
|
│ 其余状态按表处理并等待,不要擅自改用内置浏览器
|
||||||
└─→ **其余情况(默认)**:内置受管浏览器 (BrowserManage → BrowserAct → BrowserSnapshot —
|
└─→ **其余情况(默认)**:内置受管浏览器 (BrowserManage → BrowserAct → BrowserSnapshot —
|
||||||
see references/browser-tools.md, no Python needed)
|
see references/browser-tools.md, no Python needed)
|
||||||
```
|
```
|
||||||
@@ -155,10 +148,10 @@ DesireCore 能驱动**两个**浏览器,它们是平级的选项:
|
|||||||
|
|
||||||
| | L3 内置受管浏览器 | L3-external 用户自己的浏览器 |
|
| | L3 内置受管浏览器 | L3-external 用户自己的浏览器 |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| 是什么 | 应用内的浏览器实例(`Browser*` 工具族) | 用户机器上装的 Chrome,经 CDP + Python Playwright 接管 |
|
| 是什么 | 应用内的浏览器实例(`Browser*` 工具族) | 用户点名的 Chrome/Edge/Chromium,经 CDP + Python Playwright 接管 |
|
||||||
| 登录态 | 独立隔离;需 Host 授予 `browser.import.*` 才能用 `BrowserImport` 导 Cookie | **就是用户本人的登录态**,无需导入 |
|
| 登录态 | 独立隔离;需 Host 授予 `browser.import.*` 才能用 `BrowserImport` 导 Cookie | **就是用户本人的登录态**,无需导入 |
|
||||||
| 用户能看到吗 | Agent 开的标签页默认离屏,需展示到工作台 | **就在用户自己的窗口里**,他能全程看着、随时接管 |
|
| 用户能看到吗 | Agent 开的标签页默认离屏,需展示到工作台 | **就在用户自己的窗口里**,他能全程看着、随时接管 |
|
||||||
| 前置条件 | 无 | 用户需先带 `--remote-debugging-port=9222` 启动 Chrome(见 Prerequisites) |
|
| 前置条件 | 无 | `BrowserExternalProbe` 必须返回 `ready`;否则严格按结构化状态处理 |
|
||||||
| 默认 | ✅ 是 | 用户点名时 |
|
| 默认 | ✅ 是 | 用户点名时 |
|
||||||
|
|
||||||
> 登录态那一栏容易读成「内置浏览器复用不了用户的登录态」——不是那个意思。准确说法是
|
> 登录态那一栏容易读成「内置浏览器复用不了用户的登录态」——不是那个意思。准确说法是
|
||||||
@@ -169,13 +162,13 @@ DesireCore 能驱动**两个**浏览器,它们是平级的选项:
|
|||||||
|
|
||||||
**选层判据是用户意图,不是技术难度。** v3.0 把这一层当作「内置浏览器不够用时的兜底」删掉过,
|
**选层判据是用户意图,不是技术难度。** v3.0 把这一层当作「内置浏览器不够用时的兜底」删掉过,
|
||||||
那个技术判断本身没错(取文、evaluate、截图这些内置浏览器现在都能做),但它顺带删掉的是一个
|
那个技术判断本身没错(取文、evaluate、截图这些内置浏览器现在都能做),但它顺带删掉的是一个
|
||||||
**完全不同的用例**:用户想用**他自己那个**浏览器。那跟能力够不够无关——他的登录态在他自己的
|
**完全不同的用例**:用户想用**他自己那个**浏览器。那跟能力够不够无关——他在点名的外部浏览器中
|
||||||
Chrome 里,他想亲眼看着操作、随时接管。这个需求内置浏览器替代不了。
|
建立登录态,想亲眼看着操作、随时接管。这个需求内置浏览器替代不了。
|
||||||
|
|
||||||
**用户点名了就按点名的来:**
|
**用户点名了就按点名的来:**
|
||||||
|
|
||||||
- 说「我本机的 / 我自己的 / 外部浏览器 / 我的 Chrome」→ 走 **L3-external**。先按
|
- 说「我本机的 / 我自己的 / 外部浏览器 / 我的 Chrome」→ 走 **L3-external**。先 probe
|
||||||
Prerequisites 验 CDP 就绪;没就绪就告诉他启动命令并等他,**不要因为「内置浏览器也能做」
|
用户精确点名的浏览器;没就绪就按结构化状态处理并等待,**不要因为「内置浏览器也能做」
|
||||||
就擅自改用内置的**
|
就擅自改用内置的**
|
||||||
- 说「内置浏览器」或没点名 → 走 **L3 内置浏览器**(默认,零前置条件)
|
- 说「内置浏览器」或没点名 → 走 **L3 内置浏览器**(默认,零前置条件)
|
||||||
- 拿不准他指哪个 → 问一句,别猜
|
- 拿不准他指哪个 → 问一句,别猜
|
||||||
@@ -190,7 +183,7 @@ Chrome 里,他想亲眼看着操作、随时接管。这个需求内置浏览
|
|||||||
| L1 | Public, static | `WebFetch` | Low |
|
| L1 | Public, static | `WebFetch` | Low |
|
||||||
| L2 | JS-heavy, long articles, token savings | `Bash curl r.jina.ai` | **Lowest** (Markdown pre-cleaned) |
|
| L2 | JS-heavy, long articles, token savings | `Bash curl r.jina.ai` | **Lowest** (Markdown pre-cleaned) |
|
||||||
| **L3** | **登录态导航、交互与取文 (PRIMARY)** | **内置受管浏览器(BrowserManage / BrowserAct / BrowserSnapshot / BrowserScript)** | Medium |
|
| **L3** | **登录态导航、交互与取文 (PRIMARY)** | **内置受管浏览器(BrowserManage / BrowserAct / BrowserSnapshot / BrowserScript)** | Medium |
|
||||||
| L3-external | **用户点名要用他自己的浏览器**;或需要他本人的登录态而 `BrowserImport` 不可用 | `Bash + Python Playwright connect_over_cdp`(见 references/cdp-browser.md) | Medium |
|
| L3-external | **用户点名要用他自己的浏览器**,或 Agent 解释原因后用户明确同意改走此路径 | `BrowserExternalProbe` → `Bash + Python Playwright connect_over_cdp`(见 references/cdp-browser.md) | Medium |
|
||||||
|
|
||||||
**Default priority**: L1 for simple public pages → L2 for heavy → **L3 for login-gated(含正文与站内接口取数)**。
|
**Default priority**: L1 for simple public pages → L2 for heavy → **L3 for login-gated(含正文与站内接口取数)**。
|
||||||
|
|
||||||
@@ -264,10 +257,36 @@ See [references/jina-reader.md](references/jina-reader.md) for advanced endpoint
|
|||||||
4. `BrowserSnapshot(semantic)` 拿可交互元素 `ref`(跨源 iframe 的元素也在同一棵树里,ref 全局连续编号)
|
4. `BrowserSnapshot(semantic)` 拿可交互元素 `ref`(跨源 iframe 的元素也在同一棵树里,ref 全局连续编号)
|
||||||
5. 交互用 `input.*`(拟真轨迹)或 `page.element`(表单批量写);等结果用 `page.wait` 或内联 wait 块
|
5. 交互用 `input.*`(拟真轨迹)或 `page.element`(表单批量写);等结果用 `page.wait` 或内联 wait 块
|
||||||
6. 取正文用 `BrowserSnapshot(text)` 或 `BrowserAct(page.extract-text)`;取接口数据用 fetch.browser 配方
|
6. 取正文用 `BrowserSnapshot(text)` 或 `BrowserAct(page.extract-text)`;取接口数据用 fetch.browser 配方
|
||||||
7. 任务收尾 `BrowserManage(close_session)`
|
7. 收尾:**先确认、再决定关不关**,见下方「收尾:汇报之前与关闭之前」
|
||||||
|
|
||||||
多动作连续编排(导航→快照→点击→等待→取文)可用 `BrowserScript` 一段脚本完成,省去逐动作 IPC 往返。
|
多动作连续编排(导航→快照→点击→等待→取文)可用 `BrowserScript` 一段脚本完成,省去逐动作 IPC 往返。
|
||||||
|
|
||||||
|
### 收尾:汇报之前与关闭之前
|
||||||
|
|
||||||
|
**一、向用户汇报浏览器结果之前,先用一次快照确认页面仍在。**
|
||||||
|
|
||||||
|
不要拿上一次成功的导航结果直接汇报。会话可能在那之后被系统终止——最常见的是页面持续占用
|
||||||
|
过多资源触发配额保护(`BROWSER_RESOURCE_QUOTA_EXCEEDED`),重 JS 站点很容易命中。真机发生过:
|
||||||
|
Agent 导航成功后直接汇报「✅ 已打开,页面已可在内置浏览器面板中查看」,而会话早已 crashed,
|
||||||
|
用户看到的是一片空白。
|
||||||
|
|
||||||
|
汇报前补一次 `BrowserSnapshot`(`text` 或 `visual` 均可)即可暴露这类情况:会话若已终止,
|
||||||
|
工具会返回 `BROWSER_TOOL_SESSION_TERMINATED` 并说明原因。此时**如实告诉用户会话中断了**,
|
||||||
|
再决定是重试、换更轻量的页面,还是交由用户处理——绝不能把先前的成功当作现状。
|
||||||
|
|
||||||
|
**二、不要无条件 `close_session`。判据是「用户还需不需要看」。**
|
||||||
|
|
||||||
|
| 任务性质 | 收尾 |
|
||||||
|
|---|---|
|
||||||
|
| 演示 / 交互 / 用户要看结果 | **保留会话**,并告诉用户页面停在哪、可以直接接管 |
|
||||||
|
| 纯数据抓取,正文已取回 | 关闭,释放资源 |
|
||||||
|
| 用户明确说「用完关掉」 | 关闭 |
|
||||||
|
|
||||||
|
内置浏览器的画面要展示给用户,靠的是工作台把会话呈现出来;会话一关,用户就什么都看不到了。
|
||||||
|
真机发生过:Agent 用两分钟做完演示随即 `close_session`,用户回头去看只剩空白面板——它做的事
|
||||||
|
没错,只是没留给用户任何查看的机会。拿不准时**保留**:留着最多占一点资源,关早了用户就得
|
||||||
|
从头再来一遍。
|
||||||
|
|
||||||
## L3 速查(v3.0)
|
## L3 速查(v3.0)
|
||||||
|
|
||||||
### 读页面:按需求选通道
|
### 读页面:按需求选通道
|
||||||
@@ -336,8 +355,9 @@ BrowserAct:
|
|||||||
← 正文直接读出;太长就传上一页返回的 nextCursor 续读
|
← 正文直接读出;太长就传上一页返回的 nextCursor 续读
|
||||||
7. 需要确认渲染效果 → BrowserSnapshot({ mode: 'visual' })(像素直接可看)
|
7. 需要确认渲染效果 → BrowserSnapshot({ mode: 'visual' })(像素直接可看)
|
||||||
8. SitePatternRead({ domain: 'xiaohongshu.com' }) ← 读累积经验
|
8. SitePatternRead({ domain: 'xiaohongshu.com' }) ← 读累积经验
|
||||||
9. 任务结束 → BrowserManage({ action: 'close_session', sessionId })
|
9. 汇报前 → 再来一次 BrowserSnapshot 确认页面仍在(会话可能已被配额终止)
|
||||||
10. 如发现新陷阱 → SitePatternWrite({ domain, scope: 'agent', mode: 'merge', content })
|
10. 任务结束 → 用户还要看就**保留会话**并告知页面位置;纯抓取才 close_session
|
||||||
|
11. 如发现新陷阱 → SitePatternWrite({ domain, scope: 'agent', mode: 'merge', content })
|
||||||
```
|
```
|
||||||
|
|
||||||
## 站点经验积累
|
## 站点经验积累
|
||||||
|
|||||||
@@ -1,8 +1,19 @@
|
|||||||
# CDP Browser Access — Login-Gated Sites Manual
|
# CDP Browser Access — Login-Gated Sites Manual
|
||||||
|
|
||||||
Detailed recipes for accessing sites that require the user's login session, via Chrome DevTools Protocol (CDP) + Python Playwright.
|
Detailed recipes for accessing sites through the user-approved external Chromium browser, via Chrome DevTools Protocol (CDP) + Python Playwright.
|
||||||
|
|
||||||
**Precondition**: Chrome is already running with `--remote-debugging-port=9222` and the user has manually logged in to the target sites. See the main SKILL.md `Prerequisites` section for the launch command.
|
**Precondition**: `BrowserExternalProbe` has returned `ready` for the exact browser the user requested, that browser is running with its DesireCore-isolated profile, and the user has manually logged in to the target sites. Never infer readiness from a failed/successful `curl`; see the main SKILL.md status table.
|
||||||
|
|
||||||
|
The probe distinguishes these cases before Playwright is involved:
|
||||||
|
|
||||||
|
- requested browser not installed (`browser_not_installed`)
|
||||||
|
- browser installed but debug port closed (`debug_port_closed`)
|
||||||
|
- multiple browsers installed with no ready endpoint (`browser_choice_required`)
|
||||||
|
- a different browser owns the port (`browser_mismatch`)
|
||||||
|
- a non-CDP service owns the port (`invalid_cdp_endpoint`)
|
||||||
|
- desktop host cannot be inspected (`host_unavailable`)
|
||||||
|
|
||||||
|
Only `ready` permits `connect_over_cdp`. An alternative browser is a suggestion requiring user approval, never an automatic fallback.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -12,24 +23,34 @@ Detailed recipes for accessing sites that require the user's login session, via
|
|||||||
|----------|-------------|----------|-------|------|
|
|----------|-------------|----------|-------|------|
|
||||||
| Headless Playwright (new context) | ❌ Empty cookies | ❌ Flagged as bot | Slow cold start | Re-login pain |
|
| Headless Playwright (new context) | ❌ Empty cookies | ❌ Flagged as bot | Slow cold start | Re-login pain |
|
||||||
| `playwright.chromium.launch(headless=False)` | ❌ Fresh profile | ⚠ Sometimes flagged | Slow | Same |
|
| `playwright.chromium.launch(headless=False)` | ❌ Fresh profile | ⚠ Sometimes flagged | Slow | Same |
|
||||||
| **CDP attach (`connect_over_cdp`)** | ✅ User's real cookies | ✅ Looks human | Instant | Zero friction |
|
| **CDP attach (`connect_over_cdp`)** | ✅ Cookies from the DesireCore-isolated external profile where the user logged in manually | ✅ Looks human | Instant | Zero friction |
|
||||||
|
|
||||||
**Rule**: For any login-gated site, always attach to the user's running Chrome.
|
**Rule**: Attach only when the user named the external browser or explicitly accepted this route after
|
||||||
|
you explained why. BrowserImport being unavailable does not itself authorize switching to the user's
|
||||||
|
external browser.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Core Template
|
## Core Template
|
||||||
|
|
||||||
Every CDP script follows this shape:
|
Every CDP script follows this shape. `PROBE_PORT` must be the numeric `port` from the latest `ready`
|
||||||
|
`BrowserExternalProbe` result; never silently fall back to 9222 after probing another port.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from playwright.sync_api import sync_playwright
|
from playwright.sync_api import sync_playwright
|
||||||
|
|
||||||
def fetch_with_cdp(url: str, wait_selector: str | None = None) -> str:
|
PROBE_PORT: int | None = None # Assign the exact BrowserExternalProbe ready result port.
|
||||||
"""Attach to user's Chrome via CDP, fetch URL, return HTML."""
|
|
||||||
|
def cdp_url(port: int | None) -> str:
|
||||||
|
if not isinstance(port, int) or not 1 <= port <= 65535:
|
||||||
|
raise ValueError("invalid CDP port")
|
||||||
|
return f"http://127.0.0.1:{port}"
|
||||||
|
|
||||||
|
def fetch_with_cdp(url: str, cdp_port: int, wait_selector: str | None = None) -> str:
|
||||||
|
"""Attach to the user-approved external Chromium profile, fetch URL, return HTML."""
|
||||||
with sync_playwright() as p:
|
with sync_playwright() as p:
|
||||||
browser = p.chromium.connect_over_cdp("http://localhost:9222")
|
browser = p.chromium.connect_over_cdp(cdp_url(cdp_port))
|
||||||
# browser.contexts[0] is the user's default context (with cookies)
|
# contexts[0] is the DesireCore-isolated external profile where the user logged in manually.
|
||||||
context = browser.contexts[0]
|
context = browser.contexts[0]
|
||||||
page = context.new_page()
|
page = context.new_page()
|
||||||
try:
|
try:
|
||||||
@@ -41,14 +62,18 @@ def fetch_with_cdp(url: str, wait_selector: str | None = None) -> str:
|
|||||||
return page.content()
|
return page.content()
|
||||||
finally:
|
finally:
|
||||||
page.close()
|
page.close()
|
||||||
# DO NOT call browser.close() — that would close the user's Chrome!
|
# DO NOT call browser.close() — that would close the user's external browser!
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
html = fetch_with_cdp("https://example.com")
|
if PROBE_PORT is None:
|
||||||
|
raise RuntimeError(
|
||||||
|
"Run BrowserExternalProbe first and assign the port from its ready result to PROBE_PORT."
|
||||||
|
)
|
||||||
|
html = fetch_with_cdp("https://example.com", PROBE_PORT)
|
||||||
print(html[:1000])
|
print(html[:1000])
|
||||||
```
|
```
|
||||||
|
|
||||||
**Critical**: Never call `browser.close()` when using CDP attach — you'd kill the user's Chrome. Only close the page you opened.
|
**Critical**: Never call `browser.close()` when using CDP attach — you'd kill the user's external browser. Only close the page you opened.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -63,7 +88,7 @@ from bs4 import BeautifulSoup
|
|||||||
NOTE_URL = "https://www.xiaohongshu.com/explore/XXXXXXXX"
|
NOTE_URL = "https://www.xiaohongshu.com/explore/XXXXXXXX"
|
||||||
|
|
||||||
with sync_playwright() as p:
|
with sync_playwright() as p:
|
||||||
browser = p.chromium.connect_over_cdp("http://localhost:9222")
|
browser = p.chromium.connect_over_cdp(cdp_url(PROBE_PORT))
|
||||||
page = browser.contexts[0].new_page()
|
page = browser.contexts[0].new_page()
|
||||||
page.goto(NOTE_URL, wait_until="domcontentloaded")
|
page.goto(NOTE_URL, wait_until="domcontentloaded")
|
||||||
page.wait_for_selector("#detail-title", timeout=10000)
|
page.wait_for_selector("#detail-title", timeout=10000)
|
||||||
@@ -96,7 +121,7 @@ from bs4 import BeautifulSoup
|
|||||||
VIDEO_URL = "https://www.bilibili.com/video/BVxxxxxxxxx"
|
VIDEO_URL = "https://www.bilibili.com/video/BVxxxxxxxxx"
|
||||||
|
|
||||||
with sync_playwright() as p:
|
with sync_playwright() as p:
|
||||||
browser = p.chromium.connect_over_cdp("http://localhost:9222")
|
browser = p.chromium.connect_over_cdp(cdp_url(PROBE_PORT))
|
||||||
page = browser.contexts[0].new_page()
|
page = browser.contexts[0].new_page()
|
||||||
page.goto(VIDEO_URL, wait_until="networkidle")
|
page.goto(VIDEO_URL, wait_until="networkidle")
|
||||||
page.wait_for_timeout(2000)
|
page.wait_for_timeout(2000)
|
||||||
@@ -109,7 +134,7 @@ print("UP:", soup.select_one(".up-name").get_text(strip=True) if soup.select_
|
|||||||
print("Desc:", soup.select_one(".desc-info-text").get_text(" ", strip=True) if soup.select_one(".desc-info-text") else None)
|
print("Desc:", soup.select_one(".desc-info-text").get_text(" ", strip=True) if soup.select_one(".desc-info-text") else None)
|
||||||
```
|
```
|
||||||
|
|
||||||
**Tip**: For B站 evaluations, the [公开 API](https://api.bilibili.com/x/web-interface/view?bvid=XXXX) often returns JSON without needing CDP. Try it first:
|
**Tip**: When the user did not require a specific browser, the [公开 API](https://api.bilibili.com/x/web-interface/view?bvid=XXXX) often returns JSON without needing CDP. Try it first. For an explicit external-browser request, ask before replacing that route with the API.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -s "https://api.bilibili.com/x/web-interface/view?bvid=BVxxxxxxxxx" | python3 -m json.tool
|
curl -s "https://api.bilibili.com/x/web-interface/view?bvid=BVxxxxxxxxx" | python3 -m json.tool
|
||||||
@@ -127,10 +152,10 @@ WEIBO_URL = "https://weibo.com/u/1234567890" # or /detail/xxx
|
|||||||
# article[aria-label="微博"] — each feed item
|
# article[aria-label="微博"] — each feed item
|
||||||
```
|
```
|
||||||
|
|
||||||
**Note**: Weibo uses React + heavy obfuscation. Selectors change frequently. If selectors fail, pipe the HTML through Jina for clean Markdown:
|
**Note**: Weibo uses React + heavy obfuscation. If the user did not require a specific browser and selectors fail, Jina can clean the page. For an explicit external-browser request, ask before changing the execution route:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
html = fetch_with_cdp(WEIBO_URL)
|
html = fetch_with_cdp(WEIBO_URL, PROBE_PORT)
|
||||||
# Save to temp file, then:
|
# Save to temp file, then:
|
||||||
import subprocess
|
import subprocess
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
@@ -157,7 +182,7 @@ Zhihu works with CDP but often also renders enough metadata server-side for Jina
|
|||||||
curl -sL "https://r.jina.ai/https://www.zhihu.com/question/123/answer/456"
|
curl -sL "https://r.jina.ai/https://www.zhihu.com/question/123/answer/456"
|
||||||
```
|
```
|
||||||
|
|
||||||
Try Jina first, fall back to CDP if content is truncated.
|
When no browser was specified, try Jina first and fall back to the built-in browser if content is truncated. For an explicit external-browser request, do not replace that route without user approval.
|
||||||
|
|
||||||
### 飞书文档 (feishu.cn / larksuite.com)
|
### 飞书文档 (feishu.cn / larksuite.com)
|
||||||
|
|
||||||
@@ -170,7 +195,7 @@ DOC_URL = "https://xxx.feishu.cn/docs/xxx"
|
|||||||
from playwright.sync_api import sync_playwright
|
from playwright.sync_api import sync_playwright
|
||||||
|
|
||||||
with sync_playwright() as p:
|
with sync_playwright() as p:
|
||||||
browser = p.chromium.connect_over_cdp("http://localhost:9222")
|
browser = p.chromium.connect_over_cdp(cdp_url(PROBE_PORT))
|
||||||
page = browser.contexts[0].new_page()
|
page = browser.contexts[0].new_page()
|
||||||
page.goto(DOC_URL, wait_until="domcontentloaded")
|
page.goto(DOC_URL, wait_until="domcontentloaded")
|
||||||
page.wait_for_selector(".docs-render-unit", timeout=15000)
|
page.wait_for_selector(".docs-render-unit", timeout=15000)
|
||||||
@@ -259,7 +284,8 @@ page.wait_for_load_state("networkidle")
|
|||||||
|
|
||||||
### Pattern 5: Clean HTML via Jina after extraction
|
### Pattern 5: Clean HTML via Jina after extraction
|
||||||
|
|
||||||
When selectors are unreliable, dump the full page HTML and let Jina do the cleaning:
|
When no browser was specified and selectors are unreliable, dump the full page HTML and let Jina do
|
||||||
|
the cleaning. For an explicit external-browser request, ask before replacing that route:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
html = page.content()
|
html = page.content()
|
||||||
@@ -276,27 +302,35 @@ print(clean_md)
|
|||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
### `connect_over_cdp` fails with `ECONNREFUSED`
|
### `connect_over_cdp` fails after a `ready` probe
|
||||||
|
|
||||||
Chrome is not running with remote debugging. Tell the user:
|
Do not guess that the browser is merely closed. Call `BrowserExternalProbe` again:
|
||||||
> "请先用下面的命令启动 Chrome:
|
|
||||||
> `/Applications/Google\\ Chrome.app/Contents/MacOS/Google\\ Chrome --remote-debugging-port=9222 --user-data-dir=\"${DESIRECORE_ROOT}/chrome-profile\"`
|
- `debug_port_closed` → show its current `launchCommand` and wait for the user
|
||||||
> 然后手动登录需要抓取的网站,再让我继续。"
|
- `browser_mismatch` → report the actual/requested products and ask the user to correct or approve the change
|
||||||
|
- `browser_not_installed` → report that exact installation fact; alternatives require explicit approval
|
||||||
|
- `invalid_cdp_endpoint` → tell the user the port is not a valid CDP endpoint
|
||||||
|
- still `ready` → report the Playwright attach failure separately; do not switch to the built-in browser
|
||||||
|
|
||||||
### `browser.contexts[0]` is empty
|
### `browser.contexts[0]` is empty
|
||||||
|
|
||||||
Chrome was launched but no windows are open. Ask the user to open at least one tab and navigate anywhere.
|
The approved external Chromium browser is running but no windows are open. Ask the user to open at least one tab and navigate anywhere.
|
||||||
|
|
||||||
### Playwright not installed
|
### Playwright not installed
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip3 install playwright beautifulsoup4
|
python3 -m venv "${DESIRECORE_ROOT}/runtime/external-browser-playwright"
|
||||||
# No need for `playwright install` — we're attaching to existing Chrome, not downloading a new browser
|
"${DESIRECORE_ROOT}/runtime/external-browser-playwright/bin/pip" install 'playwright==1.55.0' beautifulsoup4
|
||||||
|
# No need for `playwright install` — we're attaching to an existing browser, not downloading one
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Keep the environment isolated to DesireCore; do not install Playwright globally. A missing Playwright
|
||||||
|
dependency does not change a `ready` browser/CDP result and never authorizes fallback to BrowserManage.
|
||||||
|
|
||||||
### Site detects automation
|
### Site detects automation
|
||||||
|
|
||||||
Despite CDP attach, some sites (Cloudflare-protected, Instagram) may still detect automation. Options:
|
Despite CDP attach, some sites (Cloudflare-protected, Instagram) may still detect automation. If the
|
||||||
|
user explicitly requested their external browser, ask before changing routes. Otherwise, options are:
|
||||||
1. Use Jina Reader instead (`curl -sL https://r.jina.ai/<url>`) — often succeeds where Playwright fails
|
1. Use Jina Reader instead (`curl -sL https://r.jina.ai/<url>`) — often succeeds where Playwright fails
|
||||||
2. Ask the user to manually copy the visible content
|
2. Ask the user to manually copy the visible content
|
||||||
3. Use the site's public API if available
|
3. Use the site's public API if available
|
||||||
@@ -307,7 +341,9 @@ The page uses virtualization or lazy loading. Apply Pattern 1 (scroll to bottom)
|
|||||||
|
|
||||||
### `page.wait_for_selector` times out
|
### `page.wait_for_selector` times out
|
||||||
|
|
||||||
The selector is stale — the site updated its DOM. Dump `page.content()[:5000]` and inspect manually, or fall back to Jina Reader.
|
The selector is stale — the site updated its DOM. Dump `page.content()[:5000]` and inspect manually.
|
||||||
|
Only fall back to Jina Reader when no browser was specified; for an explicit external-browser request,
|
||||||
|
ask before changing the execution route.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -316,8 +352,8 @@ The selector is stale — the site updated its DOM. Dump `page.content()[:5000]`
|
|||||||
- **Never log or print cookies** from `context.cookies()` even during debugging
|
- **Never log or print cookies** from `context.cookies()` even during debugging
|
||||||
- **Never extract and store** the user's session tokens to files
|
- **Never extract and store** the user's session tokens to files
|
||||||
- **Never use the CDP session** to perform writes (post, comment, like) unless the user explicitly requested it
|
- **Never use the CDP session** to perform writes (post, comment, like) unless the user explicitly requested it
|
||||||
- The `${DESIRECORE_ROOT}/chrome-profile` directory contains the user's credentials — treat it as sensitive
|
- `${DESIRECORE_ROOT}/browser/external-profiles/<browser-id>` contains the manually established isolated login state — treat it as sensitive
|
||||||
- If the user asks to "log in automatically", refuse and explain they must log in manually in the Chrome window; the skill only reads already-authenticated sessions
|
- If the user asks to "log in automatically", refuse and explain they must log in manually in the approved external browser window; the skill only reads already-authenticated sessions
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user