Skip to content

Commit b35e549

Browse files
kevinelliottclaude
andcommitted
Fix GitHub API rate limiting by using proper package managers
GitHub API 403 errors were occurring due to rate limiting (60 req/hour for unauthenticated requests). Fixed by using the correct package manager for each agent instead of GitHub releases. Changes: - Added PyPI package manager support in version.go - New getPyPILatestVersion() function - Fetches from https://pypi.org/pypi/{package}/json - No rate limits, more reliable - Updated Kimi CLI to use PyPI - Changed package_manager from "github" to "pypi" - Package name: "kimi-cli" - Aligns with actual installation method (uv/pip) - Updated Qwen to use npm registry - Changed package_manager from "github" to "npm" - Package name: "@qwen-code/qwen-code" - Aligns with actual installation method (npm) - Updated .golangci.yml - Added dupl exclusion for internal/registry/ - Allows intentional duplication across package manager functions Benefits: - No more GitHub API 403 errors - More reliable version detection - Uses correct source for each package - Faster response times (no rate limiting) Testing: - Verified Kimi version detection: 0.40 from PyPI ✅ - Verified Qwen version detection: 0.0.14 from npm ✅ - All tests pass: go test ./... ✅ - Linting clean: golangci-lint run ✅ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent cbb73c3 commit b35e549

File tree

3 files changed

+52
-4
lines changed

3 files changed

+52
-4
lines changed

.golangci.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ issues:
7979
- gocognit
8080
- dupl # Intentional code duplication - each adapter follows same pattern
8181

82+
# Allow version check duplication in registry (different package managers)
83+
- path: 'internal/registry/'
84+
linters:
85+
- dupl # Intentional code duplication - each package manager follows same HTTP pattern
86+
8287
# Allow complex orchestrator functions (retry logic, rate limiting)
8388
- path: 'pkg/orchestrator/'
8489
linters:

internal/registry/agents.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -197,8 +197,8 @@
197197
"command": "kimi",
198198
"description": "Kimi CLI by Moonshot AI",
199199
"docs": "https://github.com/MoonshotAI/kimi-cli",
200-
"package_manager": "github",
201-
"package_name": "MoonshotAI/kimi-cli",
200+
"package_manager": "pypi",
201+
"package_name": "kimi-cli",
202202
"install": {
203203
"darwin": "uv tool install --python 3.13 kimi-cli",
204204
"linux": "uv tool install --python 3.13 kimi-cli",
@@ -245,8 +245,8 @@
245245
"command": "qwen",
246246
"description": "Alibaba's Qwen Code CLI",
247247
"docs": "https://github.com/QwenLM/qwen-code",
248-
"package_manager": "github",
249-
"package_name": "QwenLM/qwen-code",
248+
"package_manager": "npm",
249+
"package_name": "@qwen-code/qwen-code",
250250
"install": {
251251
"darwin": "npm install -g @qwen-code/qwen-code",
252252
"linux": "npm install -g @qwen-code/qwen-code",

internal/registry/version.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ func (a *AgentDefinition) GetLatestVersion() (string, error) {
3030
return getScriptVersion(a.PackageName)
3131
case "manifest":
3232
return getManifestVersion(a.PackageName)
33+
case "pypi":
34+
return getPyPILatestVersion(a.PackageName)
3335
default:
3436
return "", fmt.Errorf("no package manager configured for %s", a.Name)
3537
}
@@ -300,6 +302,47 @@ func getManifestVersion(manifestURL string) (string, error) {
300302
return data.Latest, nil
301303
}
302304

305+
// getPyPILatestVersion fetches the latest version of a PyPI package
306+
func getPyPILatestVersion(packageName string) (string, error) {
307+
// Use PyPI JSON API
308+
url := fmt.Sprintf("https://pypi.org/pypi/%s/json", packageName)
309+
310+
client := &http.Client{
311+
Timeout: 10 * time.Second,
312+
}
313+
314+
resp, err := client.Get(url)
315+
if err != nil {
316+
return "", fmt.Errorf("failed to fetch PyPI package info: %w", err)
317+
}
318+
defer resp.Body.Close()
319+
320+
if resp.StatusCode != http.StatusOK {
321+
return "", fmt.Errorf("PyPI API returned status %d", resp.StatusCode)
322+
}
323+
324+
body, err := io.ReadAll(resp.Body)
325+
if err != nil {
326+
return "", fmt.Errorf("failed to read PyPI response: %w", err)
327+
}
328+
329+
var data struct {
330+
Info struct {
331+
Version string `json:"version"`
332+
} `json:"info"`
333+
}
334+
335+
if err := json.Unmarshal(body, &data); err != nil {
336+
return "", fmt.Errorf("failed to parse PyPI response: %w", err)
337+
}
338+
339+
if data.Info.Version == "" {
340+
return "", fmt.Errorf("no version found in PyPI response")
341+
}
342+
343+
return data.Info.Version, nil
344+
}
345+
303346
// GetInstalledVersion gets the currently installed version of an agent
304347
func GetInstalledVersion(command string) string {
305348
// Try --version first

0 commit comments

Comments
 (0)