From 4d32f205bc106b89d68f0e83a87b4d68d2865e13 Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Fri, 22 May 2026 14:38:52 -0700 Subject: [PATCH] package: include zsh fork in Codex package Summary: - add a checked-in codex-zsh DotSlash manifest for supported Unix targets - fetch the matching zsh fork artifact during package builds and install it at codex-resources/zsh/bin/zsh when available - expose the bundled zsh fork through install-context and thread it through config loading as the lowest-precedence zsh_path default - use copyfile for package executable staging so local package smoke tests do not preserve platform-specific source metadata Test Plan: - just fmt - python3 -m py_compile scripts/codex_package/cli.py scripts/codex_package/layout.py scripts/codex_package/zsh.py - python3 -m unittest discover scripts/codex_package - cargo test -p codex-core zsh_path --- codex-rs/Cargo.lock | 3 ++ codex-rs/app-server/Cargo.toml | 1 + codex-rs/app-server/src/config_manager.rs | 20 ++++++++ codex-rs/cli/src/doctor.rs | 1 + codex-rs/cli/src/main.rs | 8 ++++ codex-rs/core/src/config/config_tests.rs | 50 ++++++++++++++++++++ codex-rs/core/src/config/mod.rs | 7 ++- codex-rs/exec/Cargo.toml | 1 + codex-rs/exec/src/lib.rs | 4 ++ codex-rs/install-context/src/lib.rs | 35 ++++++++++++++ codex-rs/mcp-server/Cargo.toml | 3 +- codex-rs/mcp-server/src/codex_tool_config.rs | 4 ++ codex-rs/tui/src/lib.rs | 7 +++ scripts/codex_package/README.md | 7 +++ scripts/codex_package/cli.py | 4 +- scripts/codex_package/codex-zsh | 43 +++++++++++++++++ scripts/codex_package/layout.py | 17 ++++++- scripts/codex_package/targets.py | 1 + scripts/codex_package/zsh.py | 22 +++++++++ 19 files changed, 234 insertions(+), 4 deletions(-) create mode 100755 scripts/codex_package/codex-zsh create mode 100644 scripts/codex_package/zsh.py diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index 38ce92e46b..f16d36e1d1 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -1905,6 +1905,7 @@ dependencies = [ "codex-git-utils", "codex-guardian", "codex-hooks", + "codex-install-context", "codex-login", "codex-mcp", "codex-memories-extension", @@ -2711,6 +2712,7 @@ dependencies = [ "codex-core", "codex-feedback", "codex-git-utils", + "codex-install-context", "codex-login", "codex-model-provider-info", "codex-otel", @@ -3143,6 +3145,7 @@ dependencies = [ "codex-core", "codex-exec-server", "codex-extension-api", + "codex-install-context", "codex-login", "codex-protocol", "codex-shell-command", diff --git a/codex-rs/app-server/Cargo.toml b/codex-rs/app-server/Cargo.toml index 95baac4e9e..a2f5611201 100644 --- a/codex-rs/app-server/Cargo.toml +++ b/codex-rs/app-server/Cargo.toml @@ -45,6 +45,7 @@ codex-guardian = { workspace = true } codex-git-utils = { workspace = true } codex-file-watcher = { workspace = true } codex-hooks = { workspace = true } +codex-install-context = { workspace = true } codex-otel = { workspace = true } codex-plugin = { workspace = true } codex-shell-command = { workspace = true } diff --git a/codex-rs/app-server/src/config_manager.rs b/codex-rs/app-server/src/config_manager.rs index 25fdc5c0cb..5dd71499b0 100644 --- a/codex-rs/app-server/src/config_manager.rs +++ b/codex-rs/app-server/src/config_manager.rs @@ -9,6 +9,7 @@ use codex_core::config::Config; use codex_core::config::ConfigOverrides; use codex_exec_server::LOCAL_FS; use codex_features::feature_for_key; +use codex_install_context::InstallContext; use codex_login::AuthManager; use codex_login::default_client::set_default_client_residency_requirement; use codex_utils_absolute_path::AbsolutePathBuf; @@ -159,6 +160,7 @@ impl ConfigManager { .await?; self.apply_runtime_feature_enablement(&mut config); self.apply_arg0_paths(&mut config); + apply_default_zsh_path(&mut config); Ok(config) } @@ -180,6 +182,7 @@ impl ConfigManager { } self.apply_runtime_feature_enablement(&mut config); self.apply_arg0_paths(&mut config); + apply_default_zsh_path(&mut config); Ok(config) } @@ -219,6 +222,11 @@ impl ConfigManager { typesafe_overrides: ConfigOverrides, fallback_cwd: Option, ) -> std::io::Result { + let mut typesafe_overrides = typesafe_overrides; + if typesafe_overrides.default_zsh_path.is_none() { + typesafe_overrides.default_zsh_path = default_zsh_path(); + } + let merged_cli_overrides = cli_overrides .iter() .cloned() @@ -319,6 +327,18 @@ impl ConfigManager { } } +fn apply_default_zsh_path(config: &mut Config) { + if config.zsh_path.is_none() { + config.zsh_path = default_zsh_path(); + } +} + +fn default_zsh_path() -> Option { + InstallContext::current() + .bundled_zsh_path() + .map(AbsolutePathBuf::into_path_buf) +} + pub(crate) fn protected_feature_keys(config_layer_stack: &ConfigLayerStack) -> BTreeSet { let mut protected_features = config_layer_stack .effective_config() diff --git a/codex-rs/cli/src/doctor.rs b/codex-rs/cli/src/doctor.rs index 6ce316803c..93655c2011 100644 --- a/codex-rs/cli/src/doctor.rs +++ b/codex-rs/cli/src/doctor.rs @@ -506,6 +506,7 @@ fn config_overrides_from_interactive( codex_self_exe: arg0_paths.codex_self_exe.clone(), codex_linux_sandbox_exe: arg0_paths.codex_linux_sandbox_exe.clone(), main_execve_wrapper_exe: arg0_paths.main_execve_wrapper_exe.clone(), + default_zsh_path: crate::default_zsh_path(), show_raw_agent_reasoning: interactive.oss.then_some(true), additional_writable_roots: interactive.add_dir.clone(), ..Default::default() diff --git a/codex-rs/cli/src/main.rs b/codex-rs/cli/src/main.rs index 4bb7ef74a0..c667d7067b 100644 --- a/codex-rs/cli/src/main.rs +++ b/codex-rs/cli/src/main.rs @@ -71,6 +71,7 @@ use codex_core::config::resolve_profile_v2_config_path; use codex_features::FEATURES; use codex_features::Stage; use codex_features::is_known_feature_key; +use codex_install_context::InstallContext; use codex_login::AuthManager; use codex_login::CodexAuth; use codex_login::read_codex_access_token_from_env; @@ -1557,6 +1558,12 @@ async fn load_exec_server_config( .await?) } +pub(crate) fn default_zsh_path() -> Option { + InstallContext::current() + .bundled_zsh_path() + .map(AbsolutePathBuf::into_path_buf) +} + async fn load_exec_server_remote_auth( config: &codex_core::config::Config, missing_auth_error: &'static str, @@ -1681,6 +1688,7 @@ async fn run_debug_prompt_input_command( codex_self_exe: arg0_paths.codex_self_exe, codex_linux_sandbox_exe: arg0_paths.codex_linux_sandbox_exe, main_execve_wrapper_exe: arg0_paths.main_execve_wrapper_exe, + default_zsh_path: default_zsh_path(), show_raw_agent_reasoning: shared.oss.then_some(true), ephemeral: Some(true), bypass_hook_trust: shared.bypass_hook_trust.then_some(true), diff --git a/codex-rs/core/src/config/config_tests.rs b/codex-rs/core/src/config/config_tests.rs index 09f17e201d..82d0ebf338 100644 --- a/codex-rs/core/src/config/config_tests.rs +++ b/codex-rs/core/src/config/config_tests.rs @@ -4470,6 +4470,56 @@ async fn add_dir_override_extends_workspace_writable_roots() -> std::io::Result< Ok(()) } +#[tokio::test] +async fn default_zsh_path_is_lowest_precedence() -> std::io::Result<()> { + let codex_home = TempDir::new()?; + let configured_zsh_path = codex_home.path().join("configured-zsh"); + let default_zsh_path = codex_home.path().join("packaged-zsh"); + let override_zsh_path = codex_home.path().join("override-zsh"); + + let config = Config::load_from_base_config_with_overrides( + ConfigToml::default(), + ConfigOverrides { + default_zsh_path: Some(default_zsh_path.clone()), + ..Default::default() + }, + codex_home.abs(), + ) + .await?; + assert_eq!(config.zsh_path, Some(default_zsh_path.clone())); + + let config = Config::load_from_base_config_with_overrides( + ConfigToml { + zsh_path: Some(configured_zsh_path.abs()), + ..Default::default() + }, + ConfigOverrides { + default_zsh_path: Some(default_zsh_path.clone()), + ..Default::default() + }, + codex_home.abs(), + ) + .await?; + assert_eq!(config.zsh_path, Some(configured_zsh_path.clone())); + + let config = Config::load_from_base_config_with_overrides( + ConfigToml { + zsh_path: Some(configured_zsh_path.abs()), + ..Default::default() + }, + ConfigOverrides { + zsh_path: Some(override_zsh_path.clone()), + default_zsh_path: Some(default_zsh_path), + ..Default::default() + }, + codex_home.abs(), + ) + .await?; + assert_eq!(config.zsh_path, Some(override_zsh_path)); + + Ok(()) +} + #[tokio::test] async fn sqlite_home_defaults_to_codex_home_for_workspace_write() -> std::io::Result<()> { let codex_home = TempDir::new()?; diff --git a/codex-rs/core/src/config/mod.rs b/codex-rs/core/src/config/mod.rs index 7044b9c676..46a172198f 100644 --- a/codex-rs/core/src/config/mod.rs +++ b/codex-rs/core/src/config/mod.rs @@ -1397,6 +1397,7 @@ impl Config { cfg, ConfigOverrides { cwd: Some(self.cwd.to_path_buf()), + default_zsh_path: refreshed_config.zsh_path.clone(), ..Default::default() }, refreshed_config.codex_home.clone(), @@ -2129,6 +2130,7 @@ pub struct ConfigOverrides { pub codex_linux_sandbox_exe: Option, pub main_execve_wrapper_exe: Option, pub zsh_path: Option, + pub default_zsh_path: Option, pub base_instructions: Option, pub developer_instructions: Option, pub personality: Option, @@ -2481,6 +2483,7 @@ impl Config { codex_linux_sandbox_exe, main_execve_wrapper_exe, zsh_path: zsh_path_override, + default_zsh_path, base_instructions, developer_instructions, personality, @@ -3199,7 +3202,9 @@ impl Config { ) .await?; let compact_prompt = compact_prompt.or(file_compact_prompt); - let zsh_path = zsh_path_override.or(cfg.zsh_path.map(Into::into)); + let zsh_path = zsh_path_override + .or(cfg.zsh_path.map(Into::into)) + .or(default_zsh_path); let review_model = override_review_model.or(cfg.review_model); diff --git a/codex-rs/exec/Cargo.toml b/codex-rs/exec/Cargo.toml index 37a577e2c8..68253754a3 100644 --- a/codex-rs/exec/Cargo.toml +++ b/codex-rs/exec/Cargo.toml @@ -32,6 +32,7 @@ codex-config = { workspace = true } codex-core = { workspace = true } codex-feedback = { workspace = true } codex-git-utils = { workspace = true } +codex-install-context = { workspace = true } codex-login = { workspace = true } codex-model-provider-info = { workspace = true } codex-otel = { workspace = true } diff --git a/codex-rs/exec/src/lib.rs b/codex-rs/exec/src/lib.rs index 52590b56cc..1ffde9b539 100644 --- a/codex-rs/exec/src/lib.rs +++ b/codex-rs/exec/src/lib.rs @@ -70,6 +70,7 @@ use codex_core::format_exec_policy_error_with_source; use codex_core::path_utils; use codex_feedback::CodexFeedback; use codex_git_utils::get_git_repo_root; +use codex_install_context::InstallContext; use codex_login::AuthConfig; use codex_login::default_client::set_default_client_residency_requirement; use codex_login::default_client::set_default_originator; @@ -417,6 +418,9 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result codex_linux_sandbox_exe: arg0_paths.codex_linux_sandbox_exe.clone(), main_execve_wrapper_exe: arg0_paths.main_execve_wrapper_exe.clone(), zsh_path: None, + default_zsh_path: InstallContext::current() + .bundled_zsh_path() + .map(AbsolutePathBuf::into_path_buf), base_instructions: None, developer_instructions: None, personality: None, diff --git a/codex-rs/install-context/src/lib.rs b/codex-rs/install-context/src/lib.rs index ec5c5217ca..d92a80cbef 100644 --- a/codex-rs/install-context/src/lib.rs +++ b/codex-rs/install-context/src/lib.rs @@ -11,6 +11,7 @@ const PATH_DIRNAME: &str = "codex-path"; const RELEASES_DIRNAME: &str = "releases"; const RESOURCES_DIRNAME: &str = "codex-resources"; const STANDALONE_PACKAGES_DIRNAME: &str = "standalone"; +const ZSH_DIRNAME: &str = "zsh"; static INSTALL_CONTEXT: OnceLock = OnceLock::new(); #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -166,6 +167,18 @@ impl InstallContext { None } + + pub fn bundled_zsh_path(&self) -> Option { + if cfg!(windows) { + None + } else { + self.bundled_resource(zsh_resource_path()) + } + } + + pub fn bundled_zsh_bin_dir(&self) -> Option { + self.bundled_zsh_path()?.parent() + } } impl CodexPackageLayout { @@ -260,6 +273,10 @@ fn default_rg_command() -> PathBuf { } } +fn zsh_resource_path() -> PathBuf { + PathBuf::from(ZSH_DIRNAME).join(BIN_DIRNAME).join("zsh") +} + #[cfg(test)] mod tests { use super::*; @@ -345,6 +362,11 @@ mod tests { fs::write(&exe_path, "")?; fs::write(resources_dir.join(TEST_RESOURCE_NAME), "")?; fs::write(path_dir.join(default_rg_command()), "")?; + if !cfg!(windows) { + let zsh_path = resources_dir.join(zsh_resource_path()); + fs::create_dir_all(zsh_path.parent().expect("zsh path should have parent"))?; + fs::write(&zsh_path, "")?; + } let canonical_package_dir = AbsolutePathBuf::from_absolute_path(package_dir.path().canonicalize()?)?; let canonical_bin_dir = AbsolutePathBuf::from_absolute_path(bin_dir.canonicalize()?)?; @@ -382,6 +404,19 @@ mod tests { context.bundled_resource(TEST_RESOURCE_NAME), Some(canonical_resources_dir.join(TEST_RESOURCE_NAME)) ); + if cfg!(windows) { + assert_eq!(context.bundled_zsh_path(), None); + assert_eq!(context.bundled_zsh_bin_dir(), None); + } else { + assert_eq!( + context.bundled_zsh_path(), + Some(canonical_resources_dir.join(zsh_resource_path())) + ); + assert_eq!( + context.bundled_zsh_bin_dir(), + Some(canonical_resources_dir.join(ZSH_DIRNAME).join(BIN_DIRNAME)) + ); + } Ok(()) } diff --git a/codex-rs/mcp-server/Cargo.toml b/codex-rs/mcp-server/Cargo.toml index 29b2d6c7d7..00a79ad020 100644 --- a/codex-rs/mcp-server/Cargo.toml +++ b/codex-rs/mcp-server/Cargo.toml @@ -23,8 +23,10 @@ codex-config = { workspace = true } codex-core = { workspace = true } codex-exec-server = { workspace = true } codex-extension-api = { workspace = true } +codex-install-context = { workspace = true } codex-login = { workspace = true } codex-protocol = { workspace = true } +codex-utils-absolute-path = { workspace = true } codex-utils-cli = { workspace = true } codex-utils-json-to-toml = { workspace = true } rmcp = { workspace = true } @@ -43,7 +45,6 @@ tracing = { workspace = true, features = ["log"] } tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] } [dev-dependencies] -codex-utils-absolute-path = { workspace = true } codex-shell-command = { workspace = true } core_test_support = { workspace = true } mcp_test_support = { workspace = true } diff --git a/codex-rs/mcp-server/src/codex_tool_config.rs b/codex-rs/mcp-server/src/codex_tool_config.rs index 9c9a3da53c..5e21963ec6 100644 --- a/codex-rs/mcp-server/src/codex_tool_config.rs +++ b/codex-rs/mcp-server/src/codex_tool_config.rs @@ -4,6 +4,7 @@ use codex_arg0::Arg0DispatchPaths; use codex_core::config::Config; use codex_core::config::ConfigBuilder; use codex_core::config::ConfigOverrides; +use codex_install_context::InstallContext; use codex_protocol::ThreadId; use codex_protocol::config_types::SandboxMode; use codex_protocol::protocol::AskForApproval; @@ -175,6 +176,9 @@ impl CodexToolCallParam { codex_self_exe: arg0_paths.codex_self_exe.clone(), codex_linux_sandbox_exe: arg0_paths.codex_linux_sandbox_exe.clone(), main_execve_wrapper_exe: arg0_paths.main_execve_wrapper_exe.clone(), + default_zsh_path: InstallContext::current() + .bundled_zsh_path() + .map(codex_utils_absolute_path::AbsolutePathBuf::into_path_buf), base_instructions, developer_instructions, compact_prompt, diff --git a/codex-rs/tui/src/lib.rs b/codex-rs/tui/src/lib.rs index 7348e3b452..0f9b34a781 100644 --- a/codex-rs/tui/src/lib.rs +++ b/codex-rs/tui/src/lib.rs @@ -1851,6 +1851,13 @@ async fn load_config_or_exit_with_fallback_cwd( strict_config: bool, fallback_cwd: Option, ) -> Config { + let mut overrides = overrides; + if overrides.default_zsh_path.is_none() { + overrides.default_zsh_path = codex_install_context::InstallContext::current() + .bundled_zsh_path() + .map(AbsolutePathBuf::into_path_buf); + } + #[allow(clippy::print_stderr)] match ConfigBuilder::default() .cli_overrides(cli_kv_overrides) diff --git a/scripts/codex_package/README.md b/scripts/codex_package/README.md index af070e2c44..323a3ce5ba 100644 --- a/scripts/codex_package/README.md +++ b/scripts/codex_package/README.md @@ -13,6 +13,7 @@ The builder creates a canonical Codex package directory: │ └── [.exe] ├── codex-resources │ ├── bwrap # Linux only +│ ├── zsh/bin/zsh # supported Unix targets only │ ├── codex-command-runner.exe # Windows only │ └── codex-windows-sandbox-setup.exe # Windows only └── codex-path @@ -67,3 +68,9 @@ DotSlash manifest at `scripts/codex_package/rg`. Downloaded archives are cached under `$TMPDIR/codex-package/-rg` and are reused only after the recorded size and SHA-256 digest have been verified. Pass `--rg-bin` to use a local ripgrep executable instead. + +The patched zsh fork used by `shell_zsh_fork` is fetched from the DotSlash +manifest at `scripts/codex_package/codex-zsh` when the selected target has a +matching prebuilt artifact. Downloaded archives are cached under +`$TMPDIR/codex-package/-zsh` and installed at +`codex-resources/zsh/bin/zsh`. diff --git a/scripts/codex_package/cli.py b/scripts/codex_package/cli.py index 9c0b16258d..0152002669 100644 --- a/scripts/codex_package/cli.py +++ b/scripts/codex_package/cli.py @@ -15,6 +15,7 @@ from .targets import TARGET_SPECS from .targets import PackageInputs from .targets import default_target from .targets import resolve_input_path +from .zsh import resolve_zsh_bin from .version import read_workspace_version @@ -161,13 +162,14 @@ def main() -> int: inputs = PackageInputs( entrypoint_bin=source_outputs.entrypoint_bin, rg_bin=resolve_rg_bin(spec, args.rg_bin), + zsh_bin=resolve_zsh_bin(spec), bwrap_bin=source_outputs.bwrap_bin, codex_command_runner_bin=source_outputs.codex_command_runner_bin, codex_windows_sandbox_setup_bin=source_outputs.codex_windows_sandbox_setup_bin, ) prepare_package_dir(package_dir, force=args.force) build_package_dir(package_dir, version, variant, spec, inputs) - validate_package_dir(package_dir, variant, spec) + validate_package_dir(package_dir, variant, spec, include_zsh=inputs.zsh_bin is not None) for archive_output in args.archive_output: archive_path = archive_output.resolve() diff --git a/scripts/codex_package/codex-zsh b/scripts/codex_package/codex-zsh new file mode 100755 index 0000000000..8876474d81 --- /dev/null +++ b/scripts/codex_package/codex-zsh @@ -0,0 +1,43 @@ +#!/usr/bin/env dotslash + +{ + "name": "codex-zsh", + "platforms": { + "macos-aarch64": { + "size": 358776, + "hash": "sha256", + "digest": "c6dbb063a0135b947ab1cacc655b2b750874699472f412ec7daba97543a90c3c", + "format": "tar.gz", + "path": "codex-zsh/bin/zsh", + "providers": [ + { + "url": "https://github.com/openai/codex/releases/download/rust-v0.132.0/codex-zsh-aarch64-apple-darwin.tar.gz" + } + ] + }, + "linux-x86_64": { + "size": 433413, + "hash": "sha256", + "digest": "5f42d9fc8e9c8c399a727512002906006ae9de966ea7b3d87ca36b47efc59938", + "format": "tar.gz", + "path": "codex-zsh/bin/zsh", + "providers": [ + { + "url": "https://github.com/openai/codex/releases/download/rust-v0.132.0/codex-zsh-x86_64-unknown-linux-musl.tar.gz" + } + ] + }, + "linux-aarch64": { + "size": 411653, + "hash": "sha256", + "digest": "6c6e32c297425db02b4dbffb10925895875d14647fc3eb2f18767be97dc6a945", + "format": "tar.gz", + "path": "codex-zsh/bin/zsh", + "providers": [ + { + "url": "https://github.com/openai/codex/releases/download/rust-v0.132.0/codex-zsh-aarch64-unknown-linux-musl.tar.gz" + } + ] + } + } +} diff --git a/scripts/codex_package/layout.py b/scripts/codex_package/layout.py index 6d8982a631..c763eb2604 100644 --- a/scripts/codex_package/layout.py +++ b/scripts/codex_package/layout.py @@ -8,6 +8,7 @@ from pathlib import Path from .targets import PackageInputs from .targets import PackageVariant from .targets import TargetSpec +from .zsh import ZSH_RESOURCE_PATH LAYOUT_VERSION = 1 @@ -50,6 +51,13 @@ def build_package_dir( ) copy_executable(inputs.rg_bin, path_dir / spec.rg_name, is_windows=spec.is_windows) + if inputs.zsh_bin is not None: + copy_executable( + inputs.zsh_bin, + resources_dir / ZSH_RESOURCE_PATH, + is_windows=False, + ) + if inputs.bwrap_bin is not None: copy_executable(inputs.bwrap_bin, resources_dir / "bwrap", is_windows=False) @@ -83,6 +91,8 @@ def validate_package_dir( package_dir: Path, variant: PackageVariant, spec: TargetSpec, + *, + include_zsh: bool, ) -> None: required_dirs = [ Path("bin"), @@ -122,6 +132,11 @@ def validate_package_dir( ] executable_files = list(required_files) + if include_zsh: + zsh_path = Path("codex-resources") / ZSH_RESOURCE_PATH + required_files.append(zsh_path) + executable_files.append(zsh_path) + if spec.is_linux: required_files.append(Path("codex-resources") / "bwrap") executable_files.append(Path("codex-resources") / "bwrap") @@ -148,7 +163,7 @@ def validate_package_dir( def copy_executable(src: Path, dest: Path, *, is_windows: bool) -> None: dest.parent.mkdir(parents=True, exist_ok=True) - shutil.copy2(src, dest) + shutil.copyfile(src, dest) if not is_windows: mode = dest.stat().st_mode dest.chmod(mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) diff --git a/scripts/codex_package/targets.py b/scripts/codex_package/targets.py index 4af0d4a00d..8307a3e630 100644 --- a/scripts/codex_package/targets.py +++ b/scripts/codex_package/targets.py @@ -40,6 +40,7 @@ class PackageVariant: class PackageInputs: entrypoint_bin: Path rg_bin: Path + zsh_bin: Path | None bwrap_bin: Path | None codex_command_runner_bin: Path | None codex_windows_sandbox_setup_bin: Path | None diff --git a/scripts/codex_package/zsh.py b/scripts/codex_package/zsh.py new file mode 100644 index 0000000000..f4ee7e19dd --- /dev/null +++ b/scripts/codex_package/zsh.py @@ -0,0 +1,22 @@ +"""Fetch the patched zsh fork used by shell_zsh_fork.""" + +from pathlib import Path + +from .dotslash import fetch_dotslash_executable +from .targets import REPO_ROOT +from .targets import TargetSpec + + +ZSH_MANIFEST = REPO_ROOT / "scripts" / "codex_package" / "codex-zsh" +ZSH_RESOURCE_PATH = Path("zsh") / "bin" / "zsh" + + +def resolve_zsh_bin(spec: TargetSpec) -> Path | None: + return fetch_dotslash_executable( + spec, + manifest_path=ZSH_MANIFEST, + artifact_label="codex-zsh", + cache_key=f"{spec.target}-zsh", + dest_name="zsh", + missing_ok=True, + )