#!/usr/bin/env node /** * UserPromptSubmit Hook: 统一提交派遣器 (v6.2 M3) * * 合并 2 个 UserPromptSubmit 钩子为单进程,减少 ~50ms 串行开销: * 1. security-startup-guard (同步, <5ms, fail-open) * 2. route-interceptor-bundle (异步, 路由引擎, fail-open) * * 读取 stdin 一次: * - security-startup-guard 的 runGuard() 不需要 stdin 数据 (仅检查 lockfile + settings) * - route-interceptor-bundle 作为子进程接收 stdin 数据 (保留其 timeout 逻辑) * * 退出码: 0 (始终放行, UserPromptSubmit 不阻断) */ 'use strict'; const { spawn } = require('child_process'); const path = require('path'); const HOOKS_DIR = __dirname; function main() { let rawInput = ''; process.stdin.setEncoding('utf8'); process.stdin.on('data', (chunk) => { rawInput += chunk; if (rawInput.length > 256 * 1024) { // 输入过大,直接退出 process.exit(0); } }); process.stdin.on('end', () => { // --- 阶段 1: security-startup-guard (同步, fail-open) --- try { const guard = require('./security-startup-guard.js'); if (guard && guard.runGuard) { guard.runGuard(); } } catch {} // --- 阶段 2: route-interceptor-bundle (子进程, 保留独立 timeout) --- const bundlePath = path.join(HOOKS_DIR, 'route-interceptor-bundle.js'); const hookTimingMs = Date.now(); // [P3-6] 计时起点 try { const child = spawn('node', [bundlePath], { stdio: ['pipe', 'pipe', 'pipe'], timeout: 3000, }); let stdout = ''; let stderr = ''; child.stdout.on('data', (d) => { stdout += d; }); child.stderr.on('data', (d) => { stderr += d; }); child.on('close', () => { // [P3-6] 记录路由耗时 try { const elapsed = Date.now() - hookTimingMs; const timingPath = path.join(HOOKS_DIR, '..', 'debug', 'hook-timing.jsonl'); const entry = JSON.stringify({ ts: new Date().toISOString(), hook: 'route-interceptor-bundle', elapsed }) + '\n'; require('fs').appendFileSync(timingPath, entry); } catch {} // 转发 route-interceptor-bundle 的 stdout (含 additionalContext) if (stdout) { process.stdout.write(stdout); } if (stderr) { process.stderr.write(stderr); } process.exit(0); }); child.on('error', () => { process.exit(0); // fail-open }); // 将 stdin 数据传递给子进程 child.stdin.write(rawInput); child.stdin.end(); } catch { process.exit(0); // fail-open } }); } if (typeof module !== 'undefined') { module.exports = { main }; } if (require.main === module) { main(); }