feat: security hardening, tests, doctor command, CI update, CHANGELOG
All checks were successful
CI / build (push) Successful in 2m37s

- Add AES-256-GCM encryption for API keys (internal/secret)
- Add dangerous command detection in terminal
- Add muyue doctor command for system health checks
- Add scanner TTL cache, orchestrator history mutex, shared HTTP client
- Deduplicate MCP config generation, refactor skills YAML parser
- Add XDG-compliant config dir with legacy migration
- Add cleanup on all TUI quit paths
- Add 8 test files (config, workflow, skills, orchestrator, version,
  platform, scanner, secret)
- Update CI to actions/setup-go@v5
- Add CHANGELOG.md, update README and Makefile

🤖 Generated with Crush

Assisted-by: GLM-5.1 via Crush <crush@charm.land>
This commit is contained in:
Augustin
2026-04-20 19:56:07 +02:00
parent 44691225e7
commit 3494f6b40d
22 changed files with 1655 additions and 253 deletions

View File

@@ -5,6 +5,7 @@ import (
"os"
"path/filepath"
"github.com/muyue/muyue/internal/secret"
"gopkg.in/yaml.v3"
)
@@ -57,14 +58,30 @@ type MuyueConfig struct {
}
func ConfigDir() (string, error) {
home, err := os.UserHomeDir()
configDir, err := os.UserConfigDir()
if err != nil {
return "", err
}
dir := filepath.Join(home, ".muyue")
dir := filepath.Join(configDir, "muyue")
legacyDir := filepath.Join(homeDir(), ".muyue")
if _, err := os.Stat(legacyDir); err == nil {
if _, err := os.Stat(dir); err != nil {
os.Rename(legacyDir, dir)
}
}
return dir, nil
}
func homeDir() string {
home, err := os.UserHomeDir()
if err != nil {
return "/"
}
return home
}
func ConfigPath() (string, error) {
dir, err := ConfigDir()
if err != nil {
@@ -98,6 +115,17 @@ func Load() (*MuyueConfig, error) {
return nil, fmt.Errorf("parsing config: %w", err)
}
// Decrypt API keys
for i := range cfg.AI.Providers {
if cfg.AI.Providers[i].APIKey != "" {
decrypted, err := secret.Decrypt(cfg.AI.Providers[i].APIKey)
if err != nil {
decrypted = cfg.AI.Providers[i].APIKey
}
cfg.AI.Providers[i].APIKey = decrypted
}
}
return &cfg, nil
}
@@ -111,8 +139,21 @@ func Save(cfg *MuyueConfig) error {
return fmt.Errorf("creating config dir: %w", err)
}
// Encrypt API keys before saving
saveCfg := *cfg
saveCfg.AI.Providers = make([]AIProvider, len(cfg.AI.Providers))
for i, p := range cfg.AI.Providers {
saveCfg.AI.Providers[i] = p
if p.APIKey != "" && !secret.IsEncrypted(p.APIKey) {
enc, err := secret.Encrypt(p.APIKey)
if err == nil {
saveCfg.AI.Providers[i].APIKey = enc
}
}
}
path := filepath.Join(dir, "config.yaml")
data, err := yaml.Marshal(cfg)
data, err := yaml.Marshal(&saveCfg)
if err != nil {
return fmt.Errorf("marshaling config: %w", err)
}