Spaces:
Runtime error
Runtime error
| import readline from "node:readline"; | |
| export function createPrompt() { | |
| const rl = readline.createInterface({ | |
| input: process.stdin, | |
| output: process.stdout, | |
| }); | |
| // Non-interactive stdin (pipe, CI, EOF via `< /dev/null`) cannot answer an | |
| // interactive prompt. Without a guard, `rl.question` never fires its callback β | |
| // the await stays pending and Node warns about an "unsettled top-level await" at | |
| // exit. Resolving on the readline `close` event (which fires on stdin EOF) | |
| // returns the default/empty instead of hanging. A genuinely piped line still | |
| // arrives via the question callback first, so `echo value | omniroute β¦` keeps | |
| // working β only the no-input EOF case falls back. | |
| function ask(question, defaultValue = "") { | |
| const suffix = defaultValue ? ` (${defaultValue})` : ""; | |
| return new Promise((resolve) => { | |
| let settled = false; | |
| const done = (v) => { | |
| if (!settled) { | |
| settled = true; | |
| resolve(v); | |
| } | |
| }; | |
| rl.once("close", () => done(defaultValue)); | |
| rl.question(`${question}${suffix}: `, (answer) => { | |
| const trimmed = answer.trim(); | |
| done(trimmed || defaultValue); | |
| }); | |
| }); | |
| } | |
| function askSecret(question) { | |
| return new Promise((resolve) => { | |
| let settled = false; | |
| const saved = rl._writeToOutput.bind(rl); | |
| const done = (v) => { | |
| if (!settled) { | |
| settled = true; | |
| rl._writeToOutput = saved; | |
| resolve(v); | |
| } | |
| }; | |
| let prompted = false; | |
| rl._writeToOutput = function (str) { | |
| if (!prompted) { | |
| rl.output.write(str); | |
| if (str.endsWith(": ")) prompted = true; | |
| return; | |
| } | |
| // Suppress character echo; allow only newlines through | |
| if (str === "\r\n" || str === "\n" || str === "\r") rl.output.write("\n"); | |
| }; | |
| rl.once("close", () => done("")); // non-interactive EOF β empty secret, no hang | |
| rl.question(`${question}: `, (answer) => { | |
| done(answer.trim()); | |
| }); | |
| }); | |
| } | |
| function close() { | |
| rl.close(); | |
| } | |
| return { ask, askSecret, close }; | |
| } | |
| export function printHeading(title) { | |
| console.log(`\n\x1b[1m\x1b[36m${title}\x1b[0m\n`); | |
| } | |
| export function printSuccess(message) { | |
| console.log(`\x1b[32mβ ${message}\x1b[0m`); | |
| } | |
| export function printInfo(message) { | |
| console.log(`\x1b[2m${message}\x1b[0m`); | |
| } | |
| export function printError(message) { | |
| console.log(`\x1b[31mβ ${message}\x1b[0m`); | |
| } | |