139 lines
6.1 KiB
JavaScript
139 lines
6.1 KiB
JavaScript
|
|
#!/usr/bin/env node
|
|||
|
|
/**
|
|||
|
|
* Patch C2: 强制 evolution-log / route-feedback 走 safeAppendJsonl({useLock:true})
|
|||
|
|
* 并将 stop-dispatcher 的 dedup 从 Batch1 移到 Batch3 尾部,避免 dedup 后被
|
|||
|
|
* 后续 Batch append 抢占覆盖丢失条目。
|
|||
|
|
* 幂等: sentinel C2_SAFE_APPEND_v1 / C2_DEDUP_TAIL_v1
|
|||
|
|
*/
|
|||
|
|
const fs = require('fs');
|
|||
|
|
const path = require('path');
|
|||
|
|
|
|||
|
|
const ROOT = path.join(__dirname, '..', '..');
|
|||
|
|
const DISP = path.join(ROOT, 'hooks', 'stop-dispatcher.js');
|
|||
|
|
const ROUTE_AUDITOR = path.join(ROOT, 'hooks', 'route-auditor.js');
|
|||
|
|
const ADAPTIVE = path.join(ROOT, 'scripts', 'adaptive-disambiguator.js');
|
|||
|
|
const SAFE_APPEND_REQUIRE = "require('./lib/safe-append.js')";
|
|||
|
|
const SENTINEL_APPEND = 'C2_SAFE_APPEND_v1';
|
|||
|
|
const SENTINEL_DEDUP = 'C2_DEDUP_TAIL_v1';
|
|||
|
|
|
|||
|
|
function bak(file, src, tag) {
|
|||
|
|
const dir = path.join(path.dirname(file), 'archive');
|
|||
|
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|||
|
|
fs.writeFileSync(path.join(dir, path.basename(file) + '.bak.' + tag + '.' + Date.now()), src);
|
|||
|
|
}
|
|||
|
|
function write(file, content) {
|
|||
|
|
const tmp = file + '.tmp.' + process.pid;
|
|||
|
|
fs.writeFileSync(tmp, content);
|
|||
|
|
fs.renameSync(tmp, file);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// --- 1) stop-dispatcher: evolution-log 写入加锁 + dedup 移尾部 ---
|
|||
|
|
function patchDispatcher() {
|
|||
|
|
const src = fs.readFileSync(DISP, 'utf8');
|
|||
|
|
let patched = src;
|
|||
|
|
|
|||
|
|
if (patched.includes(SENTINEL_APPEND) && patched.includes(SENTINEL_DEDUP)) {
|
|||
|
|
console.error('[patch-c2] dispatcher already applied'); return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// (a) evolution-log append 改 safeAppendJsonl (CRLF-tolerant, 只匹配 appendFileSync 行)
|
|||
|
|
if (!patched.includes(SENTINEL_APPEND)) {
|
|||
|
|
const oldEvoLine = " fs.appendFileSync(evoLog, (needsNewline ? '\\n' : '') + JSON.stringify(entry) + '\\n');";
|
|||
|
|
if (!patched.includes(oldEvoLine)) { console.error('[patch-c2] evo anchor miss'); process.exit(3); }
|
|||
|
|
const newEvoLine =
|
|||
|
|
" // " + SENTINEL_APPEND + ": 文件锁避免并发半写\n" +
|
|||
|
|
" if (needsNewline) { try { fs.appendFileSync(evoLog, '\\n'); } catch {} }\n" +
|
|||
|
|
" try {\n" +
|
|||
|
|
" const { safeAppendJsonl } = require('./lib/safe-append.js');\n" +
|
|||
|
|
" safeAppendJsonl(evoLog, entry, { useLock: true });\n" +
|
|||
|
|
" } catch {\n" +
|
|||
|
|
" try { fs.appendFileSync(evoLog, JSON.stringify(entry) + '\\n'); } catch {}\n" +
|
|||
|
|
" }";
|
|||
|
|
patched = patched.replace(oldEvoLine, newEvoLine);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// (b) dedup 从 Batch1 移除 → Batch3 尾部 (CRLF-tolerant: 不含行尾)
|
|||
|
|
if (!patched.includes(SENTINEL_DEDUP)) {
|
|||
|
|
const oldDedupInBatch1 = " race('dedup-errors', () => deduplicateHookErrors(), 500),";
|
|||
|
|
if (!patched.includes(oldDedupInBatch1)) {
|
|||
|
|
console.error('[patch-c2] dedup batch1 anchor miss'); process.exit(4);
|
|||
|
|
}
|
|||
|
|
patched = patched.replace(
|
|||
|
|
oldDedupInBatch1,
|
|||
|
|
" // " + SENTINEL_DEDUP + ": 已迁移到 Batch3 尾部 (避免被后续 append 抢占覆盖)"
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// 将 dedup 插入到 auto-git-push 之后 (只匹配单行关键语句,CRLF 安全)
|
|||
|
|
const tailSingle = " }, 5000);";
|
|||
|
|
// 找最后一个 ` }, 5000);` (auto-git-push 的结尾)
|
|||
|
|
const lastIdx = patched.lastIndexOf(tailSingle);
|
|||
|
|
if (lastIdx === -1) { console.error('[patch-c2] tail anchor miss'); process.exit(5); }
|
|||
|
|
const before = patched.slice(0, lastIdx + tailSingle.length);
|
|||
|
|
const after = patched.slice(lastIdx + tailSingle.length);
|
|||
|
|
patched = before +
|
|||
|
|
"\n // " + SENTINEL_DEDUP + ": dedup 放最后,确保所有 append 已完成" +
|
|||
|
|
"\n await _budgetRace('dedup-errors', () => deduplicateHookErrors(), 500);" +
|
|||
|
|
after;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (patched === src) { console.error('[patch-c2] dispatcher no change'); return; }
|
|||
|
|
bak(DISP, src, 'c2');
|
|||
|
|
write(DISP, patched);
|
|||
|
|
console.error('[patch-c2] dispatcher patched');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// --- 2) route-auditor: route-feedback 写入加锁 ---
|
|||
|
|
function patchRouteAuditor() {
|
|||
|
|
if (!fs.existsSync(ROUTE_AUDITOR)) return;
|
|||
|
|
const src = fs.readFileSync(ROUTE_AUDITOR, 'utf8');
|
|||
|
|
if (src.includes(SENTINEL_APPEND)) { console.error('[patch-c2] route-auditor already applied'); return; }
|
|||
|
|
|
|||
|
|
// 只处理 route-feedback.jsonl 路径;保留其他 log
|
|||
|
|
const old = " fs.appendFileSync(logFile, JSON.stringify(entry) + '\\n');";
|
|||
|
|
if (!src.includes(old)) { console.error('[patch-c2] route-auditor anchor miss, skip'); return; }
|
|||
|
|
|
|||
|
|
const replacement =
|
|||
|
|
" // " + SENTINEL_APPEND + ": 使用 safeAppendJsonl 文件锁防并发损坏\n" +
|
|||
|
|
" try {\n" +
|
|||
|
|
" const { safeAppendJsonl } = require('./lib/safe-append.js');\n" +
|
|||
|
|
" safeAppendJsonl(logFile, entry, { useLock: true });\n" +
|
|||
|
|
" } catch {\n" +
|
|||
|
|
" try { fs.appendFileSync(logFile, JSON.stringify(entry) + '\\n'); } catch {}\n" +
|
|||
|
|
" }";
|
|||
|
|
|
|||
|
|
const patched = src.replace(old, replacement);
|
|||
|
|
if (patched === src) return;
|
|||
|
|
bak(ROUTE_AUDITOR, src, 'c2');
|
|||
|
|
write(ROUTE_AUDITOR, patched);
|
|||
|
|
console.error('[patch-c2] route-auditor patched');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// --- 3) adaptive-disambiguator: evolution-log append 加锁 ---
|
|||
|
|
function patchAdaptive() {
|
|||
|
|
if (!fs.existsSync(ADAPTIVE)) return;
|
|||
|
|
const src = fs.readFileSync(ADAPTIVE, 'utf8');
|
|||
|
|
if (src.includes(SENTINEL_APPEND)) { console.error('[patch-c2] adaptive already applied'); return; }
|
|||
|
|
|
|||
|
|
const old = " fs.appendFileSync(evolutionLog, JSON.stringify(logEntry) + '\\n');";
|
|||
|
|
if (!src.includes(old)) { console.error('[patch-c2] adaptive anchor miss, skip'); return; }
|
|||
|
|
|
|||
|
|
const replacement =
|
|||
|
|
" // " + SENTINEL_APPEND + ": 与 stop-dispatcher consistency-sentinel 共享 evolution-log,必须加锁\n" +
|
|||
|
|
" try {\n" +
|
|||
|
|
" const { safeAppendJsonl } = require(require('path').join(__dirname, '..', 'hooks', 'lib', 'safe-append.js'));\n" +
|
|||
|
|
" safeAppendJsonl(evolutionLog, logEntry, { useLock: true });\n" +
|
|||
|
|
" } catch {\n" +
|
|||
|
|
" try { fs.appendFileSync(evolutionLog, JSON.stringify(logEntry) + '\\n'); } catch {}\n" +
|
|||
|
|
" }";
|
|||
|
|
|
|||
|
|
const patched = src.replace(old, replacement);
|
|||
|
|
if (patched === src) return;
|
|||
|
|
bak(ADAPTIVE, src, 'c2');
|
|||
|
|
write(ADAPTIVE, patched);
|
|||
|
|
console.error('[patch-c2] adaptive patched');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
patchDispatcher();
|
|||
|
|
patchRouteAuditor();
|
|||
|
|
patchAdaptive();
|