package main import ( "os" "path/filepath" "gopkg.in/yaml.v3" ) // Config 存储云枢 Agent 的所有配置 type Config struct { LLM LLMConfig `yaml:"llm"` } // LLMConfig 大模型连接配置 type LLMConfig struct { Host string `yaml:"host"` Model string `yaml:"model"` Key string `yaml:"key"` } // ConfigDir 返回全局配置目录路径 ~/.config/yunshu/ func ConfigDir() string { home, err := os.UserHomeDir() if err != nil { return "./.config/yunshu" } return filepath.Join(home, ".config", "yunshu") } // oldConfigDir 旧版本配置目录,用于迁移 func oldConfigDir() string { home, err := os.UserHomeDir() if err != nil { return "" } return filepath.Join(home, ".config", "weather-cli") } // migrateOldConfig 从旧目录 weather-cli 迁移配置到 yunshu func migrateOldConfig() { old := oldConfigDir() if old == "" { return } // 旧目录不存在,无需迁移 if _, err := os.Stat(old); os.IsNotExist(err) { return } // 新目录已存在,说明已迁移过 new := ConfigDir() if _, err := os.Stat(new); err == nil { return } // 复制旧目录到新目录 copyDirRecursive(old, new) os.RemoveAll(old) } // LoadConfig 从 ~/.config/yunshu/config.yml 读取配置 func LoadConfig() (*Config, error) { path := filepath.Join(ConfigDir(), "config.yml") data, err := os.ReadFile(path) if err != nil { return nil, err } var cfg Config if err := yaml.Unmarshal(data, &cfg); err != nil { return nil, err } return &cfg, nil } // SaveConfig 将配置写入 ~/.config/yunshu/config.yml func SaveConfig(cfg *Config) error { dir := ConfigDir() if err := os.MkdirAll(dir, 0755); err != nil { return err } path := filepath.Join(dir, "config.yml") data, err := yaml.Marshal(cfg) if err != nil { return err } // 0600 - 仅所有者可读写,保护 API Key return os.WriteFile(path, data, 0600) } // SearchFile 在项目目录和全局配置目录中搜索文件 // 项目目录优先,全局配置目录作为后备 func SearchFile(relativePath string) string { paths := []string{ relativePath, // 项目目录 filepath.Join(ConfigDir(), relativePath), // 全局配置目录 } for _, p := range paths { if _, err := os.Stat(p); err == nil { return p } } return relativePath } // CopyDefaultDir 将项目目录下的默认文件复制到全局配置目录 func CopyDefaultDir(srcDir, dstDir string) { entries, err := os.ReadDir(srcDir) if err != nil { return } dstBase := filepath.Join(ConfigDir(), dstDir) for _, e := range entries { srcPath := filepath.Join(srcDir, e.Name()) dstPath := filepath.Join(dstBase, e.Name()) if e.IsDir() { os.MkdirAll(dstPath, 0755) copyDirRecursive(srcPath, dstPath) } else { data, err := os.ReadFile(srcPath) if err != nil { continue } os.MkdirAll(filepath.Dir(dstPath), 0755) os.WriteFile(dstPath, data, 0644) } } } func copyDirRecursive(src, dst string) { entries, err := os.ReadDir(src) if err != nil { return } os.MkdirAll(dst, 0755) for _, e := range entries { srcPath := filepath.Join(src, e.Name()) dstPath := filepath.Join(dst, e.Name()) if e.IsDir() { copyDirRecursive(srcPath, dstPath) } else { data, err := os.ReadFile(srcPath) if err != nil { continue } os.WriteFile(dstPath, data, 0644) } } }