mirror of
https://github.com/Twigpine/zero.git
synced 2026-11-11 23:40:28 -10:00
* chore(launch): MIT license field, windows-arm64 install guard, TUI TTY guard Three launch-readiness fixes the docs cleanup doesn't cover: - package.json: set "license": "MIT" (was the placeholder "SEE LICENSE IN LICENSE"), matching the LICENSE file and the README badge. - scripts/postinstall.mjs: skip gracefully on windows-arm64. (win32, arm64) resolves to a valid platform/arch but the release matrix ships no windows-arm64 artifact, so the installer would download a 404 and hard-fail npm install. Treat it like any unsupported target (warnSkip, exit 0) and point to the x64 build (which runs under emulation on Windows on ARM) or source. - internal/tui/run.go: fail fast when stdin is not a terminal. The interactive shell otherwise blocks forever in the Bubble Tea event loop on piped/redirected input (e.g. `echo "" | zero`); now it prints guidance toward `zero exec` and exits non-zero. Dependency-free ModeCharDevice check (no x/term added). Tests: windows-arm64 skips with exit 0; the interactive shell returns non-zero (not a hang) on non-TTY stdin. * fix(tui): use term.IsTerminal for the stdin guard; pin exit code in test Address review on the no-TTY guard: - Replace the os.Stdin.Stat()/ModeCharDevice heuristic with term.IsTerminal (github.com/charmbracelet/x/term, already in the module graph via bubbletea). It is a true TTY check — it rejects pipes, regular files, and non-terminal char devices like /dev/null — and fails closed: anything not a verified terminal blocks the interactive shell. - The test now asserts the documented exit code 2 from the guard, not just a non-zero result, so it proves the stdin guard fired.
46 lines
1.2 KiB
Go
46 lines
1.2 KiB
Go
package tui
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestUseAltScreenForInteractiveChat(t *testing.T) {
|
|
if !useAltScreen(Options{}) {
|
|
t.Fatal("normal chat should use the alternate screen")
|
|
}
|
|
if !useAltScreen(Options{Setup: SetupOptions{Visible: true}}) {
|
|
t.Fatal("setup takeover should also use the alternate screen")
|
|
}
|
|
}
|
|
|
|
// TestRunRejectsNonTTYStdin pins that the interactive shell fails fast with a
|
|
// non-zero code when stdin is not a terminal, instead of blocking forever in the
|
|
// Bubble Tea event loop (e.g. `echo "" | zero`). The guard runs before any model
|
|
// construction, so empty Options are fine.
|
|
func TestRunRejectsNonTTYStdin(t *testing.T) {
|
|
r, w, err := os.Pipe()
|
|
if err != nil {
|
|
t.Fatalf("os.Pipe: %v", err)
|
|
}
|
|
w.Close() // a pipe read-end is not a character device
|
|
|
|
old := os.Stdin
|
|
os.Stdin = r
|
|
defer func() { os.Stdin = old; r.Close() }()
|
|
|
|
done := make(chan int, 1)
|
|
go func() { done <- Run(context.Background(), Options{}) }()
|
|
|
|
select {
|
|
case code := <-done:
|
|
if code != 2 {
|
|
t.Fatalf("Run with non-TTY stdin returned %d; want exit code 2 from the stdin TTY guard", code)
|
|
}
|
|
case <-time.After(5 * time.Second):
|
|
t.Fatal("Run blocked on non-TTY stdin instead of failing fast")
|
|
}
|
|
}
|