fix(tui): require the native interactive client

This commit is contained in:
Xubin Ren
2026-08-17 20:56:10 +08:00
parent 8f9bdb210e
commit 9514b9b909
4 changed files with 162 additions and 17 deletions
+14 -10
View File
@@ -83,18 +83,21 @@ def agent(
theme = theme.strip().lower()
if theme not in {"auto", "dark", "light"}:
raise typer.BadParameter("must be auto, dark, or light", param_hint="--theme")
native_tui = (
message is None
and not classic
and markdown
and not logs
and sys.stdin.isatty()
and sys.stdout.isatty()
)
native_tui = message is None and not classic
if native_tui:
from nanobot.cli.tui_launcher import TuiUnavailableError, launch_tui
from nanobot.config.loader import get_config_path
if not sys.stdin.isatty() or not sys.stdout.isatty():
raise typer.BadParameter(
"the native TUI requires an interactive terminal; use --message for "
"one-shot input or --classic for the legacy prompt",
param_hint="terminal",
)
if not markdown:
raise typer.BadParameter("--no-markdown requires --classic", param_hint="--no-markdown")
if logs:
raise typer.BadParameter("--logs requires --classic", param_hint="--logs")
try:
exit_code = launch_tui(
runtime_config,
@@ -104,8 +107,9 @@ def agent(
theme=theme,
)
except TuiUnavailableError as exc:
console.print(f"[yellow]Native TUI unavailable: {exc}[/yellow]")
console.print("[dim]Falling back to the classic prompt.[/dim]")
console.print(f"[red]Native TUI unavailable: {exc}[/red]")
console.print("[dim]Use `nanobot agent --classic` only if you want the old prompt.[/dim]")
raise typer.Exit(1) from exc
else:
if exit_code:
raise typer.Exit(exit_code)
+20 -5
View File
@@ -123,11 +123,7 @@ def _resolve_tui_command() -> list[str]:
source_dir = Path(__file__).resolve().parents[2] / "tui"
bun = shutil.which("bun")
if bun and (source_dir / "package.json").is_file():
if not (source_dir / "node_modules" / "@opentui" / "core").is_dir():
raise TuiUnavailableError(
f"TUI dependencies are missing; run `bun install --cwd {source_dir}`"
)
return [bun, str(source_dir / "src" / "index.ts")]
return _resolve_source_tui_command(source_dir, bun)
downloaded = _download_release_tui(asset)
if downloaded is not None:
@@ -139,6 +135,25 @@ def _resolve_tui_command() -> list[str]:
)
def _resolve_source_tui_command(source_dir: Path, bun: str) -> list[str]:
dependency = source_dir / "node_modules" / "@opentui" / "core"
try:
install = subprocess.run(
[bun, "install", "--frozen-lockfile"],
cwd=source_dir,
capture_output=True,
text=True,
check=False,
)
except OSError as exc:
raise TuiUnavailableError(f"could not install TUI dependencies: {exc}") from exc
if install.returncode != 0 or not dependency.is_dir():
detail = (install.stderr or install.stdout).strip().splitlines()
suffix = f": {detail[-1]}" if detail else ""
raise TuiUnavailableError(f"could not install TUI dependencies{suffix}")
return [bun, str(source_dir / "src" / "index.ts")]
def _download_release_tui(asset: str) -> Path | None:
"""Install the version-matched release sidecar into nanobot's data directory."""
if os.environ.get("NANOBOT_TUI_NO_DOWNLOAD") == "1":