Files
workbuddy_skills/session-mechanism/assets/board-launch.py.tpl
T
admin d81f75f173 技能包:配置与代码分家 —— 每包自建 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)。
2026-10-11 21:49:28 +08:00

84 lines
4.3 KiB
Smarty
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""看板启动器(**无控制台**)· 模板。
> 🔴 用户 2026-10-04 21:0x 定案:「**看板 包括常驻 按现在的方式启动,全局只允许一个,把规则记录下来**」
> 本文件=本文档所述「现在的方式」的可复制载体。装法见 `references/supervise-persistence.md`。
## 为什么必须有它(⛔ 别再退回 `.cmd`)
计划任务的动作若写 `.cmd`/`.bat`/`powershell.exe`,那些都是**控制台程序**
⇒ 每次触发都分配一个 `conhost.exe` ⇒ **屏幕上弹黑窗**(用户已投诉两次)。
更糟:`.cmd` 里若**前台**跑 `pythonw`(没有 `start`)⇒ `cmd.exe` **不返回**
⇒ 黑窗**常驻**挂着(实测 `cmd.exe 62932` + `conhost.exe 54928` 挂在看板进程树上)。
## 解法
计划任务动作 **直接指向 `pythonw.exe`**(GUI 子系统 ⇒ 操作系统层面就不分配控制台 ⇒ 零黑窗);
本来靠 `.cmd` 的 `set` 传的环境变量,改在**本进程内**设好,再 `runpy` 起 `board.py`。
## 装法(⚠️ 三处必改:{{WS}} / {{BOARD}} / {{PORT}})
```powershell
$pyw = '<managed-python>\pythonw.exe'
$a = New-ScheduledTaskAction -Execute $pyw -Argument '"<本文件>"' `
-WorkingDirectory '<工作区根>'
$s = New-ScheduledTaskSettingsSet -ExecutionTimeLimit ([TimeSpan]::Zero) `
-MultipleInstances IgnoreNew -RestartCount 999 `
-RestartInterval (New-TimeSpan -Minutes 1)
$p = New-ScheduledTaskPrincipal -UserId '<当前用户>' -LogonType Interactive -RunLevel Highest
Register-ScheduledTask -TaskName 'dsh-board-<端口>' -Action $a -Settings $s -Principal $p -Force
```
🔴 **`RunLevel=Highest` 不是可选项**:`Limited` 实测 `LastTaskResult=1`(写日志/读库被拒)。
## ⛔ 两条封死的写法(都亲手栽过)
1. **`exec(compile(...))`** —— 从 `C:\Windows\System32`(任务默认 cwd)起时 **rc=1 且零输出**。
✅ 用 `runpy.run_path(BOARD, run_name="__main__")`:它会正确设好
`__file__`/`__name__`/`sys.path[0]`,`board.py` 顶部的 `_sm_load_roots()`
(靠 `__file__` 往上找 `env/roots.env`)才找得到。
2. **`pythonw.exe` 下 `sys.stdout/stderr` 可能是 `None`** ⇒ 任何 `print` 抛 `AttributeError` 秒退。
✅ 起 `board.py` 前先给两个流兜底(本文件已做)。
## 验收(三条全绿才算成,⛔ "打印了启动消息"不算)
- `netstat` 见 `127.0.0.1:<port> LISTENING`
- `curl /` = 200
- 🔴 **父进程是 `svchost.exe -s Schedule`**(⇒ 已脱离会话)**且进程树下零 `cmd.exe`/零 `conhost.exe`**
"""
import os
import sys
# ── 三处按实际改 ────────────────────────────────────────────────
WS = r"<工作区根,例:E:\ProgramData\AIProject\ai1net-dsh-server>"
BOARD = r"<技能脚本,例:E:\ProgramData\.workbuddy\skills\session-mechanism\scripts\board.py>"
PORT = "8788"
# ───────────────────────────────────────────────────────────────
LOG = os.path.join(WS, "tmp", "board-serve.err.log")
# 🔴 必须**先设好 env 再起 board** —— `board.py` 在**模块顶层**读这些名字。
# ⚠️ `env/roots.env` 只兜底 `COLLABD_PROD_CONFIG`,而 `board.py` 读的是 `COLLABD_CONFIG`
# ⇒ **名字对不上,不设就落到 `${脚本目录}/collabd.config.json`(不存在)⇒ 起不来**。
os.environ["COLLABD_CONFIG"] = os.path.join(WS, ".workbuddy", "collab", "collabd.config.json")
os.environ.setdefault("CODEBUDDY_CONFIG_DIR", os.path.expanduser("~/.workbuddy"))
os.environ["PYTHONIOENCODING"] = "utf-8"
os.environ["PYTHONUNBUFFERED"] = "1"
# 🔴 `pythonw.exe` 下两个流可能是 None ⇒ 兜底,否则 print 抛异常秒退
try:
_f = open(LOG, "a", encoding="utf-8", errors="replace")
except Exception:
_f = None
if _f is not None:
if sys.stdout is None:
sys.stdout = _f
if sys.stderr is None:
sys.stderr = _f
# 🔴 ⛔ 别用 `exec(compile(...))`(见文件头「两条封死的写法」)⇒ `runpy` 才会正确设好 `__file__` 等
import runpy
sys.argv = [BOARD, "--serve", PORT]
runpy.run_path(BOARD, run_name="__main__")