Repository navigation
Releases: KNLCXY/dsh-websift
Release list
dsh-websift 1.0.8 — 白框、死代理路由、并发抓页串内容
三个实测取证的 bug,全部修掉
(1.0.7 只在本机装过、未对外发布,两版的修复合起来正式发一次。三个问题都是 1.0.0 起就有的,不是新引入的。)
① 桌面上那块白色矩形 = 隐藏的内核窗口
launchChrome 的 spawn 选项里带着 windowsHide: true。它本意只是「别弹控制台窗口」,但在 Windows 上会通过 STARTUPINFO 传 SW_HIDE,把程序自己第一次 ShowWindow 覆盖掉 —— 浏览器窗口从头到尾都是隐藏的:
- 隐藏的 Chromium 窗口仍会在桌面上留下一整块纯白矩形,尺寸正好等于
--window-size - 「可视模式」形同虚设:看不到窗口,扫码登录那类操作根本没法做
取证:同一个内核、同样参数,只差 windowsHide 各起一个 —— false 的 IsWindowVisible=True,true 的 False;再对纯净桌面采样,启动一个隐藏窗口后四个采样点全从桌面蓝 RGB(0,165,247) 变成 RGB(255,255,255),对它发一次 SW_MINIMIZE 立刻恢复桌面色。
修复:去掉 windowsHide。msedge / chrome 都是 GUI 子系统程序,本来就不会创建控制台,去掉对谁都无害。
② 「端口在监听」被当成「代理能用」
proxyAlive() 只做一次 TCP 连接测试。实测 Clash Verge 进程活着、7897 也在监听,但所有节点都挂了 —— 对任何网址都返回空。旧逻辑判「代理在线」,把浏览器指到 --proxy-server=死端口,于是每个页面都是「无法访问此网站」的空白错误页。
雪上加霜:插件完全不认识 Chromium 的错误页(chrome-error://chromewebdata/),导航到错误页时 CDP 调用是「成功」的,错误页被当成正常正文返回,路由缓存 10 分钟内也不作废。
修复:
- 新增
proxyUsable():端口在监听且真的转发过一次才算能用(两个探针都不通才算死;端口探活与转发测试分开缓存,互不拖累) - 路由决策全部改用它;
browser_proxy的status/test把「端口」和「转发」分开报 - 认识错误页:
Page.navigate的errorText/chrome-error:一律如实报错,并作废该域名路由与代理健康缓存 - 代理打不开时自动退回直连重试一次(
proxy: always是用户显式要求,不擅自改)
对照实验(起一个「只接受连接、永不响应」的假代理):1.0.0 和 1.0.6 都把三个测试站点判成「走代理」,行为一模一样 —— 所以「1.0 是正常的」是因为那时代理节点还活着,不是代码变坏了。
③ 并发抓多页时串内容
web_digest 用 Promise.all 并发抓链接,而整个插件只复用一个工作标签(刻意的)。并发导航同一个标签 = 互相覆盖:
- 轻则一个链接直接
net::ERR_ABORTED(被另一次导航打断),两个链接只回来一个 - 重则 A 站读到的是 B 站的 DOM,而且错误内容会被写进 15 分钟页面缓存(缓存键取的是请求 URL),之后一直以
cached: true, ms: 0张冠李戴地供应
实盘踩到的样子:两页调用里多出「重定向重复 1 条已合并」(两个不同 URL 抓回来的 final URL 相同);紧接着单页查 example.com,标题却是 iana 的「Example Domains」、正文是 iana 那段话、耗时 0.0s,并且数字与上一次完全相同。
修复:加一把浏览器串行闸门(withBrowserLock),只锁「驱动工作标签」这一件事 —— stack=official 的 HTTP 直连那一档照旧并发;loginFlow 自己开新标签(/json/new),不进锁(否则一次登录会把队列堵几分钟)。
测试
新增三条回归测试,全部断言可复跑:
test-deadproxy—— 假代理「只监听不转发」必须判不可用、路由退回直连test-window—— 白框回归锁(DSH_WEBSIFT_LIVE=1时额外做真机窗口可见性校验)test-serial—— 本地两个内容截然不同的页面,断言每个 URL 都拿到自己的正文(可传 lib 目录做修复前后对照;修复前跑会失败:ERR_ABORTED+ 只回来 1 页)
另修 test-offline:还在用改名前的旧路由 /_browser-kernel/settings(一跑就崩),且末尾 reset 会把用户真实设置洗成默认值 —— 现在跑完自动还原。
npm test = rank / failed / route / deadproxy / window / serial,6/6 通过。
安装
dsh plugin --profile <profile> add dsh-websift@1.0.8 # 从 npm
dsh plugin --profile <profile> add file:/路径/dsh-websift-1.0.8.tgz # 离线 tgz(本 Release 附件)改完宿主代码必须重启 DSH 才生效。
dsh-websift 1.0.6 — 代理路由不再走死代理
dsh-websift 1.0.6 — 代理路由不再把请求交给死代理
1.0.5(Edge 新标签页兼容)未单独发布,已并入本版。
踩到的坑
代理端口没人监听(Clash 没开 / 挂了)时,插件会无条件判「走代理」——因为直连探测抛异常那条分支写的是 route = 'proxy',根本没查代理是否活着。
结果:内核被带上 --proxy-server=http://127.0.0.1:7897,所有抓取都报
ERR_CONNECTION_CLOSED — 无法访问此页面
看起来像「网络坏了」,其实是自己被路由到了死端口上。实测抓 example.com 就中招了。
三处修复
- 探测兜底:探测失败时先查代理是否在线 —— 不在线就仍走直连,而不是交给死代理
- 缓存校验:读路由缓存时,若缓存说走代理、而代理已经不在线 → 当场作废并重新探测
- 失败失效:走代理的抓取失败时,丢掉该域名的路由缓存 + 重置代理健康状态(下次重新探测)
测试
新增 test/test-route.mjs,确定性验证:
- 临时把
proxyUrl指向必定没人监听的127.0.0.1:1(跑完自动还原设置文件) - 让直连探测抛异常(就是旧代码走错的那条分支)
- 断言判定为 「直连可用」(旧代码会说「走代理」)
全套 6 个测试通过:test-rank / test-json / test-digest / test-find / test-failed / test-route。
本版还包含
- 1.0.5:空白页判定兼容
edge://newtab/(Edge 下不再每次浏览器重启就多开一个标签) - 1.0.4:工作标签只按自己记下的 id 认领(不按 URL 猜,绝不误关你自己的标签)
- 1.0.3:工作标签 id 落盘
~/.dsh/storages/websift/state.json,跨 DSH 重启复用
⚠️ 改完必须重启 DSH 才生效(宿主半在进程启动时加载,不热更新)。
安装
dsh plugin --profile <profile> add dsh-websift # latest = 1.0.6校验:sha256sum -c SHA256SUMS.txt
dsh-websift 1.0.4 — 按 id 认领工作标签(不再按 URL 猜)
dsh-websift 1.0.4
修掉 1.0.3 里一个会误伤用户的设计
1.0.3 为了清理「越堆越多的工作标签」,是按 URL 像不像搜索页(bing / google / baidu / …)来认领和关闭标签的。
这是错的:用户也会在同一个浏览器窗口里自己搜东西 —— 那样会把用户的标签当成插件自己的,
复用甚至关掉。
1.0.4 改成只认我们自己记下的标签 id:
~/.dsh/storages/websift/state.json里存ourTabIds(上限 20)+workTargetId- 认领、复用、关闭,都只在这些 id 里选 —— 绝不按 URL 猜
- 抓取/摘要核心一行未动(5 个测试全过)
行为说明(第一次可能还是会多一个标签)
改动后第一次运行(还没有任何记录)如果找不到空白页/新标签页,仍会新开一个工作标签;
从第二次起就固定复用同一个,跨 DSH 重启也不再堆积。
⚠️ 必须重启 DSH(宿主半在进程启动时加载)。重启后可自查:state.json出现,
并且你自己开的标签不会被关。
安装
dsh plugin --profile <profile> add dsh-websift # latest = 1.0.4校验:sha256sum -c SHA256SUMS.txt
dsh-websift 1.0.3 — 工作标签页落盘(修搜索页堆积)
dsh-websift 1.0.3 — 修「浏览器窗口里搜索页越堆越多」
现象
每次用完,浏览器窗口总是停在 Bing 搜索结果页,而且标签越攒越多。
根因
- 插件复用同一个工作标签页,并且故意停在刚访问的页面(不闪回空白页 —— 这是早先你反馈过的)
- 但「复用哪个标签」只记在内存里:
browserState.workTargetId - DSH 一重启就忘了。忘了之后再找可复用的标签时,只认
about:blank/ 新标签页 ——
而那个工作标签正停在搜索结果页,认不出来 → 再开一个 - 于是每开一次会话就多留一个搜索页
修复
- 工作标签页的 id 落盘:
~/.dsh/storages/websift/state.json,跨 DSH 重启复用同一个标签 - 重启后若发现我们自己留下的搜索页(bing / google / baidu / sogou / so.com / duckduckgo / yandex),
复用其中一个并关掉其余同类标签 —— 只动我们自己留下的,不碰你手动开的标签
⚠️ 改完必须重启 DSH(宿主半在进程启动时加载)。重启后可自查:~/.dsh/storages/websift/state.json会出现,
且窗口里的搜索页不再堆积。
安装
dsh plugin --profile <profile> add dsh-websift # latest = 1.0.3校验:sha256sum -c SHA256SUMS.txt
dsh-websift 1.0.2 — 修失败报告 + README 配图
dsh-websift 1.0.2
修了一个「把失败说成正常」的 bug
一个页面都没取到正文时,web_digest 会回:
正文本来就短(0 字符),直接给要点
——把抓取失败说成了「内容本来就少」。现在它会如实报告,并逐条列出失败的链接和原因:
一页正文都没取到(2 个链接全失败)—— 多半是打不开、被拦截或需要登录;先用 browser({ url }) 单独打开其中一个确认
未取到:a.invalid(模拟失败:https://a.invalid/) / b.invalid(模拟失败:https://b.invalid/)
新增 test/test-failed.mjs 锁住这个行为(已并入 npm run test:failed)。
README 配图
docs/images/hero.png—— 能力总览(一次调用读多页 / 真实搜索链接 / JS·登录态·截图)docs/images/ledger.png—— 一次调用的账本:抓回的正文 vs 返回的摘要
图随 npm 包一起分发(docs/ 在白名单里)。
Edge 内核实测记录
| 场景 | 结果 |
|---|---|
| 整页截图(JS 重的站点) | ✅ 1401×1845,1.2 MB,full_page 生效 |
| 多页抓取 | ✅ 单页 917 字符 → 摘要 444(压缩 52%);被墙的页面按失败计入并在抬头列出 |
login 动作超时路径 |
✅ 25.5s 干净返回「未检测到登录完成,窗口还开着」,不挂死 |
登录的成功路径需要真人在窗口里扫码/输密码,尚未实测。
安装
dsh plugin --profile <profile> add dsh-websift # npm,latest = 1.0.2
dsh plugin --profile <profile> add github:KNLCXY/dsh-websift校验:sha256sum -c SHA256SUMS.txt
dsh-websift 1.0.1 — 发布到 npm
dsh-websift 1.0.1 — 发布到 npm
内容与 1.0.0 完全一致,只是版本号 +1:npm 的暂存发布机制占住了 1.0.0(见下)。
现在可以从 npm 安装
dsh plugin --profile <你的 profile> add dsh-websift为什么是 1.0.1
npm 现行的发布政策(2026)对新账号几乎不友好,这次的完整排障路径:
| 卡点 | 突破 |
|---|---|
| 发布必须 2FA 或带 bypass-2FA 的 token | 账号没有硬件安全密钥,且 TOTP 已被 npm 服务端下线(Adding a new TOTP 2FA is no longer supported) |
| 建 bypass token 要求「至少选一个包」 | 但账号里没有任何包 → 表单里没有可选项,死锁 |
npm stage publish |
官方描述是「把 2FA 证明推迟到批准时」。暂存会为新包名创建占位包 0.0.0-stage —— 账号这才算「有包」 |
| token 表单现在可选「所有包 + bypass 2FA + 读写」 | ✅ 建出可用 token |
直接发 1.0.0 → 409 Cannot publish over previously staged version |
暂存的 1.0.0 无法批准(批准同样要求已注册的安全密钥)→ 改发 1.0.1 |
所以:npm 上的 1.0.1 == GitHub 上的 1.0.0 内容。占位的 0.0.0-stage 与暂存的 1.0.0 会自行过期。
安装(三种都可以)
dsh plugin --profile <profile> add dsh-websift # npm
dsh plugin --profile <profile> add github:KNLCXY/dsh-websift # GitHub
dsh plugin --profile <profile> add "file:<下载路径>/dsh-websift-1.0.1.tgz"校验:sha256sum -c SHA256SUMS.txt(本页附件)
装完重启 DSH 生效。
dsh-websift 1.0.0 — 首个正式版
dsh-websift 1.0.0 — 首个正式版
给 DeepSeek Harness 的省 token 网页阅读插件。底层是真 Chrome/Edge(CDP 驱动),一次调用读完多个页面、按相关性筛掉不相关的,只回一段带账本的摘要。
四个工具
| 工具 | 作用 |
|---|---|
web_find |
真浏览器搜索,返回真实链接(不是 so.com/link 那种跳转壳) |
web_digest |
搜索/给定链接 → 抓正文 → 相关性闸门 → 抽关键句 → 压缩摘要(实测省 82–98% 上下文) |
browser |
text / html / eval / screenshot / login |
browser_proxy |
三档代理 auto / always / never,实时切换不用重启 |
1.0 之前的稳定能力
- 内核自动探测:Chrome / Edge / Brave / Chromium / 应用内置(豆包、夸克…),换机器不用手改配置
- 代理三档 + Clash Verge 可选联动(端口不通时自动启动;启动后自动最小化窗口)
- 两套闭环可选:
plugin(全走浏览器,默认)/official(官方 API 搜索 + 抓取) - 所有 CDP 调用带超时:内核卡住最多等 30 秒就报错,不会拖死整轮对话
- 统一命名:路由
/_websift/*、存储~/.dsh/storages/websift(旧目录自动复制迁移,配置不丢)
安装
dsh plugin --profile <你的 profile> add "file:<下载路径>/dsh-websift-1.0.0.tgz"或直接从仓库装:
dsh plugin --profile <你的 profile> add github:KNLCXY/dsh-websift校验(同页附件 SHA256SUMS.txt):
sha256sum -c SHA256SUMS.txt装完必须重启 DSH
宿主半与 client 半都在进程启动时装配一次;面板里的按钮/开关也要重启后才出现。
版本口径
1.x 起是正式版;0.9.x 及更早全部是预发布,仅作历史留档。
1.0.0 起行为冻结:只修 bug、不再改接口;要动接口就走 2.0。
v0.9.26 — 统一改名(路由 / 存储 / id)
预发布(Pre-release),0.x 一律不算正式版。
0.9.26 — 发布前最后一次统一改名
| 改什么 | 从 → 到 |
|---|---|
| 宿主路由 | /_browser-kernel/* → /_websift/* |
| 存储目录 | ~/.dsh/storages/browser-kernel → ~/.dsh/storages/websift |
| 输入框 dock id | browser-kernel-toggle → websift-toggle |
| 清单 id | dsh-websift.local → dsh-websift |
全仓 33 处旧名已清零(包内仅剩 2 处故意保留:迁移常量与本次变更说明)。
配置不会丢
存储目录改名带复制式迁移:新目录不存在、旧目录存在时,把 settings.json 复制过去;旧目录原样保留作备份。
实测:新目录 37 个键、关键值(stack / visible 等)与旧文件一致;四条新路由(settings / clash / window / chrome-candidates)全部注册。
⚠️ 必须重启 DSH
路由和 dock id 变了,不重启的话旧 client 半会去请求已经不存在的旧路由,面板会报错。
安装
dsh plugin --profile <你的 profile> add "file:<下载路径>/dsh-websift-0.9.26.tgz"校验:sha256sum -c SHA256SUMS.txt
v0.9.25 — 修卡死根因 + 最小化窗口按钮
预发布(Pre-release),0.x 一律不算正式版。
0.9.25
修掉两次「整轮卡死」的根因
33 处 CDP 调用都没有超时,内核一旦卡住(实测 Edge 截图那次),await cdp.send(…) 永不返回 → 整个工具调用挂死 → 只能重启 DSH。
现在 connect().send() 统一 30s 超时(新设置 cdpTimeoutMs,3000–120000 可调),CDP 的 HTTP 端点也加了 AbortSignal。
面板新增
- 「最小化窗口」按钮(
POST /_browser-kernel/window);headless 时会说明「没有窗口可最小化」 - 「启动代理后最小化」开关(
autoMinimizeOnClash,默认开):点「启动 Clash Verge」成功后自动把浏览器窗口收起来 - 高级设置补上
cdpTimeoutMs输入框(此前只能改配置文件)
本次验证
| 项 | 结果 |
|---|---|
| Edge 内核截图 | 正常,1416×808,3.7s(图像已核对是真页面) |
| 四条路由 | settings / clash / window / chrome-candidates 全部注册 |
| window 路由 | 非法 action → 400;GET → 405;headless → 明确报「没有窗口可最小化」 |
| 设置读写 | autoMinimizeOnClash 与 cdpTimeoutMs 的 snapshot / POST / reset 均正常 |
| 4 个单测 | 全过 |
安装
dsh plugin --profile <你的 profile> add "file:<下载路径>/dsh-websift-0.9.25.tgz"校验:sha256sum -c SHA256SUMS.txt
装完必须重启 DSH(宿主半与 client 半都在启动时装配)。
v0.9.23 — 设置即时生效 + http 政务站可读
预发布(Pre-release),0.x 一律不算正式版。
攒了两个实修一起发。
0.9.23
browser工具每次重读设置:之前用的是启动时的快照,改visible/ 代理参数后只能重启 DSH 才生效,与面板写的「改完即生效」不符- 关掉 Chrome 的 HTTPS 强制升级(
--disable-features=HttpsUpgrades):中文政务站大量还是http://,被强升 https 后变成「网站不安全」错误页(实测qingxiu.gov.cn),官方 HTTP fetch 却正常
0.9.22
- 官方
fetch/search加超时:之前没有超时,站点不响应就会把整个工具调用拖死(实测抓 GameBanana 时整轮卡死)。现在 20s 超时后按原逻辑回退浏览器
安装
dsh plugin --profile <你的 profile> add "file:<下载路径>/dsh-websift-0.9.23.tgz"校验:sha256sum -c SHA256SUMS.txt
装完重启 DSH:宿主侧代码要重新加载。