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
This commit is contained in:
+105
@@ -0,0 +1,105 @@
|
||||
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 ""
|
||||
}
|
||||
Reference in New Issue
Block a user