Skip to content

Commit b426817

Browse files
committed
Add distribution URL validation
- Validate binary archive URLs exist via HEAD/GET requests - Validate npm packages exist on registry.npmjs.org - Validate PyPI packages exist on pypi.org - Fix agent URLs (opencode, mistral-vibe, stakpak) - Update stakpak to use stakpak/agent repo with latest URLs - Add SKIP_URL_VALIDATION env var to bypass checks - Document URL validation in README, CONTRIBUTING, CLAUDE.md
1 parent 1462842 commit b426817

9 files changed

Lines changed: 116 additions & 50 deletions

File tree

.github/workflows/build_registry.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import os
66
import re
77
import sys
8+
import urllib.request
9+
import urllib.error
810
from pathlib import Path
911

1012
try:
@@ -32,6 +34,78 @@
3234
# Icon requirements
3335
PREFERRED_ICON_SIZE = 16
3436

37+
# URL validation
38+
SKIP_URL_VALIDATION = os.environ.get("SKIP_URL_VALIDATION", "").lower() in ("1", "true", "yes")
39+
40+
41+
def url_exists(url: str, method: str = "HEAD") -> bool:
42+
"""Check if a URL exists using HEAD or GET request."""
43+
try:
44+
req = urllib.request.Request(url, method=method)
45+
req.add_header("User-Agent", "ACP-Registry-Validator/1.0")
46+
with urllib.request.urlopen(req, timeout=15) as response:
47+
return response.status in (200, 301, 302)
48+
except urllib.error.HTTPError as e:
49+
# Some servers don't support HEAD, try GET
50+
if method == "HEAD" and e.code in (403, 405):
51+
return url_exists(url, method="GET")
52+
return False
53+
except (urllib.error.URLError, TimeoutError, OSError):
54+
return False
55+
56+
57+
def extract_npm_package_name(package_spec: str) -> str:
58+
"""Extract npm package name from spec like @scope/name@version."""
59+
# Handle scoped packages: @scope/name@version -> @scope/name
60+
if package_spec.startswith("@"):
61+
# Find the second @ (version separator) if it exists
62+
at_positions = [i for i, c in enumerate(package_spec) if c == "@"]
63+
if len(at_positions) > 1:
64+
return package_spec[:at_positions[1]]
65+
return package_spec
66+
else:
67+
# Unscoped: name@version -> name
68+
return package_spec.split("@")[0]
69+
70+
71+
def validate_distribution_urls(distribution: dict) -> list[str]:
72+
"""Validate that distribution URLs exist."""
73+
if SKIP_URL_VALIDATION:
74+
return []
75+
76+
errors = []
77+
78+
# Check binary archive URLs
79+
if "binary" in distribution:
80+
for platform, target in distribution["binary"].items():
81+
if "archive" in target:
82+
url = target["archive"]
83+
if not url_exists(url):
84+
errors.append(f"Binary archive URL not accessible for {platform}: {url}")
85+
86+
# Check npm package URLs (registry.npmjs.org)
87+
seen_npm = set()
88+
for dist_type in ("npx", "bunx"):
89+
if dist_type in distribution:
90+
package = distribution[dist_type].get("package", "")
91+
pkg_name = extract_npm_package_name(package)
92+
if pkg_name and pkg_name not in seen_npm:
93+
seen_npm.add(pkg_name)
94+
npm_url = f"https://registry.npmjs.org/{pkg_name}"
95+
if not url_exists(npm_url):
96+
errors.append(f"npm package not found: {pkg_name}")
97+
98+
# Check PyPI package URLs
99+
if "uvx" in distribution:
100+
package = distribution["uvx"].get("package", "")
101+
# Extract package name without version specifier
102+
pkg_name = re.split(r'[<>=!@]', package)[0]
103+
pypi_url = f"https://pypi.org/pypi/{pkg_name}/json"
104+
if not url_exists(pypi_url):
105+
errors.append(f"PyPI package not found: {pkg_name}")
106+
107+
return errors
108+
35109

36110
def validate_icon(icon_path: Path) -> list[str]:
37111
"""Validate icon.svg and return list of warnings/errors."""
@@ -227,6 +301,16 @@ def build_registry():
227301
continue
228302
seen_ids[agent_id] = agent_dir.name
229303

