feat: init sproutclaw-web — Go+Gin backend + React frontend

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-13 20:16:09 +08:00
commit c33b143176
109 changed files with 18773 additions and 0 deletions

View File

@@ -0,0 +1,105 @@
package services
import (
"encoding/json"
"os"
"path/filepath"
"strings"
"sproutclaw-web/internal/models"
)
// ListExtensions reads the extensions directory and returns metadata.
func ListExtensions(agentDir string) []models.ExtensionInfo {
extDir := filepath.Join(agentDir, "extensions")
entries, err := os.ReadDir(extDir)
if err != nil {
return nil
}
var exts []models.ExtensionInfo
for _, e := range entries {
if !e.IsDir() {
continue
}
ext := buildExtensionInfo(filepath.Join(extDir, e.Name()), e.Name())
exts = append(exts, ext)
}
return exts
}
func buildExtensionInfo(path, id string) models.ExtensionInfo {
info := models.ExtensionInfo{
ID: id,
Name: id,
Enabled: true, // assume enabled unless configured otherwise
Source: "local",
}
// try package.json for version/name
pkgPath := filepath.Join(path, "package.json")
if data, err := os.ReadFile(pkgPath); err == nil {
var pkg struct {
Name string `json:"name"`
Version string `json:"version"`
}
if json.Unmarshal(data, &pkg) == nil {
if pkg.Name != "" {
info.Name = pkg.Name
}
info.Version = pkg.Version
// detect npm packages
if strings.Contains(pkg.Name, "/") || strings.HasPrefix(pkg.Name, "@") {
info.Source = "npm"
}
}
}
return info
}
// ToggleExtension is a placeholder full implementation requires settings.json management.
func ToggleExtension(agentDir, id string, enable bool) error {
return updateSettingsExtensions(filepath.Join(agentDir, "settings.json"), id, enable)
}
func updateSettingsExtensions(settingsPath, id string, enable bool) error {
data, err := os.ReadFile(settingsPath)
if err != nil && !os.IsNotExist(err) {
return err
}
var settings map[string]any
if len(data) > 0 {
if err := json.Unmarshal(data, &settings); err != nil {
settings = map[string]any{}
}
} else {
settings = map[string]any{}
}
// extensions is a list of enabled extension IDs
extList, _ := settings["extensions"].([]any)
set := map[string]bool{}
for _, e := range extList {
if s, ok := e.(string); ok {
set[s] = true
}
}
if enable {
set[id] = true
} else {
delete(set, id)
}
newList := make([]string, 0, len(set))
for k := range set {
newList = append(newList, k)
}
settings["extensions"] = newList
out, err := json.MarshalIndent(settings, "", " ")
if err != nil {
return err
}
return os.WriteFile(settingsPath, append(out, '\n'), 0o644)
}