All checks were successful
Beta Release / beta (push) Successful in 5m9s
Major additions: - RAG pipeline (indexing, chunking, search) with sidebar upload button - Memory system with CRUD API - Plugins and lessons modules - MCP discovery and MCP server - Advanced skills (auto-create, conditional, improver) - Agent browser/image support, delegate, sessions - File editor with CodeMirror in split panes - Markdown rendering via react-markdown + KaTeX + highlight.js - Raw markdown toggle - PWA manifest + service worker - Extension UI redesign with new design tokens and studio-style chat - Pipeline API for chat streaming - Mobile responsive layout 💘 Generated with Crush Assisted-by: GLM-5.1 via Crush <crush@charm.land>
345 lines
9.3 KiB
Go
345 lines
9.3 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/muyue/muyue/internal/secret"
|
|
"github.com/muyue/muyue/internal/version"
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
type Profile struct {
|
|
Name string `yaml:"name" json:"name"`
|
|
Pseudo string `yaml:"pseudo" json:"pseudo"`
|
|
Email string `yaml:"email" json:"email"`
|
|
Languages []string `yaml:"languages" json:"languages"`
|
|
Preferences struct {
|
|
Editor string `yaml:"editor" json:"editor"`
|
|
Shell string `yaml:"shell" json:"shell"`
|
|
Theme string `yaml:"theme" json:"theme"`
|
|
DefaultAI string `yaml:"default_ai" json:"default_ai"`
|
|
AutoUpdate bool `yaml:"auto_update" json:"auto_update"`
|
|
CheckOnStart bool `yaml:"check_on_start" json:"check_on_start"`
|
|
Language string `yaml:"language" json:"language"`
|
|
KeyboardLayout string `yaml:"keyboard_layout" json:"keyboard_layout"`
|
|
} `yaml:"preferences" json:"preferences"`
|
|
}
|
|
|
|
type AIProvider struct {
|
|
Name string `yaml:"name" json:"name"`
|
|
APIKey string `yaml:"api_key,omitempty" json:"api_key,omitempty"`
|
|
BaseURL string `yaml:"base_url,omitempty" json:"base_url,omitempty"`
|
|
Model string `yaml:"model" json:"model"`
|
|
Active bool `yaml:"active" json:"active"`
|
|
}
|
|
|
|
type ToolConfig struct {
|
|
Name string `yaml:"name" json:"name"`
|
|
Installed bool `yaml:"installed" json:"installed"`
|
|
Version string `yaml:"version" json:"version"`
|
|
AutoUpdate bool `yaml:"auto_update" json:"auto_update"`
|
|
}
|
|
|
|
type SSHConnection struct {
|
|
Name string `yaml:"name" json:"name"`
|
|
Host string `yaml:"host" json:"host"`
|
|
Port int `yaml:"port" json:"port"`
|
|
User string `yaml:"user" json:"user"`
|
|
Password string `yaml:"password,omitempty" json:"password,omitempty"`
|
|
KeyPath string `yaml:"key_path,omitempty" json:"key_path,omitempty"`
|
|
}
|
|
|
|
type PluginsConfig struct {
|
|
Enabled []string `yaml:"enabled" json:"enabled"`
|
|
Paths []string `yaml:"paths,omitempty" json:"paths,omitempty"`
|
|
}
|
|
|
|
type LessonsConfig struct {
|
|
Dirs []string `yaml:"dirs,omitempty" json:"dirs,omitempty"`
|
|
Enabled bool `yaml:"enabled" json:"enabled"`
|
|
}
|
|
|
|
type MuyueConfig struct {
|
|
Version string `yaml:"version" json:"version"`
|
|
Profile Profile `yaml:"profile" json:"profile"`
|
|
AI struct {
|
|
Providers []AIProvider `yaml:"providers" json:"providers"`
|
|
} `yaml:"ai" json:"ai"`
|
|
Tools []ToolConfig `yaml:"tools" json:"tools"`
|
|
BMAD struct {
|
|
Installed bool `yaml:"installed" json:"installed"`
|
|
Version string `yaml:"version" json:"version"`
|
|
Global bool `yaml:"global" json:"global"`
|
|
} `yaml:"bmad" json:"bmad"`
|
|
Terminal struct {
|
|
CustomPrompt bool `yaml:"custom_prompt" json:"custom_prompt"`
|
|
PromptTheme string `yaml:"prompt_theme" json:"prompt_theme"`
|
|
SSH []SSHConnection `yaml:"ssh" json:"ssh"`
|
|
FontSize int `yaml:"font_size" json:"font_size"`
|
|
FontFamily string `yaml:"font_family" json:"font_family"`
|
|
Theme string `yaml:"theme" json:"theme"`
|
|
} `yaml:"terminal" json:"terminal"`
|
|
Plugins PluginsConfig `yaml:"plugins" json:"plugins"`
|
|
Lessons LessonsConfig `yaml:"lessons" json:"lessons"`
|
|
}
|
|
|
|
type TerminalTheme struct {
|
|
Name string `yaml:"name"`
|
|
Background string `yaml:"background"`
|
|
Foreground string `yaml:"foreground"`
|
|
Cursor string `yaml:"cursor"`
|
|
Black string `yaml:"black"`
|
|
Red string `yaml:"red"`
|
|
Green string `yaml:"green"`
|
|
Yellow string `yaml:"yellow"`
|
|
Blue string `yaml:"blue"`
|
|
Magenta string `yaml:"magenta"`
|
|
Cyan string `yaml:"cyan"`
|
|
White string `yaml:"white"`
|
|
}
|
|
|
|
var DEFAULT_TERMINAL_THEMES = map[string]TerminalTheme{
|
|
"default": {
|
|
Name: "Default", Background: "#0A0A0C", Foreground: "#EAE0E2",
|
|
Cursor: "#FF0033", Black: "#0A0A0C", Red: "#FF0033",
|
|
Green: "#00E676", Yellow: "#FFD740", Blue: "#448AFF",
|
|
Magenta: "#FF1A5E", Cyan: "#00BCD4", White: "#EAE0E2",
|
|
},
|
|
"monokai": {
|
|
Name: "Monokai", Background: "#272822", Foreground: "#F8F8F2",
|
|
Cursor: "#F8F8F0", Black: "#272822", Red: "#F92672",
|
|
Green: "#A6E22E", Yellow: "#E6DB74", Blue: "#66D9EF",
|
|
Magenta: "#AE81FF", Cyan: "#A1EFE4", White: "#F8F8F2",
|
|
},
|
|
"gruvbox": {
|
|
Name: "Gruvbox", Background: "#282828", Foreground: "#EBDBB2",
|
|
Cursor: "#FB4934", Black: "#282828", Red: "#CC241D",
|
|
Green: "#98971A", Yellow: "#D79921", Blue: "#458588",
|
|
Magenta: "#B16286", Cyan: "#689D6A", White: "#EBDBB2",
|
|
},
|
|
"nord": {
|
|
Name: "Nord", Background: "#2E3440", Foreground: "#D8DEE9",
|
|
Cursor: "#D8DEE9", Black: "#2E3440", Red: "#BF616A",
|
|
Green: "#A3BE8C", Yellow: "#EBCB8B", Blue: "#81A1C1",
|
|
Magenta: "#B48EAD", Cyan: "#88C0D0", White: "#D8DEE9",
|
|
},
|
|
"solarized-dark": {
|
|
Name: "Solarized Dark", Background: "#002B36", Foreground: "#839496",
|
|
Cursor: "#D33682", Black: "#002B36", Red: "#DC322F",
|
|
Green: "#859900", Yellow: "#B58900", Blue: "#268BD2",
|
|
Magenta: "#D33682", Cyan: "#2AA198", White: "#FDF6E3",
|
|
},
|
|
"dracula": {
|
|
Name: "Dracula", Background: "#282A36", Foreground: "#F8F8F2",
|
|
Cursor: "#F8F8F2", Black: "#282A36", Red: "#FF5555",
|
|
Green: "#50FA7B", Yellow: "#F1FA8C", Blue: "#BD93F9",
|
|
Magenta: "#FF79C6", Cyan: "#8BE9FD", White: "#F8F8F2",
|
|
},
|
|
}
|
|
|
|
func migrateProviders(cfg *MuyueConfig) {
|
|
defaults := Default().AI.Providers
|
|
for _, dp := range defaults {
|
|
found := false
|
|
for _, p := range cfg.AI.Providers {
|
|
if p.Name == dp.Name {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
cfg.AI.Providers = append(cfg.AI.Providers, dp)
|
|
}
|
|
}
|
|
}
|
|
|
|
func GetTerminalTheme(name string) TerminalTheme {
|
|
if theme, ok := DEFAULT_TERMINAL_THEMES[name]; ok {
|
|
return theme
|
|
}
|
|
return DEFAULT_TERMINAL_THEMES["default"]
|
|
}
|
|
|
|
func ConfigDir() (string, error) {
|
|
configDir, err := os.UserConfigDir()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
dir := filepath.Join(configDir, "muyue")
|
|
|
|
legacyDir := filepath.Join(homeDir(), ".muyue")
|
|
if _, err := os.Stat(legacyDir); err == nil {
|
|
if _, err := os.Stat(dir); err != nil {
|
|
if err := os.Rename(legacyDir, dir); err != nil {
|
|
_ = err
|
|
}
|
|
}
|
|
}
|
|
|
|
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 {
|
|
return "", err
|
|
}
|
|
return filepath.Join(dir, "config.yaml"), nil
|
|
}
|
|
|
|
func Exists() bool {
|
|
path, err := ConfigPath()
|
|
if err != nil {
|
|
return false
|
|
}
|
|
_, err = os.Stat(path)
|
|
return err == nil
|
|
}
|
|
|
|
func Load() (*MuyueConfig, error) {
|
|
path, err := ConfigPath()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("reading config: %w", err)
|
|
}
|
|
|
|
var cfg MuyueConfig
|
|
if err := yaml.Unmarshal(data, &cfg); err != nil {
|
|
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
|
|
}
|
|
}
|
|
|
|
migrateProviders(&cfg)
|
|
|
|
return &cfg, nil
|
|
}
|
|
|
|
func Save(cfg *MuyueConfig) error {
|
|
dir, err := ConfigDir()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
|
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(&saveCfg)
|
|
if err != nil {
|
|
return fmt.Errorf("marshaling config: %w", err)
|
|
}
|
|
|
|
if err := os.WriteFile(path, data, 0600); err != nil {
|
|
return fmt.Errorf("writing config: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func Default() *MuyueConfig {
|
|
cfg := &MuyueConfig{
|
|
Version: version.Version,
|
|
Profile: Profile{
|
|
Name: "",
|
|
Pseudo: "muyue",
|
|
Languages: []string{},
|
|
},
|
|
}
|
|
|
|
cfg.Profile.Preferences.DefaultAI = "minimax"
|
|
cfg.Profile.Preferences.AutoUpdate = true
|
|
cfg.Profile.Preferences.CheckOnStart = true
|
|
cfg.Profile.Preferences.Theme = "charm"
|
|
cfg.Profile.Preferences.Language = "fr"
|
|
cfg.Profile.Preferences.KeyboardLayout = "azerty"
|
|
|
|
cfg.AI.Providers = []AIProvider{
|
|
{
|
|
Name: "minimax",
|
|
Model: "MiniMax-M2.7",
|
|
BaseURL: "https://api.minimax.io/v1",
|
|
Active: true,
|
|
},
|
|
{
|
|
Name: "mimo",
|
|
Model: "mimo-v2.5-pro",
|
|
BaseURL: "https://token-plan-ams.xiaomimimo.com/v1",
|
|
Active: false,
|
|
},
|
|
{
|
|
Name: "zai",
|
|
Model: "glm",
|
|
Active: false,
|
|
},
|
|
{
|
|
Name: "anthropic",
|
|
Model: "claude-sonnet-4-20250514",
|
|
Active: false,
|
|
},
|
|
{
|
|
Name: "openai",
|
|
Model: "gpt-4o",
|
|
BaseURL: "https://api.openai.com/v1",
|
|
Active: false,
|
|
},
|
|
{
|
|
Name: "ollama",
|
|
Model: "llama3",
|
|
BaseURL: "http://localhost:11434/api",
|
|
Active: false,
|
|
},
|
|
}
|
|
|
|
cfg.BMAD.Global = true
|
|
|
|
cfg.Terminal.CustomPrompt = true
|
|
cfg.Terminal.PromptTheme = "zerotwo"
|
|
cfg.Terminal.FontSize = 14
|
|
|
|
cfg.Plugins.Enabled = []string{}
|
|
cfg.Plugins.Paths = []string{}
|
|
|
|
cfg.Lessons.Enabled = true
|
|
cfg.Lessons.Dirs = []string{}
|
|
|
|
return cfg
|
|
}
|