Add local JSON daemon and route TUI/CLI through it.
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>
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user