// hook-helper.mjs — Shared test helper for hook scripts. // Spawns a hook as a child process and feeds it JSON via stdin. // // Source: ../../../llm-security/tests/hooks/hook-helper.mjs (copied; since order 10 // the kill limit is HOOK_TIMEOUT_MS and a call can set its own) // Provenance: borrowed within the same marketplace (same author, MIT). import { execFile } from 'node:child_process'; import { childEnv } from './child-env.mjs'; // How long a hook may run before the helper kills it. The tests check a // hook's answer, not its speed (SC #14 times itself, tests/helpers/timing.mjs), // so the limit only has to catch a hook that hangs. 5 s killed P7 // (post-edit-check) at load 95-177 in four wave sessions while it was 8/8 // green alone (order 10, 2026-10-09). hooks/hooks.json sets no timeout for // these hooks, so 5 s was stricter than anything the hook meets in use. export const HOOK_TIMEOUT_MS = 30_000; /** * Run a hook script by spawning `node ` and piping `input` to stdin. * * @param {string} scriptPath - Absolute path to the hook .mjs file * @param {object|string} input - JSON payload (object will be stringified) * @returns {Promise<{ code: number, stdout: string, stderr: string }>} */ export function runHook(scriptPath, input) { return runHookWithEnv(scriptPath, input, {}); } /** * Run a hook script with custom environment variables. * * @param {string} scriptPath - Absolute path to the hook .mjs file * @param {object|string} input - JSON payload (object will be stringified) * @param {Record} envOverrides - Extra env vars to set * @param {{ timeoutMs?: number }} [opts] - kill limit for this call (default HOOK_TIMEOUT_MS) * @returns {Promise<{ code: number, stdout: string, stderr: string }>} */ export function runHookWithEnv(scriptPath, input, envOverrides, opts = {}) { return new Promise((resolve) => { const env = childEnv({ ...envOverrides }); const child = execFile( 'node', [scriptPath], { timeout: opts.timeoutMs ?? HOOK_TIMEOUT_MS, env }, (err, stdout, stderr) => { resolve({ code: child.exitCode ?? (err && err.code === 'ERR_CHILD_PROCESS_STDIO_FINAL' ? 0 : 1), stdout: stdout || '', stderr: stderr || '', }); } ); child.stdin.end(typeof input === 'string' ? input : JSON.stringify(input)); }); }