fix(v26): 自重启任务清理加时间余量(治真机累积的 Running 僵尸)
## 问题
重启任务的 cmd 会**一直持有新 agent 进程**,所以任务状态长期是 Running。
原清理逻辑只清「非 Running」的(怕误杀当前宿主)—— 代价是真机累积:
2026-10-09 实测 D:\\Nodeagent 上残留 3 个 nodeagent-selfrestart-* 任务(全 Ready/Running)。
## 修法(保守优先)
清理条件改为「**非 Running** 或 **LastRunTime 早于 1 小时**」:
- 1 小时余量远大于一次重启的耗时(正常 <1 分钟),所以"当前宿主"绝不会被误判 ——
它刚刚才运行过,LastRunTime 就是几分钟前
- 清旧 Running 僵尸是安全的:它们的 cmd/node 早已退出,只剩注册表条目
用 includes 断言锁死这三条(AddHours(-1) / LastRunTime -lt $cut / Get-ScheduledTaskInfo),
并禁止回退到"只清非 Running"的老写法。
## 验证
单测 267 → **269** 全绿(新增 2 例源码断言);e2e 30 全绿;7 包构建通过。
⚠️ **该项在 agent 内,且涉及进程清理 —— 必须真机验证后才算可交付**。
当前被控端整机离线(2026-10-10 00:15 ping 100% 丢包),故标注为待验证,未发版。
This commit is contained in:
1 parent
68546e63e8
commit
32e0dd7bd5
2 files changed
+43
-2
No files matched your search
@@ -527,8 +527,17 @@ async function scheduleWindowsRestart(p: RestartParams): Promise<unknown> {
|
||||
const TASK = `nodeagent-selfrestart-${Date.now()}`;
|
||||
const ps = [
|
||||
`$ErrorActionPreference='SilentlyContinue'`,
|
||||
// 清理历史 selfrestart 任务(保留 Running 的 —— 那正是当前 agent 的宿主)
|
||||
`Get-ScheduledTask -TaskName 'nodeagent-selfrestart*' | Where-Object { $_.State -ne 'Running' } | Unregister-ScheduledTask -Confirm:$false`,
|
||||
// 清理历史 selfrestart 任务。
|
||||
//
|
||||
// ⚠️ 为什么不能简单清所有非 Running 的:重启任务的 cmd 会**一直持有新 agent 进程**,
|
||||
// 所以任务长期处于 Running;若把 Running 的全清掉,会连当前 agent 的宿主一起杀
|
||||
// (= 自杀,表现为"restart 成功但 agent 消失")。原实现只清非 Running,
|
||||
// 代价是真机累积了 3 个 Running 僵尸(2026-10-09 实测)。
|
||||
//
|
||||
// 现策略:非 Running **或** LastRunTime 早于 1 小时 → 清。
|
||||
// 1 小时余量远大于一次重启的耗时,因此"当前宿主"绝不会被误判(它刚刚才运行)。
|
||||
`$cut = (Get-Date).AddHours(-1)`,
|
||||
`Get-ScheduledTask -TaskName 'nodeagent-selfrestart*' -ErrorAction SilentlyContinue | Where-Object { if ($_.State -ne 'Running') { return $true }; $ti = ($_ | Get-ScheduledTaskInfo -ErrorAction SilentlyContinue); if ($ti -and $ti.LastRunTime -and $ti.LastRunTime -lt $cut) { return $true }; return $false } | Unregister-ScheduledTask -Confirm:$false -ErrorAction SilentlyContinue`,
|
||||
`$a = New-ScheduledTaskAction -Execute 'cmd.exe' -Argument '${cmdLine.replace(/'/g, "''")}'`,
|
||||
// 不限时(默认 72h 后强制结束),保证 node 能长期运行
|
||||
`$s = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -ExecutionTimeLimit (New-TimeSpan -Seconds 0)`,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFileSync } from 'node:fs';
|
||||
|
||||
/**
|
||||
* v23 连接层防护的**规则**测试(server.ts 的纯逻辑部分)。
|
||||
@@ -72,3 +73,34 @@ test('install.ps1 的 stdin 读取:校验规则与防阻塞', () => {
|
||||
assert.ok(!isHex('zz' + '0'.repeat(30)), '非 hex 拒');
|
||||
assert.equal(' a1b2c3d4e5f6a7b8\r\n'.trim(), 'a1b2c3d4e5f6a7b8', 'Trim 后应为纯 hex');
|
||||
});
|
||||
|
||||
// ---------- v26 自重启任务清理的保守判定(源码断言) ----------
|
||||
//
|
||||
// 用 includes 而非正则:这里断言的是源码字面片段,正则转义层数容易写错
|
||||
// (臣本轮就在这上面栽过两次)。
|
||||
|
||||
const restartSrc = readFileSync(
|
||||
new URL('../../apps/agent/src/capabilities/system.ts', import.meta.url),
|
||||
'utf8',
|
||||
);
|
||||
|
||||
test('自重启任务清理:必须带「运行超 1 小时」余量,禁止简单清所有 Running', () => {
|
||||
// 背景:重启任务的 cmd 长期持有新 agent 进程 → 任务状态长期 Running;
|
||||
// 若把 Running 的全清掉会连当前 agent 的宿主一起杀(= 自杀)。
|
||||
// 真机 2026-10-09 实测残留 3 个 Running 僵尸,故引入时间余量判定。
|
||||
assert.ok(restartSrc.includes('AddHours(-1)'), '缺少 1 小时余量判定');
|
||||
assert.ok(restartSrc.includes('LastRunTime -lt $cut'), '必须按 LastRunTime 与阈值比较');
|
||||
assert.ok(restartSrc.includes('Get-ScheduledTaskInfo'), '需要读取任务的运行信息');
|
||||
// 禁止回到"只清非 Running"的老写法
|
||||
assert.ok(
|
||||
!restartSrc.includes("Where-Object { $_.State -ne 'Running' } | Unregister-ScheduledTask"),
|
||||
'检测到已废弃的「只清非 Running」写法',
|
||||
);
|
||||
});
|
||||
|
||||
test('自重启任务清理:清理动作带 -ErrorAction SilentlyContinue(不因个别任务失败中断重启)', () => {
|
||||
assert.ok(
|
||||
restartSrc.includes('Unregister-ScheduledTask -Confirm:$false -ErrorAction SilentlyContinue'),
|
||||
'清理动作需容错,否则个别任务异常会中断整个重启流程',
|
||||
);
|
||||
});
|
||||
Reference in new issue
Block a user