mirror of
https://github.com/openai/codex.git
synced 2026-09-20 12:47:38 +00:00
## Why Windows release packages need the voice helper and native audio libraries. Realtime TLS connections on fresh Windows installations also need platform certificate validation so Windows can retrieve missing trusted roots on demand. ## What changed - Build and sign the voice helper and audio DLLs for Windows x64 and ARM64, bundle a pinned Microsoft CRT DLL, and verify signatures and runtime receipts before packaging. - Add verified, pinned Cygwin and native build tools plus MSVC linker, compiler, and path handling fixes for the Windows Bazel builds. - Include voice resources in primary release archives and WinGet packages. Preserve WinGet executable names, update manifest hashes, and recognize the package root through matching entrypoint metadata. Keep Python runtime wheels voice-free to preserve their existing Windows support floor. - Use Windows platform TLS validation for realtime WebSockets when no custom CA bundle is configured, preserving custom CA behavior. ## Testing Add coverage for build-input integrity and unsafe paths, signed Windows runtime assembly, WinGet file and hash preservation, package discovery, and TLS trust selection, untrusted certificate rejection, and hostname validation. GitOrigin-RevId: 423da35872fa5549d69fd4ca97d922bb49599386
179 lines
6.8 KiB
Python
179 lines
6.8 KiB
Python
"""Add a helper and its prepared runtime to a fresh private Codex package."""
|
|
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
from pathlib import Path
|
|
import re
|
|
import shutil
|
|
import sys
|
|
|
|
# Import only this script's siblings, including under PYTHONSAFEPATH.
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
from package_runtime import runtime_files
|
|
from runtime import digest
|
|
|
|
|
|
def assemble(
|
|
package: Path,
|
|
helper: Path,
|
|
voice_target: str,
|
|
commit: str,
|
|
output: Path,
|
|
*,
|
|
runtime: Path,
|
|
release_version: str | None = None,
|
|
):
|
|
package, helper = package.resolve(strict=True), helper.resolve(strict=True)
|
|
output = output.absolute()
|
|
if (
|
|
output.exists()
|
|
or output.is_symlink()
|
|
or output.resolve().is_relative_to(package)
|
|
):
|
|
raise ValueError("output must be fresh and outside the input package")
|
|
if not re.fullmatch(r"[0-9a-f]{40}", commit):
|
|
raise ValueError(
|
|
"a full build commit is required; dev builds are not distributable"
|
|
)
|
|
metadata = json.loads((package / "codex-package.json").read_text())
|
|
app_target = metadata["target"]
|
|
targets = {
|
|
f"{arch}-{suffix}": f"{arch}-{suffix.replace('musl', 'gnu')}"
|
|
for arch in ("aarch64", "x86_64")
|
|
for suffix in (
|
|
"apple-darwin",
|
|
"unknown-linux-gnu",
|
|
"unknown-linux-musl",
|
|
"pc-windows-msvc",
|
|
)
|
|
}
|
|
if targets.get(app_target) != voice_target:
|
|
raise ValueError("incompatible app and helper targets")
|
|
suffix = ".exe" if app_target.endswith("windows-msvc") else ""
|
|
entrypoint = f"bin/codex{suffix}"
|
|
expected = {
|
|
"layoutVersion": 1,
|
|
"variant": "codex",
|
|
"entrypoint": entrypoint,
|
|
"resourcesDir": "codex-resources",
|
|
"pathDir": "codex-path",
|
|
}
|
|
if any(metadata.get(key) != value for key, value in expected.items()):
|
|
raise ValueError("input is not a canonical Codex package")
|
|
if release_version is None:
|
|
if not metadata["version"].endswith(f"+{commit}"):
|
|
raise ValueError("package version does not match the declared build")
|
|
elif (
|
|
not re.fullmatch(
|
|
r"[0-9]+\.[0-9]+\.[0-9]+(?:-alpha(?:\.[0-9]+){0,2}|-beta(?:\.[0-9]+)?)?",
|
|
release_version,
|
|
)
|
|
or metadata["version"] != release_version
|
|
):
|
|
raise ValueError("package version does not match the release")
|
|
if (package / "codex-resources/voice").exists():
|
|
raise ValueError("input already contains voice resources")
|
|
for path in package.rglob("*"):
|
|
if path.is_symlink() or not (path.is_file() or path.is_dir()):
|
|
raise ValueError("package inputs must be regular files or directories")
|
|
if not helper.is_file() or not (package / entrypoint).is_file():
|
|
raise ValueError("helper and app entrypoint must be regular files")
|
|
if not suffix and not helper.stat().st_mode & 0o111:
|
|
raise ValueError("helper is not executable")
|
|
runtime = runtime.resolve(strict=True)
|
|
if any(parent.samefile(package) for parent in (runtime, *runtime.parents)):
|
|
raise ValueError("runtime must be outside the input package")
|
|
if any(
|
|
parent.exists() and parent.samefile(runtime)
|
|
for parent in output.resolve().parents
|
|
):
|
|
raise ValueError("output must be outside the runtime input")
|
|
inputs = runtime_files(
|
|
runtime, voice_target, public_release=release_version is not None
|
|
)
|
|
if release_version is not None:
|
|
receipt = json.loads((runtime / "runtime.json").read_text(encoding="utf-8"))
|
|
if receipt["sourceCommit"] != commit:
|
|
raise ValueError("release runtime source does not match the app build")
|
|
output.mkdir() # Exclusive creation: never clean or overwrite a pre-existing output.
|
|
try:
|
|
shutil.copytree(package, output, dirs_exist_ok=True)
|
|
relative_helper = f"codex-resources/voice/bin/codex-voice-host{suffix}"
|
|
destination = output / relative_helper
|
|
destination.parent.mkdir(parents=True)
|
|
shutil.copy2(helper, destination)
|
|
for relative, expected_digest in inputs.items():
|
|
copied = output / "codex-resources/voice" / relative
|
|
copied.parent.mkdir(parents=True, exist_ok=True)
|
|
shutil.copy2(runtime / relative, copied)
|
|
if digest(copied) != expected_digest:
|
|
raise ValueError("runtime file changed during copying")
|
|
if release_version is not None:
|
|
source_dir = Path(__file__).resolve().parent
|
|
for relative in (
|
|
"NOTICE.md",
|
|
"sources.json",
|
|
*(("windows-crt.json",) if suffix else ()),
|
|
"licenses/LGPL-2.1.txt",
|
|
"licenses/Opus.txt",
|
|
"licenses/PCRE2.md",
|
|
"licenses/libffi.txt",
|
|
"licenses/proxy-libintl.txt",
|
|
"licenses/sljit.txt",
|
|
"licenses/zlib.txt",
|
|
):
|
|
destination_file = destination.parent.parent / relative
|
|
destination_file.parent.mkdir(parents=True, exist_ok=True)
|
|
shutil.copy2(source_dir / relative, destination_file)
|
|
inputs[relative] = digest(destination_file)
|
|
digests = {}
|
|
for relative in (entrypoint, relative_helper):
|
|
with (output / relative).open("rb") as source:
|
|
digests[relative] = hashlib.file_digest(source, "sha256").hexdigest()
|
|
digests.update(
|
|
{f"codex-resources/voice/{name}": value for name, value in inputs.items()}
|
|
)
|
|
manifest = {
|
|
"schemaVersion": 1,
|
|
"buildCommit": commit,
|
|
"appTarget": app_target,
|
|
"voiceTarget": voice_target,
|
|
"appVersion": metadata["version"],
|
|
"sha256": digests,
|
|
}
|
|
(destination.parent.parent / "manifest.json").write_text(
|
|
json.dumps(manifest, indent=2) + "\n", encoding="utf-8"
|
|
)
|
|
except BaseException:
|
|
shutil.rmtree(output)
|
|
raise
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--package", type=Path, required=True)
|
|
parser.add_argument("--helper", type=Path, required=True)
|
|
parser.add_argument("--voice-target", required=True)
|
|
parser.add_argument("--build-commit", required=True)
|
|
parser.add_argument(
|
|
"--release-version", help="exact version of a public release package"
|
|
)
|
|
parser.add_argument("--output", type=Path, required=True)
|
|
parser.add_argument(
|
|
"--runtime",
|
|
type=Path,
|
|
required=True,
|
|
help="prepared runtime required by the helper",
|
|
)
|
|
args = parser.parse_args()
|
|
assemble(
|
|
args.package,
|
|
args.helper,
|
|
args.voice_target,
|
|
args.build_commit,
|
|
args.output,
|
|
runtime=args.runtime,
|
|
release_version=args.release_version,
|
|
)
|