All checks were successful
Beta Release / beta (push) Successful in 44s
Break down the 627-line handlers.go into specialized modules: - handlers_chat.go: chat and streaming endpoints - handlers_config.go: configuration endpoints - handlers_common.go: shared utilities - handlers_info.go: info and status endpoints - handlers_terminal.go: terminal/shell endpoints - handlers_tools.go: tool-related endpoints Also includes config improvements, orchestrator enhancements, and web component updates. 💘 Generated with Crush Assisted-by: MiniMax-M2.7 via Crush <crush@charm.land>
232 lines
5.1 KiB
Go
232 lines
5.1 KiB
Go
package scanner
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/muyue/muyue/internal/platform"
|
|
)
|
|
|
|
type ToolStatus struct {
|
|
Name string `yaml:"name"`
|
|
Installed bool `yaml:"installed"`
|
|
Version string `yaml:"version"`
|
|
Path string `yaml:"path"`
|
|
Latest string `yaml:"latest"`
|
|
NeedsUpdate bool `yaml:"needs_update"`
|
|
Category string `yaml:"category"`
|
|
}
|
|
|
|
type RuntimeStatus struct {
|
|
Name string `yaml:"name"`
|
|
Installed bool `yaml:"installed"`
|
|
Version string `yaml:"version"`
|
|
}
|
|
|
|
type ScanResult struct {
|
|
System platform.SystemInfo `yaml:"system"`
|
|
Tools []ToolStatus `yaml:"tools"`
|
|
Runtimes []RuntimeStatus `yaml:"runtimes"`
|
|
ShellSetup bool `yaml:"shell_setup"`
|
|
GitConfigured bool `yaml:"git_configured"`
|
|
}
|
|
|
|
var (
|
|
cacheMu sync.RWMutex
|
|
cacheResult *ScanResult
|
|
cacheTime time.Time
|
|
cacheTTL = 5 * time.Minute
|
|
)
|
|
|
|
func ScanSystem() *ScanResult {
|
|
cacheMu.RLock()
|
|
if cacheResult != nil && time.Since(cacheTime) < cacheTTL {
|
|
result := cacheResult
|
|
cacheMu.RUnlock()
|
|
return result
|
|
}
|
|
cacheMu.RUnlock()
|
|
|
|
result := doScan()
|
|
|
|
cacheMu.Lock()
|
|
cacheResult = result
|
|
cacheTime = time.Now()
|
|
cacheMu.Unlock()
|
|
|
|
return result
|
|
}
|
|
|
|
func InvalidateCache() {
|
|
cacheMu.Lock()
|
|
cacheResult = nil
|
|
cacheTime = time.Time{}
|
|
cacheMu.Unlock()
|
|
}
|
|
|
|
func doScan() *ScanResult {
|
|
info := platform.Detect()
|
|
result := &ScanResult{
|
|
System: info,
|
|
}
|
|
|
|
result.Tools = scanTools()
|
|
result.Runtimes = scanRuntimes()
|
|
result.ShellSetup = checkShellSetup()
|
|
result.GitConfigured = checkGitConfig()
|
|
|
|
return result
|
|
}
|
|
|
|
func scanTools() []ToolStatus {
|
|
tools := []struct {
|
|
name string
|
|
category string
|
|
version []string
|
|
}{
|
|
{"crush", "ai", []string{"version", "--short"}},
|
|
{"claude", "ai", []string{"--version"}},
|
|
{"git", "vcs", []string{"--version"}},
|
|
{"node", "runtime", []string{"--version"}},
|
|
{"npm", "runtime", []string{"--version"}},
|
|
{"pnpm", "runtime", []string{"--version"}},
|
|
{"python3", "runtime", []string{"--version"}},
|
|
{"pip3", "runtime", []string{"--version"}},
|
|
{"uv", "runtime", []string{"--version"}},
|
|
{"go", "runtime", []string{"version"}},
|
|
{"docker", "devops", []string{"--version"}},
|
|
{"gh", "devops", []string{"--version"}},
|
|
{"starship", "prompt", []string{"--version"}},
|
|
{"npx", "runtime", []string{"--version"}},
|
|
}
|
|
|
|
var statuses []ToolStatus
|
|
for _, t := range tools {
|
|
status := ToolStatus{
|
|
Name: t.name,
|
|
Category: t.category,
|
|
}
|
|
|
|
path, err := exec.LookPath(t.name)
|
|
if err != nil {
|
|
statuses = append(statuses, status)
|
|
continue
|
|
}
|
|
|
|
status.Installed = true
|
|
status.Path = path
|
|
|
|
if len(t.version) > 0 {
|
|
cmd := exec.Command(t.name, t.version...)
|
|
out, err := cmd.Output()
|
|
if err == nil {
|
|
status.Version = strings.TrimSpace(string(out))
|
|
}
|
|
}
|
|
|
|
statuses = append(statuses, status)
|
|
}
|
|
|
|
return statuses
|
|
}
|
|
|
|
func scanRuntimes() []RuntimeStatus {
|
|
runtimes := []struct {
|
|
name string
|
|
command []string
|
|
}{
|
|
{"Go", []string{"go", "version"}},
|
|
{"Node.js", []string{"node", "--version"}},
|
|
{"Python", []string{"python3", "--version"}},
|
|
{"Rust", []string{"rustc", "--version"}},
|
|
{"Java", []string{"java", "--version"}},
|
|
{"Ruby", []string{"ruby", "--version"}},
|
|
{"PHP", []string{"php", "--version"}},
|
|
{"Dotnet", []string{"dotnet", "--version"}},
|
|
}
|
|
|
|
var statuses []RuntimeStatus
|
|
for _, r := range runtimes {
|
|
status := RuntimeStatus{Name: r.name}
|
|
cmd := exec.Command(r.command[0], r.command[1:]...)
|
|
out, err := cmd.Output()
|
|
if err == nil {
|
|
status.Installed = true
|
|
status.Version = strings.TrimSpace(string(out))
|
|
}
|
|
statuses = append(statuses, status)
|
|
}
|
|
|
|
return statuses
|
|
}
|
|
|
|
func checkShellSetup() bool {
|
|
home, _ := os.UserHomeDir()
|
|
rcFiles := []string{".bashrc", ".zshrc", ".config/fish/config.fish"}
|
|
for _, f := range rcFiles {
|
|
data, err := os.ReadFile(filepath.Join(home, f))
|
|
if err != nil {
|
|
continue
|
|
}
|
|
content := string(data)
|
|
if strings.Contains(content, "starship") ||
|
|
strings.Contains(content, "muyue") {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func checkGitConfig() bool {
|
|
for _, key := range []string{"user.name", "user.email"} {
|
|
cmd := exec.Command("git", "config", "--global", key)
|
|
if _, err := cmd.Output(); err != nil {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
var versionRegex = regexp.MustCompile(`\d+\.\d+\.\d+`)
|
|
|
|
func (s *ScanResult) Summary() string {
|
|
var b strings.Builder
|
|
|
|
fmt.Fprintf(&b, "System: %s\n", s.System.String())
|
|
fmt.Fprintf(&b, "\nTools:\n")
|
|
|
|
installed := 0
|
|
for _, t := range s.Tools {
|
|
if t.Installed {
|
|
installed++
|
|
fmt.Fprintf(&b, " [v] %s %s\n", t.Name, versionRegex.FindString(t.Version))
|
|
} else {
|
|
fmt.Fprintf(&b, " [ ] %s (not installed)\n", t.Name)
|
|
}
|
|
}
|
|
fmt.Fprintf(&b, "\nInstalled: %d/%d\n", installed, len(s.Tools))
|
|
|
|
fmt.Fprintf(&b, "\nRuntimes:\n")
|
|
for _, r := range s.Runtimes {
|
|
if r.Installed {
|
|
fmt.Fprintf(&b, " [v] %s\n", r.Version)
|
|
} else {
|
|
fmt.Fprintf(&b, " [ ] %s (not installed)\n", r.Name)
|
|
}
|
|
}
|
|
|
|
if s.GitConfigured {
|
|
fmt.Fprintf(&b, "\nGit: configured\n")
|
|
} else {
|
|
fmt.Fprintf(&b, "\nGit: not fully configured\n")
|
|
}
|
|
|
|
return b.String()
|
|
}
|