diff --git a/pkg/skills/clawhub_registry.go b/pkg/skills/clawhub_registry.go index b520f3260..bd4bed8fb 100644 --- a/pkg/skills/clawhub_registry.go +++ b/pkg/skills/clawhub_registry.go @@ -8,8 +8,6 @@ import ( "net/http" "net/url" "os" - "strconv" - "strings" "time" "github.com/sipeed/picoclaw/pkg/utils" @@ -19,7 +17,6 @@ const ( defaultClawHubTimeout = 30 * time.Second defaultMaxZipSize = 50 * 1024 * 1024 // 50 MB defaultMaxResponseSize = 2 * 1024 * 1024 // 2 MB - defaultMaxRetries = 3 ) // ClawHubRegistry implements SkillRegistry for the ClawHub platform. @@ -279,7 +276,12 @@ func (c *ClawHubRegistry) DownloadAndInstall( // --- HTTP helper --- func (c *ClawHubRegistry) doGet(ctx context.Context, urlStr string) ([]byte, error) { - resp, err := c.doGetWithRetry(ctx, urlStr, "application/json") + req, err := c.newGetRequest(ctx, urlStr, "application/json") + if err != nil { + return nil, err + } + + resp, err := utils.DoRequestWithRetry(c.client, req) if err != nil { return nil, err } @@ -298,50 +300,25 @@ func (c *ClawHubRegistry) doGet(ctx context.Context, urlStr string) ([]byte, err return body, nil } -func (c *ClawHubRegistry) doGetWithRetry(ctx context.Context, urlStr, accept string) (*http.Response, error) { - var lastErr error - for attempt := 0; attempt < defaultMaxRetries; attempt++ { - req, err := http.NewRequestWithContext(ctx, "GET", urlStr, nil) - if err != nil { - return nil, err - } - req.Header.Set("Accept", accept) - if c.authToken != "" { - req.Header.Set("Authorization", "Bearer "+c.authToken) - } - - resp, err := c.client.Do(req) - if err != nil { - lastErr = err - } else { - if resp.StatusCode >= 200 && resp.StatusCode < 300 { - return resp, nil - } - - if !isRetryableStatus(resp.StatusCode) || attempt == defaultMaxRetries-1 { - return resp, nil - } - - delay := retryDelay(resp.Header.Get("Retry-After"), attempt) - resp.Body.Close() - if err := sleepWithContext(ctx, delay); err != nil { - return nil, err - } - continue - } - - if attempt == defaultMaxRetries-1 { - return nil, lastErr - } - if err := sleepWithContext(ctx, retryDelay("", attempt)); err != nil { - return nil, err - } +func (c *ClawHubRegistry) newGetRequest(ctx context.Context, urlStr, accept string) (*http.Request, error) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, urlStr, nil) + if err != nil { + return nil, err } - return nil, lastErr + req.Header.Set("Accept", accept) + if c.authToken != "" { + req.Header.Set("Authorization", "Bearer "+c.authToken) + } + return req, nil } func (c *ClawHubRegistry) downloadToTempFileWithRetry(ctx context.Context, urlStr string) (string, error) { - resp, err := c.doGetWithRetry(ctx, urlStr, "application/zip") + req, err := c.newGetRequest(ctx, urlStr, "application/zip") + if err != nil { + return "", err + } + + resp, err := utils.DoRequestWithRetry(c.client, req) if err != nil { return "", err } @@ -383,54 +360,3 @@ func (c *ClawHubRegistry) downloadToTempFileWithRetry(ctx context.Context, urlSt return tmpPath, nil } - -func isRetryableStatus(statusCode int) bool { - return statusCode == http.StatusTooManyRequests || statusCode >= http.StatusInternalServerError -} - -func retryDelay(retryAfter string, attempt int) time.Duration { - if d, ok := parseRetryAfter(retryAfter); ok { - return d - } - return time.Duration(attempt+1) * time.Second -} - -func parseRetryAfter(headerValue string) (time.Duration, bool) { - headerValue = strings.TrimSpace(headerValue) - if headerValue == "" { - return 0, false - } - - if sec, err := strconv.Atoi(headerValue); err == nil { - if sec < 0 { - sec = 0 - } - return time.Duration(sec) * time.Second, true - } - - if resetAt, err := http.ParseTime(headerValue); err == nil { - d := time.Until(resetAt) - if d < 0 { - d = 0 - } - return d, true - } - - return 0, false -} - -func sleepWithContext(ctx context.Context, delay time.Duration) error { - if delay <= 0 { - return nil - } - - timer := time.NewTimer(delay) - defer timer.Stop() - - select { - case <-ctx.Done(): - return ctx.Err() - case <-timer.C: - return nil - } -}