chore(技能): 浏览器起法收敛为「会话后台任务」+ 关窗即停手;补索引与自测清理
- browser-harness/SKILL.md:§1 起法整段重写为「只允许这一种 —— 本会话的后台任务」(字面命令/
为什么只许这一种/守卫语义/已知代价/起不来对照表);§3 补一条排障 —— 认进程命令行与它自己的日志,
⛔ 别信 pid 父链;**「删掉脚本」≠「停掉进程」**(依据 2026-10-10 实测:孤儿守卫在脚本已删后仍把浏览器拉回)
- browser-harness/agent-workspace/bh_chrome_keepalive.py:守卫语义分两档 —— Chrome 正常关窗(rc=0) ⇒ 停手退出;
异常退出(rc≠0) ⇒ 重拉(依据:用户手关被无条件拉回,连关三次弹三次)
- session-mechanism/scripts/selftest.py:删掉重复定义的 `t_check_cooldown_no_pileup`(两段逐字相同);自测 PASS 109 / FAIL 0
- session-mechanism/references/humanizer-en/SKILL.md:与顶层 `humanizer` 技能同名冲突 ⇒ 去掉本包这份的注册
(`user-invocable: false` + `disable-model-invocation: true`),6 个文件正文一字未动
- session-mechanism/references/01-文档索引.md:补登 `04-决策方法论.md`/`dsh-decision-method/`/`作业规矩/`/`humanizer-en/`
This commit is contained in:
1 parent
a04b2c08e8
commit
fd9c902bcc
5 files changed
+112
-65
No files matched your search
+79
-24
@@ -22,7 +22,7 @@ Direct browser control via CDP. For task-specific edits, use `agent-workspace/ag
|
||||
本机沙箱**父进程一退就连带杀子进程** ⇒ 同步调用(含 PowerShell `Start-Process`)里起的 Chrome **会立刻死**,
|
||||
症状是 `netstat` 查不到 9223、`curl` 报 `WinError 10061`(2026-10-09 实测踩到一次)。
|
||||
做法:**先用常驻守端口把浏览器开着,之后每次操作只连不起**。
|
||||
✅ **可直接抄的三步(含启动器在哪、怎么起、计划任务怎么兜)见下面「调 AI 工具」§1** —— 那里给的是命令,不是叮嘱。
|
||||
✅ **起法只有一条:用本会话的后台任务跑那条命令**(启动器在哪、怎么起、起不来怎么查,都在下面「调 AI 工具」§1)—— 那里给的是能整段粘的命令,不是叮嘱。
|
||||
- 🔴🔴 **一类任务只开一个标签页并复用,⛔ 禁止反复开**(2026-10-09 用户令)。
|
||||
反复开页会把共享实例堆到**整机卡死**(端口不应答、进程不死,保活脚本也救不回 —— 见下面那条的实测读数)。
|
||||
细则**只写在下面那颗同名的 🔴🔴 子弹里**,⛔ 本处不复述 —— 但它**优先于任何"顺手再开一个"的念头**。
|
||||
@@ -62,39 +62,94 @@ Direct browser control via CDP. For task-specific edits, use `agent-workspace/ag
|
||||
|
||||
> 2026-10-07 用户定案。适用于「打开 ChatGPT / Claude 网页版求建议、核对方案、做结构评审」这类活。
|
||||
|
||||
### 1. 🔴 起法:浏览器由**一个守端口的常驻循环**开着(三步照抄)
|
||||
### 1. 🔴 起法:只允许这一种 —— **本会话的后台任务**(字面命令,照抄)
|
||||
|
||||
**要什么**:浏览器活在一个**永不退出的循环**里 —— 循环起完 Chrome 就守在端口上;循环不结束,Chrome 就不会被连坐杀掉。
|
||||
(避坑依据 2026-10-09 实测对照:同步调用里起 Chrome ⇒ 端口 1 秒内就绪、**调用一返回立刻死**,随后 `curl` 报 `WinError 10061`;
|
||||
丢进「起完就守着端口、且一直不退出」的循环 ⇒ 浏览器全程常驻。)
|
||||
**目标**:本会话要用浏览器时有一个 9223 上的独立 Chrome;会话结束,浏览器随之下线。
|
||||
|
||||
- 🔴 **可直接抄的三步**(Windows 实测,2026-10-10):
|
||||
**第 0 步 · 先探测** —— 先跑这两条:
|
||||
|
||||
① 把技能包里那份启动器复制到**工作区根**(⛔ 别放 `tmp/`:`tmp/` 会被清理,脚本一没、计划任务就静默失效):
|
||||
`cp "<技能包>/agent-workspace/bh_chrome_keepalive.py" "<工作区根>/bh_chrome_keepalive.py"`
|
||||
```bash
|
||||
netstat -ano | grep -E "127\.0\.0\.1:9223 .*LISTENING"
|
||||
curl --noproxy '*' -s http://127.0.0.1:9223/json/version
|
||||
```
|
||||
|
||||
② 用 **`pythonw.exe`** 起它(⛔ 别用 `python.exe`:控制台程序会闪黑窗,2026-10-01 实测用户当场投诉):
|
||||
`"<py>/pythonw.exe" "<工作区根>/bh_chrome_keepalive.py" --log "<工作区根>/tmp/_bh_chrome.log"`
|
||||
有 pid + 回 JSON ⇒ **已经在跑,直接用**(怎么连见第 2 节),本节到此为止。
|
||||
(避坑依据 2026-10-08 用户当场点破:「为什么要重新开浏览器,先检查之前是否开过浏览能否复用」。)
|
||||
|
||||
③ 让**计划任务**兜住它(进程挂了自动拉起)—— 动作同样用 `pythonw.exe`,每 5 分钟一次、
|
||||
`MultipleInstances=IgnoreNew`。⚠️ `schtasks.exe` 在本机黑名单里(走 PowerShell 的 `ScheduledTasks` 模块)。
|
||||
**第 1 步 · 没在跑 ⇒ 用本会话的后台任务起**
|
||||
|
||||
- ✅ **校验(两条一起看)**:`netstat -ano | grep -E "127\.0\.0\.1:9223 .*LISTENING"` 有 pid;
|
||||
`curl --noproxy '*' -s http://127.0.0.1:9223/json/version` 回 JSON。
|
||||
- **起来之后,每次操作只连不起**(用第 2 节那条 env 连上去)—— 多个实例会抢同一个 profile。
|
||||
两份绝对路径固定,启动器就用**技能包内那一份**(参数化,换工作区不用改代码):
|
||||
|
||||
- 🔴 **僵死态要会认**(避坑依据 2026-10-09 实测):共享实例被堆爆后会进入「**端口不应答、进程还活着**」的僵死态
|
||||
—— 连接探不通,但 `tasklist` 里那个 chrome 进程仍在。
|
||||
**判据(两条一起看)**:① `netstat -ano | grep 127.0.0.1:9223` **无 LISTENING**;② `tasklist | grep -i chrome` **仍有那个 pid**。
|
||||
⇒ 处置:**重启实例**(关掉残留 chrome,再按本节三步重起);在原地反复重试连接只会白等。
|
||||
两条要记牢:**「端口在」=还要看进程**;**「进程在」=还要看端口** —— 两条都过才叫可用。
|
||||
- ⚠️ **守端口脚本对僵死态只记日志、不自动重启**(正向目标:**保住现场**,交给会话按上面的判据处置)——
|
||||
依据:自动重启会把正在被人排查的现场清掉。所以它只会连续写
|
||||
`port 9223 not answering (pid alive=True)`(2026-10-09 `06:56:11` → `07:06:49` 进程才退出)。
|
||||
- 启动器:`E:\ProgramData\.workbuddy\skills\browser-harness\agent-workspace\bh_chrome_keepalive.py`
|
||||
- 解释器:`E:\ProgramData\.workbuddy\binaries\python\versions\3.13.12\pythonw.exe`
|
||||
|
||||
命令(整段抄,然后**交给你这个会话的后台任务去跑**):
|
||||
|
||||
```bash
|
||||
E:/ProgramData/.workbuddy/binaries/python/versions/3.13.12/pythonw.exe \
|
||||
"E:/ProgramData/.workbuddy/skills/browser-harness/agent-workspace/bh_chrome_keepalive.py" \
|
||||
--log "E:/ProgramData/.workbuddy/tmp/_bh_chrome.log" \
|
||||
>> "E:/ProgramData/.workbuddy/tmp/_bh_bg.out" 2>&1
|
||||
```
|
||||
|
||||
🔴 **"交给你这个会话的后台任务跑"是这条起法的本体**:后台任务只活到本会话结束
|
||||
⇒ 会话在、浏览器在;会话收工、浏览器随之下线。这正是要的语义。
|
||||
|
||||
两处不能省:
|
||||
|
||||
- **`>> …_bh_bg.out 2>&1`(stdout 全重定向到文件)** —— 依据 2026-10-01 实测:常驻任务往会话里吐输出会把会话日志推过 ~10 MiB ⇒ 宿主 `diagnostic-log:dropped` ⇒ 界面不刷新、看着像卡死。
|
||||
- **走后端任务,⛔ 不在同步调用里跑** —— 本机沙箱父进程一退就连带杀子进程;同步调用里起的 Chrome 端口 1 秒内就绪、调用一返回立刻死,随后 `curl` 报 `WinError 10061`(2026-10-09 实测)。
|
||||
|
||||
**为什么只许这一种**(用户 2026-10-10 令:「只允许这一种方式,使用 browser harness 打开浏览器的时候也必须用这种方式打开浏览器」)
|
||||
|
||||
- **同步调用里起** ⇒ 父进程一退 Chrome 就死(见上一段的实测)。
|
||||
- **Windows 计划任务** ⇒ 它跨会话常驻,用户手关的浏览器会被守卫拉回来(2026-10-10 实测:连关三次、每次都在 1 秒内弹回),语义与"会话结束即回收"相反。⇒ 本机 2026-10-10 已把 `bh-chrome-keepalive` 计划任务**整个撤掉**,起法只留本会话后台任务这一条。
|
||||
|
||||
**守卫语义**(2026-10-10 改)
|
||||
|
||||
- Chrome **正常关窗**(`rc=0`,=用户自己点的关闭)⇒ 守卫**停手退出**,关掉就是关掉。
|
||||
- Chrome **异常退出**(`rc≠0`,崩溃/被杀)⇒ 立刻重拉,干活不中断。
|
||||
|
||||
**已知代价(必须知道)**
|
||||
|
||||
- 本会话只要有 `pending`/`running` 的后台任务,宿主 idle 钩子就被**静默压制**(2026-10-05 实测:被僵尸任务压 6h20m、零日志)⇒ 这段期间本会话的「钩子驱动的唤醒/监管」不生效。所以**只在确实要用浏览器时起它,用完把后台任务停掉**。
|
||||
|
||||
**第 2 步 · 校验** —— 两条一起看:
|
||||
|
||||
```bash
|
||||
netstat -ano | grep -E "127\.0\.0\.1:9223 .*LISTENING" # 有 pid
|
||||
curl --noproxy '*' -s http://127.0.0.1:9223/json/version # 回 JSON
|
||||
```
|
||||
|
||||
两条都过才叫可用。之后每次操作**只连不起**(用第 2 节那条 env 连上去)。
|
||||
|
||||
**第 3 步 · 起不来时按这张对照表查**
|
||||
|
||||
- **后台任务立刻结束、端口也没有** ⇒ 读 `E:/ProgramData/.workbuddy/tmp/_bh_bg.out` 与 `E:/ProgramData/.workbuddy/tmp/_bh_chrome.log` 的最后几行,理由都写在那里。
|
||||
- **日志写着 `chrome 可执行体不存在`** ⇒ Chrome 不在默认路径。给启动器加 `--chrome "<你的 chrome.exe>"`,或设环境变量 `BH_CHROME`。
|
||||
- **日志写着 `port 9223 already listening ⇒ exit`** ⇒ 已经有实例在跑,直接连。
|
||||
- **日志写着 `chrome closed cleanly (rc=0) ⇒ stop guarding, exit`** ⇒ 浏览器被人手关过、守卫已按新语义停手。要再用就跑第 1 步那条命令。
|
||||
- **端口被拉起来、却没人承认是自己起的** ⇒ 走**进程级证据**:先看 cmdline 找出是哪个脚本在守
|
||||
(`Get-CimInstance Win32_Process -Filter "Name='pythonw.exe'"` 打 `CommandLine`),再看**它自己写的那份日志** ——
|
||||
时间戳能直接和"关窗"动作对上(2026-10-10 实测:`[23:01:36] chrome exited rc=0 ⇒ relaunch` 连出两条,
|
||||
正是用户两次关窗被弹回)。⚠️ **删掉脚本文件 ≠ 停掉进程** —— Python 启动时已把脚本读进内存,
|
||||
文件删了它照样守;要 `Stop-Process` 按 pid 结束。⚠️ 也⛔ 别拿 pid 父链当结论(pid 会复用)。
|
||||
- **后台任务还在跑、端口却没了** ⇒ 走下面「僵死态」那两条判据。
|
||||
|
||||
**僵死态:端口不应答、进程却还活着**(避坑依据 2026-10-09 实测)
|
||||
|
||||
共享实例被堆爆后会进入这个状态 —— 连接探不通,但 `tasklist` 里那个 chrome 进程仍在。
|
||||
**判据(两条一起看)**:① `netstat -ano | grep 127.0.0.1:9223` **无 LISTENING**;② `tasklist | grep -i chrome` **仍有那个 pid**。
|
||||
⇒ 处置:**重启实例**(关掉残留 chrome,再按第 1 步重起);在原地反复重试连接只会白等。
|
||||
两条要记牢:**「端口在」=还要看进程**;**「进程在」=还要看端口** —— 两条都过才叫可用。
|
||||
|
||||
⚠️ **守端口脚本对僵死态只记日志、不自动重启**(正向目标:**保住现场**,交给会话按上面的判据处置)——
|
||||
依据:自动重启会把正在被人排查的现场清掉。所以它只会连续写
|
||||
`port 9223 not answering (pid alive=True)`(2026-10-09 `06:56:11` → `07:06:49` 进程才退出)。
|
||||
|
||||
### 2. 起独立实例(端口 9223)—— 下面这条是**启动器脚本内部**那一步
|
||||
|
||||
> 🔴 **要它常驻,用第 1 节的三步**(复制启动器 → `pythonw.exe` 起 → 计划任务兜)。
|
||||
> 🔴 **要它常驻,用第 1 节那条命令(本会话的后台任务)**。
|
||||
> 本节这条命令的用途是**看懂它在干什么**与**排障**:照抄它**同步**跑 ⇒ 父进程一退 Chrome 立刻死
|
||||
> (避坑依据 2026-10-09:端口 1 秒内就绪、调用一返回就死,随后 `curl` 报 `WinError 10061`)。
|
||||
|
||||
|
||||
@@ -7,24 +7,35 @@
|
||||
(症状:`netstat` 查不到端口、`curl` 报 `WinError 10061`)。
|
||||
要的是:浏览器活在一个**永不退出的循环**里 —— 起完 Chrome 就守在端口上。
|
||||
|
||||
## 怎么用(三步,Windows 实测 2026-10-10)
|
||||
## 怎么用(Windows 实测 2026-10-10)
|
||||
|
||||
1、复制到**工作区根**(⛔ 别放 `tmp/`:`tmp/` 会被清理,脚本一没、计划任务就静默失效)
|
||||
2、用 **`pythonw.exe`** 起(⛔ 别用 `python.exe`:控制台程序会闪黑窗)
|
||||
3、让**计划任务**兜住它(进程挂了自动拉起)
|
||||
🔴 **只允许一种起法**(用户 2026-10-10 令:「只允许这一种方式,使用 browser harness 打开浏览器的时候
|
||||
也必须用这种方式打开浏览器」):**在本会话的后台任务里跑本文件**,stdout 全重定向到文件。
|
||||
|
||||
1、命令**整段抄**(见 `SKILL.md`「调 AI 工具」§1),解释器用 **`pythonw.exe`**
|
||||
2、**交给你这个会话的后台任务去跑**(后台任务只活到本会话结束 ⇒ 会话在、浏览器在;会话收工、浏览器下线)
|
||||
3、⛔ 不往工作区复制副本、⛔ 不建 Windows 计划任务(计划任务跨会话常驻,会把用户手关的浏览器拉回来)
|
||||
⛔ 也不在同步调用里跑(父进程一退 Chrome 就死)
|
||||
|
||||
**为什么必须有这一步**:后台任务的 stdout 会灌进会话日志 ⇒ 超 ~10 MiB 后宿主丢日志、界面看着卡死
|
||||
⇒ 所以命令里那截 `>> …_bh_bg.out 2>&1` 不能省。
|
||||
|
||||
## 做法约定
|
||||
|
||||
- **幂等**:端口已在听 ⇒ 立刻退出。(避免叠加第二个实例、避免两个实例抢同一个 profile)
|
||||
- **守**:`while True` 里看着子进程;它退出了就重新起。
|
||||
- **守**:`while True` 里看着子进程;它**异常**退出(rc≠0)就重新起。
|
||||
- 🔴 **手关窗 ⇒ 停手**:Chrome 正常退出(rc=0,=用户点的关闭窗口)时脚本**自己退出**,不再拉起。
|
||||
依据(实测 2026-10-10):用户关掉浏览器,守卫在 1 秒内把它拉回,连关三次弹三次。
|
||||
要重新开,跑一条命令起本脚本即可(见 `SKILL.md`「调 AI 工具」§1)。
|
||||
- **僵死态只记日志**:端口不应答而进程仍在时,脚本**不自动重启** —— 那会把正在被排查的现场清掉,
|
||||
留给人/会话按 SKILL.md 的判据处置。
|
||||
- **清代理 env**:`HTTP_PROXY` / `HTTPS_PROXY` 会劫持本地 CDP 连接(报 `502 Bad Gateway`)。
|
||||
- **只用独立实例**:⛔ 不附着用户日常 Chrome(那是 9222,会弹「允许远程调试?」动到用户浏览器)。
|
||||
|
||||
用法:
|
||||
pythonw bh_chrome_keepalive.py --log "<工作区根>/tmp/_bh_chrome.log"
|
||||
可选:`--port` `--chrome` `--profile`(都有默认值,见下)
|
||||
pythonw bh_chrome_keepalive.py --log "E:/ProgramData/.workbuddy/tmp/_bh_chrome.log"
|
||||
(整条命令见 SKILL.md「调 AI 工具」§1;本脚本要放在**会话的后台任务**里跑)
|
||||
可选:`--port` `--chrome` `--profile` `--proxy` `--log`(都有默认值,见下)
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -105,9 +116,16 @@ def main() -> int:
|
||||
_log(a.log, "launched pid=%s" % p.pid)
|
||||
|
||||
# ── 守:循环不结束 ⇒ Chrome 不被连坐 ───────────────────────────
|
||||
# 🔴 退出码分两档(2026-10-10 用户报障「关掉就自动打开」后改):
|
||||
# rc=0 = 正常关窗(用户自己点的关闭)⇒ **停手退出**,把浏览器还给用户;
|
||||
# rc≠0 = 异常退出(崩溃/被杀)⇒ 重拉。
|
||||
# 依据(实测):`[22:45:06] chrome exited rc=0 ⇒ relaunch` —— 用户手关三次、被拉回三次。
|
||||
while True:
|
||||
time.sleep(5)
|
||||
if p.poll() is not None:
|
||||
if p.returncode == 0:
|
||||
_log(a.log, "chrome closed cleanly (rc=0) ⇒ stop guarding, exit")
|
||||
return 0
|
||||
_log(a.log, "chrome exited rc=%s ⇒ relaunch" % p.returncode)
|
||||
p = _launch(a.chrome, a.profile, a.port, a.proxy)
|
||||
_log(a.log, "relaunched pid=%s" % p.pid)
|
||||
|
||||
Reference in new issue
Block a user