2026-10-15 21:17:58 -07:00
|
|
|
package server
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"io"
|
|
|
|
|
"net/http"
|
|
|
|
|
"net/url"
|
2026-10-20 18:52:04 -07:00
|
|
|
"strings"
|
2026-10-15 21:17:58 -07:00
|
|
|
|
2026-10-20 18:52:04 -07:00
|
|
|
"g.fc.run/ai/byre/internal/api"
|
2026-10-18 15:32:07 -07:00
|
|
|
"g.fc.run/ai/byre/internal/workflow"
|
2026-10-15 21:17:58 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const maxWorkflowFileBytes = 1 << 20
|
|
|
|
|
|
|
|
|
|
func (s *Server) ListWorkflows(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
workflows, err := s.store.ListWorkflows(r.Context())
|
|
|
|
|
if err != nil {
|
|
|
|
|
writeStoreError(w, err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
writeJSON(w, http.StatusOK, workflows)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Server) GetWorkflow(w http.ResponseWriter, r *http.Request, name string) {
|
2026-10-20 18:52:04 -07:00
|
|
|
accept := r.Header.Get("Accept")
|
|
|
|
|
if strings.Contains(accept, "application/json") && !strings.Contains(accept, "yaml") {
|
|
|
|
|
def, err := s.store.GetWorkflow(r.Context(), name)
|
|
|
|
|
if err != nil {
|
|
|
|
|
writeStoreError(w, err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
writeJSON(w, http.StatusOK, def)
|
|
|
|
|
return
|
|
|
|
|
}
|
2026-10-15 21:17:58 -07:00
|
|
|
file, err := s.agents.WorkflowFile(r.Context(), name)
|
|
|
|
|
if err != nil {
|
|
|
|
|
writeAgentError(w, err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
w.Header().Set("Content-Type", "application/yaml")
|
|
|
|
|
w.Header().Set("Content-Disposition", "attachment; filename*=UTF-8''"+url.PathEscape(workflow.Filename(name)))
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
w.Write(file)
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-20 20:09:38 -07:00
|
|
|
func (s *Server) PutWorkflow(w http.ResponseWriter, r *http.Request, name string, params api.PutWorkflowParams) {
|
2026-10-15 21:17:58 -07:00
|
|
|
file, err := io.ReadAll(http.MaxBytesReader(w, r.Body, maxWorkflowFileBytes))
|
|
|
|
|
if err != nil {
|
|
|
|
|
writeError(w, http.StatusBadRequest, fmt.Errorf("read workflow file: %w", err))
|
|
|
|
|
return
|
|
|
|
|
}
|
2026-10-20 20:09:38 -07:00
|
|
|
from := ""
|
|
|
|
|
if params.From != nil {
|
|
|
|
|
from = *params.From
|
|
|
|
|
}
|
|
|
|
|
summary, err := s.agents.SaveWorkflow(r.Context(), name, from, file, actor(r))
|
2026-10-15 21:17:58 -07:00
|
|
|
if err != nil {
|
|
|
|
|
writeAgentError(w, err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
writeJSON(w, http.StatusOK, summary)
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-20 18:52:04 -07:00
|
|
|
func (s *Server) SetReviewWorkflow(w http.ResponseWriter, r *http.Request, projectID string) {
|
|
|
|
|
req := api.ReviewWorkflow{}
|
|
|
|
|
if !readJSON(w, r, &req) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
p, err := s.agents.SetReviewWorkflow(r.Context(), projectID, req.Workflow, actor(r))
|
|
|
|
|
if err != nil {
|
|
|
|
|
writeAgentError(w, err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
writeJSON(w, http.StatusOK, p)
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-15 21:17:58 -07:00
|
|
|
func (s *Server) DeleteWorkflow(w http.ResponseWriter, r *http.Request, name string) {
|
|
|
|
|
err := s.store.DeleteWorkflow(r.Context(), name, actor(r))
|
|
|
|
|
if err != nil {
|
|
|
|
|
writeStoreError(w, err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
w.WriteHeader(http.StatusNoContent)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Server) GetTaskWorkflow(w http.ResponseWriter, r *http.Request, taskID string) {
|
|
|
|
|
tw, err := s.agents.TaskWorkflow(r.Context(), taskID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
writeStoreError(w, err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
writeJSON(w, http.StatusOK, tw)
|
|
|
|
|
}
|
2026-10-16 11:01:53 -07:00
|
|
|
|
|
|
|
|
func (s *Server) GetWorkflowRun(w http.ResponseWriter, r *http.Request, taskID string) {
|
|
|
|
|
run, err := s.agents.WorkflowRun(r.Context(), taskID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
writeAgentError(w, err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
writeJSON(w, http.StatusOK, run)
|
|
|
|
|
}
|