package api import ( "encoding/json" "fmt" "net/http" "os" "path/filepath" "strings" "time" "github.com/muyue/muyue/internal/config" "github.com/muyue/muyue/internal/mcpserver" ) func (s *Server) handleFileContent(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: path := r.URL.Query().Get("path") if path == "" { writeError(w, "path parameter required", http.StatusBadRequest) return } home, _ := os.UserHomeDir() path = strings.ReplaceAll(path, "~", home) if !filepath.IsAbs(path) { writeError(w, "path must be absolute", http.StatusBadRequest) return } data, err := os.ReadFile(path) if err != nil { writeError(w, fmt.Sprintf("Error reading file: %v", err), http.StatusNotFound) return } ext := strings.ToLower(filepath.Ext(path)) lang := "text" switch ext { case ".go": lang = "go" case ".js", ".jsx": lang = "javascript" case ".ts", ".tsx": lang = "typescript" case ".py": lang = "python" case ".json": lang = "json" case ".yaml", ".yml": lang = "yaml" case ".md": lang = "markdown" case ".css": lang = "css" case ".html": lang = "html" case ".sh", ".bash": lang = "shell" case ".rs": lang = "rust" case ".java": lang = "java" } stat, _ := os.Stat(path) modTime := "" if stat != nil { modTime = stat.ModTime().Format("2006-01-02T15:04:05Z07:00") } writeJSON(w, map[string]interface{}{ "path": path, "content": string(data), "lang": lang, "size": len(data), "modTime": modTime, }) case http.MethodPut: var body struct { Path string `json:"path"` Content string `json:"content"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil { writeError(w, err.Error(), http.StatusBadRequest) return } if body.Path == "" { writeError(w, "path required", http.StatusBadRequest) return } home, _ := os.UserHomeDir() path := strings.ReplaceAll(body.Path, "~", home) if !filepath.IsAbs(path) { writeError(w, "path must be absolute", http.StatusBadRequest) return } dir := filepath.Dir(path) if err := os.MkdirAll(dir, 0755); err != nil { writeError(w, fmt.Sprintf("Error creating directory: %v", err), http.StatusInternalServerError) return } if err := os.WriteFile(path, []byte(body.Content), 0644); err != nil { writeError(w, fmt.Sprintf("Error writing file: %v", err), http.StatusInternalServerError) return } writeJSON(w, map[string]interface{}{ "status": "ok", "path": path, "size": len(body.Content), }) default: writeError(w, "GET/PUT only", http.StatusMethodNotAllowed) } } func (s *Server) handleMuyueMCPServerStatus(w http.ResponseWriter, r *http.Request) { writeJSON(w, map[string]interface{}{ "enabled": s.mcpServer != nil, "running": s.mcpServer != nil, "port": s.getMCPServerPort(), }) } func (s *Server) handleMuyueMCPServerStart(w http.ResponseWriter, r *http.Request) { if s.mcpServer != nil { writeJSON(w, map[string]string{"status": "already_running"}) return } s.startMCPServer() writeJSON(w, map[string]interface{}{ "status": "started", "port": s.getMCPServerPort(), }) } func (s *Server) handleMuyueMCPServerStop(w http.ResponseWriter, r *http.Request) { if s.mcpServer == nil { writeJSON(w, map[string]string{"status": "not_running"}) return } s.mcpServer.Stop() s.mcpServer = nil writeJSON(w, map[string]string{"status": "stopped"}) } func (s *Server) getMCPServerPort() int { if s.mcpServer == nil { return 0 } return s.mcpServer.Port() } func (s *Server) startMCPServer() { port := 8096 if s.config != nil { } s.mcpServer = mcpserver.New(port) s.mcpServer.Start() } func (s *Server) handleAgentSessionsList(w http.ResponseWriter, r *http.Request) { sessions := s.agentTracker.Discover() writeJSON(w, map[string]interface{}{ "sessions": sessions, }) } func (s *Server) handleAgentSessionOutput(w http.ResponseWriter, r *http.Request) { id := strings.TrimPrefix(r.URL.Path, "/api/agent-sessions/") if id == "" { writeError(w, "session id required", http.StatusBadRequest) return } session := s.agentTracker.Get(id) if session == nil { writeError(w, "session not found", http.StatusNotFound) return } writeJSON(w, session) } func (s *Server) handleWorkspaceList(w http.ResponseWriter, r *http.Request) { dir, err := configWorkspacesDir() if err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } entries, err := os.ReadDir(dir) if err != nil { if os.IsNotExist(err) { writeJSON(w, map[string]interface{}{"workspaces": []interface{}{}}) return } writeError(w, err.Error(), http.StatusInternalServerError) return } var workspaces []map[string]interface{} for _, entry := range entries { if entry.IsDir() { continue } if !strings.HasSuffix(entry.Name(), ".json") { continue } name := strings.TrimSuffix(entry.Name(), ".json") data, err := os.ReadFile(filepath.Join(dir, entry.Name())) if err != nil { continue } var ws map[string]interface{} if err := json.Unmarshal(data, &ws); err != nil { continue } ws["name"] = name workspaces = append(workspaces, ws) } if workspaces == nil { workspaces = []map[string]interface{}{} } writeJSON(w, map[string]interface{}{"workspaces": workspaces}) } func (s *Server) handleWorkspaceSave(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { writeError(w, "POST only", http.StatusMethodNotAllowed) return } var body struct { Name string `json:"name"` Layout string `json:"layout"` Tabs string `json:"tabs"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil { writeError(w, err.Error(), http.StatusBadRequest) return } if body.Name == "" { writeError(w, "name required", http.StatusBadRequest) return } dir, err := configWorkspacesDir() if err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } wsData := map[string]interface{}{ "name": body.Name, "layout": body.Layout, "tabs": body.Tabs, "updated": fmt.Sprintf("%d", time.Now().Unix()), } data, err := json.MarshalIndent(wsData, "", " ") if err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } if err := os.WriteFile(filepath.Join(dir, body.Name+".json"), data, 0644); err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } writeJSON(w, map[string]string{"status": "ok"}) } func (s *Server) handleWorkspaceGet(w http.ResponseWriter, r *http.Request) { name := strings.TrimPrefix(r.URL.Path, "/api/workspace/") if name == "" { writeError(w, "name required", http.StatusBadRequest) return } if r.Method == "DELETE" { dir, err := configWorkspacesDir() if err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } if err := os.Remove(filepath.Join(dir, name+".json")); err != nil { writeError(w, "workspace not found", http.StatusNotFound) return } writeJSON(w, map[string]string{"status": "ok"}) return } dir, err := configWorkspacesDir() if err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } data, err := os.ReadFile(filepath.Join(dir, name+".json")) if err != nil { writeError(w, "workspace not found", http.StatusNotFound) return } var result map[string]interface{} json.Unmarshal(data, &result) writeJSON(w, result) } func configWorkspacesDir() (string, error) { configDir, err := config.ConfigDir() if err != nil { return "", err } dir := filepath.Join(configDir, "workspaces") if err := os.MkdirAll(dir, 0755); err != nil { return "", fmt.Errorf("create workspaces dir: %w", err) } return dir, nil }