import fs from "node:fs"; import readline from "node:readline"; import { stdin as input, stdout as output } from "node:process"; const POLL_MS = 400; function sleep(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)); } function consumeTrigger(triggerPath: string): void { if (fs.existsSync(triggerPath)) { fs.rmSync(triggerPath, { force: true }); } } async function waitForTriggerFile( triggerPath: string, label: string, ): Promise { consumeTrigger(triggerPath); const rel = triggerPath.replace(/\\/g, "/"); console.log(` [录证] ${label} — 文件触发 PowerShell: New-Item -ItemType File -Force -Path "${rel}" 或 CMD: type nul > "${rel}" `); while (!fs.existsSync(triggerPath)) { await sleep(POLL_MS); } consumeTrigger(triggerPath); console.log(`[录证] ${label} 已触发`); } export async function waitForHumanCheckpoint(options: { label: string; message: string; triggerPath: string; rl?: readline.Interface | null; }): Promise { const useFileTrigger = process.env.HUMAN_RECORD_FILE_TRIGGER === "1" || !process.stdin.isTTY; console.log(`\n[录证] ── ${options.label} ──`); console.log(options.message); if (useFileTrigger) { await waitForTriggerFile(options.triggerPath, options.label); return; } const rl = options.rl ?? readline.createInterface({ input, output, terminal: true }); await new Promise((resolve) => { rl.question("\n操作完成后按 Enter 继续… ", () => resolve()); }); if (!options.rl) { rl.close(); } } export function createSharedReadline(): readline.Interface { return readline.createInterface({ input, output, terminal: true }); }