304+
# Validate distribution URLs
305+
if "distribution" in agent:
306+
url_errors = validate_distribution_urls(agent["distribution"])
307+
if url_errors:
308+
print(f"Error: {agent_dir.name} distribution URL validation failed:")
309+
for error in url_errors:
310+
print(f" - {error}")
311+
has_errors = True
312+
continue
313+
230314
# Validate and set icon URL if icon exists
231315
icon_path = agent_dir / "icon.svg"
232316
if icon_path.exists():

CLAUDE.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ This is a registry of ACP (Agent Client Protocol) agents. The structure is:
3939
- `version`: semantic versioning (e.g., `1.0.0`)
4040
- `distribution`: at least one of `binary`, `npx`, `bunx`, `uvx`
4141
- `icon.svg`: must be 16x16 (warnings for non-compliance)
42+
- **URL validation**: All distribution URLs must be accessible (binary archives, npm/PyPI packages)
43+
44+
Set `SKIP_URL_VALIDATION=1` to bypass URL checks during local development.
4245

4346
## Distribution Types
4447

CONTRIBUTING.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,14 @@ To update your agent's version or distribution URLs:
122122

123123
## Validation
124124

125-
All submissions are validated against the [JSON Schema](agent.schema.json). Run locally:
125+
All submissions are validated against the [JSON Schema](agent.schema.json). Additionally, **all distribution URLs must be accessible** - the CI validates that:
126+
127+
- Binary archive URLs return HTTP 200
128+
- npm packages exist on registry.npmjs.org
129+
- PyPI packages exist on pypi.org
130+
131+
Run validation locally:
126132

