dsh-browser-playwright-codex
DSH 的浏览器:登录一次,它就用你的账号替你办事 —— 而且不抢你的屏幕。
查后台 / 填表 / 抓列表 / 多标签流程,它都能干。需要你出手时(登录、验证码、网页弹框)它会停下来等你,绝不替你点「确定」。它待在自己的窗口里,你最小化丢到后台,它就安静干活。
| 你想要的 | 它是怎么做的 |
|---|---|
| 用我自己的账号上网办事 | 登录一次就一直记得(独立窗口,不动你日常在用的浏览器) |
| 干活时别跳出来打断我 | 你最小化它,它就安静待在后面;只有网页自己要开新窗口时会跳出来提醒你——你随时知道 AI 在哪动手 |
| 危险动作别自作主张 | 删数据 / 付款 / 弹框确认:一律停下来问你 |
| 别乱跑网站 | 能去哪些网站你说了算;执行任意网页 JS 这类高风险功能默认关着 |
| 卡住了要能查 | 点了没反应,它直接告诉你是页面报错还是背后的请求失败 |
浏览器类插件通常走两条路:要么挂在你自己的 Chrome 上(agent 就看得到你所有标签页),要么干脆做成没有窗口的无头模式(真需要你接手时反而没有窗口)。这个插件走第三条:自己的窗口、你最小化就不吵你、需要你时窗口随时在。
装法:dsh plugin --profile <你的 profile> add github:miqian-nomad/dsh-browser-playwright-codex(或 clone 后 link: 该目录)
想看细节:中文逐条说明——23 个工具分别干什么、背后怎么实现 docs/中文说明.md · 与同类工具的逐维度对比 docs/对比与优势.md
上游
dsh-browser-playwright(MIT)的 Codex 风格整合版,面向 DeepSeek Harness。
0.3.0 is the release where "compiled output only" ended. Up to 0.2.0 this plugin existed as a build artifact whose source was not on the machine; since 0.3.0 everything is source:
src/(12 typed modules),tests/(71 tests),scripts/(acceptance + diagnostics tooling), and aCHANGELOG.mdthat says what changed and why. The old compiled-only snapshot is in this repository's git history.0.3.0 就是"只有编译产物"这个状态结束的版本。 0.2.0 及以前它是一份源码已失的编译产物;0.3.0 起全部是源码,旧的"只有产物"快照留在本仓库的历史里。
中文说明(用途 / 每个功能 / 每个作用 / 每个机制)
docs/中文说明.md — 完整的中文逐条说明:
- 用途:给 DSH 一个真浏览器,模型用无障碍快照 + 稳定 ref 操作页面,而不是猜 CSS 选择器;
- 23 个工具逐个讲(20 个常驻 + 3 个闸门):
snapshot/click/click_at/hover/fill/press/scroll/navigate/tabs/switch_tab/open_tab/close_tab/back/forward/wait/screenshot/dialog/console_messages/network_requests/close/extract/evaluate/cdp—— 每个都写清什么时候用、关键参数、背后机制、注意什么; - 13 个机制逐个讲:快照与 nonce ref、几何点击阶梯与落点回报、原生弹窗挂起、持久化 profile 与登录态回注、URL 白名单(五条路全在投递前校验)、CDP 白名单、契约层与加载期守卫、兼容层、资源回收与崩溃自愈、不打扰桌面的三条修复与一条残留、反检测立场(只做减法不伪造)、提示词成本纪律、验收体系;
- 已知限制:共享窗口、
target="_blank"残留、JS 跳转、canvas 无 ref、跨域 iframe、evaluate 是配置级信任、反风控能力上限。
中文一句话:这是一个"以你的真实登录态操作网页"的插件,抢不抢你的桌面、看不看得见、花多少 prompt token,都被逐条量过、写清、并有测试钉住。
与同类对比:docs/对比与优势.md —— 与 DSH 生态内的浏览器插件(CDP 附加型 / 独立 profile 型 / stealth 无头型)以及通用 Playwright MCP、裸脚本做逐维度对照,并同时写出别人更强的地方。最硬的三条差异:① 桌面打扰被当成问题修完并实测;② URL 白名单 / 原生弹窗 / CDP 三道护栏是机制级且有测试钉住;③ 验收自包含,clone 下来就能自己复核,不用信我们。
Status: what is fixed, and how it is gated / 现状:修了什么,靠什么守住
| # | Symptom / 症状 | State / 状态 |
|---|---|---|
| 1 | A minimized window is raised on every tab switch / tab creation 切标签、开新标签时最小化窗口被弹出 |
Fixed (src/playwright.ts isWindowMinimized / createBackgroundPage, switch gated) · Upstream patch ready (patches/) |
| 2 | A minimized window is raised by any tool call, even one that fails before touching the page 任何一次调用都弹窗,连"导航前就失败"的调用也弹 |
Mitigated in the fork (the login-state export is skipped while minimized) — but not reproducible in a minimal harness, so the mechanism is still unexplained: docs/FOCUS-STEALING.md §Cause 3 |
| 3 | Clicking a target="_blank" link (or a page-side window.open()) still raises the window点 target="_blank" 链接 / 页面 window.open() 仍会拽窗 |
Open — help wanted. Chromium's own default (a new tab must be shown); the plugin does not issue that call, the page does |
| 4 | browser_cdp was completely dead — CDPSession.send was called unbound, so every command failed as a misleading protocol errorbrowser_cdp 整体失效(方法解绑,报错误导) |
Fixed + two gates: a tool-level DOM.getDocument round-trip test and a verify group |
| 5 | In non-persistent mode the login-state export was written to <cwd>/.tmp as plaintext cookies/localStorage, on every call非持久模式把登录态明文写进进程工作目录,且每次调用都写 |
Fixed (stateFile is string | undefined again) + a regression test |
| 6 | allowedDomains was bypassable: browser_click_at (both modes) never asked the policy, and a page-opened tab could be driven白名单可被 browser_click_at 与"网页自开的标签页"绕过 |
Fixed — one shared rule for every click path, and switch_tab refuses hosts outside the allow-list + 3 tool-level tests |
| 7 | javascript: links were unclickable through browser_click (non-http scheme treated as a disallowed host)javascript: 链接被误当非法主机拒绝 |
Fixed — javascript: is exempt (in-page, navigates nowhere); other non-http(s) schemes stay refused |
What the fork still lists as known limitations (JS-driven location.href navigation, cross-origin iframes, canvas widgets, extract needing its own model route, …) is in docs/PLUGIN-README.md — the plugin's own README, kept verbatim.
Everything else the plugin does (snapshot, evaluate, screenshot, click, fill, scroll, wheel, goto, reload, storageState, CDP session creation) was measured to leave a minimized window alone. / 插件其它操作实测都不会碰最小化的窗口。
Install / build / 安装
One command, straight from GitHub — no npm account, no build step:
dsh plugin --profile <name> add github:miqian-nomad/dsh-browser-playwright-codex
From a local checkout instead:
git clone https://github.com/miqian-nomad/dsh-browser-playwright-codex
cd dsh-browser-playwright-codex
dsh plugin --profile <name> add link:<absolute path to this directory>
What is verified about the one-liner, and what is not. Verified: fetching the repository works, and the build-script gate no longer blocks the install (lib/ ships, there is no prepare hook — see below). Not verified: full dependency resolution inside a fresh profile. Installing it in a bare directory instead of a profile fails with ERR_PNPM_FETCH_404 on @deepseek-ai/dsh-type-meta, a package that is absent from public npm — part of the harness family is not published there, and our @deepseek-ai/* requirements are written as *. Inside a real DSH profile the harness supplies those packages, but this repository has not been through that test; if it fails for you, the local-checkout route above is the one that is exercised daily.
Why there is no prepare hook: lib/ is committed here (unlike upstream, which ships it inside the npm tarball). pnpm 11 refuses to run a git dependency's build scripts unless the consumer allowlists the package — ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED — so a prepare hook would make the one-liner above fail on a clean machine. If you edit src/, run npm install && npm run build yourself; npm run verify fails if lib/ is older than src/ (check [7]).
Config lives in the DSH profile (cordis.patch.yml in this repo is the bundle layer: headful persistent window, allowEvaluate / allowCdp on, idle never auto-closes). A restart of DSH is required for a rebuilt lib/ to load.
Name note: the package is still named dsh-browser-playwright inside package.json (the module ids in cordis.patch.yml mirror it), so this fork cannot be installed into a profile that already has the upstream package — pick one. The repository name is what distinguishes the fork.
一条命令从 GitHub 装(不需要 npm 账号、不需要构建):
dsh plugin --profile <name> add github:miqian-nomad/dsh-browser-playwright-codex
lib/ 是入库的(上游是把 lib 打进 npm tarball,这里 GitHub 就是分发渠道):pnpm 11 默认拒绝执行 git 依赖的构建脚本(ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED),所以本仓库故意不放 prepare 钩子——否则上面那条命令在干净机器上会直接失败。改过 src/ 要自己 npm run build;npm run verify 的 [7] 会在 lib/ 落后于 src/ 时报错。
Verification / 验收
Nothing here is asserted without a command that reproduces it:
| Command | What it is | Last result |
|---|---|---|
npm test |
71 tests: contract/tool-schema, provider, scenario journeys (checkout, login, multi-tab, stale refs, lazy load, infinite scroll, popup, dialog), CDP policy, click/tab URL policy, snapshot rendering, description budgets | 68 pass / 0 fail / 3 live-skipped |
npm run verify |
Self-contained acceptance script (no DSH, no network): snapshot naming/folding, hover modes, dialog handling, tool-surface ↔ contract, CDP binding, lib/ vs src/ freshness |
52/52 |
npm run doctor |
Are the harness packages this plugin imports still exporting what it uses, and where does playwright-core resolve from at deploy time? |
all available |
npm run cost |
Resident prompt cost per configuration layer (schema defaults / shipped bundle / everything registered) | 20 tools ≈ 4,176 tok/turn · 22 ≈ 4,842 · 23 ≈ 4,968 |
The two analysis documents / 两份分析文档
These are the reason this repository exists at all — they answer questions the plugin's own README cannot:
docs/FOCUS-STEALING.md— "why does a minimized browser window come back?" Four causes, each marked measured or unmeasured, with the plugin-free reproduction and an explicit limits section. Includes the honest correction that native dialogs do not need a visible window.docs/MODE-TRADE-OFFS.md— measured headless vs headful (UA, viewport, DPR, responsive breakpoints, screenshot size), what actually needs a human eye, the live minimize ⇄ show hand-off that avoids mode switches entirely, and the conclusion that "default to headless" costs the most exactly when a human is needed.
中文:前者回答"最小化窗口为什么会被拽回来"(四条根因,逐条标注实测/未实测);后者是 headless 与 headful 的实测代价对照,结论是"默认 headless 的代价恰好都落在需要人的那一刻"。
Provenance & license / 来源与许可
- Upstream: ChenyuHeee/dsh-browser-playwright v0.1.1, MIT — the accessibility-snapshot engine, the tool family, the URL policy, the test suite. Copyright (c) 2026 dsh-browser-playwright contributors.
- Merged on top (Codex-inspired layer): geometric click (
browser_click_at) with landing-element reporting, the CDP policy layer, the persistent profile + login-state export, dialog parking, the console/network diagnostics tools, and the fixes listed above. - Licensing is deliberately split — see
NOTICE.md:- MIT (
LICENSE):src/,tests/,scripts/, configs,CHANGELOG.md, and the fork's own plugin docs. - CC BY-NC-SA 4.0 (
LICENSE-DOCS): the analysis prose written for this repository — this README,docs/FOCUS-STEALING.md,docs/MODE-TRADE-OFFS.md. - No OpenAI code is redistributed here.
- MIT (
Help wanted / 求助
- Upstream patch —
patches/upstream-window-activation.patchapplies cleanly to upstreammainand its helper code now compiles in this tree, but it has never run against upstream's test suite. Also tracked upstream: ChenyuHeee/dsh-browser-playwright#7. - Cause 2 above — reproduce or refute "a long-lived instance raises a minimized window on a post-op
context.storageState()". A stable reproduction is worth more than any patch here. target="_blank"/window.open()— stop it restoring a minimized window without rewriting the page's semantics behind the user's back. Known approaches and their costs are listed in the analysis.- Other platforms / Chromium versions — every measurement in this repository comes from one Windows 11 machine; data for or against these mechanisms elsewhere is welcome.
Open an issue or a PR — both are welcome. / 欢迎开 issue 或直接提 PR。
No comments yet. Be the first to write one.