Files
dsh_ai1net_server/归档/技能修-删ps1模板-20261006-2355/start-supervise.ps1.tpl
T
admin c1b5e4d966 chore(工作区): 全量入库 + 补齐 .gitignore(以工作区为准)
- 变更规模:新增 514 / 修改 62 / 重命名 155 / 删除 4(归档重组与文档轮次)
- .gitignore 修:`归档/**/db-cwd归一-备份-*/` —— 原规则写绝对层级(归档/db-cwd归一-…),
  目录搬进 归档/配置与备份/ 后**静默失效**,43 MB 的 DB 备份又变成未跟踪
- .gitignore 补:嵌套 git 内部数据(归档/内嵌git-20261008/、归档/skills-git-旧线-20261007/dotgit-原样移出/)
- .gitignore 补:运行态与部署副本(.workbuddy/collab/、.workbuddy/tools/、.workbuddy/.load-pending、.workbuddy/tmp-*)
- .gitignore 补:备份件(*.bak-*)
- 未跟踪文件从 2190 降到 890(其余为 归档/ 归档件与 .workbuddy/memory/ 知识文件,按口径入库)
2026-10-10 23:13:22 +08:00

133 lines
5.6 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.
# collabd supervisor keeper -- NEVER returns on purpose.
#
# 🔴🔴 2026-10-04 修:首行原本多打了三个双引号(即「三连双引号 + #」开头,
# 把整份 `.ps1` 变成了「Python 看着合法、PowerShell 看着语法错」的畸形文件
# ⇒ 生成物被 PowerShell 判定 9 处语法错、**一秒内秒退** ⇒ 常驻彻底没人续命。
# ⛔ 这个文件是 **PowerShell**(不是 Python)⇒ 注释一律用 `#`,⛔ 不要三引号。
#
# ⚙️ 本文件由 `init_workspace.py` **按工作区变量自动生成**(⛔ 请勿手改:要改改模板 + 重跑脚本)。
# 生成时间:__STAMP__
# 适用工作区:__WS__
#
# Why this file exists(⛔ 下面几条是踩出来的,⛔ 别"简化"掉):
# ① 计划任务**只在任务失败(非零退出)时重启**。早前版本末尾直接前台调 pythonw,
# pythonw 死掉后脚本返回 0 ⇒ 任务被判"成功"⇒ RestartCount=999 永不触发
# ⇒ **常驻死了就永远死着**。⇒ **重启逻辑必须住在脚本里**。
# ② ⛔ **绝不能用 `& $pyw ...`** 起常驻:pythonw 是 GUI 子系统程序,PowerShell 的
# 调用运算符**不阻塞**它 ⇒ 立刻返回 ⇒ 脚本以为"collabd 退出了"⇒ 每几秒就重拉
# ⇒ **叠出一堆重复常驻**。⇒ 用 `Start-Process -Wait`(真阻塞)。
# ③ **存活判据=心跳,不是退出码**:collabd 发现有活的常驻时会**故意 exit 0**(幂等),
# 那个 0 退出**绝不能**触发重拉风暴。
# ④ UTF-8 **带 BOM**(⛔ 不带 ⇒ PowerShell 5.1 按 ANSI/GBK 解码 ⇒ CJK 路径被毁
# ⇒ 任务一秒内 Result=1 退出)。
$ErrorActionPreference = "Continue"
$env:CODEBUDDY_CONFIG_DIR = "__CFGDIR__"
$env:COLLABD_CONFIG = "__COLLABDCFG__"
$env:PYTHONIOENCODING = "utf-8"
$env:PYTHONUNBUFFERED = "1" # stdout 重定向到文件,Python 会块缓冲 ⇒ 不设它诊断全压在 4KB 之后
$root = "__WS__"
$pyw = "__PYW__"
$script = "__SCRIPT__"
$hbPath = "__HBPATH__"
$logPath = "__LOG__"
$stampPath = "__STAMP__"
# 🔴 停止标志:机制侧 `--set-life 已完成` ⇒ 写它 ⇒ 常驻优雅退出。
# ⚠️ 改前缺陷:keeper 不知道它 ⇒ 目标收口后**每 60 秒重拉一次、每次秒退**
# (实测**空转 370 次/6 小时**)⇒ "完成即收工"只做到"停掉常驻",
# ⛔ **没做到"别再拉它起来"** —— 那是同一件事的两半。
$stopFlag = "__STOPFLAG__"
$staleSec = 90 # 与 collabd.supervise_alive() 同一陈旧阈值
Set-Location $root
function Say([string]$m) {
$ts = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
Add-Content -LiteralPath $logPath -Value ("[" + $ts + "] " + $m) -Encoding UTF8
}
function Get-HbAge {
try {
if (-not (Test-Path -LiteralPath $hbPath)) { return -1 }
$raw = Get-Content -LiteralPath $hbPath -Raw -Encoding UTF8
if (-not $raw) { return -1 }
$ts = [double](($raw | ConvertFrom-Json).ts)
if ($ts -le 0) { return -1 }
return [int]((Get-Date).ToString("U") - $ts)
} catch { return -1 }
}
function Wait-HeartbeatFresh([int]$maxWait) {
$waited = 0
while ($waited -lt $maxWait) {
$age = Get-HbAge
if ($age -ge 0 -and $age -lt $staleSec) {
Say ("another supervisor is alive (heartbeat " + $age + "s old) => standing down")
return $true
}
Start-Sleep -Seconds 5
$waited += 5
}
return $false
}
Say ("keeper up. keeper_pid=" + $PID + " script=" + $script)
$backoff = 5
while ($true) {
# ① 循环开头:目标已完成 ⇒ 收工,不再重拉
if (Test-Path -LiteralPath $stopFlag) {
Say "guard.stop present => target finished; keeper stands down (no more respawn)"
exit 0
}
$ageBefore = Get-HbAge
if ($ageBefore -ge 0 -and $ageBefore -lt $staleSec) {
if (Wait-HeartbeatFresh 300) { continue }
}
Say ("spawn collabd --supervise (backoff=" + $backoff + "s)")
$outLog = "__OUTLOG__"
$errLog = "__ERRLOG__"
try {
# -Wait 给的是**真阻塞**(`&` 给不了 GUI 子系统程序);-Redirect* 让证据落盘
$proc = Start-Process -FilePath $pyw `
-ArgumentList @("-u", $script, "--supervise") `
-WorkingDirectory $root `
-WindowStyle Hidden `
-RedirectStandardOutput $outLog `
-RedirectStandardError $errLog `
-PassThru -Wait
$code = $proc.ExitCode
} catch {
Say ("spawn threw: " + $_.Exception.Message + " => retry in " + $backoff + "s")
Start-Sleep -Seconds $backoff
if ($backoff -lt 60) { $backoff = [Math]::Min(60, $backoff * 2) }
continue
}
$ageAfter = Get-HbAge
if ($ageAfter -ge 0 -and $ageAfter -lt $staleSec) {
$verdict = "heartbeat still fresh (" + $ageAfter + "s) => idle exit, no respawn yet"
} elseif ($ageAfter -ge 0) {
$verdict = "heartbeat stale (" + $ageAfter + "s) => supervisor really died"
} else {
$verdict = "heartbeat unreadable"
}
Say ("collabd exited (exit=" + $code + ", " + $verdict + ")")
Set-Content -LiteralPath $stampPath -Value ((Get-Date).ToString("yyyy-MM-dd HH:mm:ss")) -Encoding UTF8
# ② 退避**期间**标志可能出现 ⇒ 别再睡满一轮(⛔ 漏这里 ⇒ 收工后还会多拉一次)
$waitLeft = $backoff
while ($waitLeft -gt 0) {
if (Test-Path -LiteralPath $stopFlag) {
Say "guard.stop appeared during backoff => keeper stands down early"
exit 0
}
Start-Sleep -Seconds 5
$waitLeft -= 5
}
if ($backoff -lt 60) { $backoff = [Math]::Min(60, $backoff * 2) }
}