This commit is contained in:
Javier Soto
2025-11-03 11:33:17 -08:00
parent 84a3cddc76
commit 11028280dc
16 changed files with 722 additions and 39 deletions

View File

@@ -16,13 +16,14 @@ RESPONSES_API_PROXY_NPM_ROOT = REPO_ROOT / "codex-rs" / "responses-api-proxy" /
CODEX_SDK_ROOT = REPO_ROOT / "sdk" / "typescript"
PACKAGE_NATIVE_COMPONENTS: dict[str, list[str]] = {
"codex": ["codex", "rg"],
"codex": ["codex", "codex-notifier", "rg"],
"codex-responses-api-proxy": ["codex-responses-api-proxy"],
"codex-sdk": ["codex"],
}
COMPONENT_DEST_DIR: dict[str, str] = {
"codex": "codex",
"codex-responses-api-proxy": "codex-responses-api-proxy",
"codex-notifier": "codex-notifier",
"rg": "path",
}

View File

@@ -34,8 +34,11 @@ BINARY_TARGETS = (
@dataclass(frozen=True)
class BinaryComponent:
artifact_prefix: str # matches the artifact filename prefix (e.g. codex-<target>.zst)
dest_dir: str # directory under vendor/<target>/ where the binary is installed
binary_basename: str # executable name inside dest_dir (before optional .exe)
dest_dir: str # directory under vendor/<target>/ where the binary/artifact is installed
binary_basename: str # executable or directory name inside dest_dir (before optional .exe)
archive_format: str = "zst" # one of "zst", "zip", "tar.gz"
archive_member: str | None = None # optional archive member to extract
supported_targets: tuple[str, ...] | None = None # restrict installation to these targets
BINARY_COMPONENTS = {
@@ -49,6 +52,15 @@ BINARY_COMPONENTS = {
dest_dir="codex-responses-api-proxy",
binary_basename="codex-responses-api-proxy",
),
"codex-notifier": BinaryComponent(
artifact_prefix="codex-notifier",
dest_dir="codex-notifier",
binary_basename="codex-notifier",
supported_targets=(
"x86_64-apple-darwin",
"aarch64-apple-darwin",
),
),
}
RG_TARGET_PLATFORM_PAIRS: list[tuple[str, str]] = [
@@ -101,7 +113,7 @@ def main() -> int:
vendor_dir = codex_cli_root / VENDOR_DIR_NAME
vendor_dir.mkdir(parents=True, exist_ok=True)
components = args.components or ["codex", "rg"]
components = args.components or ["codex", "codex-notifier", "rg"]
workflow_url = (args.workflow_url or DEFAULT_WORKFLOW_URL).strip()
if not workflow_url:
@@ -218,11 +230,19 @@ def install_binary_components(
return
for component in selected_components:
component_targets = [
target
for target in targets
if not component.supported_targets or target in component.supported_targets
]
if not component_targets:
continue
print(
f"Installing {component.binary_basename} binaries for targets: "
+ ", ".join(targets)
+ ", ".join(component_targets)
)
max_workers = min(len(targets), max(1, (os.cpu_count() or 1)))
max_workers = min(len(component_targets), max(1, (os.cpu_count() or 1)))
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = {
executor.submit(
@@ -232,7 +252,7 @@ def install_binary_components(
target,
component,
): target
for target in targets
for target in component_targets
}
for future in as_completed(futures):
installed_path = future.result()
@@ -246,7 +266,7 @@ def _install_single_binary(
component: BinaryComponent,
) -> Path:
artifact_subdir = artifacts_dir / target
archive_name = _archive_name_for_target(component.artifact_prefix, target)
archive_name = _archive_name_for_target(component, target)
archive_path = artifact_subdir / archive_name
if not archive_path.exists():
raise FileNotFoundError(f"Expected artifact not found: {archive_path}")
@@ -255,20 +275,29 @@ def _install_single_binary(
dest_dir.mkdir(parents=True, exist_ok=True)
binary_name = (
f"{component.binary_basename}.exe" if "windows" in target else component.binary_basename
f"{component.binary_basename}.exe"
if "windows" in target and component.archive_format == "zst"
else component.binary_basename
)
dest = dest_dir / binary_name
dest.unlink(missing_ok=True)
extract_archive(archive_path, "zst", None, dest)
if "windows" not in target:
extract_archive(
archive_path, component.archive_format, component.archive_member, dest
)
if "windows" not in target and dest.is_file():
dest.chmod(0o755)
return dest
def _archive_name_for_target(artifact_prefix: str, target: str) -> str:
def _archive_name_for_target(component: BinaryComponent, target: str) -> str:
fmt = component.archive_format
if fmt == "zip":
return f"{component.artifact_prefix}-{target}.zip"
if fmt == "tar.gz":
return f"{component.artifact_prefix}-{target}.tar.gz"
if "windows" in target:
return f"{artifact_prefix}-{target}.exe.zst"
return f"{artifact_prefix}-{target}.zst"
return f"{component.artifact_prefix}-{target}.exe.zst"
return f"{component.artifact_prefix}-{target}.zst"
def _fetch_single_rg(
@@ -346,16 +375,25 @@ def extract_archive(
return
if archive_format == "zip":
if not archive_member:
raise RuntimeError("Missing 'path' for zip archive in DotSlash manifest.")
with zipfile.ZipFile(archive_path) as archive:
try:
with archive.open(archive_member) as src, open(dest, "wb") as out:
shutil.copyfileobj(src, out)
except KeyError as exc:
raise RuntimeError(
f"Entry '{archive_member}' not found in archive {archive_path}."
) from exc
if archive_member:
try:
with archive.open(archive_member) as src, open(dest, "wb") as out:
shutil.copyfileobj(src, out)
except KeyError as exc:
raise RuntimeError(
f"Entry '{archive_member}' not found in archive {archive_path}."
) from exc
else:
with tempfile.TemporaryDirectory() as tmp_dir_str:
tmp_dir = Path(tmp_dir_str)
archive.extractall(tmp_dir)
entries = [p for p in tmp_dir.iterdir() if not p.name.startswith(".__")]
if len(entries) != 1:
raise RuntimeError(
f"Expected a single top-level entry in {archive_path}, found {len(entries)}"
)
shutil.move(str(entries[0]), dest)
return
raise RuntimeError(f"Unsupported archive format '{archive_format}'.")