技能包:配置与代码分家 —— 每包自建 env/ 放本机专属配置,scripts/ 不再写死本机路径

用户 2026-10-11 原话:
· 19:22「这个环境都是针对一台机器的,同步到别的机器把环境都搞坏了」
· 19:28「不用挪出 用env文件夹区分就行,重点是 脚本是脚本 配置是配置」
· 19:29「每个技能 需要环境配置的 都有自己的 env文件夹」
· 19:30「修改完成后要仔细检查 避免调整后 各技能脚本运行失败」

改动:
1、session-mechanism 新增 env/(roots.env=本机真身不入库 + roots.env.example=样例入库),
   install.py 的 ROOTS_ENV 改指 env/roots.env;全包脚本/hook 的配置定位统一为
   「向上 4 层 × env/roots.env 优先 → 包根 roots.env 兼容 → 技能库那份兜底」。
2、去硬编码:collabctl / keepalive / supervise-launch / board-launch 里的本机路径与本机工作区
   清单全清,改为 DSH_AIROOT(兜底 dirname(DSH_WS_ROOT))+ 扫 <AIROOT>/*/.workbuddy/collab/
   自动发现;保活任务名按 collabd-keepalive-<工作区名> 现推;启动器默认值改 ~/.workbuddy 形态。
3、browser-harness:bh_chrome_keepalive.py 默认值全部改 env 驱动,新增 env/bh.env.example。
4、.gitignore:env/ 下只放行 *.example(三行三种层级)+ 兜住旧位置的包根 roots.env。
5、改包纪律.md 新增「四、包结构:脚本是脚本,配置是配置」,立为全仓通用规矩。
6、文档同步:SKILL.md / deploy.md(新增 §3b 配置层级、§6 换机器必查 6 件事)/ rules.md /
   pitfalls.md / supervise-persistence.md。

验收(本机):py_compile 34 个 py 全过;selftest PASS 109 / FAIL 0;
逐文件实证 8 个 loader 命中 env/roots.env;preflight-lock.sh 同目录实测解出两个根;
install.py --manifest 77 份、语法失败 0。

异机验证(包拷成「新克隆」(只有 *.example)+ 另造假工作区 ws-alpha/ws-beta):
install.py --apply 自生成该机 env/roots.env;collabctl/keepalive 自动发现的确实是
ws-alpha/ws-beta、任务名现推(写死清单确已消除);部署副本本地无 roots.env 时靠
CODEBUDDY_CONFIG_DIR 兜底读到技能库那份;整套 selftest 有宿主库时 PASS 108 / FAIL 1
(唯一 FAIL=该区没有常驻心跳,环境所致;本机常驻在跑时同一套为 PASS 109 / FAIL 0)。
This commit is contained in:
admin committed 2026-10-11 21:49:28 +08:00
1 parent 90cc4315d5
commit d81f75f173
37 files changed
+763 -291

No files matched your search

+30 -21
View File
@@ -23,7 +23,7 @@ Direct browser control via CDP. For task-specific edits, use `agent-workspace/ag
症状是 `netstat` 查不到 9223、`curl` 报 `WinError 10061`(2026-10-09 实测踩到一次)。
做法:**先用常驻守端口把浏览器开着,之后每次操作只连不起**。
✅ **起法只有一条:用本会话的后台任务跑那条命令**(启动器在哪、怎么起、起不来怎么查,都在下面「调 AI 工具」§1)—— 那里给的是能整段粘的命令,不是叮嘱。
- 🔴🔴 **一类任务只开一个标签页并复用,⛔ 禁止反复开**(2026-10-09 用户令)。
- 🔴🔴 **标签页按「页面类型」分:一类页面一个标签、同类只开一个、异类各开一个**(2026-10-09 用户令「一类任务只开一个标签页并复用」;2026-10-11 用户令「不同的网站单独开标签 不要相互抢标签页」+「不同类型的网页 开不同的标签 不要相互覆盖」)。
反复开页会把共享实例堆到**整机卡死**(端口不应答、进程不死,保活脚本也救不回 —— 见下面那条的实测读数)。
细则**只写在下面那颗同名的 🔴🔴 子弹里**,⛔ 本处不复述 —— 但它**优先于任何"顺手再开一个"的念头**。
- 🔴 **先探测、能复用就复用**(2026-10-08 用户当场点破:「为什么要重新开浏览器,先检查之前是否开过浏览能否复用」)。
@@ -35,24 +35,28 @@ Direct browser control via CDP. For task-specific edits, use `agent-workspace/ag
`curl --noproxy '*' -s http://127.0.0.1:9223/json/version` ⇒ 回 JSON 说明端口后面确实是 CDP。
确认在跑之后按下面第 2 条的 env 写法连上去,先 `list_tabs()` 摸清现有页,
自己需要的页用 `new_tab()` 开 —— ⛔ 不碰别人的页,也⛔ 不为自己方便关掉现有页。
- 🔴🔴 **一类任务只开一个标签页并复用,⛔ 禁止反复开**(2026-10-08 用户令「后续调试网页 一个页面开一个标签就行了不要反复开」;2026-10-09 加严到**按任务算**:「一类任务只允许开一个标签页并复用,禁止反复开标签页」)。
这条接在上一颗子弹后面:**先复用浏览器**是做对了,但**别在同一个浏览器里反复开页**。
⚠️ **这不是"标签堆着难看"的问题 —— 它能把整机拖到卡死**(2026-10-09 实测):反复开页把共享实例堆爆后,
Chrome 会进入**端口不应答、进程却还活着**的僵死态,而保活脚本只看端口 ⇒ 查不出、也救不回。
当天现场读数:保活日志 `06:56 端口 9223 不应答(pid alive=True)`,直到 `07:06` 进程才退出;用户手动关掉全部会话。
- 🔴🔴 **标签页按「页面类型」分:一类页面一个标签,各不覆盖**(2026-10-08 用户令「一个页面开一个标签就行了不要反复开」;2026-10-09 加严「一类任务只开一个标签页并复用」;2026-10-11 用户令,逐字:「**不同的网站单独开标签 不要相互抢标签页**」+「**不同类型的网页 开不同的标签 不要相互覆盖**」)。
🔴 这是**同一条规则的两面,一起读**:**同一类页面只开一个**(别堆)+**不同类页面各开一个**(别合并)。
⛔ 最容易犯的错是**只记住前一半** —— 把「复用同一个标签」当成原则,于是换页面时 `goto_url` 顶掉正在用的页;
用户说的「相互覆盖 / 抢标签页」就是这个。
⚠️ **堆页的代价**(2026-10-09 实测):反复开页把共享实例堆爆后,Chrome 会进入**端口不应答、进程却还活着**的僵死态,
而保活脚本只看端口 ⇒ 查不出、也救不回。当天读数:`06:56 端口 9223 不应答(pid alive=True)`,
直到 `07:06` 进程才退出;用户手动关掉全部会话。
典型错法:调试一个原型,量一次 `new_tab(原型)` 一次、量完忘了关 ⇒ 一次会话堆出一串同一个页面的标签。
**什么算「一类任务」**:同一件事的一整串动作 —— 同一个原型 / 同一轮站点调研 / 同一批数据抓取。
这类任务**从头到尾只用一个标签**:
- **同一个页面**(同一 URL / 同一份原型)⇒ 就那一个标签。
- **换路由** ⇒ 改 hash(`js("location.hash='#/x'")`)或 `goto_url()`,⛔ 不要 `new_tab()`。
- **换到同任务的另一个页面** ⇒ 也优先在**同一个标签**里 `goto_url()`,⛔ 不要为新页面开新标签。
**分页口径**:
- 🔴 **一个用途一个标签**:列表页 / 登录页 / 正文页 / 解析页 / 后台面板 / 原型页 …… **每类页面各占自己的标签**。
- 🔴 **动手前先 `list_tabs()` 认领本用途那一个**:已有 ⇒ 用它;没有 ⇒ `new_tab()`。
- 🔴 **绝不对一个不属于本用途的页 `goto_url()`** —— 那会把上面正开着的东西顶掉,就是「覆盖」。
- **同一类页面内部换路由** ⇒ 改 hash(`js("location.hash='#/x'")`)或 `goto_url()`,就在**这一个**标签里做。
- **同一类页面反复开**(同一 URL 开两次以上)⇒ 那才是「堆页」,⛔ 禁止。
- **换视口** ⇒ `cdp("Emulation.setDeviceMetricsOverride", width=…, height=…)` 在**同一个标签**里改,
⛔ 不要为了量另一个宽度再开一个标签;量完 `cdp("Emulation.clearDeviceMetricsOverride")` 还原。
- **只有必须并排看两个页面时**(参照物 vs 我方)才开第二个,**用完立刻 `close_tab()`**。
- **完全不需要页面时**(只取一个数、截一张图)⇒ 复用**当前那个**标签,别再开。
- 🔴 **收工前必须 `list_tabs()` 复核一遍,把自己开的一个不留**。共享实例是别人的工作现场,
⛔ 别留尾巴;反向也一样 —— ⛔ 别顺手关掉不是自己开的页。
- 🔴 **收工按用途清点**:自己为本任务开的页交代去留,⛔ 别留尾巴;反向也一样 —— ⛔ 别顺手关掉不是自己开的页。
(依据 2026-10-11 同一目标两轮实测:① 主会话开「小侣坊登录页」等用户登录,任务会话在**同一标签** `goto_url`
去 B 站列表 ⇒ 登录页当场消失、用户没法登录;② 任务会话又用**同一标签**从 B 站列表转去小侣坊解析页
⇒ B 站列表那页没了。两次都只因「复用同一个标签」做过头,⛔ 不是标签开多了。)
- ✅ **动手前先 `list_tabs()`** 看清有哪些标签页,别盲点。(这条同时是上面那条的**收工复核**用的)
- ⛔ **别自建取数通道**:需要 DOM 数值就用 `js(...)`、需要截图就用 harness 的截图helper,
⛔ 不要为了「量一个高度」另写一个脚本去起 Chrome。
@@ -78,20 +82,25 @@ curl --noproxy '*' -s http://127.0.0.1:9223/json/version
**第 1 步 · 没在跑 ⇒ 用本会话的后台任务起**
两份绝对路径固定,启动器就用**技能包内那一份**(参数化,换工作区不用改代码):
启动器就用**技能包内那一份**(参数化,换工作区不用改代码)。🔴 **路径按环境变量拼,⛔ 不写死本机路径**
(2026-10-11 用户报「这个环境都是针对一台机器的,同步到别的机器把环境都搞坏了」):
- 启动器:`E:\ProgramData\.workbuddy\skills\browser-harness\agent-workspace\bh_chrome_keepalive.py`
- 解释器:`E:\ProgramData\.workbuddy\binaries\python\versions\3.13.12\pythonw.exe`
- 技能包根 = `$CODEBUDDY_CONFIG_DIR/skills/browser-harness`(`CODEBUDDY_CONFIG_DIR` 由宿主给;本机=`E:/ProgramData/.workbuddy`)
- 启动器 = `<技能包根>/agent-workspace/bh_chrome_keepalive.py`
- 解释器 = 与**当前会话同一个 Python** 的兄弟 `pythonw.exe`(本机例 `<配置目录>/binaries/python/versions/<版本>/pythonw.exe`)
- 机器专属值(profile/日志/端口)⇒ 本包 **`env/bh.env`**(不入库;样例 `env/bh.env.example`)
命令(整段抄,然后**交给你这个会话的后台任务去跑**):
```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
SK="$CODEBUDDY_CONFIG_DIR/skills/browser-harness"
PYW="$(dirname "$(readlink -f "$(command -v python)")")/pythonw.exe" # 与当前会话同版本
"$PYW" "$SK/agent-workspace/bh_chrome_keepalive.py" \
>> "$CODEBUDDY_CONFIG_DIR/tmp/_bh_bg.out" 2>&1
```
(`--log` 不必给:默认取 `env/bh.env` 的 `BH_LOG`,再兜底到 `<配置目录>/tmp/_bh_chrome.log`。)
🔴 **"交给你这个会话的后台任务跑"是这条起法的本体**:后台任务只活到本会话结束
⇒ 会话在、浏览器在;会话收工、浏览器随之下线。这正是要的语义。