Use @inquirer/prompts for interactive login input
Replaces the hand-rolled readline/raw-mode password prompt with @inquirer/prompts (input + password). The manual approach broke in bun-compiled binaries because bun doesn't properly re-open stdin after closing a readline instance. inquirer handles TTY, raw mode, and password masking correctly across both node and bun runtimes.
This commit is contained in:
59
bin/quak.ts
59
bin/quak.ts
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { createInterface } from "node:readline/promises";
|
||||
import { stdin, stdout, stderr } from "node:process";
|
||||
import { input, password as passwordPrompt } from "@inquirer/prompts";
|
||||
import { stdout, stderr } from "node:process";
|
||||
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { Command } from "commander";
|
||||
@@ -45,46 +45,10 @@ const requireSession = (): Client => {
|
||||
return Client.fromJSON(snapshot);
|
||||
};
|
||||
|
||||
const prompt = async (message: string): Promise<string> => {
|
||||
const rl = createInterface({ input: stdin, output: stderr });
|
||||
const answer = await rl.question(message);
|
||||
rl.close();
|
||||
return answer;
|
||||
};
|
||||
const prompt = async (message: string): Promise<string> => input({ message });
|
||||
|
||||
const promptPassword = async (message: string): Promise<string> => {
|
||||
if (!stdin.isTTY) {
|
||||
return prompt(message);
|
||||
}
|
||||
stderr.write(message);
|
||||
stdin.setRawMode(true);
|
||||
stdin.resume();
|
||||
const chars: string[] = [];
|
||||
return new Promise((resolve) => {
|
||||
const onData = (buf: Buffer) => {
|
||||
for (const byte of buf) {
|
||||
if (byte === 3) {
|
||||
stdin.setRawMode(false);
|
||||
process.exit(130);
|
||||
}
|
||||
if (byte === 13 || byte === 10) {
|
||||
stdin.setRawMode(false);
|
||||
stdin.removeListener("data", onData);
|
||||
stdin.pause();
|
||||
stderr.write("\n");
|
||||
resolve(chars.join(""));
|
||||
return;
|
||||
}
|
||||
if (byte === 127 || byte === 8) {
|
||||
chars.pop();
|
||||
} else {
|
||||
chars.push(String.fromCharCode(byte));
|
||||
}
|
||||
}
|
||||
};
|
||||
stdin.on("data", onData);
|
||||
});
|
||||
};
|
||||
const promptSecret = async (message: string): Promise<string> =>
|
||||
passwordPrompt({ message, mask: true });
|
||||
|
||||
const program = new Command();
|
||||
|
||||
@@ -98,18 +62,9 @@ program
|
||||
.description("Log in to an Ente account and save the session")
|
||||
.action(async () => {
|
||||
await init();
|
||||
const email =
|
||||
process.env.QUAK_EMAIL ??
|
||||
(stdin.isTTY ? await prompt("Email: ") : null);
|
||||
const email = process.env.QUAK_EMAIL ?? (await prompt("Email"));
|
||||
const password =
|
||||
process.env.QUAK_PASSWORD ??
|
||||
(stdin.isTTY ? await promptPassword("Password: ") : null);
|
||||
if (!email || !password) {
|
||||
stderr.write(
|
||||
"Set QUAK_EMAIL and QUAK_PASSWORD env vars for non-interactive use.\n",
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
process.env.QUAK_PASSWORD ?? (await promptSecret("Password"));
|
||||
|
||||
stderr.write("Authenticating...\n");
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user