sdk: launch packaged Codex runtimes

## Why

The Python and TypeScript SDKs launch the native Codex runtime directly, so they need to understand the same package layout that the release artifacts now use. Without this, SDK runtime wheels would keep carrying only a loose `bin/codex` plus ad hoc helper binaries, the TypeScript SDK would keep resolving the legacy optional-dependency layout, and bundled `codex-path` tools such as `rg` would not be prepended when the SDK starts Codex.

## What Changed

- Stage `openai-codex-cli-bin` from a Codex package directory, including `codex-package.json`, `bin/`, `codex-resources/`, and `codex-path/`.
- Make `stage-runtime` package-dir-only; legacy loose-binary wrapping now happens in `_runtime_setup.py` before calling the staging script.
- Expose Python runtime helpers for the bundled package directory and `codex-path`, and prepend that path when `openai_codex` launches the installed runtime without duplicating Windows `Path`/`PATH` keys.
- Teach the TypeScript SDK to resolve package-layout optional dependencies while keeping the legacy layout fallback, and preserve the existing Windows path variable casing when prepending `codex-path`.
- Update the Rust release workflows to build Python runtime wheels from the generated Codex package directories instead of loose binaries.

## Test Plan

- `python3 -m py_compile sdk/python/scripts/update_sdk_artifacts.py sdk/python/_runtime_setup.py sdk/python/src/openai_codex/client.py sdk/python-runtime/src/codex_cli_bin/__init__.py`
- `uv run --frozen --project sdk/python --extra dev ruff check sdk/python/scripts/update_sdk_artifacts.py sdk/python/_runtime_setup.py sdk/python/src/openai_codex/client.py sdk/python/tests/test_artifact_workflow_and_binaries.py sdk/python-runtime/src/codex_cli_bin/__init__.py`
- `uv run --frozen --project sdk/python --extra dev pytest sdk/python/tests/test_artifact_workflow_and_binaries.py`
- `pnpm eslint src/exec.ts tests/exec.test.ts`
- `pnpm test --runInBand tests/exec.test.ts`
This commit is contained in:
Michael Bolin
2026-05-20 16:54:50 -07:00
parent 896ee672cc
commit 3e77859d0a
10 changed files with 479 additions and 167 deletions

View File

@@ -36,7 +36,12 @@ exclude = [
[tool.hatch.build.targets.wheel]
packages = ["src/codex_cli_bin"]
include = ["src/codex_cli_bin/bin/**"]
include = [
"src/codex_cli_bin/codex-package.json",
"src/codex_cli_bin/bin/**",
"src/codex_cli_bin/codex-resources/**",
"src/codex_cli_bin/codex-path/**",
]
[tool.hatch.build.targets.wheel.hooks.custom]

View File

@@ -1,14 +1,23 @@
from __future__ import annotations
import os
from pathlib import Path
PACKAGE_NAME = "openai-codex-cli-bin"
PACKAGE_METADATA_FILENAME = "codex-package.json"
def bundled_package_dir() -> Path:
path = Path(__file__).resolve().parent
metadata_path = path / PACKAGE_METADATA_FILENAME
if not metadata_path.is_file():
raise FileNotFoundError(
f"{PACKAGE_NAME} is installed but missing its package metadata at {metadata_path}"
)
return path
def bundled_codex_path() -> Path:
exe = "codex.exe" if os.name == "nt" else "codex"
path = Path(__file__).resolve().parent / "bin" / exe
path = bundled_package_dir() / "bin" / exe
if not path.is_file():
raise FileNotFoundError(
f"{PACKAGE_NAME} is installed but missing its packaged codex binary at {path}"
@@ -16,4 +25,14 @@ def bundled_codex_path() -> Path:
return path
__all__ = ["PACKAGE_NAME", "bundled_codex_path"]
def bundled_path_dir() -> Path | None:
path = bundled_package_dir() / "codex-path"
return path if path.is_dir() else None
__all__ = [
"PACKAGE_NAME",
"bundled_codex_path",
"bundled_package_dir",
"bundled_path_dir",
]