package httpapi import ( "encoding/json" "net/http" "strconv" workflowrunapp "inbox/internal/app/workflowrun" "inbox/internal/base/httpx" "inbox/internal/domain/workflow" ) func (h *Handler) getWorkflowRun(w http.ResponseWriter, r *http.Request) { item, err := h.WorkflowRun.Get(r.Context(), r.PathValue("runID")) if err != nil { writeStoreError(w, err) return } httpx.WriteJSON(w, http.StatusOK, workflowRunResponse(item)) } func (h *Handler) listWorkflowRuns(w http.ResponseWriter, r *http.Request) { items, err := h.WorkflowRun.ListByTopic(r.Context(), r.PathValue("topicID")) if err != nil { writeStoreError(w, err) return } out := make([]map[string]any, 0, len(items)) for _, item := range items { out = append(out, workflowRunResponse(item)) } httpx.WriteJSON(w, http.StatusOK, map[string]any{"workflow_runs": out}) } func (h *Handler) createWorkflowRun(w http.ResponseWriter, r *http.Request) { type request struct { WorkspaceID string `json:"workspace_id"` RoleName string `json:"role_name"` Stage string `json:"stage"` Mode string `json:"mode"` RequestMessageID string `json:"request_message_id"` CommandJSON string `json:"command_json"` } var req request if err := httpx.DecodeJSON(r, &req); err != nil { httpx.WriteError(w, http.StatusBadRequest, err.Error()) return } item, err := h.WorkflowRun.Start(r.Context(), workflow.Run{ WorkspaceID: req.WorkspaceID, TopicID: r.PathValue("topicID"), RoleName: req.RoleName, Stage: workflow.Stage(req.Stage), Mode: req.Mode, RequestMessageID: req.RequestMessageID, CommandJSON: req.CommandJSON, }) if err != nil { writeStoreError(w, err) return } httpx.WriteJSON(w, http.StatusCreated, item) } func (h *Handler) patchWorkflowRun(w http.ResponseWriter, r *http.Request) { type request struct { Status *workflow.RunStatus `json:"status"` ReplyMessageID *string `json:"reply_message_id"` ExitCode *int `json:"exit_code"` CompletedAt *string `json:"completed_at"` ErrorMessage *string `json:"error_message"` CommandJSON *string `json:"command_json"` } var req request if err := httpx.DecodeJSON(r, &req); err != nil { httpx.WriteError(w, http.StatusBadRequest, err.Error()) return } item, err := h.WorkflowRun.Patch(r.Context(), r.PathValue("runID"), workflowrunapp.Patch{ Status: req.Status, ReplyMessageID: req.ReplyMessageID, ExitCode: req.ExitCode, CompletedAt: req.CompletedAt, ErrorMessage: req.ErrorMessage, CommandJSON: req.CommandJSON, }) if err != nil { writeStoreError(w, err) return } httpx.WriteJSON(w, http.StatusOK, item) } func (h *Handler) listWorkflowRunLogs(w http.ResponseWriter, r *http.Request) { afterSeq := 0 if raw := r.URL.Query().Get("after_seq"); raw != "" { value, err := strconv.Atoi(raw) if err != nil { httpx.WriteError(w, http.StatusBadRequest, "after_seq must be an integer") return } afterSeq = value } items, err := h.WorkflowRun.ListLogs(r.Context(), r.PathValue("runID"), afterSeq) if err != nil { writeStoreError(w, err) return } httpx.WriteJSON(w, http.StatusOK, map[string]any{"logs": items}) } func (h *Handler) appendWorkflowRunLog(w http.ResponseWriter, r *http.Request) { type request struct { Stream string `json:"stream"` Content string `json:"content"` CreatedAt string `json:"created_at"` } var req request if err := httpx.DecodeJSON(r, &req); err != nil { httpx.WriteError(w, http.StatusBadRequest, err.Error()) return } item, err := h.WorkflowRun.AppendLog(r.Context(), workflow.RunLog{ RunID: r.PathValue("runID"), Stream: workflow.LogStream(req.Stream), Content: req.Content, CreatedAt: req.CreatedAt, }) if err != nil { writeStoreError(w, err) return } httpx.WriteJSON(w, http.StatusCreated, item) } func workflowRunResponse(item workflow.Run) map[string]any { payload := map[string]any{} raw, err := json.Marshal(item) if err == nil { _ = json.Unmarshal(raw, &payload) } if payload == nil { payload = map[string]any{} } if planning, ok := decodeWorkflowRunPlanning(item.CommandJSON); ok { payload["planning"] = planning } return payload } func decodeWorkflowRunPlanning(commandJSON string) (map[string]any, bool) { if commandJSON == "" { return nil, false } var payload map[string]any if err := json.Unmarshal([]byte(commandJSON), &payload); err != nil { return nil, false } planning, ok := payload["planning"] if !ok { return nil, false } typed, ok := planning.(map[string]any) return typed, ok }