source dump of claude code
at main 77 lines 2.3 kB view raw
1import { openSync } from 'fs' 2import { ReadStream } from 'tty' 3import type { RenderOptions } from '../ink.js' 4import { isEnvTruthy } from './envUtils.js' 5import { logError } from './log.js' 6 7// Cached stdin override - computed once per process 8let cachedStdinOverride: ReadStream | undefined | null = null 9 10/** 11 * Gets a ReadStream for /dev/tty when stdin is piped. 12 * This allows interactive Ink rendering even when stdin is a pipe. 13 * Result is cached for the lifetime of the process. 14 */ 15function getStdinOverride(): ReadStream | undefined { 16 // Return cached result if already computed 17 if (cachedStdinOverride !== null) { 18 return cachedStdinOverride 19 } 20 21 // No override needed if stdin is already a TTY 22 if (process.stdin.isTTY) { 23 cachedStdinOverride = undefined 24 return undefined 25 } 26 27 // Skip in CI environments 28 if (isEnvTruthy(process.env.CI)) { 29 cachedStdinOverride = undefined 30 return undefined 31 } 32 33 // Skip if running MCP (input hijacking breaks MCP) 34 if (process.argv.includes('mcp')) { 35 cachedStdinOverride = undefined 36 return undefined 37 } 38 39 // No /dev/tty on Windows 40 if (process.platform === 'win32') { 41 cachedStdinOverride = undefined 42 return undefined 43 } 44 45 // Try to open /dev/tty as an alternative input source 46 try { 47 const ttyFd = openSync('/dev/tty', 'r') 48 const ttyStream = new ReadStream(ttyFd) 49 // Explicitly set isTTY to true since we know /dev/tty is a TTY. 50 // This is needed because some runtimes (like Bun's compiled binaries) 51 // may not correctly detect isTTY on ReadStream created from a file descriptor. 52 ttyStream.isTTY = true 53 cachedStdinOverride = ttyStream 54 return cachedStdinOverride 55 } catch (err) { 56 logError(err as Error) 57 cachedStdinOverride = undefined 58 return undefined 59 } 60} 61 62/** 63 * Returns base render options for Ink, including stdin override when needed. 64 * Use this for all render() calls to ensure piped input works correctly. 65 * 66 * @param exitOnCtrlC - Whether to exit on Ctrl+C (usually false for dialogs) 67 */ 68export function getBaseRenderOptions( 69 exitOnCtrlC: boolean = false, 70): RenderOptions { 71 const stdin = getStdinOverride() 72 const options: RenderOptions = { exitOnCtrlC } 73 if (stdin) { 74 options.stdin = stdin 75 } 76 return options 77}