fix(web-access): 修复 Windows CDP attach 配方 (#95)

## 中文

- 将 web-access 升级到 v3.4.1
- Windows attach 明确禁止把 POSIX heredoc/`/tmp` 交给 PowerShell
- 增加 Playwright import 预检、DesireCore 隔离 venv 和 PowerShell here-string
写脚本配方
- 保持 ready 与依赖缺失分离,禁止回落内置浏览器

## English

- Bump web-access to v3.4.1
- Forbid POSIX heredocs and `/tmp` paths in Windows PowerShell attach
flows
- Add Playwright import preflight, DesireCore-isolated venv guidance,
and a native PowerShell here-string recipe
- Keep browser readiness separate from dependency availability and never
fall back silently

## Validation

- `test_validate_i18n.py`: 9/9
- `validate-i18n.py skills/web-access`: pass
- `translate.py --check skills/web-access`: pass
- Windows live-device observation: probe reached Chrome ready, then the
old recipe attempted Bash `/tmp` and PowerShell `cat <<EOF`; this PR
fixes that deterministic cross-shell failure.
This commit is contained in:
2026-08-29 16:40:38 +08:00
committed by GitHub
parent 2ccd176dad
commit ceeada3625
3 changed files with 116 additions and 16 deletions

View File

@@ -15,7 +15,7 @@ description: >-
新闻、网址、URL、找一下、搜一下、查一下、小红书、B站、微博、飞书、Twitter、 新闻、网址、URL、找一下、搜一下、查一下、小红书、B站、微博、飞书、Twitter、
推特、X、知乎、公众号、已登录、登录状态。 推特、X、知乎、公众号、已登录、登录状态。
license: Complete terms in LICENSE.txt license: Complete terms in LICENSE.txt
version: 3.4.0 version: 3.4.1
type: procedural type: procedural
risk_level: low risk_level: low
status: enabled status: enabled
@@ -54,14 +54,14 @@ metadata:
short_desc: 联网搜索、网页抓取、内置受管浏览器登录态访问与取文、研究调研工作流 short_desc: 联网搜索、网页抓取、内置受管浏览器登录态访问与取文、研究调研工作流
description: 联网访问工具包——搜索公开页面、Jina 优化抓取、内置受管浏览器完成登录态访问与取文,以及用户点名时接管他自己的 Chrome/Edge/Chromium。 description: 联网访问工具包——搜索公开页面、Jina 优化抓取、内置受管浏览器完成登录态访问与取文,以及用户点名时接管他自己的 Chrome/Edge/Chromium。
body: ./SKILL.zh-CN.md body: ./SKILL.zh-CN.md
source_hash: sha256:1704b973e3a90e89 source_hash: sha256:efcfd466dd6026a0
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 named Chrome/Edge/Chromium 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:1704b973e3a90e89 source_hash: sha256:efcfd466dd6026a0
translated_by: human translated_by: human
market: market:
icon: >- icon: >-
@@ -188,16 +188,46 @@ from “browser installed but debugging disabled”, and a random HTTP service m
⚠️ When attached over CDP, **never call `browser.close()`** — that would close the user's own external browser. ⚠️ 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). Only close the page you opened. Full recipes in [references/cdp-browser.md](references/cdp-browser.md).
### Platform-safe Playwright execution
After a `ready` probe, identify the current OS **before** creating or running an attach script:
1. Check whether the selected Python interpreter can import Playwright. A missing dependency is
separate from browser readiness: report it explicitly and do not attach yet.
2. Keep Playwright in a DesireCore-owned isolated virtual environment. Never install it globally and
never run `playwright install`; CDP attach reuses the browser that is already running.
3. On Unix-like hosts, use Bash paths and shell syntax. On Windows, use the `PowerShell` tool and a
PowerShell here-string plus `[IO.File]::WriteAllText(...)` to create the temporary `.py` file.
**Never send `cat <<EOF`, `/tmp/...`, or another POSIX heredoc to PowerShell.**
4. Invoke the virtual environment's platform-specific Python (`bin/python` on Unix,
`Scripts\python.exe` on Windows), remove only the temporary script you created, and leave the
external browser running.
If the platform cannot be determined or no isolated runtime can be created safely, stop and report
the missing prerequisite. Do not improvise a cross-shell command and do not fall back to BrowserManage.
--- ---
## Tool Selection Decision Tree ## Tool Selection Decision Tree
``` ```
User intent User intent
├─ **Any request that names "my own / my machine's / external Chrome/Edge/Chromium"**
│ └─→ L3-external first, whether the verb is search/read/open/click:
│ BrowserExternalProbe(exact requested browser), then connect_over_cdp() only on `ready`
│ Otherwise follow the status guidance and wait; never route to WebSearch/WebFetch/Jina/built-in browser
├─ "Local browser" without saying built-in or external
│ └─→ Ask which browser identity the user means before selecting any route or tool
├─ "Search for information about X" (no specific URL) ├─ "Search for information about X" (no specific URL)
│ └─→ WebSearch → pick top 3-5 results → fetch each (see next branches) │ └─→ WebSearch → pick top 3-5 results → fetch each (see next branches)
├─ "Open / go to this URL" (no external/own-browser qualifier)
│ └─→ BrowserManage(create_space/start_session) → BrowserAct(tab.navigate)
│ Keep the built-in browser visible. "Open" is an interaction request, not a synonym for WebFetch.
├─ "Read this public page" (static HTML, docs, news) ├─ "Read this public page" (static HTML, docs, news)
│ └─→ WebFetch(url) directly │ └─→ WebFetch(url) directly
@@ -219,11 +249,8 @@ User intent
│ - npm: curl https://registry.npmjs.org/<pkg> │ - npm: curl https://registry.npmjs.org/<pkg>
│ - PyPI: curl https://pypi.org/pypi/<pkg>/json │ - PyPI: curl https://pypi.org/pypi/<pkg>/json
└─ "Real-time interactive task" (click, fill form, scroll, screenshot) └─ "Real-time interactive task" (click, fill form, scroll, screenshot; no external qualifier)
─→ **User named "my own / my machine's / the external browser"** → L3-external: ─→ Built-in browser by default (BrowserManage → BrowserAct → BrowserSnapshot —
│ 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) see references/browser-tools.md, no Python needed)
``` ```

