37 lines
1.9 KiB
JavaScript
37 lines
1.9 KiB
JavaScript
#!/usr/bin/env node
|
||
/**
|
||
* enforce-floor-escape-consume (М6) — PostToolUse-консьюмер. После фактического
|
||
* исполнения floor-действия с совпавшим пропуском пишет отметку погашения
|
||
* (one-shot). Снимок-блок (PreToolUse) → действие не исполнилось → PostToolUse не
|
||
* сработал → пропуск НЕ сгорел. fail-open (никогда не бросаем из PostToolUse).
|
||
*/
|
||
import { appendFileSync, mkdirSync } from 'node:fs';
|
||
import { join, dirname } from 'node:path';
|
||
import { homedir } from 'node:os';
|
||
import { readStdin, parseEventJson } from './enforce-hook-helpers.mjs';
|
||
import { loadFloorEscapes, loadConsumed, canonicalAction } from './escape-grant.mjs';
|
||
import { consumeDecision } from './floor-escape-consume.mjs';
|
||
|
||
/** Чистая проводка: канон-строка из события + решение погашения. grants/consumed инъектируемы. */
|
||
export function decideMark(event, { grants, consumed, now = Date.now() }) {
|
||
const action = canonicalAction(event && event.tool_name, (event && event.tool_input) || {});
|
||
return consumeDecision({ action, grants, consumed, now });
|
||
}
|
||
|
||
async function main() {
|
||
try {
|
||
const ev = parseEventJson(await readStdin());
|
||
const sess = (ev && ev.session_id) || 'unknown';
|
||
const mark = decideMark(ev, { grants: loadFloorEscapes(sess), consumed: loadConsumed(sess) });
|
||
if (mark) {
|
||
const path = join(homedir(), '.claude', 'runtime', `floor-escape-consumed-${sess}.jsonl`);
|
||
try { mkdirSync(dirname(path), { recursive: true }); } catch {}
|
||
try { appendFileSync(path, JSON.stringify(mark) + '\n'); } catch {}
|
||
}
|
||
} catch { /* fail-open: никогда не бросаем из PostToolUse */ }
|
||
process.exit(0);
|
||
}
|
||
|
||
const isCli = (process.argv[1] || '').replace(/\\/g, '/').endsWith('/enforce-floor-escape-consume.mjs');
|
||
if (isCli) main();
|