Files
openclaw/src/cli/prompt.ts
NVIDIAN a387068694 fix(cli): handle closed plugin uninstall prompt (#73566)
Merged via squash.

Prepared head SHA: d754ddcf29
Co-authored-by: ai-hpc <183861985+ai-hpc@users.noreply.github.com>
Co-authored-by: hxy91819 <8814856+hxy91819@users.noreply.github.com>
Reviewed-by: @hxy91819
2026-05-05 23:05:20 +08:00

56 lines
1.7 KiB
TypeScript

import { stdin as input, stdout as output } from "node:process";
import readline from "node:readline/promises";
import { isVerbose, isYes } from "../globals.js";
import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js";
export class PromptInputClosedError extends Error {
constructor() {
super("Prompt input closed before an answer was received.");
this.name = "PromptInputClosedError";
}
}
type ReadlineInterface = ReturnType<typeof readline.createInterface>;
function questionUntilClose(rl: ReadlineInterface, question: string): Promise<string> {
return new Promise((resolve, reject) => {
let settled = false;
const finish = (complete: () => void) => {
if (settled) {
return;
}
settled = true;
rl.off("close", onClose);
complete();
};
const onClose = () => finish(() => reject(new PromptInputClosedError()));
rl.once("close", onClose);
void rl.question(question).then(
(answer) => finish(() => resolve(answer)),
(error: unknown) => finish(() => reject(error)),
);
});
}
export async function promptYesNo(question: string, defaultYes = false): Promise<boolean> {
// Simple Y/N prompt honoring global --yes and verbosity flags.
if (isVerbose() && isYes()) {
return true;
} // redundant guard when both flags set
if (isYes()) {
return true;
}
const rl = readline.createInterface({ input, output });
const suffix = defaultYes ? " [Y/n] " : " [y/N] ";
const answer = normalizeLowercaseStringOrEmpty(
await questionUntilClose(rl, `${question}${suffix}`).finally(() => {
rl.close();
}),
);
if (!answer) {
return defaultYes;
}
return answer.startsWith("y");
}