package client import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" "time" ) type Client struct { baseURL string httpClient *http.Client } func New(baseURL string) *Client { return &Client{ baseURL: baseURL, httpClient: &http.Client{Timeout: 30 * time.Second}, } } func (c *Client) do(ctx context.Context, method, path string, body, result any) error { var reqBody io.Reader if body != nil { data, err := json.Marshal(body) if err != nil { return fmt.Errorf("marshal request: %w", err) } reqBody = bytes.NewReader(data) } req, err := http.NewRequestWithContext(ctx, method, c.baseURL+path, reqBody) if err != nil { return fmt.Errorf("create request: %w", err) } req.Header.Set("Content-Type", "application/json") resp, err := c.httpClient.Do(req) if err != nil { return fmt.Errorf("do request: %w", err) } defer resp.Body.Close() if resp.StatusCode >= 400 { var errResp struct { Error string `json:"error"` } json.NewDecoder(resp.Body).Decode(&errResp) if errResp.Error != "" { return fmt.Errorf("%s: %s", http.StatusText(resp.StatusCode), errResp.Error) } return fmt.Errorf("%s", http.StatusText(resp.StatusCode)) } if result != nil { return json.NewDecoder(resp.Body).Decode(result) } return nil } type Capability struct { ID string `json:"id"` Name string `json:"name"` Description string `json:"description,omitempty"` TargetTypes []string `json:"target_types"` TargetEntityID string `json:"target_entity_id,omitempty"` Provider string `json:"provider"` Operation string `json:"operation"` Risk string `json:"risk,omitempty"` ReadOnly bool `json:"read_only"` ExpectedSideEffects string `json:"expected_side_effects,omitempty"` } type Execution struct { ID string `json:"id"` CapabilityID string `json:"capability_id"` TargetEntityID string `json:"target_entity_id"` Status string `json:"status"` Result map[string]any `json:"result,omitempty"` Error string `json:"error,omitempty"` RequestedBy map[string]string `json:"requested_by,omitempty"` CorrelationID string `json:"correlation_id,omitempty"` IdempotencyKey string `json:"idempotency_key,omitempty"` } type ExecuteRequest struct { CapabilityID string `json:"capability_id"` TargetEntityID string `json:"target_entity_id"` Arguments map[string]any `json:"arguments,omitempty"` RequestedBy map[string]string `json:"requested_by,omitempty"` Origin map[string]string `json:"origin,omitempty"` IdempotencyKey string `json:"idempotency_key,omitempty"` CorrelationID string `json:"correlation_id,omitempty"` } type CreateCapabilityRequest struct { Name string `json:"name"` Description string `json:"description,omitempty"` TargetTypes []string `json:"target_types"` TargetEntityID string `json:"target_entity_id,omitempty"` Provider string `json:"provider"` Operation string `json:"operation"` Risk string `json:"risk,omitempty"` ReadOnly bool `json:"read_only"` ExpectedSideEffects string `json:"expected_side_effects,omitempty"` } func (c *Client) Capabilities(ctx context.Context, entityID string) ([]Capability, error) { var result []Capability path := "/api/v1/capabilities" if entityID != "" { path += "?entity_id=" + entityID } if err := c.do(ctx, http.MethodGet, path, nil, &result); err != nil { return nil, err } return result, nil } func (c *Client) GetCapability(ctx context.Context, id string) (*Capability, error) { var result Capability if err := c.do(ctx, http.MethodGet, "/api/v1/capabilities/"+id, nil, &result); err != nil { return nil, err } return &result, nil } func (c *Client) CreateCapability(ctx context.Context, req CreateCapabilityRequest) (*Capability, error) { var result Capability if err := c.do(ctx, http.MethodPost, "/api/v1/capabilities", req, &result); err != nil { return nil, err } return &result, nil } func (c *Client) Execute(ctx context.Context, req ExecuteRequest) (*Execution, error) { var result Execution if err := c.do(ctx, http.MethodPost, "/api/v1/execute", req, &result); err != nil { return nil, err } return &result, nil } func (c *Client) GetExecution(ctx context.Context, id string) (*Execution, error) { var result Execution if err := c.do(ctx, http.MethodGet, "/api/v1/executions/"+id, nil, &result); err != nil { return nil, err } return &result, nil } func (c *Client) Health(ctx context.Context) error { return c.do(ctx, http.MethodGet, "/health", nil, nil) }