Files
nono-opencode-tinfoil/tui/checks.go
T
kawaiipunk a5f97a3a1b Add TUI for managing sandbox profile, relicense to GPLv3
- Add Go/Bubble Tea TUI app under tui/ with dashboard (status checks,
  launch, validate) and profile editor (commands, filesystem, network,
  environment sub-tabs)
- Add Makefile targets: tui, tui-build, tui-install
- Add CI step to build TUI
- Relicense MIT -> GPL-3.0
- Rewrite wrapper to resolve real opencode binary from PATH (supports
  brew, npm, volta)
- Add PATH ordering detection to install script
- Expand README with how-it-works diagram, TUI docs, keybinding table
2026-08-12 19:37:54 +01:00

106 lines
2.8 KiB
Go

package main
import (
"os"
"os/exec"
"path/filepath"
"strings"
)
type CheckStatus int
const (
StatusUnknown CheckStatus = iota
StatusOK
StatusFail
)
type CheckResult struct {
Label string
Status CheckStatus
Detail string
}
func RunChecks() []CheckResult {
var results []CheckResult
if path, err := exec.LookPath("nono"); err == nil {
version := getNonoVersion()
detail := path
if version != "" {
detail += " " + version
}
results = append(results, CheckResult{"nono installed", StatusOK, detail})
} else {
results = append(results, CheckResult{"nono installed", StatusFail, "not found in PATH"})
}
profilePath, _ := DefaultProfilePath()
if profilePath != "" {
if info, err := os.Stat(profilePath); err == nil {
results = append(results, CheckResult{"profile installed", StatusOK,
profilePath + " (" + info.ModTime().Format("2006-01-02") + ")"})
} else {
results = append(results, CheckResult{"profile installed", StatusFail, "not found"})
}
}
home, _ := os.UserHomeDir()
wrapperPath := filepath.Join(home, ".local", "bin", "opencode")
if _, err := os.Stat(wrapperPath); err == nil {
results = append(results, CheckResult{"wrapper installed", StatusOK, wrapperPath})
} else {
results = append(results, CheckResult{"wrapper installed", StatusFail, "not found"})
}
if path, err := exec.LookPath("opencode"); err == nil {
if path == wrapperPath {
results = append(results, CheckResult{"wrapper first on PATH", StatusOK, path})
} else {
results = append(results, CheckResult{"wrapper first on PATH", StatusFail, "resolves to " + path})
}
} else {
results = append(results, CheckResult{"wrapper first on PATH", StatusFail, "opencode not in PATH"})
}
opencodePath := findRealOpencode(wrapperPath)
if opencodePath != "" {
results = append(results, CheckResult{"opencode binary", StatusOK, opencodePath})
} else {
results = append(results, CheckResult{"opencode binary", StatusFail, "real opencode not found"})
}
tinfoilDir := filepath.Join(home, ".tinfoil")
if info, err := os.Stat(tinfoilDir); err == nil && info.IsDir() {
results = append(results, CheckResult{"tinfoil credentials", StatusOK, tinfoilDir})
} else {
results = append(results, CheckResult{"tinfoil credentials", StatusFail, "no ~/.tinfoil directory"})
}
return results
}
func getNonoVersion() string {
cmd := exec.Command("nono", "--version")
out, err := cmd.Output()
if err != nil {
return ""
}
return strings.TrimSpace(string(out))
}
func findRealOpencode(wrapperPath string) string {
path := os.Getenv("PATH")
for _, dir := range filepath.SplitList(path) {
for _, name := range []string{"opencode", "opencode.exe"} {
candidate := filepath.Join(dir, name)
if info, err := os.Stat(candidate); err == nil && !info.IsDir() {
if candidate != wrapperPath {
return candidate
}
}
}
}
return ""
}