mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-05-17 11:47:28 +08:00
skills api: auto check updates from clawhub on list
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
@@ -512,19 +513,34 @@ func (s *RegistryServer) handleWebUISkills(w http.ResponseWriter, r *http.Reques
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
type skillItem struct {
|
type skillItem struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
|
UpdateChecked bool `json:"update_checked"`
|
||||||
|
RemoteFound bool `json:"remote_found,omitempty"`
|
||||||
|
RemoteVersion string `json:"remote_version,omitempty"`
|
||||||
|
CheckError string `json:"check_error,omitempty"`
|
||||||
}
|
}
|
||||||
items := make([]skillItem, 0, len(entries))
|
items := make([]skillItem, 0, len(entries))
|
||||||
|
checkUpdates := strings.TrimSpace(r.URL.Query().Get("check_updates")) != "0"
|
||||||
for _, e := range entries {
|
for _, e := range entries {
|
||||||
if !e.IsDir() {
|
if !e.IsDir() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
name := e.Name()
|
name := e.Name()
|
||||||
enabled := !strings.HasSuffix(name, ".disabled")
|
enabled := !strings.HasSuffix(name, ".disabled")
|
||||||
items = append(items, skillItem{Name: strings.TrimSuffix(name, ".disabled"), Enabled: enabled})
|
baseName := strings.TrimSuffix(name, ".disabled")
|
||||||
|
it := skillItem{Name: baseName, Enabled: enabled, UpdateChecked: checkUpdates}
|
||||||
|
if checkUpdates {
|
||||||
|
found, version, checkErr := queryClawHubSkillVersion(r.Context(), baseName)
|
||||||
|
it.RemoteFound = found
|
||||||
|
it.RemoteVersion = version
|
||||||
|
if checkErr != nil {
|
||||||
|
it.CheckError = checkErr.Error()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
items = append(items, it)
|
||||||
}
|
}
|
||||||
_ = json.NewEncoder(w).Encode(map[string]interface{}{"ok": true, "skills": items})
|
_ = json.NewEncoder(w).Encode(map[string]interface{}{"ok": true, "skills": items, "source": "clawhub"})
|
||||||
case http.MethodPost:
|
case http.MethodPost:
|
||||||
var body struct {
|
var body struct {
|
||||||
Action string `json:"action"`
|
Action string `json:"action"`
|
||||||
@@ -567,6 +583,71 @@ func (s *RegistryServer) handleWebUISkills(w http.ResponseWriter, r *http.Reques
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func queryClawHubSkillVersion(ctx context.Context, skill string) (found bool, version string, err error) {
|
||||||
|
skill = strings.TrimSpace(skill)
|
||||||
|
if skill == "" {
|
||||||
|
return false, "", fmt.Errorf("skill empty")
|
||||||
|
}
|
||||||
|
cctx, cancel := context.WithTimeout(ctx, 8*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
cmd := exec.CommandContext(cctx, "clawhub", "search", skill, "--json")
|
||||||
|
out, runErr := cmd.Output()
|
||||||
|
if runErr != nil {
|
||||||
|
return false, "", runErr
|
||||||
|
}
|
||||||
|
var payload interface{}
|
||||||
|
if err := json.Unmarshal(out, &payload); err != nil {
|
||||||
|
return false, "", err
|
||||||
|
}
|
||||||
|
lowerSkill := strings.ToLower(skill)
|
||||||
|
var walk func(v interface{}) (bool, string)
|
||||||
|
walk = func(v interface{}) (bool, string) {
|
||||||
|
switch t := v.(type) {
|
||||||
|
case map[string]interface{}:
|
||||||
|
name := strings.ToLower(strings.TrimSpace(anyToString(t["name"])))
|
||||||
|
if name == "" {
|
||||||
|
name = strings.ToLower(strings.TrimSpace(anyToString(t["id"])))
|
||||||
|
}
|
||||||
|
if name == lowerSkill || strings.Contains(name, lowerSkill) {
|
||||||
|
ver := strings.TrimSpace(anyToString(t["version"]))
|
||||||
|
if ver == "" {
|
||||||
|
ver = strings.TrimSpace(anyToString(t["latest_version"]))
|
||||||
|
}
|
||||||
|
return true, ver
|
||||||
|
}
|
||||||
|
for _, vv := range t {
|
||||||
|
if ok, ver := walk(vv); ok {
|
||||||
|
return ok, ver
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case []interface{}:
|
||||||
|
for _, vv := range t {
|
||||||
|
if ok, ver := walk(vv); ok {
|
||||||
|
return ok, ver
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, ""
|
||||||
|
}
|
||||||
|
ok, ver := walk(payload)
|
||||||
|
return ok, ver, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func anyToString(v interface{}) string {
|
||||||
|
switch t := v.(type) {
|
||||||
|
case string:
|
||||||
|
return t
|
||||||
|
case fmt.Stringer:
|
||||||
|
return t.String()
|
||||||
|
default:
|
||||||
|
if v == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
b, _ := json.Marshal(v)
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *RegistryServer) handleWebUIExecApprovals(w http.ResponseWriter, r *http.Request) {
|
func (s *RegistryServer) handleWebUIExecApprovals(w http.ResponseWriter, r *http.Request) {
|
||||||
if !s.checkAuth(r) {
|
if !s.checkAuth(r) {
|
||||||
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
||||||
|
|||||||
Reference in New Issue
Block a user