mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 04:12:10 +03:00
* fix(cli): non-interactive-safe prompts + singular `context` alias Two CLI-polish follow-ups (the third — an async-warn refactor for #2807 — is moot: #4373 replaced the Gemini remote-URL drop+warn with fileData pass-through, so no async warn remains). 1) createPrompt (io.mjs) — the shared interactive helper used rl.question with no EOF guard, so a non-interactive stdin (pipe, CI, `< /dev/null`) left the await pending and Node warned about an 'unsettled top-level await' while the command hung. ask/askSecret now resolve on the readline `close` event (fired on EOF) with the default / empty string. 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. This fixes every command that prompts, centrally (mirrors the contexts `confirm()` fix from #4397). 2) `contexts` gains a singular `context` alias — the connect output and older docs said `omniroute context current`; the alias keeps that muscle-memory working. Tests: cli-io.test.ts (ask default/empty + askSecret resolve on EOF, no hang) and a cli-contexts.test.ts case asserting the `context` alias is registered. 11/11. * test(cli): mock .alias() in the fake program for the contexts subcommand test The existing 'registers a current subcommand' test uses a minimal fake commander program; registerContexts now calls .alias("context"), so the fake needs an alias() stub (returns this) or the chain throws. Add it. (Self-introduced by the alias in this branch; my 4 new tests already pass in CI.)
84 lines
2.5 KiB
JavaScript
84 lines
2.5 KiB
JavaScript
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`);
|
|
}
|