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>
54 lines
1.1 KiB
Go
54 lines
1.1 KiB
Go
package client
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"task/internal/daemon/api"
|
|
)
|
|
|
|
// SubscribeEvents streams SSE change events until ctx is cancelled.
|
|
func (c *Client) SubscribeEvents(ctx context.Context, emit func(api.Event)) error {
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://daemon/v1/events", nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
req.Header.Set(api.HeaderClient, c.kind)
|
|
if c.kind == api.ClientMCP {
|
|
if c.token != "" {
|
|
req.Header.Set("Authorization", "Bearer "+c.token)
|
|
}
|
|
if c.project != "" {
|
|
req.Header.Set("X-TM-Project", c.project)
|
|
}
|
|
}
|
|
// No overall timeout for long-lived SSE.
|
|
httpClient := *c.http
|
|
httpClient.Timeout = 0
|
|
resp, err := httpClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode >= 400 {
|
|
return fmt.Errorf("events: %s", resp.Status)
|
|
}
|
|
sc := bufio.NewScanner(resp.Body)
|
|
for sc.Scan() {
|
|
line := sc.Text()
|
|
if !strings.HasPrefix(line, "data: ") {
|
|
continue
|
|
}
|
|
var ev api.Event
|
|
if err := json.Unmarshal([]byte(strings.TrimPrefix(line, "data: ")), &ev); err != nil {
|
|
continue
|
|
}
|
|
emit(ev)
|
|
}
|
|
return sc.Err()
|
|
}
|