Codex-merged dsh-browser-playwright (compiled-only fork for DeepSeek Harness) + full diagnosis and patches for the 'minimized browser window gets yanked back' bug. 面向 DeepSeek Harness 的浏览器插件整合版 + 最小化窗口被拽回桌面的诊断与补丁。
在 DeepSeek Harness 终端运行:
dsh plugin --profile web add miqian-nomad/dsh-browser-playwright-codexA Codex-merged, headful build of dsh-browser-playwright for DeepSeek Harness — plus a full diagnosis of the "minimized browser window gets yanked back onto my screen" bug, and the patches that fix it.
面向 DeepSeek Harness 的浏览器插件(上游 dsh-browser-playwright + Codex 浏览器插件功能整合版)——附带"最小化窗口被自动拽回桌面"这个 bug 的完整诊断与修复补丁。
⚠️ Read this before anything else / 先读这一段
This repository contains compiled output only (
lib/*.js+lib/*.d.ts). The TypeScript source of this build does not exist anywhere. It was produced by merging Codex-style browser features into upstreamdsh-browser-playwrightv0.1.1, and only the compiled product survived.本仓库只有编译产物(
lib/*.js+lib/*.d.ts),这套构建的 TypeScript 源码在任何地方都不存在。 它是把 Codex 风格的浏览器功能整合进上游dsh-browser-playwrightv0.1.1 得到的,只剩编译产物。That is exactly why this repo is asking for help — see Help wanted / 求助. If you want to fix logic rather than patch a build artifact, the right starting point is the upstream source tree.
这正是这个仓库存在的理由——见 Help wanted / 求助。想改逻辑而不是给编译产物打补丁,请从上游源码树开始。
| # | Symptom / 症状 | Root cause / 根因 | State / 状态 |
|---|---|---|---|
| 1 | Minimized window is raised when the plugin switches tabs 切标签时最小化的窗口被弹出来 |
switchTab calls page.bringToFront() unconditionallyswitchTab 无条件调用 page.bringToFront() |
Fixed here (lib/playwright.js L1417) · Patch ready for upstream (patches/upstream-window-activation.patch) |
| 2 | Minimized window is raised when the plugin opens a tab 开新标签时窗口被弹出来 |
context.newPage() → CDP Target.createTarget without background, and Chromium activates the window on every tab creationcontext.newPage() 内部发的 Target.createTarget 不带 background,Chromium 建标签必激活窗口 |
Fixed here (createBackgroundPage, L153) · Patch ready for upstream |
| 3 | Minimized window is raised by any tool call, even one that fails before touching the page 任何一次工具调用都会弹窗,连"导航前就失败"的调用也弹 |
Post-op login-state export (endOp → persistStateSoon → context.storageState()) in this fork's persistent-profile layer本 fork 持久化 profile 层里"每次操作后导出登录态" |
Mitigated here (skip export while minimized, L574) — but not reproducible in a minimal harness, so the mechanism is still unknown: see docs/FOCUS-STEALING.md |
| 4 | Clicking a link with target="_blank" (or any page-side window.open()) still raises the window点击 target="_blank" 链接(或页面自己 window.open())仍会拽窗 |
Chromium's own default: a new tab must be selected and shown, so a minimized window is restored. The plugin never issues this call — the page does Chromium 自身默认行为:新标签要"选中并显示",于是恢复最小化的窗口。这一下不是插件发起的,是网页发起的 |
Open — help wanted |
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. / 插件其它操作(快照、evaluate、截图、点击、填写、滑动、滚轮、导航、刷新、storageState、建 CDP 会话)实测都不会碰最小化的窗口。
injected.js, snapshot-render.js), the tool family, the URL policy, and the test suite are upstream work. Copyright (c) 2026 dsh-browser-playwright contributors.browser_click_at) with landing-element reporting, the CDP policy layer (cdp-policy.js), the persistent profile + login-state export (runtime-state.js, persistState/restoreState), dialog parking (DIALOG-POLICY.md), snapshot rules (SNAPSHOT-RULES.md), safety rules (SAFETY-RULES.md), and the Codex rule set (codex-rules.md).LICENSE.# from a checkout / 从本地检出安装
dsh plugin --profile <name> add link:<path-to-this-repo>
Config lives in the DSH profile; the browser profile directory defaults to ~/.dsh/browser-profiles/playwright. Set launch.headless: false (this build defaults to headful) for login/captcha hand-off, or launch.persistent: false for the legacy shared-browser mode. / 浏览器 profile 目录默认 ~/.dsh/browser-profiles/playwright;launch.headless: false 用于登录/验证码人工接管,launch.persistent: false 回到上游的共享浏览器模式。
No DSH needed — the mechanism is pure Playwright + Chromium:
cd scripts
set PW_MODE=launch # or: persistent
set PW_CHANNEL=chrome
set PW_CORE_PATH=<path>\node_modules\playwright-core
node repro-minimize.mjs
Observed output (Windows 11, playwright-core 1.62.1, Google Chrome 151, PW_MODE=launch and persistent — identical in both):
safe baseline: sleep only (3s) before=minimized after=minimized
RAISES context.newPage() before=minimized after=normal
safe CDP Target.createTarget({background:true}) before=minimized after=minimized
RAISES page.bringToFront() before=minimized after=normal
safe context.newCDPSession(anchor) before=minimized after=minimized
safe context.storageState() before=minimized after=minimized
safe anchor.goto() (same tab) before=minimized after=minimized
safe anchor.screenshot() before=minimized after=minimized
Full analysis, method and limitations: docs/FOCUS-STEALING.md.
patches/upstream-window-activation.patch applies cleanly to upstream main (61 insertions, 5 deletions in src/playwright.ts). It adds isWindowMinimized() + createBackgroundPage() and gates bringToFront. It has not been run against upstream's test suite — review and run it. / 补丁能干净地应用到上游 main,但没有跑过上游测试套件。context.storageState()". Our minimal harness says storageState() is safe; the live instance said otherwise. A reliable reproduction would settle it. / 长存活实例上"操作后导出登录态会顶起最小化窗口",最小复现里 storageState() 是安全的——需要有人做出稳定复现或证伪。target="_blank" / window.open() from restoring a minimized window, without rewriting the page's semantics behind the user's back. Known approaches (rewriting target via addInitScript, hooking window.open, or intercepting at the Target.targetCreated level) each have real costs; see the "Remaining" section of the analysis. / 在不改网页原意的前提下拦住 target="_blank" / window.open() 恢复最小化窗口。src/, that would be worth more than any patch here. / 知道这套 0.2.0 从哪来、或手上有 src/,比这里任何补丁都值钱。Open an issue or a PR — both are welcome. / 欢迎开 issue 或直接提 PR。
Short answer: the choice is not free, and "default to headless" is the option that costs the most exactly when a human is needed. Measured comparison of headless vs headful (UA, viewport, DPR, responsive breakpoints, screenshot size), what actually requires a visible window (spoiler: not native dialogs), and the measured minimize ⇄ show hand-off that avoids mode switches entirely: docs/MODE-TRADE-OFFS.md.
一句话:这个选择不是免费的,而且"默认 headless"的代价恰好都落在"需要人"的那一刻。 量化对照见 docs/MODE-TRADE-OFFS.md。
This repository is intentionally not under a single licence:
| Part | Licence | Commercial use |
|---|---|---|
lib/, scripts/, patches/, cordis.patch.yml, package.json |
MIT (LICENSE) — required, because lib/ is a derivative of MIT-licensed upstream work and adding a non-commercial restriction to it would be an unauthorised restriction on downstream users |
allowed |
Documentation: this README, docs/** |
CC BY-NC-SA 4.0 (LICENSE-DOCS) — share and adapt with attribution and share-alike, non-commercial only |
not allowed |
No OpenAI code is redistributed here. docs/codex-rules.md is an independently written Chinese summary of browser-automation behaviour plus a source map to the original documentation; no verbatim text from it is reproduced. Full details, including the provenance of every file: NOTICE.md.
中文:代码走 MIT(因为 lib/ 派生自 MIT 的上游,往上加"禁止商用"是越权);文档走 CC BY-NC-SA 4.0(禁止商用、需署名、相同方式共享)。仓库里不含任何 OpenAI 的代码;docs/codex-rules.md 是原创中文归纳 + 原文出处对照表。细节见 NOTICE.md。
登录后即可为该插件评分和评价。
还没有人评价这个插件,来抢个沙发吧!