| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677 |
- import { openSync } from 'fs'
- import { ReadStream } from 'tty'
- import type { RenderOptions } from '../ink.js'
- import { isEnvTruthy } from './envUtils.js'
- import { logError } from './log.js'
- // Cached stdin override - computed once per process
- let cachedStdinOverride: ReadStream | undefined | null = null
- /**
- * Gets a ReadStream for /dev/tty when stdin is piped.
- * This allows interactive Ink rendering even when stdin is a pipe.
- * Result is cached for the lifetime of the process.
- */
- function getStdinOverride(): ReadStream | undefined {
- // Return cached result if already computed
- if (cachedStdinOverride !== null) {
- return cachedStdinOverride
- }
- // No override needed if stdin is already a TTY
- if (process.stdin.isTTY) {
- cachedStdinOverride = undefined
- return undefined
- }
- // Skip in CI environments
- if (isEnvTruthy(process.env.CI)) {
- cachedStdinOverride = undefined
- return undefined
- }
- // Skip if running MCP (input hijacking breaks MCP)
- if (process.argv.includes('mcp')) {
- cachedStdinOverride = undefined
- return undefined
- }
- // No /dev/tty on Windows
- if (process.platform === 'win32') {
- cachedStdinOverride = undefined
- return undefined
- }
- // Try to open /dev/tty as an alternative input source
- try {
- const ttyFd = openSync('/dev/tty', 'r')
- const ttyStream = new ReadStream(ttyFd)
- // Explicitly set isTTY to true since we know /dev/tty is a TTY.
- // This is needed because some runtimes (like Bun's compiled binaries)
- // may not correctly detect isTTY on ReadStream created from a file descriptor.
- ttyStream.isTTY = true
- cachedStdinOverride = ttyStream
- return cachedStdinOverride
- } catch (err) {
- logError(err as Error)
- cachedStdinOverride = undefined
- return undefined
- }
- }
- /**
- * Returns base render options for Ink, including stdin override when needed.
- * Use this for all render() calls to ensure piped input works correctly.
- *
- * @param exitOnCtrlC - Whether to exit on Ctrl+C (usually false for dialogs)
- */
- export function getBaseRenderOptions(
- exitOnCtrlC: boolean = false,
- ): RenderOptions {
- const stdin = getStdinOverride()
- const options: RenderOptions = { exitOnCtrlC }
- if (stdin) {
- options.stdin = stdin
- }
- return options
- }
|