Files
Agent-Squad/platform/http.go
T
weichenandCursor 5cbac00b99
ci / vet-and-test (pull_request) Failing after 1s
ci / docker-checkout-hostgateway (pull_request) Failing after 3s
Add a SQLite orchestration platform beside the goal engine
Why: Squad runs were one file workspace and one JSON file the leader had to emit, so agents, issues, and retries had no durable queue.
Evidence: GOWORK=off go test -count=1 -timeout 180s ./...
Impact: /v1/runs stays. New routes mount only when the orchestration database opens. SSE for a run id now skips other runs. External CLIs are not a sandbox.
Refs: feature/orchestration-platform S1-S7
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-01 10:30:01 +00:00

121 lines
3.7 KiB
Go

package platform
import (
"encoding/json"
"io"
"net/http"
"time"
)
func nowTime() time.Time { return time.Now() }
// Mount attaches the orchestration routes. The goal-engine /v1/runs routes stay separate.
func Mount(mux *http.ServeMux, st *Store) {
mux.HandleFunc("POST /v1/workspaces", func(w http.ResponseWriter, r *http.Request) {
var body struct{ Name string }
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
ws, err := st.CreateWorkspace(body.Name)
write(w, ws, err)
})
mux.HandleFunc("POST /v1/agents", func(w http.ResponseWriter, r *http.Request) {
var a Agent
if err := json.NewDecoder(r.Body).Decode(&a); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
created, err := st.CreateAgent(a)
write(w, created, err)
})
mux.HandleFunc("POST /v1/squads", func(w http.ResponseWriter, r *http.Request) {
var body struct {
Squad
Members []SquadMember
}
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
created, err := st.CreateSquad(body.Squad, body.Members)
write(w, created, err)
})
mux.HandleFunc("POST /v1/issues", func(w http.ResponseWriter, r *http.Request) {
var issue Issue
if err := json.NewDecoder(r.Body).Decode(&issue); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
created, err := st.CreateIssue(issue)
write(w, created, err)
})
mux.HandleFunc("POST /v1/issues/{id}/assign", func(w http.ResponseWriter, r *http.Request) {
var body struct {
Kind string
ID string
Start bool
}
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
err := st.AssignIssue(r.PathValue("id"), body.Kind, body.ID, body.Start)
write(w, map[string]bool{"ok": err == nil}, err)
})
mux.HandleFunc("POST /v1/issues/{id}/comments", func(w http.ResponseWriter, r *http.Request) {
var c Comment
if err := json.NewDecoder(r.Body).Decode(&c); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
c.IssueID = r.PathValue("id")
created, err := st.AddComment(c)
write(w, created, err)
})
mux.HandleFunc("POST /v1/tasks/claim", func(w http.ResponseWriter, r *http.Request) {
task, ok, err := st.Claim(r.URL.Query().Get("runtime"), nowTime())
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
write(w, map[string]any{"task": task, "ok": ok}, nil)
})
mux.HandleFunc("POST /v1/webhooks/autopilots/{token}", func(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(io.LimitReader(r.Body, 256*1024+1))
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
headers := map[string]string{}
for _, key := range []string{"Idempotency-Key", "X-GitHub-Delivery", "X-Hub-Signature-256", "X-GitHub-Event"} {
if v := r.Header.Get(key); v != "" {
headers[key] = v
}
}
event, err := st.AcceptWebhook(r.PathValue("token"), body, headers)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
write(w, map[string]string{"event": event}, nil)
})
mux.HandleFunc("GET /v1/events/ws", func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Upgrade") == "" {
http.Error(w, "upgrade required", http.StatusUpgradeRequired)
return
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte("{\"type\":\"ready\"}\n"))
})
}
func write(w http.ResponseWriter, v any, err error) {
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(v)
}