fix(tui): enforce supported platform boundaries

This commit is contained in:
Xubin Ren
2026-08-17 20:56:10 +08:00
parent 406509aeb8
commit 14c5607e5e
5 changed files with 26 additions and 2 deletions
+1 -1
View File
@@ -120,7 +120,7 @@ The default `--theme auto` mode probes the terminal's real foreground and backgr
`Enter` sends the current message. Press `Alt+Enter` to add a newline and use `Up`/`Down` at the composer edge to recall recent prompts. `Ctrl+C` copies a selection, stops a running turn, clears a non-empty composer, or exits when idle. Use `PageUp`/`PageDown` to scroll, `Ctrl+Home`/`Ctrl+End` to jump to the transcript edges, and `Ctrl+O` to expand or collapse long tool traces. Selections copy through OSC 52 when the terminal supports it. The transcript reflows when the terminal is resized, and exiting restores the previous screen.
Packaged releases fetch a version-matched, checksummed terminal binary for macOS, Linux, or Windows on first use and cache it under the nanobot data directory. Set `NANOBOT_TUI_NO_DOWNLOAD=1` or pass `--classic` to keep the Python-only path. A source checkout can run the client with Bun after `bun install --cwd tui`.
Packaged releases fetch a version-matched, checksummed terminal binary for macOS (Apple Silicon and Intel), Linux (x64 and ARM64), or Windows x64 on first use and cache it under the nanobot data directory. Windows ARM64 currently falls back to the classic prompt because the Bun runtime disables the FFI required by OpenTUI on that platform. Set `NANOBOT_TUI_NO_DOWNLOAD=1` or pass `--classic` to keep the Python-only path. A source checkout can run the client with Bun after `bun install --cwd tui`.
Non-interactive input/output, `--logs`, and `--no-markdown` automatically retain the classic prompt so existing scripts and diagnostic workflows do not acquire terminal control sequences or silently ignore their options.
+5
View File
@@ -108,6 +108,11 @@ def _resolve_tui_command() -> list[str]:
platform.machine(),
platform.machine().lower(),
)
if system == "win32" and machine == "arm64":
raise TuiUnavailableError(
"the native TUI is not available on Windows ARM64 because Bun FFI is disabled "
"on that platform; use the classic prompt until the upstream runtime supports it"
)
asset = f"nanobot-tui-{system}-{machine}{suffix}"
packaged = Path(__file__).resolve().parents[1] / "tui" / "bin" / asset
if packaged.is_file():
+11
View File
@@ -46,6 +46,17 @@ def test_explicit_tui_binary_must_exist(
_resolve_tui_command()
def test_windows_arm64_uses_the_classic_prompt(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.delenv("NANOBOT_TUI_BIN", raising=False)
monkeypatch.setattr("nanobot.cli.tui_launcher.platform.system", lambda: "Windows")
monkeypatch.setattr("nanobot.cli.tui_launcher.platform.machine", lambda: "ARM64")
with pytest.raises(TuiUnavailableError, match="Windows ARM64"):
_resolve_tui_command()
def test_interactive_agent_uses_native_tui(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
+8 -1
View File
@@ -3,8 +3,15 @@ import { basename, join } from "node:path"
const target = process.argv[2] || `${process.platform}-${process.arch}`
const [platform, arch] = target.split("-")
const supportedTargets = new Set([
"darwin-arm64",
"darwin-x64",
"linux-arm64",
"linux-x64",
"win32-x64",
])
if (!platform || !arch || !["darwin", "linux", "win32"].includes(platform)) {
if (!platform || !arch || !supportedTargets.has(target)) {
throw new Error(`unsupported target: ${target}`)
}
+1
View File
@@ -637,6 +637,7 @@ if (process.platform !== "win32") {
NANOBOT_TUI_WORKSPACE: "/tmp/nanobot-test",
NANOBOT_TUI_VERSION: "test",
NANOBOT_TUI_ACCESS: "workspace access",
NANOBOT_TUI_THEME: "dark",
},
stdout: "pipe",
stderr: "pipe",