Wails v3 tray panel and window, TUI and CLI. Switches model, effort and provider for Claude Code, Codex, Gemini CLI, OpenCode, Pi, Goose, Cursor, Copilot CLI and Crush with format-preserving config edits. Providers page and registry, stored keys, live vendor model lists, profiles. Co-Authored-By: Claude <noreply@anthropic.com>
59 lines
1.4 KiB
Go
59 lines
1.4 KiB
Go
// Package edit provides format-preserving editors for the config files that
|
|
// coding agents keep: JSON/JSONC, TOML and YAML. Only the requested key
|
|
// changes; comments, ordering and indentation are left as they are.
|
|
package edit
|
|
|
|
import (
|
|
"errors"
|
|
"io/fs"
|
|
"os"
|
|
"path/filepath"
|
|
)
|
|
|
|
// Read returns the file contents, or (nil, nil) when the file does not exist.
|
|
func Read(path string) ([]byte, error) {
|
|
b, err := os.ReadFile(path)
|
|
if errors.Is(err, fs.ErrNotExist) {
|
|
return nil, nil
|
|
}
|
|
return b, err
|
|
}
|
|
|
|
// WriteAtomic writes data to path via a temp file + rename so a crash can
|
|
// never leave a half-written config behind. File mode is preserved.
|
|
func WriteAtomic(path string, data []byte) error {
|
|
mode := fs.FileMode(0o644)
|
|
if st, err := os.Stat(path); err == nil {
|
|
mode = st.Mode().Perm()
|
|
}
|
|
dir := filepath.Dir(path)
|
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
return err
|
|
}
|
|
tmp, err := os.CreateTemp(dir, "."+filepath.Base(path)+".*.tmp")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
tmpName := tmp.Name()
|
|
cleanup := func() { _ = os.Remove(tmpName) }
|
|
if _, err := tmp.Write(data); err != nil {
|
|
tmp.Close()
|
|
cleanup()
|
|
return err
|
|
}
|
|
if err := tmp.Chmod(mode); err != nil {
|
|
tmp.Close()
|
|
cleanup()
|
|
return err
|
|
}
|
|
if err := tmp.Close(); err != nil {
|
|
cleanup()
|
|
return err
|
|
}
|
|
if err := os.Rename(tmpName, path); err != nil {
|
|
cleanup()
|
|
return err
|
|
}
|
|
return nil
|
|
}
|