View File

@@ -103,16 +103,40 @@ If any fetch fails, explicitly tell the user which URL failed and which fallback
⚠️ 用 CDP attach 时**绝不能调 `browser.close()`**,那会关掉用户自己的外部浏览器;只关你开的 page。 ⚠️ 用 CDP attach 时**绝不能调 `browser.close()`**,那会关掉用户自己的外部浏览器;只关你开的 page。
完整配方见 [references/cdp-browser.md](references/cdp-browser.md)。 完整配方见 [references/cdp-browser.md](references/cdp-browser.md)。
### 按平台安全执行 Playwright
probe 返回 `ready` 后,创建或执行 attach 脚本前**必须先确认当前操作系统**
1. 先检查选定的 Python 能否 import Playwright。依赖缺失与浏览器 ready 是两件事:明确报告缺失,暂不 attach。
2. Playwright 只能装进 DesireCore 拥有的隔离 venv不得全局安装也不得运行 `playwright install`,因为 CDP attach 复用已运行浏览器。
3. Unix-like 主机使用 Bash 路径和语法Windows 必须使用 `PowerShell` 工具,以 PowerShell here-string 和
`[IO.File]::WriteAllText(...)` 创建临时 `.py`。**绝不能把 `cat <<EOF``/tmp/...` 或其他 POSIX heredoc 交给 PowerShell。**
4. 调用 venv 对应平台的 PythonUnix 为 `bin/python`Windows 为 `Scripts\python.exe`),只删除本轮创建的临时脚本,外部浏览器保持运行。
无法判断平台或无法安全创建隔离运行时时,停止并报告缺失前置;不得即兴混用 shell也不得回落 BrowserManage。
--- ---
## Tool Selection Decision Tree ## Tool Selection Decision Tree
``` ```
User intent User intent
├─ **任何点名「我自己的 / 我本机的 / 外部 Chrome、Edge、Chromium」的请求**
│ └─→ 不论动词是搜索、读取、打开还是点击,都优先走 L3-external
│ BrowserExternalProbe精确点名产品仅 `ready` 后 connect_over_cdp()
│ 否则按状态提示并等待;绝不能改走 WebSearch、WebFetch、Jina 或内置浏览器
├─ 只说「本地浏览器」,没有说明内置还是外部
│ └─→ 选择任何路线/工具前先澄清浏览器身份
├─ "Search for information about X" (no specific URL) ├─ "Search for information about X" (no specific URL)
│ └─→ WebSearch → pick top 3-5 results → fetch each (see next branches) │ └─→ WebSearch → pick top 3-5 results → fetch each (see next branches)
├─ 「打开 / 导航到这个 URL」没有点名自己的/外部浏览器)
│ └─→ BrowserManage(create_space/start_session) → BrowserAct(tab.navigate)
│ 保持内置浏览器可见。「打开」是交互请求,不等同于 WebFetch。
├─ "Read this public page" (static HTML, docs, news) ├─ "Read this public page" (static HTML, docs, news)
│ └─→ WebFetch(url) directly │ └─→ WebFetch(url) directly
@@ -134,12 +158,9 @@ User intent
│ - npm: curl https://registry.npmjs.org/<pkg> │ - npm: curl https://registry.npmjs.org/<pkg>
│ - PyPI: curl https://pypi.org/pypi/<pkg>/json │ - PyPI: curl https://pypi.org/pypi/<pkg>/json
└─ "Real-time interactive task" (click, fill form, scroll, screenshot) └─ "Real-time interactive task"(点击、填表、滚动、截图;没有外部限定)
─→ **用户点名「我本机的 / 我自己的 / 外部的浏览器」** → L3-external ─→ 默认使用内置浏览器BrowserManage → BrowserAct → BrowserSnapshot —
BrowserExternalProbe精确点名的浏览器仅 `ready` 后 connect_over_cdp() 见 references/browser-tools.md零 Python 依赖)
│ 其余状态按表处理并等待,不要擅自改用内置浏览器
└─→ **其余情况(默认)**:内置受管浏览器 (BrowserManage → BrowserAct → BrowserSnapshot —
see references/browser-tools.md, no Python needed)
``` ```
### 两个浏览器,按用户意图选,不按能力难度选 ### 两个浏览器,按用户意图选,不按能力难度选