127133
```bash
128-
python .github/workflows/build_registry.py
134+
uv run --with jsonschema .github/workflows/build_registry.py
129135
```

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ A registry of agents implementing the [Agent Client Protocol (ACP)](https://gith
77
Fetch the registry index:
88

99
```
10-
https://github.com/<org>/<repo>/releases/latest/download/registry.json
10+
https://github.com/agentclientprotocol/registry/releases/latest/download/registry.json
1111
```
1212

1313
Fetch agent icons:
1414

1515
```
16-
https://github.com/<org>/<repo>/releases/latest/download/<agent-id>.svg
16+
https://github.com/agentclientprotocol/registry/releases/latest/download/<agent-id>.svg
1717
```
1818

1919
## Registry Format

auggie/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@ bunx @augmentcode/auggie --acp
1919

2020
## License
2121

22-
MIT
22+
Proprietary
2323

2424
Brand, product, and service names and marks are trademarks of Augment Code and may not be used without explicit permission.

mistral-vibe/agent.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@
1818
"cmd": "./vibe-acp"
1919
},
2020
"linux-aarch64": {
21-
"archive": "https://github.com/mistralai/mistral-vibe/releases/download/v1.1.1/vibe-acp-linux-aarch64-1.1.3.zip",
21+
"archive": "https://github.com/mistralai/mistral-vibe/releases/download/v1.1.3/vibe-acp-linux-aarch64-1.1.3.zip",
2222
"cmd": "./vibe-acp"
2323
},
2424
"linux-x86_64": {
2525
"archive": "https://github.com/mistralai/mistral-vibe/releases/download/v1.1.3/vibe-acp-linux-x86_64-1.1.3.zip",
2626
"cmd": "./vibe-acp"
2727
},
2828
"windows-aarch64": {
29-
"archive": "https://github.com/mistralai/mistral-vibe/releases/download/v1.1.1/vibe-acp-windows-aarch64-1.1.3.zip",
29+
"archive": "https://github.com/mistralai/mistral-vibe/releases/download/v1.1.3/vibe-acp-windows-aarch64-1.1.3.zip",
3030
"cmd": "./vibe-acp.exe"
3131
},
3232
"windows-x86_64": {

opencode/agent.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,27 @@
1010
"distribution": {
1111
"binary": {
1212
"darwin-aarch64": {
13-
"archive": "https://github.com/sst/opencode/releases/download/v1.0.164/opencode_darwin_arm64.zip",
13+
"archive": "https://github.com/sst/opencode/releases/download/v1.0.164/opencode-darwin-arm64.zip",
1414
"cmd": "./opencode",
1515
"args": ["acp"]
1616
},
1717
"darwin-x86_64": {
18-
"archive": "https://github.com/sst/opencode/releases/download/v1.0.164/opencode_darwin_amd64.zip",
18+
"archive": "https://github.com/sst/opencode/releases/download/v1.0.164/opencode-darwin-x64.zip",
1919
"cmd": "./opencode",
2020
"args": ["acp"]
2121
},
2222
"linux-aarch64": {
23-
"archive": "https://github.com/sst/opencode/releases/download/v1.0.164/opencode_linux_arm64.tar.gz",
23+
"archive": "https://github.com/sst/opencode/releases/download/v1.0.164/opencode-linux-arm64.tar.gz",
2424
"cmd": "./opencode",
2525
"args": ["acp"]
2626
},
2727
"linux-x86_64": {
28-
"archive": "https://github.com/sst/opencode/releases/download/v1.0.164/opencode_linux_amd64.tar.gz",
28+
"archive": "https://github.com/sst/opencode/releases/download/v1.0.164/opencode-linux-x64.tar.gz",
2929
"cmd": "./opencode",
3030
"args": ["acp"]
3131
},
3232
"windows-x86_64": {
33-
"archive": "https://github.com/sst/opencode/releases/download/v1.0.164/opencode_windows_amd64.zip",
33+
"archive": "https://github.com/sst/opencode/releases/download/v1.0.164/opencode-windows-x64.zip",
3434
"cmd": "./opencode.exe",
3535
"args": ["acp"]
3636
}

stakpak/README.md

Lines changed: 0 additions & 32 deletions
This file was deleted.

stakpak/agent.json

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,36 @@
11
{
22
"id": "stakpak",
33
"name": "Stakpak",
4-
"version": "0.1.6",
4+
"version": "0.3.3",
55
"description": "Open-source DevOps agent in Rust with enterprise-grade security.",
6-
"repository": "https://github.com/stakpak/zed-stakpak-agent-server",
6+
"repository": "https://github.com/stakpak/agent",
77
"authors": ["Stakpak Team <contact@stakpak.dev>"],
88
"license": "Apache-2.0",
99
"icon": "./icon.svg",
1010
"distribution": {
1111
"binary": {
1212
"darwin-aarch64": {
13-
"archive": "https://github.com/stakpak/stakpak/releases/download/v0.1.6/stakpak-aarch64-apple-darwin.zip",
13+
"archive": "https://github.com/stakpak/agent/releases/latest/download/stakpak-darwin-aarch64.tar.gz",
1414
"cmd": "./stakpak",
1515
"args": ["acp"]
1616
},
1717
"darwin-x86_64": {
18-
"archive": "https://github.com/stakpak/stakpak/releases/download/v0.1.6/stakpak-x86_64-apple-darwin.zip",
18+
"archive": "https://github.com/stakpak/agent/releases/latest/download/stakpak-darwin-x86_64.tar.gz",
19+
"cmd": "./stakpak",
20+
"args": ["acp"]
21+
},
22+
"linux-aarch64": {
23+
"archive": "https://github.com/stakpak/agent/releases/latest/download/stakpak-linux-aarch64.tar.gz",
1924
"cmd": "./stakpak",
2025
"args": ["acp"]
2126
},
2227
"linux-x86_64": {
23-
"archive": "https://github.com/stakpak/stakpak/releases/download/v0.1.6/stakpak-x86_64-unknown-linux-gnu.zip",
28+
"archive": "https://github.com/stakpak/agent/releases/latest/download/stakpak-linux-x86_64.tar.gz",
2429
"cmd": "./stakpak",
2530
"args": ["acp"]
2631
},
2732
"windows-x86_64": {
28-
"archive": "https://github.com/stakpak/stakpak/releases/download/v0.1.6/stakpak-x86_64-pc-windows-msvc.zip",
33+
"archive": "https://github.com/stakpak/agent/releases/latest/download/stakpak-windows-x86_64.zip",
2934
"cmd": "./stakpak.exe",
3035
"args": ["acp"]
3136
}

0 commit comments

Comments
 (0)