All checks were successful
PR Check / check (pull_request) Successful in 57s
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
79 lines
1.6 KiB
Go
79 lines
1.6 KiB
Go
package platform
|
|
|
|
import (
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
)
|
|
|
|
func fileContains(path, substr string) bool {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return strings.Contains(strings.ToLower(string(data)), substr)
|
|
}
|
|
|
|
func execLookPath(name string) (string, error) {
|
|
return exec.LookPath(name)
|
|
}
|
|
|
|
func readOSReleaseName() string {
|
|
data, err := os.ReadFile("/etc/os-release")
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
var pretty, name, version string
|
|
for _, line := range strings.Split(string(data), "\n") {
|
|
key, val, ok := strings.Cut(line, "=")
|
|
if !ok {
|
|
continue
|
|
}
|
|
val = strings.Trim(val, `"'`)
|
|
switch key {
|
|
case "PRETTY_NAME":
|
|
pretty = val
|
|
case "NAME":
|
|
name = val
|
|
case "VERSION_ID":
|
|
version = val
|
|
}
|
|
}
|
|
if pretty != "" {
|
|
return pretty
|
|
}
|
|
if name != "" && version != "" {
|
|
return name + " " + version
|
|
}
|
|
return name
|
|
}
|
|
|
|
func readMacOSVersion() string {
|
|
out, err := exec.Command("sw_vers", "-productVersion").Output()
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return strings.TrimSpace(string(out))
|
|
}
|
|
|
|
func readWindowsVersion() string {
|
|
if v := os.Getenv("OS"); v != "" && strings.Contains(strings.ToLower(v), "windows") {
|
|
// Try to detect Windows 11 vs 10 via build number
|
|
if build := os.Getenv("MUYUE_WIN_BUILD"); build != "" {
|
|
return "Windows " + build
|
|
}
|
|
}
|
|
out, err := exec.Command("cmd", "/c", "ver").Output()
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
s := strings.TrimSpace(string(out))
|
|
if strings.Contains(s, "10.0.22") || strings.Contains(s, "10.0.23") {
|
|
return "Windows 11"
|
|
}
|
|
if strings.Contains(s, "10.0.") {
|
|
return "Windows 10"
|
|
}
|
|
return s
|
|
}
|