bookworm-smart-assistant/scripts/patches/patch-c1-batch3-budget.js
Bookworm Admin b7a8e29d21 release: v6.7.0 - OTA E2E test release
- VERSION file as authoritative version source
- export.mjs reads VERSION with package.json fallback
- bw-ota.ps1 DryRun mode for safe testing
- auto-setup.ps1 bumped to v3.2.0 (Phase 8 OTA)
2026-04-27 17:59:44 +08:00

93 lines
4.3 KiB
JavaScript
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.

#!/usr/bin/env node
/**
* Patch C1: stop-dispatcher Batch3 预算控制
* Batch3 串行 5 阶段累计最高 14800ms远超 Stop hook 5000ms 预算。
* 引入 deadline = _perf_start + BUDGET_MS超时后跳过剩余阶段。
* 每阶段超时自动截断为 min(原预算, deadline - now)。
* 幂等: sentinel C1_BATCH3_BUDGET_v1
*/
const fs = require('fs');
const path = require('path');
const TARGET = path.join(__dirname, '..', '..', 'hooks', 'stop-dispatcher.js');
const SENTINEL = 'C1_BATCH3_BUDGET_v1';
function main() {
const src = fs.readFileSync(TARGET, 'utf8');
if (src.includes(SENTINEL)) { console.error('[patch-c1] already applied'); process.exit(0); }
// 查找 Batch3 头部锚点
const anchor = " // ─── Batch 3 · 尾部串行 (明确依赖顺序) ───\n";
if (!src.includes(anchor)) { console.error('[patch-c1] batch3 header anchor missing'); process.exit(3); }
// 原 Batch3 5 个阶段 (consistency-sentinel/auto-cleanup/log-rotator/auto-backup/auto-git-push)
const oldBlock =
" // ─── Batch 3 · 尾部串行 (明确依赖顺序) ───\n" +
" // sentinel append evolution-log → cleanup 才能 truncate\n" +
" // auto-backup → auto-git-push (快照先于远端推送)\n" +
" _stageRecords.push(await race('consistency-sentinel', () => runConsistencySentinel(), 1000));\n" +
" _stageRecords.push(await race('auto-cleanup', () => {\n" +
" const ac = require('../scripts/auto-cleanup.js');\n" +
" if (ac.main) ac.main({ execute: true, ifStale: 86400 });\n" +
" }, 5000));\n" +
" _stageRecords.push(await race('log-rotator', () => {\n" +
" const lr = require('./log-rotator.js');\n" +
" if (lr.runRotation) lr.runRotation();\n" +
" }, 800));\n" +
" _stageRecords.push(await race('auto-backup', () => {\n" +
" const backup = require('../scripts/auto-backup.js');\n" +
" backup();\n" +
" }, 3000));\n" +
" _stageRecords.push(await race('auto-git-push', () => {\n" +
" const sync = require('../scripts/auto-git-sync.js');\n" +
" sync.pushChanges();\n" +
" }, 5000));\n";
if (!src.includes(oldBlock)) { console.error('[patch-c1] batch3 body anchor missing'); process.exit(4); }
const newBlock =
" // ─── Batch 3 · 尾部串行 (明确依赖顺序) ───\n" +
" // " + SENTINEL + ": 总预算硬截断,防止 Stop hook 整体超过 5000ms 宿主 kill\n" +
" // sentinel append evolution-log → cleanup 才能 truncate\n" +
" // auto-backup → auto-git-push (快照先于远端推送)\n" +
" const _BUDGET_MS = 4200;\n" +
" const _deadline = _perf_start + _BUDGET_MS;\n" +
" const _budgetRace = async (name, fn, origMs) => {\n" +
" const remaining = _deadline - Date.now();\n" +
" if (remaining <= 100) {\n" +
" _stageRecords.push({ name, ok: false, ms: 0, skipped: true, reason: 'budget-exhausted' });\n" +
" return;\n" +
" }\n" +
" _stageRecords.push(await race(name, fn, Math.min(origMs, remaining)));\n" +
" };\n" +
" await _budgetRace('consistency-sentinel', () => runConsistencySentinel(), 1000);\n" +
" await _budgetRace('auto-cleanup', () => {\n" +
" const ac = require('../scripts/auto-cleanup.js');\n" +
" if (ac.main) ac.main({ execute: true, ifStale: 86400 });\n" +
" }, 5000);\n" +
" await _budgetRace('log-rotator', () => {\n" +
" const lr = require('./log-rotator.js');\n" +
" if (lr.runRotation) lr.runRotation();\n" +
" }, 800);\n" +
" await _budgetRace('auto-backup', () => {\n" +
" const backup = require('../scripts/auto-backup.js');\n" +
" backup();\n" +
" }, 3000);\n" +
" await _budgetRace('auto-git-push', () => {\n" +
" const sync = require('../scripts/auto-git-sync.js');\n" +
" sync.pushChanges();\n" +
" }, 5000);\n";
const patched = src.replace(oldBlock, newBlock);
if (patched === src) { console.error('[patch-c1] no change'); process.exit(5); }
const bakDir = path.join(path.dirname(TARGET), 'archive');
if (!fs.existsSync(bakDir)) fs.mkdirSync(bakDir, { recursive: true });
fs.writeFileSync(path.join(bakDir, 'stop-dispatcher.js.bak.c1.' + Date.now()), src);
const tmp = TARGET + '.tmp.' + process.pid;
fs.writeFileSync(tmp, patched);
fs.renameSync(tmp, TARGET);
console.error('[patch-c1] applied');
}
main();