package tools import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" "time" ) // GatewayClient is a lightweight HTTP client for calling Gateway internal APIs // (reminders, schedules, etc.) from ai-core tools. type GatewayClient struct { baseURL string internalToken string httpClient *http.Client } // NewGatewayClient creates a new Gateway API client. func NewGatewayClient(baseURL, internalToken string) *GatewayClient { return &GatewayClient{ baseURL: baseURL, internalToken: internalToken, httpClient: &http.Client{Timeout: 10 * time.Second}, } } // Reminder represents a reminder from the Gateway API. type Reminder struct { ID string `json:"id"` Title string `json:"title"` Description string `json:"description"` RemindAt string `json:"remind_at"` Status string `json:"status"` RepeatType string `json:"repeat_type"` } // CreateReminder calls POST /api/v1/internal/reminders on the Gateway. func (c *GatewayClient) CreateReminder(ctx context.Context, userID, title, description, remindAt, repeatType, sessionID, platform, channelType, channelID, adapterName string) (*Reminder, error) { body := map[string]interface{}{ "user_id": userID, "title": title, "description": description, "remind_at": remindAt, "repeat_type": repeatType, "session_id": sessionID, "platform": platform, "channel_type": channelType, "channel_id": channelID, "adapter_name": adapterName, } reqBody, _ := json.Marshal(body) req, err := http.NewRequestWithContext(ctx, "POST", c.baseURL+"/api/v1/internal/reminders", bytes.NewReader(reqBody)) if err != nil { return nil, err } req.Header.Set("Content-Type", "application/json") req.Header.Set("X-Internal-Token", c.internalToken) resp, err := c.httpClient.Do(req) if err != nil { return nil, fmt.Errorf("gateway request failed: %w", err) } defer resp.Body.Close() if resp.StatusCode >= 400 { bodyBytes, _ := io.ReadAll(resp.Body) return nil, fmt.Errorf("gateway returned %d: %s", resp.StatusCode, string(bodyBytes)) } var reminder Reminder if err := json.NewDecoder(resp.Body).Decode(&reminder); err != nil { return nil, fmt.Errorf("decode response: %w", err) } return &reminder, nil } // ListReminders calls GET /api/v1/internal/reminders on the Gateway. func (c *GatewayClient) ListReminders(ctx context.Context, userID, status string, limit int) ([]Reminder, error) { url := fmt.Sprintf("%s/api/v1/internal/reminders?user_id=%s&status=%s&limit=%d", c.baseURL, userID, status, limit) req, err := http.NewRequestWithContext(ctx, "GET", url, nil) if err != nil { return nil, err } req.Header.Set("X-Internal-Token", c.internalToken) resp, err := c.httpClient.Do(req) if err != nil { return nil, fmt.Errorf("gateway request failed: %w", err) } defer resp.Body.Close() if resp.StatusCode >= 400 { bodyBytes, _ := io.ReadAll(resp.Body) return nil, fmt.Errorf("gateway returned %d: %s", resp.StatusCode, string(bodyBytes)) } var result struct { Reminders []Reminder } if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return nil, fmt.Errorf("decode response: %w", err) } return result.Reminders, nil } // DeleteReminder calls DELETE /api/v1/internal/reminders/:id on the Gateway. func (c *GatewayClient) DeleteReminder(ctx context.Context, reminderID string) error { req, err := http.NewRequestWithContext(ctx, "DELETE", c.baseURL+"/api/v1/internal/reminders/"+reminderID, nil) if err != nil { return err } req.Header.Set("X-Internal-Token", c.internalToken) resp, err := c.httpClient.Do(req) if err != nil { return fmt.Errorf("gateway request failed: %w", err) } defer resp.Body.Close() if resp.StatusCode >= 400 { bodyBytes, _ := io.ReadAll(resp.Body) return fmt.Errorf("gateway returned %d: %s", resp.StatusCode, string(bodyBytes)) } return nil }