bookworm-smart-assistant/scripts/patches/patch-w3-slow-log-alert.js

118 lines
4.2 KiB
JavaScript
Raw Permalink 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
/**
* W3 补丁 — 2026-04-16 audit-2026-04-16 WARNING
*
* 问题: stop-dispatcher.js L172 慢 Stop 事件 (>2s) 静默写入 hook-slow.log
* 无 stderr 输出Claude Code 界面无法感知log-rotator 已轮转 (P3+)
* 但用户不知道问题存在。
*
* 修复方案:
* (a) 在 appendFileSync 后追加 process.stderr.write 告警
* (b) 基于文件 mtime 的 60s 冷却窗口防刷屏
* (c) 冷却 sentinel 路径: debug/.slow-alert.sentinel
*
* 冷却逻辑:
* - 读 sentinel mtime若 now - mtime < 60000ms 则只写文件不输出 stderr
* - 否则输出 stderr + 更新 sentinel mtime
* - 文件不存在视为冷却已过
*
* 文件: hooks/stop-dispatcher.js (L168-179 块)
*
* 幂等: sentinel = 'slow-alert.sentinel'
*/
'use strict';
const fs = require('fs');
const path = require('path');
const TARGET = path.join(__dirname, '..', '..', 'hooks', 'stop-dispatcher.js');
const SENTINEL = 'slow-alert.sentinel';
function main() {
if (!fs.existsSync(TARGET)) {
console.error('[patch-w3] 目标文件不存在');
process.exit(1);
}
const before = fs.readFileSync(TARGET, 'utf8');
if (before.includes(SENTINEL)) {
console.log('[patch-w3] 已打过补丁,跳过');
process.exit(0);
}
const anchor =
' // P2.1 总耗时记录: runAll 超过 2s 视为慢 Stop写 slow.log 辅助诊断\n' +
' try {\n' +
' const _total = Date.now() - _perf_start;\n' +
' if (_total > 2000) {\n' +
' const errLog = path.join(require(\'./lib/root.js\'), \'debug\', \'hook-slow.log\');\n' +
' fs.appendFileSync(errLog, JSON.stringify({\n' +
' ts: new Date().toISOString(),\n' +
' hook: \'stop-dispatcher\',\n' +
' totalMs: _total,\n' +
' }) + \'\\n\');\n' +
' }\n' +
' } catch {}';
const replacement =
' // P2.1 总耗时记录: runAll 超过 2s 视为慢 Stop写 slow.log 辅助诊断\n' +
' // W3 (2026-04-16): 追加 stderr 告警 + 60s 冷却窗口防刷屏\n' +
' try {\n' +
' const _total = Date.now() - _perf_start;\n' +
' if (_total > 2000) {\n' +
' const debugDir = path.join(require(\'./lib/root.js\'), \'debug\');\n' +
' const errLog = path.join(debugDir, \'hook-slow.log\');\n' +
' fs.appendFileSync(errLog, JSON.stringify({\n' +
' ts: new Date().toISOString(),\n' +
' hook: \'stop-dispatcher\',\n' +
' totalMs: _total,\n' +
' }) + \'\\n\');\n' +
' // W3: 冷却聚合 — 60s 内仅告警 1 次防刷屏\n' +
' try {\n' +
' const alertSentinel = path.join(debugDir, \'.slow-alert.sentinel\');\n' +
' const now = Date.now();\n' +
' let lastAlert = 0;\n' +
' try {\n' +
' if (fs.existsSync(alertSentinel)) lastAlert = fs.statSync(alertSentinel).mtimeMs || 0;\n' +
' } catch {}\n' +
' if (now - lastAlert > 60000) {\n' +
' process.stderr.write(\'[stop-dispatcher] 慢 Stop 事件 (\' + _total + \'ms) — 详见 debug/hook-slow.log\\n\');\n' +
' try {\n' +
' const _tmp = alertSentinel + \'.tmp.\' + process.pid;\n' +
' fs.writeFileSync(_tmp, String(now));\n' +
' fs.renameSync(_tmp, alertSentinel);\n' +
' } catch {}\n' +
' }\n' +
' } catch {}\n' +
' }\n' +
' } catch {}';
if (!before.includes(anchor)) {
console.error('[patch-w3] 锚点未匹配');
process.exit(2);
}
const after = before.replace(anchor, replacement);
if (after === before) {
console.error('[patch-w3] 替换无效果');
process.exit(3);
}
const backup = TARGET + '.bak.w3.' + Date.now();
fs.writeFileSync(backup, before);
const _tmp = TARGET + '.tmp.' + process.pid;
fs.writeFileSync(_tmp, after);
fs.renameSync(_tmp, TARGET);
console.log('[patch-w3] ✓ stop-dispatcher.js 慢 Stop 告警通道已加装');
console.log('[patch-w3] 备份: ' + path.basename(backup));
console.log('[patch-w3] 冷却窗口: 60s, sentinel = debug/.slow-alert.sentinel');
}
try {
main();
} catch (e) {
console.error('[patch-w3] 异常:', e.message);
process.exit(99);
}