Files
stack/packages/mosaic/src/commands/gateway/piped-credentials.ts
T
ops-deploy-01 96eb0fb010
ci/woodpecker/pr/ci Pipeline failed
fix(#1394): recover-token headless — dual path (flag + piped stdin) with documented precedence
ensureSession credential resolution when sign-in is needed, in order:
1. explicit --email flag (highest)
2. non-TTY stdin: line 1 email, line 2 password (printf 'email\npassword\n' | …)
   — for headless callers without argv access
3. interactive prompt (TTY only)
Precedence documented on ensureSession and in --help; headless with NO
credentials exits 2 with guidance instead of hanging on prompts.
Password never read from argv (ps-visible); stdin line kept untrimmed.
New piped-credentials.ts module so the reader is testable against real
streams (4 stream specs) while token-ops specs mock the seam (5 specs).
2026-08-25 10:08:14 -05:00

27 lines
961 B
TypeScript

import { createInterface } from 'node:readline';
/**
* Read email + password as two lines from non-TTY stdin (the headless dual
* path for callers that cannot pass argv: printf 'email\npassword\n' | …).
* Caller gates on !isTTY; the password line is kept as-is (no trim —
* whitespace may be intentional).
*
* Separate module (not login.ts) so tests can exercise the REAL reader
* against real streams while token-ops specs mock this seam cleanly.
*/
export function readCredentialsFromPipedStdin(
input: NodeJS.ReadableStream = process.stdin,
): Promise<{ email: string | null; password: string | null }> {
return new Promise((resolve) => {
const lines: string[] = [];
const rl = createInterface({ input });
rl.on('line', (l) => {
lines.push(l);
if (lines.length >= 2) rl.close();
});
rl.on('close', () => {
resolve({ email: (lines[0] ?? '').trim() || null, password: lines[1] ?? null });
});
});
}