Bold questions
This commit is contained in:
+132
-43
@@ -22,12 +22,11 @@ import (
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
store *store.Store
|
||||
agents *agents.Manager
|
||||
hub *hub
|
||||
worktreeRoot string
|
||||
startedAt time.Time
|
||||
spa fs.FS
|
||||
store *store.Store
|
||||
agents *agents.Manager
|
||||
hub *hub
|
||||
startedAt time.Time
|
||||
spa fs.FS
|
||||
|
||||
usersMu sync.Mutex
|
||||
users map[string]string
|
||||
@@ -42,11 +41,10 @@ func New(dataDir string, spa fs.FS) (*Server, error) {
|
||||
}
|
||||
|
||||
s := &Server{
|
||||
hub: newHub(),
|
||||
worktreeRoot: filepath.Join(dataDir, "worktrees"),
|
||||
startedAt: time.Now().UTC(),
|
||||
spa: spa,
|
||||
users: map[string]string{},
|
||||
hub: newHub(),
|
||||
startedAt: time.Now().UTC(),
|
||||
spa: spa,
|
||||
users: map[string]string{},
|
||||
}
|
||||
|
||||
s.store, err = store.Open(filepath.Join(dataDir, "byre.db"), s.hub.publish)
|
||||
@@ -54,7 +52,7 @@ func New(dataDir string, spa fs.FS) (*Server, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.agents = agents.New(s.store, agents.DefaultLimits)
|
||||
s.agents = agents.New(s.store, agents.DefaultLimits, filepath.Join(dataDir, "worktrees"))
|
||||
err = s.agents.Recover(context.Background())
|
||||
if err != nil {
|
||||
s.store.Close()
|
||||
@@ -245,25 +243,7 @@ func (s *Server) CreateTask(w http.ResponseWriter, r *http.Request, projectID st
|
||||
return
|
||||
}
|
||||
|
||||
id := store.NewID()
|
||||
t := api.Task{
|
||||
Id: id,
|
||||
ProjectId: p.Id,
|
||||
Title: req.Title,
|
||||
Branch: "byre/" + id,
|
||||
WorktreePath: filepath.Join(s.worktreeRoot, p.Id, id),
|
||||
Status: api.TaskStatusActive,
|
||||
CreatedBy: actor(r),
|
||||
CreatedAt: time.Now().UTC(),
|
||||
}
|
||||
|
||||
t.BaseRef, err = git.AddWorktree(p.RepoPath, t.WorktreePath, t.Branch, p.DefaultBranch)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = s.store.CreateTask(r.Context(), t)
|
||||
t, err := s.agents.CreateTask(r.Context(), p, req.Title, actor(r), p.DefaultBranch)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
@@ -280,37 +260,75 @@ func (s *Server) GetTask(w http.ResponseWriter, r *http.Request, taskID string)
|
||||
writeJSON(w, http.StatusOK, t)
|
||||
}
|
||||
|
||||
func (s *Server) CompleteTask(w http.ResponseWriter, r *http.Request, taskID string) {
|
||||
t, err := s.store.GetTask(r.Context(), taskID)
|
||||
func (s *Server) GetUnmerged(w http.ResponseWriter, r *http.Request, taskID string) {
|
||||
t, p, ok := s.activeTask(w, r, taskID)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
u, err := git.UnmergedChanges(t.WorktreePath, p.DefaultBranch)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, api.Unmerged{
|
||||
Commits: u.Commits,
|
||||
Files: u.Files,
|
||||
Insertions: u.Insertions,
|
||||
Deletions: u.Deletions,
|
||||
Uncommitted: u.Uncommitted,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) MergeTask(w http.ResponseWriter, r *http.Request, taskID string) {
|
||||
t, p, ok := s.activeTask(w, r, taskID)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
u, err := s.store.GetUser(r.Context(), actor(r))
|
||||
if err != nil {
|
||||
writeStoreError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
if t.Status != api.TaskStatusActive {
|
||||
writeError(w, http.StatusConflict, fmt.Errorf("task %s is %s", t.Id, t.Status))
|
||||
if u.GitName == "" || u.GitEmail == "" {
|
||||
writeError(w, http.StatusBadRequest, errors.New("set your git name and email (PUT /api/me) before merging"))
|
||||
return
|
||||
}
|
||||
|
||||
p, err := s.store.GetProject(r.Context(), t.ProjectId)
|
||||
sessions, err := s.store.ListSessions(r.Context(), t.Id)
|
||||
if err != nil {
|
||||
writeStoreError(w, err)
|
||||
return
|
||||
}
|
||||
for _, sess := range sessions {
|
||||
if sess.State == api.AgentSessionStateRunning || sess.State == api.AgentSessionStateQueued {
|
||||
writeError(w, http.StatusConflict, fmt.Errorf("session %s is %s; wait for the turn to end or interrupt it", sess.Id, sess.State))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
err = s.agents.StopTask(r.Context(), t.Id, actor(r))
|
||||
commit, err := git.SquashMerge(p.RepoPath, t.WorktreePath, p.DefaultBranch, squashMessage(t), []string{
|
||||
"GIT_AUTHOR_NAME=" + u.GitName,
|
||||
"GIT_AUTHOR_EMAIL=" + u.GitEmail,
|
||||
"GIT_COMMITTER_NAME=" + u.GitName,
|
||||
"GIT_COMMITTER_EMAIL=" + u.GitEmail,
|
||||
})
|
||||
conflict := &git.ConflictError{}
|
||||
if errors.As(err, &conflict) {
|
||||
writeError(w, http.StatusConflict, err)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = git.RemoveWorktree(p.RepoPath, t.WorktreePath, t.Branch)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, err)
|
||||
if !s.removeTask(w, r, t, p) {
|
||||
return
|
||||
}
|
||||
|
||||
t, err = s.store.CompleteTask(r.Context(), taskID, actor(r))
|
||||
t, err = s.store.MergeTask(r.Context(), taskID, commit, actor(r))
|
||||
if err != nil {
|
||||
writeStoreError(w, err)
|
||||
return
|
||||
@@ -318,6 +336,63 @@ func (s *Server) CompleteTask(w http.ResponseWriter, r *http.Request, taskID str
|
||||
writeJSON(w, http.StatusOK, t)
|
||||
}
|
||||
|
||||
func (s *Server) DiscardTask(w http.ResponseWriter, r *http.Request, taskID string) {
|
||||
t, p, ok := s.activeTask(w, r, taskID)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if !s.removeTask(w, r, t, p) {
|
||||
return
|
||||
}
|
||||
|
||||
t, err := s.store.DiscardTask(r.Context(), taskID, actor(r))
|
||||
if err != nil {
|
||||
writeStoreError(w, err)
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, t)
|
||||
}
|
||||
|
||||
func squashMessage(t api.Task) string {
|
||||
return t.Title
|
||||
}
|
||||
|
||||
func (s *Server) activeTask(w http.ResponseWriter, r *http.Request, taskID string) (api.Task, api.Project, bool) {
|
||||
t, err := s.store.GetTask(r.Context(), taskID)
|
||||
if err != nil {
|
||||
writeStoreError(w, err)
|
||||
return t, api.Project{}, false
|
||||
}
|
||||
|
||||
if t.Status != api.TaskStatusActive {
|
||||
writeError(w, http.StatusConflict, fmt.Errorf("task %s is %s", t.Id, t.Status))
|
||||
return t, api.Project{}, false
|
||||
}
|
||||
|
||||
p, err := s.store.GetProject(r.Context(), t.ProjectId)
|
||||
if err != nil {
|
||||
writeStoreError(w, err)
|
||||
return t, p, false
|
||||
}
|
||||
return t, p, true
|
||||
}
|
||||
|
||||
func (s *Server) removeTask(w http.ResponseWriter, r *http.Request, t api.Task, p api.Project) bool {
|
||||
err := s.agents.StopTask(r.Context(), t.Id, actor(r))
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, err)
|
||||
return false
|
||||
}
|
||||
|
||||
err = git.RemoveWorktree(p.RepoPath, t.WorktreePath, t.Branch)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, err)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *Server) GetStatus(w http.ResponseWriter, r *http.Request) {
|
||||
c, err := s.store.Counts(r.Context())
|
||||
if err != nil {
|
||||
@@ -448,7 +523,21 @@ func (s *Server) SendMessage(w http.ResponseWriter, r *http.Request, sessionID s
|
||||
return
|
||||
}
|
||||
|
||||
sess, err := s.agents.Send(r.Context(), sessionID, actor(r), req.Text)
|
||||
model := ""
|
||||
if req.Model != nil {
|
||||
model = *req.Model
|
||||
}
|
||||
|
||||
sess, err := s.agents.Send(r.Context(), sessionID, actor(r), req.Text, model)
|
||||
if err != nil {
|
||||
writeAgentError(w, err)
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusAccepted, sess)
|
||||
}
|
||||
|
||||
func (s *Server) CompactSession(w http.ResponseWriter, r *http.Request, sessionID string) {
|
||||
sess, err := s.agents.Compact(r.Context(), sessionID)
|
||||
if err != nil {
|
||||
writeAgentError(w, err)
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user