package agentbridge import ( "bytes" "context" "encoding/json" "errors" "fmt" "io" "net/http" "strings" "git.cin.su/m/Assistant/contracts/work" ) var ErrUnavailable = errors.New("agent execution bridge unavailable") type HTTPExecutor struct { URL string Client *http.Client } func NewHTTPExecutor(url string, client *http.Client) (*HTTPExecutor, error) { url = strings.TrimSpace(url) if url == "" || !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") { return nil, ErrUnavailable } if client == nil { client = http.DefaultClient } return &HTTPExecutor{URL: strings.TrimRight(url, "/"), Client: client}, nil } func (e *HTTPExecutor) Execute(ctx context.Context, request work.AgentExecutionRequest) (work.AgentExecutionResult, error) { body, err := json.Marshal(request) if err != nil { return work.AgentExecutionResult{}, err } httpRequest, err := http.NewRequestWithContext(ctx, http.MethodPost, e.URL+"/v1/execute", bytes.NewReader(body)) if err != nil { return work.AgentExecutionResult{}, err } httpRequest.Header.Set("Content-Type", "application/json") response, err := e.Client.Do(httpRequest) if err != nil { return work.AgentExecutionResult{}, fmt.Errorf("%w: %v", ErrUnavailable, err) } defer response.Body.Close() if response.StatusCode < 200 || response.StatusCode >= 300 { return work.AgentExecutionResult{}, fmt.Errorf("%w: status=%s", ErrUnavailable, response.Status) } decoder := json.NewDecoder(io.LimitReader(response.Body, 1<<20)) var result work.AgentExecutionResult if err := decoder.Decode(&result); err != nil { return work.AgentExecutionResult{}, err } if result.State == "" || result.Summary == "" || result.ObservedAtUnixMillis < 1 { return work.AgentExecutionResult{}, errors.New("agent execution bridge returned invalid result") } return result, nil }