Skip to content

Commit e71ba04

Browse files
committed
Fix download-binary script to pull latest version of stagehand-server
1 parent 16583e0 commit e71ba04

File tree

1 file changed

+77
-3
lines changed

1 file changed

+77
-3
lines changed

scripts/download-binary.py

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,14 @@
1515
from __future__ import annotations
1616

1717
import sys
18+
import os
19+
import json
1820
import argparse
1921
import platform
2022
import urllib.error
2123
import urllib.request
2224
from pathlib import Path
25+
from typing import Any
2326

2427

2528
def get_platform_info() -> tuple[str, str]:
@@ -49,6 +52,71 @@ def get_local_filename(plat: str, arch: str) -> str:
4952
name = f"stagehand-{plat}-{arch}"
5053
return name + (".exe" if plat == "win32" else "")
5154

55+
def _parse_server_tag(tag: str) -> tuple[int, int, int] | None:
56+
# Expected: stagehand-server/vX.Y.Z
57+
if not tag.startswith("stagehand-server/v"):
58+
return None
59+
60+
ver = tag.removeprefix("stagehand-server/v")
61+
# Drop any pre-release/build metadata (we only expect stable tags here).
62+
ver = ver.split("-", 1)[0].split("+", 1)[0]
63+
parts = ver.split(".")
64+
if len(parts) != 3:
65+
return None
66+
try:
67+
return int(parts[0]), int(parts[1]), int(parts[2])
68+
except ValueError:
69+
return None
70+
71+
72+
def _http_get_json(url: str) -> Any:
73+
headers = {
74+
"Accept": "application/vnd.github+json",
75+
"User-Agent": "stagehand-python/download-binary",
76+
}
77+
# Optional, but helps avoid rate limits in CI.
78+
token = (os.environ.get("GITHUB_TOKEN") or os.environ.get("GH_TOKEN") or "").strip()
79+
if token:
80+
headers["Authorization"] = f"Bearer {token}"
81+
82+
req = urllib.request.Request(url, headers=headers)
83+
with urllib.request.urlopen(req, timeout=30) as resp:
84+
data = resp.read()
85+
return json.loads(data.decode("utf-8"))
86+
87+
88+
def resolve_latest_server_tag() -> str:
89+
"""Resolve the latest stagehand-server/v* tag from GitHub releases."""
90+
repo = "browserbase/stagehand"
91+
releases_url = f"https://api.github.com/repos/{repo}/releases?per_page=100"
92+
try:
93+
releases = _http_get_json(releases_url)
94+
except urllib.error.HTTPError as e: # type: ignore[misc]
95+
raise RuntimeError(f"Failed to query GitHub releases (HTTP {e.code}): {releases_url}") from e # type: ignore[union-attr]
96+
except Exception as e:
97+
raise RuntimeError(f"Failed to query GitHub releases: {releases_url}") from e
98+
99+
if not isinstance(releases, list):
100+
raise RuntimeError(f"Unexpected GitHub API response for releases: {type(releases).__name__}")
101+
102+
best: tuple[tuple[int, int, int], str] | None = None
103+
for r in releases:
104+
if not isinstance(r, dict):
105+
continue
106+
tag = r.get("tag_name")
107+
if not isinstance(tag, str):
108+
continue
109+
parsed = _parse_server_tag(tag)
110+
if parsed is None:
111+
continue
112+
if best is None or parsed > best[0]:
113+
best = (parsed, tag)
114+
115+
if best is None:
116+
raise RuntimeError("No stagehand-server/v* GitHub Releases found for browserbase/stagehand")
117+
118+
return best[1]
119+
52120

53121
def download_binary(version: str) -> None:
54122
"""Download the binary for the current platform."""
@@ -123,12 +191,18 @@ def main() -> None:
123191
)
124192
parser.add_argument(
125193
"--version",
126-
default="v3.2.0",
127-
help="Version to download (default: v3.2.0)",
194+
default=None,
195+
help="Stagehand server release tag/version to download (e.g. v3.2.0 or stagehand-server/v3.2.0). Defaults to latest stagehand-server/* GitHub Release.",
128196
)
129197

130198
args = parser.parse_args()
131-
download_binary(args.version)
199+
version = str(args.version).strip() if args.version is not None else ""
200+
if not version:
201+
latest_tag = resolve_latest_server_tag()
202+
download_binary(latest_tag)
203+
return
204+
205+
download_binary(version)
132206

133207

134208
if __name__ == "__main__":

0 commit comments

Comments
 (0)