import { createInterface } from 'node:readline'; import { signIn, saveSession } from '../../auth.js'; import { readMeta } from './daemon.js'; /** * Prompt for a single line of input (with echo). */ export function promptLine(question: string): Promise { const rl = createInterface({ input: process.stdin, output: process.stdout }); return new Promise((resolve) => { rl.question(question, (answer) => { rl.close(); resolve(answer.trim()); }); }); } /** * Prompt for a secret value without echoing the typed characters to the terminal. * Uses TTY raw mode when available so that passwords do not appear in terminal * recordings, scrollback, or shared screen sessions. */ export function promptSecret(question: string): Promise { return new Promise((resolve) => { process.stdout.write(question); if (process.stdin.isTTY) { process.stdin.setRawMode(true); } process.stdin.resume(); process.stdin.setEncoding('utf-8'); let secret = ''; const onData = (char: string): void => { if (char === '\n' || char === '\r' || char === '\u0004') { process.stdout.write('\n'); if (process.stdin.isTTY) { process.stdin.setRawMode(false); } process.stdin.pause(); process.stdin.removeListener('data', onData); resolve(secret); } else if (char === '\u0003') { // ^C process.stdout.write('\n'); if (process.stdin.isTTY) { process.stdin.setRawMode(false); } process.stdin.pause(); process.stdin.removeListener('data', onData); process.exit(130); } else if (char === '\u007f' || char === '\b') { secret = secret.slice(0, -1); } else { secret += char; } }; process.stdin.on('data', onData); }); } /** * Shared login helper used by both `mosaic login` and `mosaic gateway login`. * Prompts for email/password if not supplied, signs in, and persists the session. */ export async function runLogin(opts: { gatewayUrl: string; email?: string; password?: string; }): Promise { const email = opts.email ?? (await promptLine('Email: ')); // Do not trim password — it may intentionally contain leading/trailing whitespace const password = opts.password ?? (await promptSecret('Password: ')); const auth = await signIn(opts.gatewayUrl, email, password); saveSession(opts.gatewayUrl, auth); console.log(`Signed in as ${auth.email} (${opts.gatewayUrl})`); } /** * Derive the gateway base URL from meta.json with a fallback. */ export function getGatewayUrl(overrideUrl?: string): string { if (overrideUrl) return overrideUrl; const meta = readMeta(); if (meta) return `http://${meta.host}:${meta.port.toString()}`; return 'http://localhost:14242'; }