84 lines
3.3 KiB
JavaScript
84 lines
3.3 KiB
JavaScript
|
|
#!/usr/bin/env node
|
||
|
|
/**
|
||
|
|
* P0-1 Patch: 可观测性指标
|
||
|
|
* - prompt-dispatcher.js: 追加 metrics.emit('route', ...) 含路由延迟/置信度/技能
|
||
|
|
* - agent-claim-observer.js: 追加 model/duration_ms 字段 + metrics.emit('agent', ...)
|
||
|
|
* 幂等: sentinel [P0-1] METRICS_EMIT_v1
|
||
|
|
*/
|
||
|
|
'use strict';
|
||
|
|
const fs = require('fs');
|
||
|
|
const path = require('path');
|
||
|
|
|
||
|
|
const HOOKS = path.resolve(__dirname, '..', '..', 'hooks');
|
||
|
|
const SENTINEL = '[P0-1] METRICS_EMIT_v1';
|
||
|
|
|
||
|
|
function patchFile(name, fn) {
|
||
|
|
const fp = path.join(HOOKS, name);
|
||
|
|
if (!fs.existsSync(fp)) { console.log('[SKIP] ' + name + ' — not found'); return false; }
|
||
|
|
const src = fs.readFileSync(fp, 'utf8');
|
||
|
|
if (src.includes(SENTINEL)) { console.log('[SKIP] ' + name + ' — already patched'); return false; }
|
||
|
|
fs.writeFileSync(fp + '.bak-p01.' + Date.now(), src);
|
||
|
|
const out = fn(src);
|
||
|
|
const tmp = fp + '.tmp.' + process.pid;
|
||
|
|
fs.writeFileSync(tmp, out, 'utf8');
|
||
|
|
fs.renameSync(tmp, fp);
|
||
|
|
console.log('[DONE] ' + name);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// --- prompt-dispatcher.js ---
|
||
|
|
function patchDispatcher(src) {
|
||
|
|
const marker = "require('fs').appendFileSync(timingPath, entry);";
|
||
|
|
if (!src.includes(marker)) throw new Error('marker not found in prompt-dispatcher.js');
|
||
|
|
|
||
|
|
const lines = [
|
||
|
|
'',
|
||
|
|
' // ' + SENTINEL,
|
||
|
|
" try {",
|
||
|
|
" const metrics = require('./lib/metrics.js');",
|
||
|
|
" let ri = {};",
|
||
|
|
" try {",
|
||
|
|
" const parsed = JSON.parse(stdout || '{}');",
|
||
|
|
" const ctx = (parsed.hookSpecificOutput && parsed.hookSpecificOutput.additionalContext) || '';",
|
||
|
|
" const cm = ctx.match(/\\u7f6e\\u4fe1\\u5ea6\\s+(\\d+)%/);",
|
||
|
|
" const sm = ctx.match(/\\u4e3b\\u8def\\u7531:\\s+(\\S+)/);",
|
||
|
|
" ri = { confidence: cm ? +cm[1] : null, skill: sm ? sm[1] : null };",
|
||
|
|
" } catch {}",
|
||
|
|
" metrics.emit('route', Object.assign({ hook: 'prompt-dispatcher', elapsed_ms: elapsed }, ri));",
|
||
|
|
" } catch {}",
|
||
|
|
];
|
||
|
|
return src.replace(marker, marker + lines.join('\n'));
|
||
|
|
}
|
||
|
|
|
||
|
|
// --- agent-claim-observer.js ---
|
||
|
|
function patchObserver(src) {
|
||
|
|
// 1. 追加字段到 record 对象
|
||
|
|
const fieldMarker = ' text_length: text.length,';
|
||
|
|
if (!src.includes(fieldMarker)) throw new Error('field marker not found in agent-claim-observer.js');
|
||
|
|
|
||
|
|
const newFields = [
|
||
|
|
' text_length: text.length,',
|
||
|
|
' // ' + SENTINEL,
|
||
|
|
" model: (input.tool_input && input.tool_input.model) || '',",
|
||
|
|
" tool_name: (input.tool_input && input.tool_input.name) || 'Agent',",
|
||
|
|
" duration_ms: (input.tool_response && input.tool_response.duration_ms) || null,",
|
||
|
|
].join('\n');
|
||
|
|
let result = src.replace(fieldMarker, newFields);
|
||
|
|
|
||
|
|
// 2. 追加 metrics emit
|
||
|
|
const appendMarker = "fs.appendFileSync(LOG_FILE, JSON.stringify(record) + '\\n');";
|
||
|
|
if (!result.includes(appendMarker)) throw new Error('append marker not found');
|
||
|
|
|
||
|
|
result = result.replace(
|
||
|
|
appendMarker,
|
||
|
|
appendMarker + "\n // " + SENTINEL + " — 指标发射\n try { require('./lib/metrics.js').emit('agent', record); } catch {}"
|
||
|
|
);
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
// --- Execute ---
|
||
|
|
let count = 0;
|
||
|
|
if (patchFile('prompt-dispatcher.js', patchDispatcher)) count++;
|
||
|
|
if (patchFile('agent-claim-observer.js', patchObserver)) count++;
|
||
|
|
console.log('\nP0-1 Metrics: ' + count + '/2 files patched');
|