diff --git a/setup.sh b/setup.sh index e877694..175e0d5 100644 --- a/setup.sh +++ b/setup.sh @@ -152,7 +152,7 @@ EOF start_bot() { # Bun auto-loads .env; no need to run dotenv explicitly # Ensure Bun has a TTY for interactive menu input - exec bun run start < /dev/tty + exec bun index.ts < /dev/tty > /dev/tty 2>&1 } main "$@" diff --git a/src/ui/App.tsx b/src/ui/App.tsx index b0cd784..c2b76e2 100644 --- a/src/ui/App.tsx +++ b/src/ui/App.tsx @@ -1,5 +1,5 @@ import React, { useState } from "react"; -import { Box, Text, useInput } from "ink"; +import { Box, Text, useInput, useStdin } from "ink"; import { TrendApp } from "./TrendApp"; import { MakerApp } from "./MakerApp"; import { OffsetMakerApp } from "./OffsetMakerApp"; @@ -32,9 +32,15 @@ const STRATEGIES: StrategyOption[] = [ }, ]; -const inputSupported = Boolean(process.stdin && (process.stdin as any).isTTY); +// Prefer Ink's stdin detection to avoid false negatives when spawned by shells +// that don't propagate process.stdin TTY flags correctly. +function useInputSupported() { + const { isRawModeSupported } = useStdin(); + return Boolean(isRawModeSupported); +} export function App() { + const inputSupported = useInputSupported(); const [cursor, setCursor] = useState(0); const [selected, setSelected] = useState(null); diff --git a/src/ui/MakerApp.tsx b/src/ui/MakerApp.tsx index 4c1a88d..85be540 100644 --- a/src/ui/MakerApp.tsx +++ b/src/ui/MakerApp.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useRef, useState } from "react"; -import { Box, Text, useInput } from "ink"; +import { Box, Text, useInput, useStdin } from "ink"; import { makerConfig } from "../config"; import { AsterExchangeAdapter } from "../exchanges/aster-adapter"; import { MakerEngine, type MakerEngineSnapshot } from "../core/maker-engine"; @@ -10,9 +10,13 @@ interface MakerAppProps { onExit: () => void; } -const inputSupported = Boolean(process.stdin && (process.stdin as any).isTTY); +function useInputSupported() { + const { isRawModeSupported } = useStdin(); + return Boolean(isRawModeSupported); +} export function MakerApp({ onExit }: MakerAppProps) { + const inputSupported = useInputSupported(); const [snapshot, setSnapshot] = useState(null); const [error, setError] = useState(null); const engineRef = useRef(null); diff --git a/src/ui/OffsetMakerApp.tsx b/src/ui/OffsetMakerApp.tsx index bd9a1d9..df8405f 100644 --- a/src/ui/OffsetMakerApp.tsx +++ b/src/ui/OffsetMakerApp.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useRef, useState } from "react"; -import { Box, Text, useInput } from "ink"; +import { Box, Text, useInput, useStdin } from "ink"; import { makerConfig } from "../config"; import { AsterExchangeAdapter } from "../exchanges/aster-adapter"; import { OffsetMakerEngine, type OffsetMakerEngineSnapshot } from "../core/offset-maker-engine"; @@ -10,9 +10,13 @@ interface OffsetMakerAppProps { onExit: () => void; } -const inputSupported = Boolean(process.stdin && (process.stdin as any).isTTY); +function useInputSupported() { + const { isRawModeSupported } = useStdin(); + return Boolean(isRawModeSupported); +} export function OffsetMakerApp({ onExit }: OffsetMakerAppProps) { + const inputSupported = useInputSupported(); const [snapshot, setSnapshot] = useState(null); const [error, setError] = useState(null); const engineRef = useRef(null); diff --git a/src/ui/TrendApp.tsx b/src/ui/TrendApp.tsx index 6eef9a2..8301d09 100644 --- a/src/ui/TrendApp.tsx +++ b/src/ui/TrendApp.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useRef, useState } from "react"; -import { Box, Text, useInput } from "ink"; +import { Box, Text, useInput, useStdin } from "ink"; import { tradingConfig } from "../config"; import { AsterExchangeAdapter } from "../exchanges/aster-adapter"; import { TrendEngine, type TrendEngineSnapshot } from "../core/trend-engine"; @@ -12,9 +12,13 @@ interface TrendAppProps { onExit: () => void; } -const inputSupported = Boolean(process.stdin && (process.stdin as any).isTTY); +function useInputSupported() { + const { isRawModeSupported } = useStdin(); + return Boolean(isRawModeSupported); +} export function TrendApp({ onExit }: TrendAppProps) { + const inputSupported = useInputSupported(); const [snapshot, setSnapshot] = useState(null); const [error, setError] = useState(null); const engineRef = useRef(null);