A per-DataDir background process owns ticket JSON, history, and sync so multiple TUI instances stay consistent; MCP token APIs are ready for phase 2. Co-authored-by: Cursor <cursoragent@cursor.com>
101 lines
2.4 KiB
Go
101 lines
2.4 KiB
Go
package client
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
|
|
"task/internal/command"
|
|
"task/internal/ticket"
|
|
)
|
|
|
|
// Executor implements command.Executor via daemon mutation endpoints.
|
|
type Executor struct{ c *Client }
|
|
|
|
func (c *Client) Executor() *Executor { return &Executor{c: c} }
|
|
|
|
func (e *Executor) Do(cmd command.Command) error {
|
|
switch c := cmd.(type) {
|
|
case *command.CreateTicket:
|
|
var out ticket.Ticket
|
|
if err := e.c.do(http.MethodPost, "/v1/tickets", c.Ticket, &out); err != nil {
|
|
return err
|
|
}
|
|
c.Ticket = out
|
|
return nil
|
|
case *command.UpdateTicket:
|
|
return e.c.do(http.MethodPatch, "/v1/tickets/"+url.PathEscape(c.After.ID), c.After, nil)
|
|
case *command.DeleteTicket:
|
|
return e.c.do(http.MethodDelete, "/v1/tickets/"+url.PathEscape(c.Snapshot.ID), nil, nil)
|
|
case *command.MoveTicket:
|
|
var out ticket.Ticket
|
|
if err := e.c.do(http.MethodPost, "/v1/tickets/"+url.PathEscape(c.Before.ID)+"/move", map[string]string{
|
|
"toProjectId": c.After.ProjectID,
|
|
}, &out); err != nil {
|
|
return err
|
|
}
|
|
c.After = out
|
|
return nil
|
|
case *command.AddTimeEntry:
|
|
var out ticket.TimeEntry
|
|
if err := e.c.do(http.MethodPost, "/v1/time", c.Entry, &out); err != nil {
|
|
return err
|
|
}
|
|
c.Entry = out
|
|
return nil
|
|
case *command.RemoveTimeEntry:
|
|
return e.c.do(http.MethodDelete, "/v1/time/"+url.PathEscape(c.Snapshot.ID), nil, nil)
|
|
case *command.UpdateTimeEntry:
|
|
return e.c.do(http.MethodPatch, "/v1/time/"+url.PathEscape(c.After.ID), c.After, nil)
|
|
default:
|
|
return fmt.Errorf("unsupported command type %T", cmd)
|
|
}
|
|
}
|
|
|
|
func (e *Executor) Undo() error {
|
|
return e.c.do(http.MethodPost, "/v1/undo", map[string]any{}, nil)
|
|
}
|
|
|
|
func (e *Executor) Redo() error {
|
|
return e.c.do(http.MethodPost, "/v1/redo", map[string]any{}, nil)
|
|
}
|
|
|
|
func (e *Executor) CanUndo() bool {
|
|
st, err := e.status()
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return st.CanUndo
|
|
}
|
|
|
|
func (e *Executor) CanRedo() bool {
|
|
st, err := e.status()
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return st.CanRedo
|
|
}
|
|
|
|
func (e *Executor) LastUndoName() string {
|
|
st, _ := e.status()
|
|
return st.LastUndoName
|
|
}
|
|
|
|
func (e *Executor) LastRedoName() string {
|
|
st, _ := e.status()
|
|
return st.LastRedoName
|
|
}
|
|
|
|
type historyStatus struct {
|
|
CanUndo bool `json:"canUndo"`
|
|
CanRedo bool `json:"canRedo"`
|
|
LastUndoName string `json:"lastUndoName"`
|
|
LastRedoName string `json:"lastRedoName"`
|
|
}
|
|
|
|
func (e *Executor) status() (historyStatus, error) {
|
|
var st historyStatus
|
|
err := e.c.do(http.MethodGet, "/v1/history", nil, &st)
|
|
return st, err
|
|
}
|