View File

@@ -318,12 +318,64 @@ The approved external Chromium browser is running but no windows are open. Ask t
### Playwright not installed ### Playwright not installed
First ensure the isolated venv exists, then run the import-only check with **that venv's interpreter**.
Do not generate or execute the attach script until this succeeds.
#### Unix-like hosts
```bash ```bash
python3 -m venv "${DESIRECORE_ROOT}/runtime/external-browser-playwright" test -x "<DESIRECORE_HOME>/runtime/external-browser-playwright/bin/python" || \
"${DESIRECORE_ROOT}/runtime/external-browser-playwright/bin/pip" install 'playwright==1.55.0' beautifulsoup4 python3 -m venv "<DESIRECORE_HOME>/runtime/external-browser-playwright"
if ! "<DESIRECORE_HOME>/runtime/external-browser-playwright/bin/python" -c 'import playwright'; then
"<DESIRECORE_HOME>/runtime/external-browser-playwright/bin/python" -m pip install 'playwright==1.55.0' beautifulsoup4
fi
"<DESIRECORE_HOME>/runtime/external-browser-playwright/bin/python" -c 'import playwright' || {
echo 'Playwright is still unavailable in the isolated venv' >&2
exit 1
}
# No need for `playwright install` — we're attaching to an existing browser, not downloading one # No need for `playwright install` — we're attaching to an existing browser, not downloading one
``` ```
#### Windows hosts
Use the `PowerShell` tool. Resolve `<DESIRECORE_HOME>` from the current DesireCore instance; do not
guess another instance's directory.
```powershell
$venv = Join-Path '<DESIRECORE_HOME>' 'runtime\external-browser-playwright'
$python = Join-Path $venv 'Scripts\python.exe'
if (-not (Test-Path -LiteralPath $python)) {
python -m venv $venv
if ($LASTEXITCODE -ne 0) { throw 'Failed to create the isolated Playwright venv.' }
}
& $python -c "import playwright"
if ($LASTEXITCODE -ne 0) {
# Explain the missing dependency and obtain any required install approval first.
& $python -m pip install 'playwright==1.55.0' beautifulsoup4
if ($LASTEXITCODE -ne 0) { throw 'Failed to install Playwright in the isolated venv.' }
& $python -c "import playwright"
if ($LASTEXITCODE -ne 0) { throw 'Playwright is still unavailable in the isolated venv.' }
}
# Do NOT run `playwright install`; CDP attach uses the already-running external browser.
```
To run an attach script on Windows, do not send a Bash heredoc to PowerShell. Use a native
PowerShell here-string and explicit UTF-8 write:
```powershell
$scriptPath = Join-Path $env:TEMP ("desirecore-external-cdp-{0}.py" -f [guid]::NewGuid().ToString('N'))
$script = @'
# Paste the reviewed Python attach script here.
'@
try {
[IO.File]::WriteAllText($scriptPath, $script, (New-Object Text.UTF8Encoding($false)))
& $python $scriptPath
if ($LASTEXITCODE -ne 0) { throw "Playwright attach failed with exit code $LASTEXITCODE." }
} finally {
Remove-Item -LiteralPath $scriptPath -ErrorAction SilentlyContinue
}
```
Keep the environment isolated to DesireCore; do not install Playwright globally. A missing Playwright 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. dependency does not change a `ready` browser/CDP result and never authorizes fallback to BrowserManage.