Files
MuyueWorkspace/internal/platform/platform.go
Muyue 6a7b4d8001
All checks were successful
PR Check / check (pull_request) Successful in 57s
release: v0.6.0 — security audit fixes + 7 new features
Audit corrections (security, concurrency, stability):
- chat_engine: bound resp.Choices[0] access, release tool slot per-iteration
- conversation_multi: synchronous save under existing lock (was racy fire-and-forget)
- workflow/engine: short-circuit on failed deps (no more infinite busy-wait); track failed/skipped status
- handlers_workflow: rune-aware truncate for plan goal (UTF-8 safe)
- server: CORS limited to localhost origins (was wildcard)
- handlers_info / terminal: mask API keys and SSH passwords as "***" in GET responses; preserve stored secret if "***" sent on update
- terminal: sshpass uses -e + SSHPASS env var (was both -p and -e)
- handlers_chat: MaxBytesReader 50 MB on /api/chat
- image_cache: 10 MB cap per image
- handlers_config: font size <= 72; profile-save unmarshal errors propagated
- handlers_info: /lsp/auto-install ProjectDir restricted to user home
- Shell.jsx: parenthesized resize-condition (operator precedence)
- orchestrator_test: CleanAIResponse capitalization (fixes failing vet)

New features:
- platform: detect OS name (Debian, Ubuntu, Windows 11, macOS X.Y) and inject in Studio system prompt next to the date
- agents: default timeout 30 min for crush_run/claude_run (cap also 30 min)
- agents: new cwd, wsl_distro, wsl_user params; on Windows hosts launch via "wsl -d <distro> -u <user> --cd <cwd> --"
- agents: new claude_run tool (mirror of crush_run for Claude Code CLI)
- terminal: list installed WSL distros individually in new-tab menu (Windows only)
- studio: system prompt rewritten around BMAD-METHOD personas + mandatory delegation template
- studio: "Réflexion avancée" toggle — inactive provider produces a preliminary report injected as [RAPPORT PRÉALABLE] context for the active provider
- studio: "Historique compressé" toggle — collapses past tool calls to last action only, with "Tout afficher" expansion
2026-04-27 10:12:11 +02:00

140 lines
2.8 KiB
Go

package platform
import (
"runtime"
"strings"
)
type OS string
const (
Linux OS = "linux"
MacOS OS = "darwin"
Windows OS = "windows"
Unknown OS = "unknown"
)
type Arch string
const (
AMD64 Arch = "amd64"
ARM64 Arch = "arm64"
ARM Arch = "arm"
X86 Arch = "386"
)
type SystemInfo struct {
OS OS `json:"os"`
OSName string `json:"os_name"`
Arch Arch `json:"arch"`
IsWSL bool `json:"is_wsl"`
Shell string `json:"shell"`
Terminal string `json:"terminal"`
PackageManager string `json:"package_manager"`
}
func Detect() SystemInfo {
info := SystemInfo{
OS: OS(runtime.GOOS),
Arch: Arch(runtime.GOARCH),
}
info.IsWSL = detectWSL()
info.OSName = detectOSName(info.OS, info.IsWSL)
info.Shell = detectShell()
info.Terminal = detectTerminal()
info.PackageManager = detectPackageManager(info.OS)
return info
}
func detectOSName(os OS, isWSL bool) string {
switch os {
case Linux:
if name := readOSReleaseName(); name != "" {
if isWSL {
return name + " (WSL)"
}
return name
}
if isWSL {
return "Linux (WSL)"
}
return "Linux"
case MacOS:
if v := readMacOSVersion(); v != "" {
return "macOS " + v
}
return "macOS"
case Windows:
if v := readWindowsVersion(); v != "" {
return v
}
return "Windows"
}
return string(os)
}
func detectWSL() bool {
return fileContains("/proc/version", "microsoft") ||
fileContains("/proc/version", "WSL")
}
func detectShell() string {
shells := []string{"zsh", "bash", "fish", "pwsh", "powershell"}
for _, s := range shells {
if _, err := execLookPath(s); err == nil {
return s
}
}
return "sh"
}
func detectTerminal() string {
terms := []string{
"hyper", "alacritty", "kitty", "wezterm", "ghostty",
"windows-terminal", "gnome-terminal", "konsole",
"xterm", "tilix", "terminator",
}
for _, t := range terms {
if _, err := execLookPath(t); err == nil {
return t
}
}
return "unknown"
}
func detectPackageManager(os OS) string {
managers := map[string][]string{
"linux": {"apt", "dnf", "pacman", "zypper", "nix", "apk", "snap", "flatpak"},
"darwin": {"brew", "nix"},
"windows": {"winget", "choco", "scoop"},
}
if list, ok := managers[string(os)]; ok {
for _, mgr := range list {
if _, err := execLookPath(mgr); err == nil {
return mgr
}
}
}
return "unknown"
}
func (s SystemInfo) String() string {
parts := []string{
"OS: " + string(s.OS),
}
if s.OSName != "" {
parts = append(parts, "Name: "+s.OSName)
}
parts = append(parts, "Arch: "+string(s.Arch))
if s.IsWSL {
parts = append(parts, "WSL: yes")
}
parts = append(parts, "Shell: "+s.Shell)
parts = append(parts, "Terminal: "+s.Terminal)
parts = append(parts, "PackageManager: "+s.PackageManager)
return strings.Join(parts, ", ")
}