package server import ( "encoding/json" "errors" "net/http" "strings" "codex-agent-manager/internal/agents" "codex-agent-manager/internal/app" "codex-agent-manager/internal/codexhome" "codex-agent-manager/internal/projects" "codex-agent-manager/internal/runtime" "codex-agent-manager/internal/workflow" ) func New(cfg app.Config) http.Handler { mux := http.NewServeMux() agentStore := agents.Store{CodexHome: cfg.CodexHome} projectStore := projects.Store{CodexHome: cfg.CodexHome} runtimeStore := runtime.Store{CodexHome: cfg.CodexHome} workspaceRoot := cfg.WorkspaceRoot if workspaceRoot == "" { workspaceRoot = "." } workflowStore := workflow.Store{WorkspaceRoot: workspaceRoot, Runtime: runtimeStore} mux.HandleFunc("/api/health", func(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusOK, map[string]string{"status": "ok"}) }) mux.HandleFunc("/api/agents", func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { writeJSON(w, http.StatusMethodNotAllowed, map[string]string{"error": "方法不允许"}) return } items, err := agentStore.List() if err != nil { writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } writeJSON(w, http.StatusOK, map[string]any{"items": items}) }) mux.HandleFunc("/api/agents/", func(w http.ResponseWriter, r *http.Request) { id, action, ok := parseAgentActionPath(r.URL.Path) if !ok { http.NotFound(w, r) return } if r.Method != http.MethodPost { writeJSON(w, http.StatusMethodNotAllowed, map[string]string{"error": "方法不允许"}) return } switch action { case "validate": handleAgentValidate(w, r, agentStore, id) case "write": handleAgentWrite(w, r, agentStore, id) default: http.NotFound(w, r) } }) mux.HandleFunc("/api/projects", func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { writeJSON(w, http.StatusMethodNotAllowed, map[string]string{"error": "方法不允许"}) return } items, err := projectStore.List() if err != nil { writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } writeJSON(w, http.StatusOK, map[string]any{"items": items}) }) mux.HandleFunc("/api/runtime/threads", func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { writeJSON(w, http.StatusMethodNotAllowed, map[string]string{"error": "方法不允许"}) return } snapshot, err := runtimeStore.Snapshot() if err != nil { writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } writeJSON(w, http.StatusOK, map[string]any{ "items": snapshot.Threads, "edges": snapshot.SpawnEdges, "goals": snapshot.Goals, "source": snapshot.Source, "sources": snapshot.Sources, }) }) mux.HandleFunc("/api/workflow/events", func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { writeJSON(w, http.StatusMethodNotAllowed, map[string]string{"error": "方法不允许"}) return } view, err := workflowStore.View() if err != nil { writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } writeJSON(w, http.StatusOK, map[string]any{ "items": view.Events, "handoffEdges": view.HandoffEdges, "phases": view.Phases, "source": view.Source, }) }) return mux } type validateRequest struct { Content string `json:"content"` } type writeRequest struct { Content string `json:"content"` ExpectedHash string `json:"expectedHash"` } func parseAgentActionPath(path string) (string, string, bool) { rest := strings.TrimPrefix(path, "/api/agents/") parts := strings.Split(rest, "/") if len(parts) != 2 || parts[0] == "" || parts[1] == "" { return "", "", false } return parts[0], parts[1], true } func handleAgentValidate(w http.ResponseWriter, r *http.Request, store agents.Store, id string) { var body validateRequest if err := json.NewDecoder(r.Body).Decode(&body); err != nil { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "请求体不是有效 JSON"}) return } result, err := store.ValidateDraft(id, body.Content) if err != nil { writeAgentError(w, err) return } writeJSON(w, http.StatusOK, result) } func handleAgentWrite(w http.ResponseWriter, r *http.Request, store agents.Store, id string) { var body writeRequest if err := json.NewDecoder(r.Body).Decode(&body); err != nil { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "请求体不是有效 JSON"}) return } result, err := store.WriteDraft(id, body.Content, body.ExpectedHash) if err != nil { writeAgentError(w, err) return } writeJSON(w, http.StatusOK, result) } func writeAgentError(w http.ResponseWriter, err error) { status := http.StatusBadRequest if errors.Is(err, agents.ErrWriteConflict) { status = http.StatusConflict } else if errors.Is(err, codexhome.ErrForbiddenPath) || errors.Is(err, codexhome.ErrOutsideCodexHome) { status = http.StatusForbidden } writeJSON(w, status, map[string]string{"error": err.Error()}) } func writeJSON(w http.ResponseWriter, status int, body any) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) _ = json.NewEncoder(w).Encode(body) }