Major changes: - Refactor CLI entry point to Cobra commands (root, setup, scan, doctor, install, update, lsp, mcp, skills, config, version) - Add LSP registry with health checks, auto-install, and editor config generation - Add MCP registry with editor detection, status tracking, and per-editor configuration - Add workflow engine with planner and step execution for automated task chains - Add conversation search, export (Markdown/JSON), and detailed token counting - Add streaming shell chat handler with tool call/result events - Add skill validation, dry-run testing, and export endpoints - Enrich dashboard with Tools/Activity/Status tabs and tool cards grid - Add PRD documentation - Complete i18n for both EN and FR 💘 Generated with Crush Assisted-by: GLM-5.1 via Crush <crush@charm.land>
120 lines
2.7 KiB
Go
120 lines
2.7 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"sync"
|
|
|
|
"github.com/muyue/muyue/internal/installer"
|
|
"github.com/muyue/muyue/internal/scanner"
|
|
"github.com/muyue/muyue/internal/updater"
|
|
)
|
|
|
|
func (s *Server) handleUpdates(w http.ResponseWriter, r *http.Request) {
|
|
result := scanner.ScanSystem()
|
|
statuses := updater.CheckUpdates(result)
|
|
type updateInfo struct {
|
|
Tool string `json:"tool"`
|
|
Current string `json:"current"`
|
|
Latest string `json:"latest"`
|
|
NeedsUpdate bool `json:"needsUpdate"`
|
|
Error string `json:"error,omitempty"`
|
|
}
|
|
updates := make([]updateInfo, len(statuses))
|
|
for i, u := range statuses {
|
|
updates[i] = updateInfo{
|
|
Tool: u.Tool,
|
|
Current: u.Current,
|
|
Latest: u.Latest,
|
|
NeedsUpdate: u.NeedsUpdate,
|
|
Error: u.Error,
|
|
}
|
|
}
|
|
writeJSON(w, map[string]interface{}{
|
|
"updates": updates,
|
|
})
|
|
}
|
|
|
|
func (s *Server) handleInstall(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != "POST" {
|
|
writeError(w, "POST only", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
var body struct {
|
|
Tools []string `json:"tools"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
|
writeError(w, err.Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
if len(body.Tools) == 0 {
|
|
writeError(w, "no tools specified", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
results := make([]installer.InstallResult, len(body.Tools))
|
|
var wg sync.WaitGroup
|
|
var mu sync.Mutex
|
|
|
|
for i, tool := range body.Tools {
|
|
wg.Add(1)
|
|
go func(idx int, name string) {
|
|
defer wg.Done()
|
|
inst := installer.New(s.config)
|
|
res := inst.InstallTool(name)
|
|
mu.Lock()
|
|
results[idx] = res
|
|
mu.Unlock()
|
|
}(i, tool)
|
|
}
|
|
|
|
wg.Wait()
|
|
|
|
writeJSON(w, map[string]interface{}{
|
|
"status": "done",
|
|
"tools": body.Tools,
|
|
"results": results,
|
|
})
|
|
}
|
|
|
|
func (s *Server) handleRunUpdate(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != "POST" {
|
|
writeError(w, "POST only", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
var body struct {
|
|
Tool string `json:"tool"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
|
writeError(w, "invalid request body", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
result := scanner.ScanSystem()
|
|
statuses := updater.CheckUpdates(result)
|
|
|
|
if body.Tool != "" {
|
|
for _, u := range statuses {
|
|
if u.Tool == body.Tool && u.NeedsUpdate {
|
|
updater.RunAutoUpdate([]updater.UpdateStatus{u})
|
|
}
|
|
}
|
|
writeJSON(w, map[string]string{"status": "ok", "tool": body.Tool})
|
|
return
|
|
}
|
|
|
|
needsUpdate := make([]updater.UpdateStatus, 0)
|
|
for _, u := range statuses {
|
|
if u.NeedsUpdate {
|
|
needsUpdate = append(needsUpdate, u)
|
|
}
|
|
}
|
|
if len(needsUpdate) > 0 {
|
|
updater.RunAutoUpdate(needsUpdate)
|
|
}
|
|
writeJSON(w, map[string]interface{}{
|
|
"status": "ok",
|
|
"updated": len(needsUpdate),
|
|
})
|
|
}
|