From 3b29f118621ff9ef20b149e838aa7b337f824da4 Mon Sep 17 00:00:00 2001 From: fuyou Date: Fri, 22 Aug 2025 23:42:03 +0800 Subject: [PATCH] fix(cli): improve stdin handling and add initial state check (#6747) --- packages/cli/src/utils/readStdin.ts | 104 ++++++++++++++++++++-------- 1 file changed, 75 insertions(+), 29 deletions(-) diff --git a/packages/cli/src/utils/readStdin.ts b/packages/cli/src/utils/readStdin.ts index f309c53e..26ebeb3a 100644 --- a/packages/cli/src/utils/readStdin.ts +++ b/packages/cli/src/utils/readStdin.ts @@ -9,40 +9,86 @@ export async function readStdin(): Promise { return new Promise((resolve, reject) => { let data = ''; let totalSize = 0; + let hasReceivedData = false; + process.stdin.setEncoding('utf8'); - const onReadable = () => { - let chunk; - while ((chunk = process.stdin.read()) !== null) { - if (totalSize + chunk.length > MAX_STDIN_SIZE) { - const remainingSize = MAX_STDIN_SIZE - totalSize; - data += chunk.slice(0, remainingSize); - console.warn( - `Warning: stdin input truncated to ${MAX_STDIN_SIZE} bytes.`, - ); - process.stdin.destroy(); // Stop reading further - break; - } - data += chunk; - totalSize += chunk.length; - } - }; - - const onEnd = () => { - cleanup(); - resolve(data); - }; - - const onError = (err: Error) => { - cleanup(); - reject(err); - }; - - const cleanup = () => { + function cleanup() { + clearTimeout(timeout); process.stdin.removeListener('readable', onReadable); process.stdin.removeListener('end', onEnd); process.stdin.removeListener('error', onError); - }; + } + + function processChunk(chunk: string): boolean { + hasReceivedData = true; + if (totalSize + chunk.length > MAX_STDIN_SIZE) { + const remainingSize = MAX_STDIN_SIZE - totalSize; + data += chunk.slice(0, remainingSize); + console.warn( + `Warning: stdin input truncated to ${MAX_STDIN_SIZE} bytes.`, + ); + process.stdin.destroy(); + return true; // Indicates truncation occurred + } else { + data += chunk; + totalSize += chunk.length; + return false; + } + } + + function checkInitialState(): boolean { + if (process.stdin.destroyed || process.stdin.readableEnded) { + cleanup(); + resolve(''); + return true; + } + + const chunk = process.stdin.read(); + if (chunk !== null) { + processChunk(chunk); + return false; + } + + if (!process.stdin.readable) { + cleanup(); + resolve(''); + return true; + } + + return false; + } + + if (checkInitialState()) { + return; + } + + function onReadable() { + let chunk; + while ((chunk = process.stdin.read()) !== null) { + const truncated = processChunk(chunk); + if (truncated) { + break; + } + } + } + + function onEnd() { + cleanup(); + resolve(data); + } + + function onError(err: Error) { + cleanup(); + reject(err); + } + + const timeout = setTimeout(() => { + if (!hasReceivedData) { + cleanup(); + resolve(''); + } + }, 50); process.stdin.on('readable', onReadable); process.stdin.on('end', onEnd);